如何使用PortAudio将交错缓冲区解析为不同的多通道缓冲区

时间:2015-02-20 20:01:09

标签: audio portaudio asio

希望你能帮助我:)。

我试图通过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 );

2 个答案:

答案 0 :(得分:1)

Interleaved只是意味着每个通道的字节跟在后面,如:

aabbccddeeaabbccddeeaabbccddee (each character represents one byte)

其中该输入缓冲器包含5个通道中的每一个的两个字节(16位):a,b,c,d&amp; 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变化但是上面假设样本是两个字节

答案 1 :(得分:1)

感谢Scott的帮助。解决方案正好在我眼前,我终于不必处理样本偏移。我没有给你足够的代码信息,所以你的方法非常好,但代码本身提供了一种更简单的方法:

数据存储在一个结构中:

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;
}

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