Objective-C - передача потоковых данных в аудио-очередь
В настоящее время я разрабатываю приложение для iOS, которое считывает аудиоданные IMA-ADPCM через TCP-сокет, преобразует их в PCM и затем воспроизводит поток. На этом этапе я закончил класс, который извлекает (или я должен сказать реагирует на нажатия) данные из потока и декодирует их в PCM. Я также настроил класс Audio Queue, чтобы он воспроизводил тестовый сигнал. Где мне нужна помощь, это лучший способ передать данные в аудио-очередь.
Аудиоданные поступают из декодера ADPCM в виде 16-битного LPCM с тактовой частотой 8 кГц и частотой 640 байт. (он создается как 160 байтов данных ADPCM, но распаковывается до 640). Он входит в функцию как массив uint_8t и передает объект NSData. Поток является потоком "push", поэтому каждый раз при отправке аудио он создает / очищает объект.
-(NSData*)convertADPCM:(uint8_t[]) adpcmdata {
Конечно, обратный вызов Audio Queue - это функция извлечения, которая ищет данные на каждом проходе цикла выполнения, на каждом проходе:
-(OSStatus) fillBuffer: (AudioQueueBufferRef) buffer {
Я работал над этим в течение нескольких дней, и преобразование PCM было довольно обременительным, и у меня возникли небольшие проблемы с сборкой в моей голове наилучшего способа соединить данные между ними. Это не так, как если бы я создавал данные, тогда я мог бы просто включить создание данных в подпрограмму fillbuffer, скорее, данные выталкиваются.
Я установил круговой буфер, равный 0,5 секунды, в uint16_t[] ~, но я думаю, что измотал свой мозг и не смог придумать аккуратный способ толкать и извлекать из буфера, так что в итоге я получил треск с треском,
Я завершил проект в основном на Android, но обнаружил, что AudioTrack - это совсем не то, что Core-Audio Queues.
На этом этапе я также скажу, что я взял копию Learning Core Audio от 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, я поместил его в журнал, и он заполняется и пополняется по кругу, поэтому я знаю, что по крайней мере этот бит работает.
1 ответ
Трудная часть в управлении ставками, и что делать, если они не совпадают. Сначала попробуйте использовать огромный круговой буфер (много-много секунд) и в основном заполнить его, прежде чем запускать аудио-очередь, чтобы извлечь из нее. Затем следите за уровнем буфера, чтобы увидеть его большую проблему соответствия скорости.