如何使用 PortAudio 将交错缓冲区解析为不同的多通道缓冲区
How to parse interleaved buffer into distinct multiple channel buffers with PortAudio
希望你能帮助我:)
我正在尝试使用 PortAudio 库从多声道 ASIO 设备获取音频数据。一切正常:我设法将默认主机 API 设置为 ASIO,并且我还设法 select 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 );
Interleaved 只是意味着每个通道的字节彼此跟随,如:
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 发生变化但以上假设样本是两个字节
感谢斯科特的帮助。解决方案就在我眼前,我终于不用再使用样本偏移了。我没有为您提供有关代码的足够信息,因此您的方法非常好,但代码本身提供了一种更简单的方法:
数据存储在一个结构中:
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;
}
希望这可以帮助其他人。再次感谢斯科特
希望你能帮助我:)
我正在尝试使用 PortAudio 库从多声道 ASIO 设备获取音频数据。一切正常:我设法将默认主机 API 设置为 ASIO,并且我还设法 select 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 );
Interleaved 只是意味着每个通道的字节彼此跟随,如:
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 发生变化但以上假设样本是两个字节
感谢斯科特的帮助。解决方案就在我眼前,我终于不用再使用样本偏移了。我没有为您提供有关代码的足够信息,因此您的方法非常好,但代码本身提供了一种更简单的方法:
数据存储在一个结构中:
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;
}
希望这可以帮助其他人。再次感谢斯科特