【问题标题】:Writing a wave generator with SDL使用 SDL 编写波发生器
【发布时间】:2020-06-10 21:50:39
【问题描述】:

我使用 SDL 1.2 和 SDL_mixer(播放 .wav 文件)用 C 语言编写了一个简单的音序器。它运行良好,我想在这个程序中添加一些音频合成。我查了一下,发现这个正弦波代码使用 SDL2(https://github.com/lundstroem/synth-samples-sdl2/blob/master/src/synth_samples_sdl2_2.c)

下面是正弦波在程序中的编码方式:

static void build_sine_table(int16_t *data, int wave_length)
 {
    /* 
        Build sine table to use as oscillator:
        Generate a 16bit signed integer sinewave table with 1024 samples.
        This table will be used to produce the notes.
        Different notes will be created by stepping through
        the table at different intervals (phase).
    */

    double phase_increment = (2.0f * pi) / (double)wave_length;
    double current_phase = 0;
    for(int i = 0; i < wave_length; i++) {
        int sample = (int)(sin(current_phase) * INT16_MAX);
        data[i] = (int16_t)sample;
        current_phase += phase_increment;
    }
}

static double get_pitch(double note) {

    /*
        Calculate pitch from note value.
        offset note by 57 halfnotes to get correct pitch from the range we have chosen for the notes.
    */
    double p = pow(chromatic_ratio, note - 57);
    p *= 440;
    return p;
}

static void audio_callback(void *unused, Uint8 *byte_stream, int byte_stream_length) {

    /*
        This function is called whenever the audio buffer needs to be filled to allow
        for a continuous stream of audio.
        Write samples to byteStream according to byteStreamLength.
        The audio buffer is interleaved, meaning that both left and right channels exist in the same
        buffer.
    */

    // zero the buffer
    memset(byte_stream, 0, byte_stream_length);

    if(quit) {
        return;
    }

    // cast buffer as 16bit signed int.
    Sint16 *s_byte_stream = (Sint16*)byte_stream;

    // buffer is interleaved, so get the length of 1 channel.
    int remain = byte_stream_length / 2;

    // split the rendering up in chunks to make it buffersize agnostic.
    long chunk_size = 64;
    int iterations = remain/chunk_size;
    for(long i = 0; i < iterations; i++) {
        long begin = i*chunk_size;
        long end = (i*chunk_size) + chunk_size;
        write_samples(s_byte_stream, begin, end, chunk_size);
    }
}

static void write_samples(int16_t *s_byteStream, long begin, long end, long length) {

    if(note > 0) {
        double d_sample_rate = sample_rate;
        double d_table_length = table_length;
        double d_note = note;

        /*
            get correct phase increment for note depending on sample rate and table length.
        */
        double phase_increment = (get_pitch(d_note) / d_sample_rate) * d_table_length;

        /*
            loop through the buffer and write samples.
        */
        for (int i = 0; i < length; i+=2) {
            phase_double += phase_increment;
            phase_int = (int)phase_double;
            if(phase_double >= table_length) {
                double diff = phase_double - table_length;
                phase_double = diff;
                phase_int = (int)diff;
            }

            if(phase_int < table_length && phase_int > -1) {
                if(s_byteStream != NULL) {
                    int16_t sample = sine_wave_table[phase_int];
                    sample *= 0.6; // scale volume.
                    s_byteStream[i+begin] = sample; // left channel
                    s_byteStream[i+begin+1] = sample; // right channel
                }
            }
        }
    }
}

我不明白如何更改正弦波公式以生成其他波形,如方波/三角波/锯齿波等...

编辑: 因为我忘记解释了,这就是我尝试过的。 我按照我在这个视频系列中看到的例子(https://www.youtube.com/watch?v=tgamhuQnOkM)。视频提供的方法源码在github上,生成波的代码是这样的:

double w(double dHertz)
{
    return dHertz * 2.0 * PI;
}

// General purpose oscillator


double osc(double dHertz, double dTime, int nType = OSC_SINE)
{
    switch (nType)
    {
    case OSC_SINE: // Sine wave bewteen -1 and +1
        return sin(w(dHertz) * dTime);

    case OSC_SQUARE: // Square wave between -1 and +1
        return sin(w(dHertz) * dTime) > 0 ? 1.0 : -1.0;

    case OSC_TRIANGLE: // Triangle wave between -1 and +1
        return asin(sin(w(dHertz) * dTime)) * (2.0 / PI);
}

因为这里的 C++ 代码使用 windows soun api,所以我无法复制/粘贴此方法以使其适用于我使用 SDL2 找到的代码。 所以我尝试这样做以获得方波:

static void build_sine_table(int16_t *data, int wave_length)
 {
    double phase_increment = ((2.0f * pi) / (double)wave_length) > 0 ? 1.0 : -1.0;
    double current_phase = 0;
    for(int i = 0; i < wave_length; i++) {
        int sample = (int)(sin(current_phase) * INT16_MAX);
        data[i] = (int16_t)sample;
        current_phase += phase_increment;
    }
}

这并没有给我一个方波,而是一个锯齿波。 这是我试图获得三角波的方法:

static void build_sine_table(int16_t *data, int wave_length)
 {
    double phase_increment = (2.0f * pi) / (double)wave_length;
    double current_phase = 0;
    for(int i = 0; i < wave_length; i++) {
        int sample = (int)(asin(sin(current_phase) * INT16_MAX)) * (2 / pi);
        data[i] = (int16_t)sample;
        current_phase += phase_increment;
    }
}

这也给了我另一种波形,不是三角形。

【问题讨论】:

  • 用正方形/三角形而不是正弦波填充表格

标签: c linux sdl


【解决方案1】:

您可以将 sin 函数调用替换为对以下之一的调用:

// this is a helper function only
double normalize(double phase)
{
  double cycles = phase/(2.0*M_PI);
  phase -= trunc(cycles) * 2.0 * M_PI;
  if (phase < 0) phase += 2.0*M_PI;
  return phase;
}

double square(double phase)
{ return (normalize(phase) < M_PI) ? 1.0 : -1.0; }

double sawtooth(double phase)
{ return -1.0 + normalize(phase) / M_PI; }

double triangle(double phase)
{
  phase = normalize(phase);
  if (phase >= M_PI)
    phase = 2*M_PI - phase;
  return -1.0 + 2.0 * phase / M_PI;
}

您将像构建正弦表一样构建表格,只是它们分别是方形、锯齿形和三角形表格。

【讨论】:

  • 非常感谢,合成运行良好!
猜你喜欢
  • 2014-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-24
相关资源
最近更新 更多