在 PortAudio 中输出

Output in PortAudio

我正在尝试学习 PortAudio,我正在学习 doc/src/tutorials 中的教程,这是 writing_a_callback.dox[ 中的代码=25=]:

typedef struct
{
    float left_phase;
    float right_phase;
}   
paTestData;

/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/ 
static int patestCallback( const void *inputBuffer, void *outputBuffer,
                           unsigned long framesPerBuffer,
                           const PaStreamCallbackTimeInfo* timeInfo,
                           PaStreamCallbackFlags statusFlags,
                           void *userData )
{
    /* Cast data passed through stream to our structure. */
    paTestData *data = (paTestData*)userData; 
    float *out = (float*)outputBuffer;
    unsigned int i;
    (void) inputBuffer; /* Prevent unused variable warning. */
    
    for( i=0; i<framesPerBuffer; i++ )
    {
        *out++ = data->left_phase;  /* left */
        *out++ = data->right_phase;  /* right */

        /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
        data->left_phase += 0.01f;
        /* When signal reaches top, drop back down. */
        if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;

        /* higher pitch so we can distinguish left and right. */
        data->right_phase += 0.03f;
        if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;
    }
    return 0;
}

它工作正常,但我有几个问题:

  1. 我有一个带有 2 个 float 变量的 struct,但是 PortAudio 如何知道哪个是左扬声器,哪个是右扬声器
  2. 在这个例子中,我传递给输出缓冲区的是什么、频率、音量或其他东西,以及我如何告诉 PortAudio 其他的信息,例如,如果我传递频率,那么我该如何告诉它调整音量

可能我遗漏了什么但是...我不知道


任何帮助的尝试表示赞赏

让我为您分解一下:

该结构定义了用于控制生成信号的“状态”。 left_phase 用于存储左声道的下一个样本值。 right_phase 存储右通道的下一个值。

typedef struct
{
    float left_phase;
    float right_phase;
}   
paTestData;

这是注册到PortAudio的回调函数。 PortAudio 将数据从输入流(麦克风)传递到 inputBuffer 供用户处理。用户将数据传递给 outputBuffer,然后发送到声卡的输出流(扬声器)。 userData 是注册回调时传递的一些值或结构。在这种情况下,它是 paTestData.

的一个实例
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/ 
static int patestCallback( const void *inputBuffer, void *outputBuffer,
                           unsigned long framesPerBuffer,
                           const PaStreamCallbackTimeInfo* timeInfo,
                           PaStreamCallbackFlags statusFlags,
                           void *userData )
{
    /* Cast data passed through stream to our structure. */
    paTestData *data = (paTestData*)userData; 
    float *out = (float*)outputBuffer;
    unsigned int i;
    (void) inputBuffer; /* Prevent unused variable warning. */

这个 for 循环是生成信号的地方,一次一帧。 framesPerBuffer(部分由用户在注册回调时确定,部分由 OS 决定)控制生成的样本数。循环的每次迭代都会生成两个音频数据样本。对于立体声流,样本是交错的。第一个样本进入左声道。第二个样本进入右通道。

    for( i=0; i<framesPerBuffer; i++ )
    {
        *out++ = data->left_phase;  /* left */
        *out++ = data->right_phase;  /* right */

        /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
        data->left_phase += 0.01f;
        /* When signal reaches top, drop back down. */
        if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;

        /* higher pitch so we can distinguish left and right. */
        data->right_phase += 0.03f;
        if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;
    }
    return 0;
}

for 循环中的这段代码控制频率和振幅(音量)。每个样本代表该时间片的信号幅度(由采样率确定)。在这种情况下,频率由值 0.01f0.03f 确定。振幅由左声道 if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f; 和右声道 if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f; 决定。

data->left_phase += 0.01f;
/* When signal reaches top, drop back down. */
if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;

/* higher pitch so we can distinguish left and right. */
data->right_phase += 0.03f;
if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;

正如代码注释中的描述,for 循环生成一种称为 sawtooth wave. The frequency is determined by the distance between the "teeth." If there are 1000 teeth per second of signal, a 1000 Hz tone is generated.The amplitude is determined by how tall the teeth are. If the peak-to-peak distance (bottom to top) is -1 to 1, that is an amplitude of 1 (100% for floating point samples). That is also 0 db, or unity gain. You can calculate db from the signal amplitude using equations defined in this article. (See Acoustics.) A good exercise would be to generate a different waveform, like a square wave, triangle wave, and sine wave 的周期函数。他们每个人都会产生具有独特特征的球场。

在代码中,左相位的幅度在每个样本中增加 0.01,在 200 个样本中从 -1 增加到 1。频率由该波形与音频流的采样率之间的关系确定。如果采样率为 48 kHz,则每个样本代表 1/48000 秒长的时间片。在这种情况下,信号频率为 240 Hz (48000 / 200)

为了更好地理解这一切,您可以将此算法放入电子表格中并绘制点,或者甚至更好地放入 python 中并使用绘图库来可视化波浪。你也可以在纸上算出来。 x 值是时间,y 值是振幅。我还建议您继续阅读 pulse-code modulation (PCM)inputBufferoutputBuffer 是 PCM 数据的缓冲区(数组)。它也是 WAV 文件中使用的编码。