【问题标题】:How to parse interleaved buffer into distinct multiple channel buffers with PortAudio如何使用 PortAudio 将交错缓冲区解析为不同的多通道缓冲区
【发布时间】:2015-04-22 14:53:13
【问题描述】:

希望你能帮助我:)

我正在尝试使用 PortAudio 库从多通道 ASIO 设备获取音频数据。一切正常:我设法将默认主机 API 设置为 ASIO,并且我还设法选择了 4 个特定通道作为输入。然后,我得到一个听起来正确的交错音频流,但我想分别获取每个通道数据。

PortAudio 允许进行非交错录制,但我不知道如何编写或修改我的 RecordCallBack 和多缓冲区指针(每个通道一个缓冲区)。当然我已经试过了...... :(

如果有人知道如何处理这个问题,那将对我有很大帮助。

原始的 RecordCallBack 函数取自一个众所周知的立体声示例(稍作修改以管理 4 个通道而不是 2 个),但它管理单个交错缓冲区:

static int recordCallback( const void *inputBuffer, void *outputBuffer,
                       unsigned long framesPerBuffer,
                       const PaStreamCallbackTimeInfo* timeInfo,
                       PaStreamCallbackFlags statusFlags,
                       void *userData )
{
paTestData *data = (paTestData*)userData;
const short *rptr = (const short*)inputBuffer;
short *wptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS_I];
long framesToCalc;
long i;
int finished;
unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;

(void) outputBuffer; /* Prevent unused variable warnings. */
(void) timeInfo;
(void) statusFlags;
(void) userData;

if( framesLeft < framesPerBuffer )
{
    framesToCalc = framesLeft;
    finished = paComplete;
}
else
{
    framesToCalc = framesPerBuffer;
    finished = paContinue;
}

if( inputBuffer == NULL )
{
    for( i=0; i<framesToCalc; i++ )
    {
        *wptr++ = SAMPLE_SILENCE;  /* ch1*/
        if( NUM_CHANNELS_I == 4 ){
            *wptr++ = SAMPLE_SILENCE;/* ch2*/
            *wptr++ = SAMPLE_SILENCE;/* ch3*/
            *wptr++ = SAMPLE_SILENCE;}  /* ch4*/
    }
}
else
{
    for( i=0; i<framesToCalc; i++ )
    {
        *wptr++ = *rptr++;  /* ch1*/
        if( NUM_CHANNELS_I == 4 ){ 
            *wptr++ = *rptr++;/* ch2*/
            *wptr++ = *rptr++;/* ch3*/
            *wptr++ = *rptr++;}  /* ch4*/
    }
}
data->frameIndex += framesToCalc;

return finished;
}

*inputbuffer 指针声明为:

PaStream* stream;

并且调用了 Open_Stream 函数:

err = Pa_OpenStream(
          &stream,
          NULL, /* no input */
          &outputParameters,
          SAMPLE_RATE,
          FRAMES_PER_BUFFER,
          paClipOff,      /* we won't output out of range samples so don't bother clipping them */
          playCallback,
          &data );

【问题讨论】:

    标签: audio portaudio asio


    【解决方案1】:

    交错只是意味着每个通道的字节一个接一个,如下所示:

    aabbccddeeaabbccddeeaabbccddee (each character represents one byte)
    

    此输入缓冲区在 5 个通道中的每个通道中包含两个字节(16 位):a、b、c、d 和 e,因为它在一组通道中重复 3 次,相当于每个通道 3 个样本...所以知道输入是交错的,它可以被提取到每个通道一个单独的输出通道缓冲区中,但是在您的代码中,您只有一个输出缓冲区,正如您所说,这是由于必要的回调签名......一种方法是编写每个输出通道到单个输出缓冲区中,每个通道由不同的偏移量分隔,因此输出将是

    aaaaaabbbbbbccccccddddddeeeeee 
    

    然后在回调外部提取每个通道,每个通道也使用相同的偏移量

    首先,您需要获取给定输出缓冲区的大小,例如 X、通道数 Y,以及每个样本每个通道的字节数 Z。因此,全局通道偏移量为

    size_offset = X / (Y * Z) # assure this is an integer 
                              # if its a fraction then error in assumptions
    

    所以当在回调内部和外部寻址输出缓冲区时,我们使用这个偏移量以及我们在哪个通道上的知识、W(值 0、1、2、3,...),以及哪个样本 K:

    index_output_buffer = K + (W * size_offset)     # 1st byte of sample pair
    

    现在使用 index_output_buffer ...然后计算后续索引:

    index_output_buffer = K + (W * size_offset) + 1 # 2nd byte of sample pair
    

    并使用它...您可以将给定样本的上述两个命令放入一个循环中,如果 Z 要改变,则使用 Z 来控制迭代次数,但以上假设样本是两个字节

    【讨论】:

    • 嗨,斯科特,感谢您的回答。如果我说对了,由于我的样本长度为 16 位(短),因此交错 4 个通道意味着一个缓冲区:samplech1samplech2samplech3samplech4samplech1samplech2samplech3samplech4...???我的第一种方法是这样处理输出缓冲区,并按如下方式获取每个通道(在循环中): datach2(i)=data(i+1);datach3(i)=data(i+2);datach4(i) =数据(i+3)。其中“数据”包含整个输出缓冲区流。但它没有用......也许我错过了一些东西......
    • 我看到您在通道之间使用的偏移量仅为 1,并且您对每个通道使用单独的缓冲区 - 我建议了一种上述方法,该方法在回调内部和外部都可以工作......祝你好运
    • 非常感谢 Scott,非常感谢您的帮助。我会尽快按照你的建议去做,然后我会告诉你我成功与否。再次感谢
    • 虽然这是有用的信息,而且在技术上没有错误,但它并不能真正回答问题。 PortAudio 文档特别提到检索非交错数据是可能的:portaudio.com/docs/v19-doxydocs-dev/… 遍历并手动对缓冲区进行去交错会增加音频回调的大量额外开销,因此没有必要。
    【解决方案2】:

    感谢斯科特的帮助。解决方案就在我眼前,我终于不必处理样本偏移了。我没有为您提供有关代码的足够信息,因此您的方法非常好,但代码本身提供了一种更简单的方法:

    数据存储在一个结构中:

    typedef struct
    {
    int          frameIndex;  /* Index into sample array. */
    int          maxFrameIndex;
    short       *recordedSamples;
    }
    paTestData;
    

    我修改为:

    typedef struct
    {
    int          frameIndex;  /* Index into sample array. */
    int          maxFrameIndex;
    short       *recordedSamples;
    short       * recordedSamples2; //ch2
    short       * recordedSamples3; //ch3
    short       *recordedSamples4; //ch4
    }
    paTestData;
    

    然后我只需要在内存中分配这个变量并修改recordCallback函数如下:

    static int recordCallback( const void *inputBuffer, void *outputBuffer,
                           unsigned long framesPerBuffer,
                           const PaStreamCallbackTimeInfo* timeInfo,
                           PaStreamCallbackFlags statusFlags,
                           void *userData )
     {
     paTestData *data = (paTestData*)userData;
     const short *rptr = (const short*)inputBuffer;
    
    short *wptr = &data->recordedSamples[data->frameIndex];
    short *wptr2=&data->recordedSamples2[data->frameIndex];
    short *wptr3=&data->recordedSamples3[data->frameIndex];
    short *wptr4=&data->recordedSamples4[data->frameIndex];
    long framesToCalc;
    long i;
    int finished;
    unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
    
    (void) outputBuffer; /* Prevent unused variable warnings. */
    (void) timeInfo;
    (void) statusFlags;
    (void) userData;
    
    if( framesLeft < framesPerBuffer )
    {
        framesToCalc = framesLeft;
        finished = paComplete;
    }
    else
    {
        framesToCalc = framesPerBuffer;
        finished = paContinue;
    }
    
    if( inputBuffer == NULL )
    {
        for( i=0; i<framesToCalc; i++ )
        {
            *wptr++ = SAMPLE_SILENCE;  //ch1
            if( NUM_CHANNELS_I == 4 ){
                *wptr2++ = SAMPLE_SILENCE;//ch2
                *wptr3 ++= SAMPLE_SILENCE;//ch3
                *wptr4++ = SAMPLE_SILENCE;}  //ch4
        }
    }
    else
    {
        for( i=0; i<framesToCalc; i++ )
        {
            *wptr++ = *rptr++;  //ch1
            if( NUM_CHANNELS_I == 4 ){ 
                *wptr2++ = *rptr++;//ch2
                *wptr3++ = *rptr++;//ch3
                *wptr4 ++= *rptr++;}  //ch4
        }
    }
    data->frameIndex += framesToCalc;
    
    return finished;
    }
    

    希望这可以帮助其他人。再次感谢斯科特

    【讨论】:

      猜你喜欢
      • 2012-04-12
      • 2020-11-27
      • 2012-05-27
      • 2021-01-14
      • 1970-01-01
      • 1970-01-01
      • 2020-09-11
      • 2019-03-22
      • 2011-01-11
      相关资源
      最近更新 更多