我目前正在iOS上开发一个应用程序,它通过TCP套接字读取IMA-ADPCM音频数据并将其转换为PCM然后播放流。在这个阶段,我已经完成了从流中提取数据(或者我应该说是对推动作出反应)并将其解码为PCM的类。我还设置了音频队列类并让它播放测试音。我需要帮助的地方是将数据传递到音频队列的最佳方式。
音频数据来自ADPCM解码器,为8 Khz 16bit LPCM,每块640字节。 (它起源为160字节的ADPCM数据,但解压缩到640)。它作为uint_8t数组进入函数并传递出一个NSData对象。流是一个“推送”流,因此每次发送音频时,它都会创建/刷新对象。
-(NSData*)convertADPCM:(uint8_t[]) adpcmdata {
音频队列回调当然是一个拉函数,它会在运行循环的每次传递中查找数据,在它运行的每个传递中:
-(OSStatus) fillBuffer: (AudioQueueBufferRef) buffer {
我已经在这方面工作了几天,PCM转换非常费力,而且我在脑海中汇集两者之间桥接数据的最佳方法有点麻烦。这不像我在创建数据,然后我可以简单地将数据创建合并到fillbuffer例程中,而不是推送数据。
我确实设置了一个0.5秒的循环缓冲区,在uint16_t []〜但是我觉得我已经把我的大脑磨掉了,并且无法通过一个简洁的方式从缓冲区中推出和拔出,所以我最终得到了snap snaple pop。
我主要在Android上完成了这个项目,但发现AudioTrack与Core-Audio Queues完全不同。
在这个阶段,我还会说我收集了Adamson和Avila的学习核心音频副本,发现这对于那些希望揭开核心音频神秘面纱的人来说是一个很好的资源。
更新 这是缓冲区管理代码:
-(OSStatus) fillBuffer: (AudioQueueBufferRef) buffer {
int frame = 0;
double frameCount = bufferSize / self.streamFormat.mBytesPerFrame;
// buffersize = framecount = 8000 / 2 = 4000
//
// incoming buffer uint16_t[] convAudio holds 64400 bytes (big I know - 100 x 644 bytes)
// playedHead is set by the function to say where in the buffer the
// next starting point should be
if (playHead > 99) {
playHead = 0;
}
// Playstep factors playhead to get starting position
int playStep = playHead * 644;
// filling the buffer
for (frame = 0; frame < frameCount; ++frame)
// framecount = 4000
{
// pointer to buffer
SInt16 *data = (SInt16*)buffer->mAudioData;
// load data from uint16_t[] convAudio array into frame
(data)[frame] = convAudio[(frame + playStep)];
}
// set buffersize
buffer->mAudioDataByteSize = bufferSize;
// return no Error - Osstatus will return an error otherwise if there is one. (I think)
return noErr;
}
正如我所说,当我写这篇文章时,我的大脑模糊不清,而且我可能会发现一些明显缺失的东西。
以上代码由回调调用:
static void MyAQOutputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inCompleteAQBuffer)
{
soundHandler *sHandler = (__bridge soundHandler*)inUserData;
CheckError([sHandler fillBuffer: inCompleteAQBuffer],
"can't refill buffer",
"buffer refilled");
CheckError(AudioQueueEnqueueBuffer(inAQ,
inCompleteAQBuffer,
0,
NULL),
"Couldn't enqueue buffer (refill)",
"buffer enqued (refill)");
}
在convAudio数组方面,我把它转储到日志中,并且它以循环方式填充和重新填充,所以我知道至少那个位有效。
答案 0 :(得分:0)
管理费率的难点,以及如果不匹配将该怎么办。首先,尝试使用一个巨大的循环缓冲区(许多秒),然后在启动音频队列之前将其填充,然后再将其拉出。然后监控缓冲水平,看看他有一个很大的速率匹配问题。