Главная » iPhone » Запись звука iPhone

0

Запись звука происходит совершенно аналогичным образом, как и его воспроизведение: однако создаваемая очередь задается как очередь записи, храня в себе ввод, а не вывод. Звук может быть записан во множестве различных форматов, включая Apple Lossless. РСМ и др. Приводимый в этом разделе пример проводит четкую параллель с нашим предыдущим примером аудио- очерерди, но с некоторыми изменениями. Мы прокомментируем их в самом примере. При записи звука ленточный конвейер аудиоочереди вращается в обратную сторону. Микрофон iPhone делает всю работу по заполнению ящиков звуком и отправки их от микрофона к вашему приложению. Вы остаетесь ответственными за информирование платформы о том. какой формат и частоту дорожки вы бы хотели, но теперь вы отвечаете не за заполнение ящиков, а за их опустошение и запись на диск. Для записи непосредственно в файл, а не копирования его в память, вы будете использовать функции аудиофайлов из Audio Toolbox. Очередь записи строго придерживается принципа "первым прибыл — первым обслужен" (first in, first out), т. е. ленточный конвейер воспроизводит дорожки именно в том порядке, в каком они были записаны.

Очередь записи Audio Toolbox работает следующим образом:

1.    Создается аудиоочередь и задаются свойства, определяющие тип звука, который будет записываться (формат, частота дорожки и т. д.).

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

3.     Разработчик предоставляет "входную" функцию обратного вызова, которая вызывает аудиоочередь каждый раз, когда звуковой буфер заполняется записанным аудио. Эта функция обратного вызова отвечает за запись записанных фреймов на диск, а затем отправку ящика по кругу для следующего заполнения.

Структура аудиоочереди

Как мы уже объясняли ранее, платформа Audio Toolbox использует низкоуровневые интерфейсы С, поэтому в ней нет понятия класса. Структура обратного вызова изначально создается для того, чтобы содержать все переменные. которые будут перемещаться по вашему записывающему приложению. Структура AQCallbackStruct, приведенная далее, похожа на свою версию обратного, но с несколькими добавлениями: typedef struct AQCallbackStruct {

AudioStreamBasicDescription mDataFormat; AudioQueueRef queue;

AudioQueueBufferRef mBuffers[AUDIO_3UFFERS]; AudioFilelD outputFile; unsigned long frameSize; long long recPtr; int run; } AQCallbackStruct;

Следующие компоненты сгруппированы в данную структуру для обслуживания аудиоплатформы:

?        AudioQueueRef queue — указатель на объект аудиоочереди. который создаст ваша программа;

?        AudioFilelD outputFile— указатель на выходной файл, который будет записан после того, как запишется звук;

?         unsigned long frameSize — общее количество дорожек для копирования за одну аудиосинхронизацию. Это полностью отдается на откуп реализующему;

?         long long recPtr — числовой указатель на текущую позицию записывающей "иголки" на основе того, что приложение уже обработало. Он увеличивается по мере увеличения записываемых данных;

?         int run — значение этой переменной определяет необходимость повторной постановки в очередь звуковых буферов, т. е. надо или нет отправлять ящики назад для повторного заполнения. Когда придет время завершить запись, это значение должно быть установлено вашим приложением в нулевое значение;

?        AudioQueueBufferRef mBuffers— массив, содержащий общее количество звуковых буферов, которые будут использоваться. Корректное количество элементов будет обсуждаться в разд. "Звуковые буферы" далее в этой главе;

?          AudioStreamBasicDescription mDataFormat — информация О формате аудио, которое будет записываться.

Прежде чем создать очередь записи, необходимо инициализировать экземпляры всех этих переменных:

AQCallbackStruct aqc;

aqc.mDataFormat.mFormatID = kAudioFormatLinearPCM;

aqc.mDataFormat.mSampleRate = 44100.0;

aqc.mDataFormat.mChannelsPerFrame = 2;

aqc.mDataFormat.mBitsPerChannel = 16;

aqc.mDataFormat.mBytesPerPacket =

aqc.mDataFormat.mBytesPerFrame =

aqc.mDataFormat.mChannelsPerFrame * sizeof (short int); aqc.mDataFormat.mFramesPerPacket = 1;

aqc.mDataFormat.mFormatFlags = kLinearPCMFormatFlaglsBigEndian

I kLinearPCMFormatFlaglsSignedlnteger I kLinearPCMFormatFlaglsPacked;

aqc.frameSize = 735;

В данном примере мы подготовили структуру для 16-битного стереозвука (два канала) с частотой дорожки 44 кГц (44 100). Выходная дорожка будет представлена в виде двух двухбайтных коротких (short) целых чисел, следовательно, по четыре байта на фрейм (по два байта на левый и правый каналы).

Частота дорожки и размер фрейма предписывают то, как часто ваше приложение будет получать звук. При частоте 44 100 дорожек в секунду приложение можно заставить синхронизировать звук каждую 1/60-ую часть секунды, задав размер фрейма в 735 дорожек (44100/60 = 735). Это очень высокая интенсивность синхронизации для приложений, обрабатывающих звук в реальном времени, поэтому если вам не нужна такая частая синхронизация, то вы можете выбрать больший размер фрейма, например, 22 050, который будет синхронизировать каждые полсекунды.

В данном примере будет использоваться формат РСМ (необработанные данные), но в iPhone поддерживаются и другие форматы:

?           kAudioFormatLinearPCM;

?           kAudioFormatApplelMA4;

?    kAudioFormatMPEG4AAC;

?    kAudioFormatULaw;

?    kAudioFormatALaw;

?    kAudioFormatMPEGLayer3;

?    kAudioFormatAppleLossless;

?    kAudioFormatAMR.

Подготовка аудиовывода

После того как свойства аудиоочереди будут определены, можно подготовить объект новой аудиоочереди. За подготовку входного (записывающего)

канала и прикрепление его к очереди отвечает функция AudioQueueNewinput.

Прототип функции выглядит следующим образом:

AudioQueueNewOutput(

const AudioStreamBasicDes,cription *inFormat, AudioQueueOutputCallback inCallbackProc, void * inUserData, CFRunLoopRef inCallbackRunLoop, CFStringRef inCallbackRunLoopMode, UInt32 inFlags, AudioQueueRef * outAQ);

Здесь:

?    inFormat — указатель на структуру, описывающую аудиоформат, который будет воспроизводиться. Мы определили эту структуру ранее как члена типа данных AudioStreamBasicDescription в рамках нашей структуры AQCallbackStruct;

?    inCallbackProc— имя функции обратного вызова, которая будет вызываться, когда буфер аудиоочереди будет заполнен записанными данными;

?    inUserData — указатель на данные, которые разработчик при желании может передавать в функцию обратного вызова. Он будет содержать указатель на экземпляр определяемой пользователем структуры AQCallbackStruct, которая должна содержать информацию как об аудиоочереди, так и любую другую относящуюся к приложению информацию о записываемых дорожках;

?   inCallbackRunLoopMode— сообщает аудиоочереди о том, какое должно быть зацикливание аудио. Если указан null, то функция обратного вызова вызывается каждый раз, когда опустошается звуковой буфер. Для запуска обратного вызова при других условиях существуют дополнительные модели;

?   inFlags — не используется, зарезервировано;

?   outAO— когда функция AudioQueueNewOutput возвращает, данный указатель устанавливается на только что созданную аудиоочередь. Присутствие данного аргумента позволяет коду ошибки использоваться в качестве возвращаемого значения данной функции.

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

AudioQueueNewInput(&aqc.mDataFormat, AQInputCallback, &aqc, NULL,

kCFRunLoopCcmmonModes, 0,

&aqc.queue);

В данном примере имя функции обратного вызова определено как AQBuf ferCallback. Эта функция будет создана в нескольких следующих разделах. Это функция, которая будет отвечать за прием записываемого звука и копирование его на диск.

Звуковые буферы

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

for (i=0; i<AUDIOBUFFERS; i++) {

AudioQueueAllocateBuffer (aqc.queue, aqc.frameSize, Saqc.mBuffers[i]); AudioQueueEnqueueBuffer (aqc.queue, aqc.mBuffers[i], 0, NULL);

}

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

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

AudioQueueStop(aqc.queue, true); AudioQueueDispose(aqc.queue, true);

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

Теперь аудиоочередь запущена и каждую 1/60-ую часть секунды приложению предоставляется звуковой буфер, содержащий записанный фрейм. До сих пор еще не было объяснено, как это происходит. После того как буфер будет заполнен и готов к опустошению, аудиоочередь вызывает функцию обратного вызова, заданную вами в качестве второго аргумента в AudioQueueNewinput. Данная функция обратного вызова— это место, где приложение проделывает свою работу; она опустошает ящик, который переносит ваше записанное аудио, записывает содержимое на диск или сохраняет его как-либо иначе. Очередь вызывает функцию каждый раз, когда буфер необходимо опустошить. При вызове вы записываете буфер аудиоочереди — в нашем примере 735 дорожек: static void AQInputCallback ( void *aqr, AudioQueueRef inQ, AudioQueueBufferRef inQB, Audio Toolbox | 137 const AudioTimeStamp *timestamp, unsigned long frameSize,

const AudioStreamPacketDescription *mDataFormat)

{

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

AQCallbackStruct *aqc = (AQCallbackStruct *)aqr;

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

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

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

int recNeedle = 0;

myBuffer = malloc(aqc.frameSize * nSamples);

static void AQInputCallback ( void *aqr, AudioQueueRef inQ, AudioQueueBufferRef inQB, const AudioTimeStamp *timestamp, unsigned long frameSize,

const AudioStreamPacketDescription *mDataFormat)

$

AQCallbackStruct *aqc = (AQCallbackStruct *) aqr;

short *CoreAudioBuffer = (short *) inQB->mAudioData; memcpy(myBuffer + recNeedle, CoreAudioBuffer,

aqc.mDataFormat.mBytesPerFrame * aqc.frameSize); recNeedle += aqc.frameSize; if (!aqc->run) return;

AudioQueueEnqueueBuffer (aqc->queue, inQB, 0, NULL);

}

Чтобы писать в файл, вы будете использовать набор функций AudioFile из Audio Toolbox. Чтобы подготовить аудиофайл, сначала вам потребуется определить формат файла. Приведенный далее код определяет свойство, необходимое для аудиофайла AIFF:

AudioFileTypelD fileFormat = kAudioFileAIFFType;

Для хранения действительного пути к аудиофайлу вы будете использовать структуру cfurl:

CFURLRef filename = CFURLCreateFromFileSystemRepresentation(NULL,

(const unsigned char *) path_to_file, strlen (path_to_file), false);

Наконец, создается сам аудиофайл с помощью вызова функции AudioFileCreateWithURL, содержащей имя файла и только что созданных свойств формата. Указатель на файл записывается в структуру AQCallbackStruct, так что теперь мы знаем, как получить доступ к данному файлу, когда нам понадобится писать в него звук:

AudioFileCreateWithURL(filename/ fileFormat, &aqc.mDataFormat,

kAudioFileFlags_EraseFile, saqc.mAudioFile);

По мере записи новых аудиодорожек вы будете писать в этот файл с помощью функции AudioFileWritePackets, которая является еще одной функцией, встроенной в Audio Toolbox специально для записи аудиопакетов в файл. В приведенном далее коде будет показан пример ее использования.

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

По теме:

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