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