我正在使用带有ALSA的Debian 9和PortAudio来创建和应用程序,该应用程序可以将任意音频数据发送到24通道MOTU 24Ao设备。设备为USB 2.0。我的应用程序成功打开一个流以将数据发送到设备,并使用回调(PaStreamCallback
)通过memcpy
操作将数据提供给输出缓冲区。只要应用程序继续运行,流就保持打开状态。每当准备好发送数据时,回调就会将数据提供给设备,如果没有要播放的数据,则回调会静音。
该应用程序可以正常运行,但仅需要一定时间(约5分钟)。此后,数据开始以“转移和损坏的方式”播放。例如,我尝试在通道1中播放一个正弦波,并在其他23个通道中播放一个零正弦波,持续时间为400毫秒。每当我按键盘上的一个键时,就会播放该音乐。刚开始时,所有操作都可以与回调一起正常工作,并且当我按下该键时,数据会在相应的通道上播放。 5分钟后,当我按键时,通道1和通道9开始播放数据。两个通道上的数据都不是正确的正弦波,而是信号的错误重构。当我按住启动键时,这种情况持续约1分钟。在这段时间之后,正弦波开始正常播放,但现在在第9通道开始播放,而在其他23个通道上保持静音。在其他时间段后,此行为将重复出现,但现在涉及通道9和17,最终使通道17播放正弦波。
这种情况反复发生,并且看起来输出缓冲区以循环的方式映射我的数据,即通道1映射到9,过一会儿,它映射到17,然后又回到1。 8个通道的偏移量(1 + 8 = 9、9 + 8 = 17和17 + 8 = 1)。当我尝试在Windows中实现相同的代码时,这种奇怪的变化永远不会发生。
我曾经尝试过切换MOTU设备,甚至尝试过干净安装Debian,而没有PortAudio。
我播放的信号始终存储在一个浮动缓冲区中,该缓冲区具有从左到右,从上到下的线性数组形式的矩阵状结构。每个矩阵有24列(每个通道1个)和一定数量的行,该行数由每个信号的持续时间以及用于创建它们的采样频率(所有信号为44.1 kHz)确定。每个矩阵都包含在Symbol
对象中,该对象可以:确定要消耗的矩阵剩余行数(Symbol::remainingRows()
),获取表示矩阵中哪些行的当前索引保留在此索引(Symbol::getMatrixRowIndex()
之后使用,从某个行索引(Symbol::samplesFromRow()
)开始获取指向矩阵中所有样本的指针,并增加表示以下行的行索引尚未消耗(Symbol::increaseIndexBy(int increase)
)。通过这种结构,以下是我的PaCallback:
int MotuPlayer::paCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData)
{
float *out = (float*)outputBuffer;
MotuPlayer *player = (MotuPlayer*)userData;
Symbol* symbol = player->getCurrentPlayingSymbol();
unsigned long i;
(void) timeInfo; /* Prevent unused variable warnings. */
(void) statusFlags;
(void) inputBuffer;
if(symbol != 0)
{
unsigned long rowsRemaining = symbol->remainingRows();
if( rowsRemaining <= framesPerBuffer)
{
//Copy the remaining samples into the buffer
memcpy(out, symbol->samplesFromRow((int)symbol->getMatrixRowIndex()), sizeof(float)*rowsRemaining*24);
//Fill the rest of the buffer with zeros
out += 24*rowsRemaining;
memcpy(out, player->getZeros(), sizeof(float)*24*(framesPerBuffer-rowsRemaining));
player->signalSymbolCallback(TapsNoError);
player->currentPlayingSymbol = 0;
}
else
{
//Fill all the frames of the buffer with data from matrix and increase the index by the frames consumed
memcpy(out, symbol->samplesFromRow((int)symbol->getMatrixRowIndex()), sizeof(float)*framesPerBuffer*24);
symbol->increaseIndexBy(framesPerBuffer);
}
}
else
{
memcpy(out, player->getZeros(), sizeof(float)*24*framesPerBuffer);
}
return paContinue;
由于相同的代码在Windows中没有表现出这种现象,因此我无法确定问题的原因。我想知道PortAudio是否使用某种环形缓冲区将我的数据播放到硬件中,是否可能是问题所在。在我看来,输出缓冲区指针的行为是问题的根源,但实际上并不知道有什么解决方案。