Главная » iPhone » Звуковые буферы iPhone

0

Звуковой буфер (sound buffer) содержит данные, которые находятся по пути к выводящему устройству. Возвратимся к нашей концепции ящика на ленточном конвейере: буфер — это ящик, переносящий ваш звук к динамикам. Если у вас нет достаточного количества звука для заполнения ящика, то он доставляется к динамикам не до конца заполненным, что может привести к интервалам в передаче аудио. Чем имеется у вас больше ящиков, тем больше звука вы можете заранее поместить в очередь, чтобы избежать интервалов (или замедлений). Обратной стороной всего этого является то, что для звука на конце динамика перехват звука, поставляемого приложением, занимает больше времени. Это может стать проблемой, если символ в вашей игре прыгает, но пользователь не слышит этого до тех пор, пока он не приземлится.

Когда звук готов к началу, создаются звуковые буферы и заполняются первыми фреймами звукового вывода вашего приложения. Минимальное количество необходимых буферов для начала воспроизведения на настольных системах Apple — всего лишь один, но на iPhone — это три. В приложениях, которые могут привести к интенсивному использованию процессора, более подходящим является использование большего количества буферов для предотвращения перегрузок. Чтобы подготовить буферы с первыми фреймами звуковых данных, заполняется каждый буфер в том порядке, в котором они создавались.

Это означает то, что к моменту заполнения буферов вам лучше уже иметь некоторое количество звука для их заполнения:

#define AUDIO_BUFFERS 3

unsigned long bufferSize;

bufferSize = aqc.frameCount * aqc.mDataFormat.mBytesPerFrame; for (i=0; i<AUDIO_BUFFERS; i++) { AudioQueueAllocateBuffer(aqc.queue,

bufferSize, &aqc.mBuffers[i]); AQBufferCallback (&aqc, aqc.queue, aqc.mBuffers[i]);

}

При выполнении этого кода аудиобуферы заполняются первыми фреймами звуковых данных из вашего приложения. Теперь очередь готова к активации, а это включает ленточный конвейер, отправляющий звуковые буферы к динамикам. Как только это произойдет, буферы освобождаются от своего содержимого (нет, память не обнуляется), а ящики возвращаются по ленточному конвейеру на повторное заполнение: AudioQueueStart(aqc.queue, NULL);

Далее, когда вы готовы отключить звуковую очередь, просто воспользуйтесь функцией AudioQueueDispose, и все остановится:

AudioQueueDispose(aqc.queue, true);

Функция обратного вызова

Теперь аудиоочередь запущена, и каждую I/60-ую часть секунды приложение запрашивается на заполнение данными нового звукового буфера. До сих пор еще не было объяснено, как это происходит. После опустошения буфера он готов к повторному заполнению, аудиоочередь вызывает функцию обратного вызова, заданную вами в качестве второго аргумента в AudioQueueNewOutput. Данная функция обратного вызова— это место, где приложение проделывает свою работу; она заполняет ящик, который переносит ваш выводимый звук к динамикам. Вы должны вызвать ее до начала очереди, чтобы заполнить звуковые буферы некоторым количеством начального звука. Затем очередь вызывает функцию каждый раз, когда буфер необходимо заполнить. При вызове вы заполните буфер аудиоочереди, передаваемый путем копирования последнего звукового фрейма из вашего приложения — в нашем примере 735 дорожек:

static void AQBufferCallback( void *aqc, AudioQueueRef inQ, AudioQueueBufferRef outQB)

{

Структура обратного вызова, которую вы создали в самом начале, aqc, передается как определяемый пользователем аргумент, вместе с указателями на саму аудиоочередь и аудиобуфер для заполнения:

AQCallbackStruct *inData = (AQCallbackStruct *)aqc;

Поскольку структура AQCallbackStruct считается пользовательскими данными, то она передается в функцию обратного вызова в виде указателя на объект неизвестного типа (void pointer), и должна быть приведена к структуре AQCallbackStruct (здесь называемой inData) до того, как к ней может быть получен доступ. Данный код захватывает указатель на необработанные аудиоданные внутри буфера, в результате чего приложение может писать в него свой звук:

short *CoreAudioBuffer = (short *) outOB->mAudioData;

Переменная CoreAudioBuffer представляет пространство внутри звукового буфера, в который будут копироваться необработанные дорожки вашего приложения во время каждой синхронизации. Вашему приложению потребуется поддерживать тип "пишущая игла", чтобы отслеживать то, какой звук уже был отправлен в аудиоочередь: if (inData->frameCount >0) {

Переменная frameCount — это количество фреймов, которое ожидает увидеть буфер. Она должна равняться переменной f rameCount, которая передавалась в структуру AQCallbackStruct (в нашем примере — 735):

outQB->mAudioDataByteSize = 4 * inData->frameCount;

Это то место, где вы точно сообщаете буферу, сколько данных он собирается принять: своего рода упаковочная ведомость для ящика. Общий размер выходного буфера должен равняться размеру обоих стереоканалов (два байта на канал = четыре байта), умноженному на количество отправленных фреймов (735):

for(i = 0 ; i < inData->frameCount * 2; i += 2) {

CoreAudioBuffer[i] = (LEFT CHANNEL DATA); CoreAudioBuffer[i+1] = (RIGHT CHANNEL DATA);

}

Здесь функция обратного вызова пошагово проходит каждый выходной фрейм в буфере и копирует данные из того, что станет выводимым звуком вашего приложения в coreAudioBuffer. Поскольку левый и правый каналы перемежаются, то циклу придется считаться с этим, пропуская в приращении по 2:

AudioQueueEnqueueBuffer(inQ, outQB, О, NULL); } /* if (inData->frameCount > 0) */ } /* AQBufferCallback */

Наконец, после того как фрейм будет скопирован в звуковой буфер, он помещается обратно в очередь воспроизведения.

Источник: Здзиарски Дж. iPhone. Разработка приложений с открытым кодом: Пер„с англ. — 2-е изд., перераб. и доп. — СПб.: БХВ-Петербург, 2009. — 368 е.: ил.

По теме:

  • Комментарии