Техническое примечание TN2097

Игра звукового файла с помощью Выходного Аудиоустройства По умолчанию

Этот документ обсуждает, как играть небольшой аудиофайл, не используя классы C++ Предприятия коммунального обслуживания, доступные в качестве части «демонстрационной загрузки» Классов Утилиты Core Audio. Цель этого документа состоит в том, чтобы представить программиста некоторым существенным понятиям Core Audio с помощью Выходного Аудиоустройства По умолчанию. Это покрывает использование Аудиоустройств, Аудио Преобразователей и некоторых структур данных Core Audio, используемых для содержания аудиоданных.

Игра звукового файла через Core Audio требует изучения нескольких фундаментальных понятий. Одно из самых важных понятий для понимания является ролью AudioUnit в Core Audio.

AudioUnits являются единственным блоком обработки, что любой - источник аудиоданных (например, синтезатор программного обеспечения), место назначения аудиоданных (например, AudioUnit, обертывающий аудиоустройство), или и источник и место назначения, например модуль DSP, такой как реверберация, берущая аудиоданные и обрабатывающая или преобразовывающая эти данные. Для получения дополнительной информации об Аудиоустройствах см. Аудио и MIDI на Mac OS X.

Этот пример будет использовать AudioUnit и AudioConverter для преобразования аудиоданных из формата файла, таким образом, это сможет играться и управляться Core Audio.

Установка Вывода По умолчанию AudioUnit
Получение информации от Аудиофайла
Установка Аудио Преобразователя
Рендеринг аудиоданных
Заключение
История версии документа

Установка Вывода По умолчанию AudioUnit

AudioUnits используются для множества целей; для генерации обработайте, получите, или иначе управляйте потоками аудио. Они могут или быть источником данных (например, синтезатор программного обеспечения), место назначения аудиоданных (например, AudioUnit служит интерфейсом к Аудиоустройству), или оба. Они - стандартные блоки, которые могут использоваться отдельно или соединяться вместе для формирования графика звукового сигнала. AudioUnit является также компонентом, который является частью кода, обеспечивающего определенный набор служб одному или более клиентам. Предопределенный AudioUnit в платформе AudioUnit является Выводом По умолчанию AudioUnit, который является интерфейсом к устройству, выбранному пользователем в звуковой предпочтительной панели. Этот AudioUnit может легко быть создан при помощи a ComponentDescription.

Перечисление 1  , создающее DefaultOutputAudioUnit

 //An AudioUnit is an OS component.
    //A component description must be setup, then used to
    //initialize an AudioUnit
 
    ComponentDescription desc;
    Component comp;
 
    //There are several Different types of AudioUnits.
    //Some audio units serve as Outputs, Mixers, or DSP
    //units. See AUComponent.h for listing
 
    desc.componentType = kAudioUnitType_Output;
 
    //Every Component has a subType, which will give a clearer picture
    //of what this components function will be.
 
    desc.componentSubType = kAudioUnitSubType_DefaultOutput;
 
    //All AudioUnits in AUComponent.h must use
    //"kAudioUnitManufacturer_Apple" as the Manufacturer
 
    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
    desc.componentFlags = 0;
    desc.componentFlagsMask = 0;
 
    //Finds a component that meets the desc spec's
    comp = FindNextComponent(NULL, &desc);
    if (comp == NULL) exit (-1);
 
    //gains access to the services provided by the component
    err = OpenAComponent(comp, theOutputUnit);

Поскольку AudioUnits используются для множества обработки аудиоданных на аудиопотоках, у них есть много внутренних свойств. Свойства AudioUnit могут легко быть изменены при помощи AudioUnitGetProperty и AudioUnitSetProperty вызовы. AudioUnitGetPropertyInfo может использоваться для определения размера свойства и может ли быть изменено свойство. AudioUnitGetPropertyInfo может использоваться до AudioUnitGetProperty и AudioUnitSetProperty вызовы для предотвращения ошибки.

Одно из самых важных свойств AudioUnit является своим потоковым форматом. Потоковые форматы описывают природу потока аудиоданных. Они обеспечивают информацию о форматировании на потоке данных как частоты дискретизации, типы информации пакета данных и типы кодировки.

Потоковые форматы сохранены в структурах под названием AudioStreamBasicDescriptions (ASBD), использующиеся широко всюду по Core Audio. Поскольку AudioUnits может быть просмотрен как наличие двух концов, ввода и вывода, потоковый формат ввода и вывода должен быть установлен перед использованием AudioUnit. Получить потоковый формат текущей производительности, выбранный пользователем, вызовом к AudioUnitGetProperty() с параметрами kAudioUnitScope_Output и kAudioUnitProperty_StreamFormat возвратит текущий ASBD.

Перечисление 2  Используя AudioUnit Добирается и подпрограммы Набора

 //AudioUnit *theUnit - points to the current AudioUnit
 //AudioStreamBasicDescription *theDesc - current ASBD for user output
 
/***Getting the size of a Property***/
UInt32 size;
 
 
//Gets the size of the Stream Format Property and if it is writable
OSStatus result = AudioUnitGetPropertyInfo(*theUnit,
                            kAudioUnitProperty_StreamFormat,
                            kAudioUnitScope_Output,
                            0,
                            &size,
                            &outWritable);
 
//Get the current stream format of the output
result = AudioUnitGetProperty (*theUnit,
                            kAudioUnitProperty_StreamFormat,
                            kAudioUnitScope_Output,
                            0,
                            theDesc,
                            &size);
 
//Set the stream format of the output to match the input
result = AudioUnitSetProperty (*theUnit,
                            kAudioUnitProperty_StreamFormat,
                            kAudioUnitScope_Input,
                            theInputBus,
                            theDesc,
                            size);

Инициализируйте AudioUnit с AudioUnitInitialize после того, как потоковое свойство формата было установлено. Инициализация AudioUnit может быть дорогой работой, поскольку она может включить сбор активов (например, банк звуков для MusicDevice), выделение буферов памяти, требуемых для обработки, включенной в модуле, и т.д. Как только модуль инициализируется, это находится в основном в состоянии, в котором это, как могут в основном ожидать, выполнит работу.

Мы должны также указать, где этот AudioUnit получит свои входные данные из путем установки его обратного вызова рендеринга. Это может быть сделано путем установки kAudioUnitProperty_SetRenderCallback свойство с a AURenderCallbackStruct.

Перечисление 3  , устанавливающее обратный вызов рендеринга для AudioUnit

OSStatus SetupCallbacks(AudioUnit *theOutputUnit,
                                    AURenderCallbackStruct *renderCallback)
{
    OSStatus err= noErr;
    memset(renderCallback, 0, sizeof(AURenderCallbackStruct));
 
 
    //inputProc takes a name of a method that will be used as the
    //input procedure when rendering data to the AudioUnit.
    //The input procedure will be called only when the Audio Converter needs
    //more data to process.
 
 
    //Set "fileRenderProc" as the name of the input proc
    renderCallback->inputProc = MyFileRenderProc;
    //Can pass ref Con with callback, but this isnt needed in out example
    renderCallback->inputProcRefCon =0;
 
    //Sets the callback for the AudioUnit to the renderCallback
 
    err = AudioUnitSetProperty (*theOutputUnit,
                                kAudioUnitProperty_SetRenderCallback,
                                kAudioUnitScope_Input,
                                0,
                                renderCallback,
                                sizeof(AURenderCallbackStruct));
    //Note: Some old V1 examples may use
    //"kAudioUnitProperty_SetInputCallback" which existed in
    //the old API, instead of "kAudioUnitProperty_SetRenderCallback".
    //"kAudioUnitProperty_SetRenderCallback" should
    //be used from now on.
 
 
    return err;
}

Получение информации от Аудиофайла

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

Перечисление 4  , Получающее информацию от аудиофайла

UInt64 gTotalPacketCount=0;
UInt64 gFileByteCount =0;
UInt32 gMaxPacketSize =0;
 
...
 
OSStatus GetFileInfo(FSRef *fileRef,
                               AudioFileID *fileID,
                               AudioStreamBasicDescription *fileASBD,
                               const char *fileName)
{
    OSStatus err= noErr;
    UInt32 size;
 
 
    //Obtain filesystem reference to the file using the file path
    FSPathMakeRef ((const UInt8 *)fileName, fileRef, 0);
   //Open an AudioFile and obtain AudioFileID using the file system ref
    err = AudioFileOpen(fileRef, fsRdPerm,0,fileID);
 
    size = sizeof(AudioStreamBasicDescription);
    memset(fileASBD, 0, size);
 
    //Fetch the AudioStreamBasicDescription of the audio file. we can
    //skip calling AudioFileGetPropertyInfo because we already know the
    //size of a ASBD
    err = AudioFileGetProperty(*fileID,
                                               kAudioFilePropertyDataFormat,
                                               &size,
                                               fileASBD);
    if(err)
        return err;
 
 
    //We need to get the total packet count, byte count, and max packet size
    //Theses values will be used later when grabbing data from the
    //audio file in the input callback procedure.
    size = sizeof(gTotalPacketCount); //type is UInt64
    err = AudioFileGetProperty(*fileID,
                           kAudioFilePropertyAudioDataPacketCount,
                                              &size,
                                              &gTotalPacketCount);
    if(err)
        return err;
 
    size = sizeof(gFileByteCount); //type is UInt64
    err = AudioFileGetProperty(*fileID,
                                   kAudioFilePropertyAudioDataByteCount,
                                              &size,
                                              &gFileByteCount);
   if(err)
        return err;
 
    size = sizeof(gMaxPacketSize); //type is UInt32
    err = AudioFileGetProperty(*fileID,
                                      kAudioFilePropertyMaximumPacketSize,
                                      &size,
                                      &gMaxPacketSize);
   if(err)
        return err;
 
    return err;
}

Установка Аудио Преобразователя

Аудио Преобразователи должны использоваться при декодировании или кодировании аудиоданных. AudioConverters может обработать изменяющиеся частоты дискретизации и целое число <=> преобразования плавающие. Очень общее использование AudioConverter к преобразованию аудиоданных, полученных от звукового файла до данных PCM, таким образом, это может играться. Аудио Преобразователь обеспечивает простой способ декодирования данных от одного формата до другого. AudioConverter требует аудиокодека, исходного потокового формата и целевого потокового формата для создания.

Перечисление 5  , Создающее новый Аудио экземпляр Преобразователя

AudioStreamBasicDescription *source_AudioStreamBasicDescription;
AudioStreamBasicDescription *destination_AudioStreamBasicDescription;
AudioConverterRef *converter;
 
...
 
AudioConverterNew(source_AudioStreamBasicDescription,
                               destination_AudioStreamBasicDescription ,
                               converter);

Рендеринг аудиоданных

Использовать AudioFileReadPackets считывать данные из аудиофайла. В нашем примере мы читаем аудиоданные из очень небольшого аудиофайла; поэтому, мы можем это непосредственно в память прежде, чем преобразовать данные. Когда большие файлы читаются, не было бы разумно считать весь файл в память, данные должны быть поточными в AudioConverter когда требуется больше данных. В целом чтение и обработка аудиоданных должны быть сделаны в отдельных потоках. AudioFilePlayer в классах C++ Предприятия коммунального обслуживания, включенных в Core Audio, SDK задерживает чтение аудиоданных к другому потоку (См. Пример кода: PlayAudioFile). Чтение от аудиофайла в потоке I/O может блокировать, поэтому должен быть реализован в другом потоке.

Перечисление 6  , Читающее из аудиофайла в память

//Reads the entire audio file into memory. No Conversions are done here.
OSStatus ReadFileIntoMem()
{
    OSStatus err = noErr;
 
    //total bytes read from audio file
    UInt32  bytesReturned = 0;
 
    //total amount of packets in audio file
    UInt32 packets =gTotalPacketCount;
 
    //alloc a buffer of memory to hold the data read from disk.
    gEntireFileBuffer = malloc(gFileByteCount);
    memset(gEntireFileBuffer, 0, gFileByteCount);
 
    //Read in the ENTIRE file into a memory buffer
    err = AudioFileReadPackets (*gSourceAudioFileID,
                                false,
                                &bytesReturned,
                                NULL,
                                0,
                                &packets,
                                gEntireFileBuffer);
 
 
    return err;
}

Для фактического начала преобразования данных, полученных из Аудиофайла, AudioUnit должен быть запущен путем вызова AudioOutputUnitStart. AudioUnit тогда «вытянет» данные от того, везде, где его ввод установлен в. В настоящее время в нашем примере, мы только назвали входную процедуру для AudioUnit (MyFileRenderProc). Мы еще не создали входную процедуру для AudioUnit. Во вводе proc, мы хотим получить преобразованные аудиоданные из аудиофайла. Вызов AudioConverterFillComplexBuffer в рендеринге обратный вызов возвратит преобразованные данные в AudioUnit. Поскольку этот обратный вызов рендеринга является управляемым требованием и вызовет метод, который Вы обеспечили каждый раз, когда AudioUnit нужно больше данных. Данные будут возвращены в AudioBufferList, который тогда может использоваться для обработки. Однако AudioConverterFillComplexBuffer действительно требует, чтобы другая входная процедура была записана для снабжения данными к Аудио Преобразователю.

  Использование Рендеринга перечисления 7 В качестве примера AudioConverterFillComplexBuffer

OSStatus MyFileRenderProc(void     *inRefCon,
                        AudioUnitRenderActionFlags  *inActionFlags,
                        const AudioTimeStamp *inTimeStamp,
                        UInt32     inBusNumber,
                        UInt32    inNumFrames,
                        AudioBufferList     *ioData)
 
{
    OSStatus err= noErr;
    //To obtain a data buffer of converted data from a complex input
    //source(compressed files, etc.) use AudioConverterFillComplexBuffer.
    AudioConverterFillComplexBuffer(converter,
                MyACComplexInputProc ,
                0 ,
                &inNumFrames,
                ioData,
                0);
 
    return err;
}
/*
Parameters for AudioConverterFillComplexBuffer()
 
converter - the converter being used
 
MyACComplexInputProc() - input procedure to supply data to the Audio
 Converter.
 
inNumFrames - The amount of requested data on input. On output, this
number is the amount actually received.
 
ioData - Buffer of the converted data recieved on return
*/

Это - пример входной процедуры, снабжающей данными к Аудио Преобразователю. Параметры этого обратного вызова определяются тем, что было включено в AudioConverterFillComplexBuffer() вызвать. Новыми данными, которые будут возвращены, является AudioBufferList, данный в параметрах (йодат в этом примере).

  Входная Процедура Комплекса перечисления 8 В качестве примера, читающая из Аудиофайла

OSStatus MyACComplexInputProc (AudioConverterRef        inAudioConverter,
                 UInt32        *ioNumberDataPackets,
                 AudioBufferList             *ioData,
                 AudioStreamPacketDescription    **outDataPacketDescription,
                 void          *inUserData)
{
    OSStatus    err = noErr;
    UInt32  bytesCopied = 0;
 
    // initialize in case of failure
    ioData->mBuffers[0].mData = NULL;
    ioData->mBuffers[0].mDataByteSize = 0;
 
    //if there are not enough packets to satisfy request,
    //then read what's left
    if (gPacketOffset + *ioNumberDataPackets > gTotalPacketCount)
        *ioNumberDataPackets = gTotalPacketCount - gPacketOffset;
 
    // do nothing if there are no packets available
    if (*ioNumberDataPackets)
    {
        if (gSourceBuffer != NULL) {
            free(gSourceBuffer);
            gSourceBuffer = NULL;
        }
 
        //the total amount of data requested by the AudioConverter
        bytesCopied = *ioNumberDataPackets * gMaxPacketSize;
        //alloc a small buffer for the AudioConverter to use.
        gSourceBuffer = (void *) calloc (1, bytesCopied);
 
        //copy the amount of data needed (bytesCopied)
        //from buffer of audio file
        memcpy(gSourceBuffer, gEntireFileBuffer + gByteOffset,bytesCopied);
 
        // keep track of where we want to read from next time
        gByteOffset +=*ioNumberDataPackets * gMaxPacketSize;
        gPacketOffset += *ioNumberDataPackets;
 
        // tell the Audio Converter where it's source data is
        ioData->mBuffers[0].mData = gSourceBuffer;
        // tell the Audio Converter how much data in each buffer
        ioData->mBuffers[0].mDataByteSize = bytesCopied;
    }
    else
    {
        // there aren't any more packets to read.
        // Set the amount of data read (mDataByteSize) to zero
        // and return noErr to signal the AudioConverter there are
        // no packets left.
 
        ioData->mBuffers[0].mData = NULL;
        ioData->mBuffers[0].mDataByteSize = 0;
        gIsPlaying=FALSE;
        err = noErr;
    }
 
    return err;
 
}

Заключение

Core Audio даст Вам больше контроля аудио в Mac OS X. Аудиоданные, обработанные в Core Audio, имеют большее разрешение и производительность, чем Звуковой менеджер, позволяющий Вам дать лучший аудио опыт клиентам.



История версии документа


ДатаПримечания
23.10.2012

Передовая статья

15.11.2006

Новый документ, что, играя Аудиофайл с помощью Выходного Аудиоустройства По умолчанию