我目前正在 iOS 上开发一个应用程序,它通过 TCP 套接字读取 IMA-ADPCM 音频数据并将其转换为 PCM,然后播放流。在这个阶段,我已经完成了从流中提取(或者我应该说是对推送做出反应)数据并将其解码为 PCM 的类。我还设置了音频队列类并让它播放测试音。我需要帮助的地方是将数据传递到音频队列的最佳方式。
音频数据以 8 Khz 16 位 LPCM 格式从 ADPCM 解码器输出,每块 640 字节。(它起源于 160 字节的 ADPCM 数据,但解压缩为 640)。它作为 uint_8t 数组进入函数并传递一个 NSData 对象。该流是“推送”流,因此每次发送音频时,它都会创建/刷新对象。
-(NSData*)convertADPCM:(uint8_t[]) adpcmdata {
Audio Queue 回调当然是一个拉取函数,它在运行循环的每一次传递中寻找数据,在它运行的每一次传递中:
-(OSStatus) fillBuffer: (AudioQueueBufferRef) buffer {
我已经为此工作了几天,PCM 转换非常费力,我在脑海中组装了连接两者之间数据的最佳方式时遇到了一些麻烦。这不像我在创建数据,然后我可以简单地将数据创建合并到 fillbuffer 例程中,而不是推送数据。
我确实在 uint16_t[] 中设置了一个 0.5 秒的循环缓冲区〜但我认为我已经耗尽了我的大脑,无法找到一种从缓冲区推拉的简洁方法,所以我最终得到了啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪声.
我主要在 Android 上完成了该项目,但发现 AudioTrack 与 Core-Audio Queues 完全不同。
在这个阶段,我还要说我拿起了 Adamson 和 Avila 的 Learning Core Audio 的副本,发现这对于任何想要揭开核心音频神秘的人来说都是一个极好的资源。
更新: 这是缓冲区管理代码:
-(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 数组方面,我已经将它转储到日志中,并且它正在以循环方式填充和重新填充,所以我知道至少那一点是有效的。