Игра аудио

При игре использования аудио Audio Queue Services источник может быть примерно чем-либо — дисковый файл, основанный на программном обеспечении аудио синтезатор, объект в памяти, и т.д. В этой главе описываются наиболее распространенный сценарий: воспроизведение дискового файла.

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

  1. Определите пользовательскую структуру для управления состоянием, форматом и информацией о пути.

  2. Запишите функцию обратного вызова аудио-очереди для выполнения фактического воспроизведения.

  3. Запишите код для определения хорошего размера для буферов аудио-очереди.

  4. Откройте аудиофайл для воспроизведения и определите его формат аудиоданных.

  5. Создайте аудио-очередь воспроизведения и сконфигурируйте ее для воспроизведения.

  6. Выделите и ставьте в очередь буферы аудио-очереди. Скажите аудио-очереди начинать играть. Когда сделано, обратный вызов воспроизведения говорит аудио-очереди останавливаться.

  7. Избавьтесь от аудио-очереди. Высвободите средства.

Остаток от этой главы описывает каждый из этих шагов подробно.

Определите пользовательскую структуру для управления состоянием

Для запуска определите пользовательскую структуру, которую Вы будете использовать для управления форматом аудио и информацией о состоянии аудио-очереди. Перечисление 3-1 иллюстрирует такую структуру:

Перечисление 3-1  пользовательская структура для аудио-очереди воспроизведения

static const int kNumberBuffers = 3;                              // 1
struct AQPlayerState {
    AudioStreamBasicDescription   mDataFormat;                    // 2
    AudioQueueRef                 mQueue;                         // 3
    AudioQueueBufferRef           mBuffers[kNumberBuffers];       // 4
    AudioFileID                   mAudioFile;                     // 5
    UInt32                        bufferByteSize;                 // 6
    SInt64                        mCurrentPacket;                 // 7
    UInt32                        mNumPacketsToRead;              // 8
    AudioStreamPacketDescription  *mPacketDescs;                  // 9
    bool                          mIsRunning;                     // 10
};

Большинство полей в этой структуре идентично (или почти так) тем в пользовательской структуре, используемой для записи, как описано в Записывающей Аудио главе в Определяют Пользовательскую Структуру для Управления состоянием. Например, mDataFormat поле используется здесь для содержания формата играемого файла. При записи аналогичное поле содержит формат файла, записанного в диск.

Вот описание полей в этой структуре:

  1. Определяет номер буферов аудио-очереди для использования. Три обычно большое количество, как описано в Буферах Аудио-очереди.

  2. AudioStreamBasicDescription структура (от CoreAudioTypes.h) представление формата аудиоданных играемого файла. Этот формат привыкает аудио-очередью, указанной в mQueue поле.

    mDataFormat поле заполняется путем запросов аудиофайла kAudioFilePropertyDataFormat свойство, как описано в Получении Формата Аудиоданных Файла.

    Для получения дополнительной информации на AudioStreamBasicDescription структура, посмотрите Ссылку Типов данных Core Audio.

  3. Аудио-очередь воспроизведения создается Вашим приложением.

  4. Массив, содержащий указатели на буферы аудио-очереди, которыми управляет аудио-очередь.

  5. Объект аудиофайла, представляющий аудиофайл Ваши игры программы.

  6. Размер, в байтах, для каждого буфера аудио-очереди. Это значение вычисляется в этих примерах в DeriveBufferSize функция, после того, как аудио-очередь создается и прежде чем это будет запущено. Посмотрите Запись Функция для Получения Размера буфера Аудио-очереди Воспроизведения.

  7. Пакетный индекс для следующего пакета, который будет играть от аудиофайла.

  8. Число пакетов для чтения на каждом вызове обратного вызова воспроизведения аудио-очереди. Как bufferByteSize поле, это значение вычисляется в этих примерах в DeriveBufferSize функция, после того, как аудио-очередь создается и прежде чем это будет запущено.

  9. Для аудиоданных VBR, массива описаний пакета для играемого файла. Для данных CBR значение этого поля NULL.

  10. Булево значение, указывающее, работает ли аудио-очередь.

Запишите обратный вызов аудио-очереди воспроизведения

Затем, запишите функцию обратного вызова аудио-очереди воспроизведения. Этот обратный вызов делает три главное:

Этот раздел показывает объявление обратного вызова в качестве примера, описывает каждую из этих задач отдельно, и наконец представляет весь обратный вызов воспроизведения. Для иллюстрации роли обратного вызова воспроизведения можно вернуться к рисунку 1-4.

Объявление обратного вызова аудио-очереди воспроизведения

Перечисление 3-2 показывает объявление в качестве примера для функции обратного вызова аудио-очереди воспроизведения, объявленной как AudioQueueOutputCallback в AudioQueue.h заголовочный файл:

Перечисление 3-2  объявление обратного вызова аудио-очереди воспроизведения

static void HandleOutputBuffer (
    void                 *aqData,                 // 1
    AudioQueueRef        inAQ,                    // 2
    AudioQueueBufferRef  inBuffer                 // 3
)

Вот то, как работает этот код:

  1. Как правило, aqData пользовательская структура, содержащая информацию состояния для аудио-очереди, как описано в Определяют Пользовательскую Структуру для Управления состоянием.

  2. Аудио-очередь, которой принадлежит этот обратный вызов.

  3. Буфер аудио-очереди, который обратный вызов должен заполнить данными путем чтения из аудиофайла.

Чтение от файла в буфер аудио-очереди

Первое действие обратного вызова аудио-очереди воспроизведения должно считать данные из аудиофайла и поместить их в буфер аудио-очереди. Перечисление 3-3 показывает, как сделать это.

Перечисление 3-3  , Читающее из аудиофайла в буфер аудио-очереди

AudioFileReadPackets (                        // 1
    pAqData->mAudioFile,                      // 2
    false,                                    // 3
    &numBytesReadFromFile,                    // 4
    pAqData->mPacketDescs,                    // 5
    pAqData->mCurrentPacket,                  // 6
    &numPackets,                              // 7
    inBuffer->mAudioData                      // 8
);

Вот то, как работает этот код:

  1. AudioFileReadPackets функция, объявленная в AudioFile.h заголовочный файл, считывает данные из аудиофайла и помещает их в буфер.

  2. Аудиофайл для чтения из.

  3. Использует значение false указать, что функция не должна кэшировать данные при чтении.

  4. На выводе, числе байтов аудиоданных, считанных из аудиофайла.

  5. На выводе, массиве описаний пакета для данных, считанных из аудиофайла. Для данных CBR входное значение этого параметра NULL.

  6. Пакетный индекс для первого пакета, который будет читать из аудиофайла.

  7. На вводе, числе пакетов для чтения из аудиофайла. На выводе фактически читало число пакетов.

  8. На выводе, заполненный буфер аудио-очереди, содержащий данные, считанные из аудиофайла.

Постановка в очередь буфера аудио-очереди

Теперь, когда данные были считаны из аудиофайла и помещены в буфер аудио-очереди, обратный вызов ставит в очередь буфер, как показано в Перечислении 3-4. Один раз в буферной очереди, аудиоданные в буфере доступны для аудио-очереди для отправки к устройству вывода.

Перечисление 3-4  , Ставящее в очередь буфер аудио-очереди после чтения от диска

AudioQueueEnqueueBuffer (                      // 1
    pAqData->mQueue,                           // 2
    inBuffer,                                  // 3
    (pAqData->mPacketDescs ? numPackets : 0),  // 4
    pAqData->mPacketDescs                      // 5
);

Вот то, как работает этот код:

  1. AudioQueueEnqueueBuffer функция добавляет буфер аудио-очереди к буферной очереди.

  2. Аудио-очередь, которой принадлежит буферная очередь.

  3. Буфер аудио-очереди для постановки в очередь

  4. Число пакетов представлено в данных буфера аудио-очереди. Для данных CBR, не использующих описаний пакета, использования 0.

  5. Для сжатых форматов аудиоданных, использующих описания пакета, описания пакета для пакетов в буфере..

Остановка аудио-очереди

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

Перечисление 3-5  , Останавливающее аудио-очередь

if (numPackets == 0) {                          // 1
    AudioQueueStop (                            // 2
        pAqData->mQueue,                        // 3
        false                                   // 4
    );
    pAqData->mIsRunning = false;                // 5
}

Вот то, как работает этот код:

  1. Проверки, если число пакетов, считанных AudioFileReadPackets функция (вызванный ранее обратным вызовом) 0.

  2. AudioQueueStop функционируйте останавливает аудио-очередь.

  3. Аудио-очередь для остановки.

  4. Когда все буферы с очередями игрались, останавливает аудио-очередь асинхронно. Посмотрите Управление Аудио-очередью и состояние.

  5. Устанавливает флаг в пользовательской структуре, чтобы указать, что закончено воспроизведение.

Полный обратный вызов аудио-очереди воспроизведения

Перечисление 3-6 показывает базовую версию полного обратного вызова аудио-очереди воспроизведения. Как с остальной частью примеров кода в этом документе, это перечисление исключает обработку ошибок.

Перечисление 3-6  функция обратного вызова аудио-очереди воспроизведения

static void HandleOutputBuffer (
    void                *aqData,
    AudioQueueRef       inAQ,
    AudioQueueBufferRef inBuffer
) {
    AQPlayerState *pAqData = (AQPlayerState *) aqData;        // 1
    if (pAqData->mIsRunning == 0) return;                     // 2
    UInt32 numBytesReadFromFile;                              // 3
    UInt32 numPackets = pAqData->mNumPacketsToRead;           // 4
    AudioFileReadPackets (
        pAqData->mAudioFile,
        false,
        &numBytesReadFromFile,
        pAqData->mPacketDescs, 
        pAqData->mCurrentPacket,
        &numPackets,
        inBuffer->mAudioData 
    );
    if (numPackets > 0) {                                     // 5
        inBuffer->mAudioDataByteSize = numBytesReadFromFile;  // 6
       AudioQueueEnqueueBuffer ( 
            pAqData->mQueue,
            inBuffer,
            (pAqData->mPacketDescs ? numPackets : 0),
            pAqData->mPacketDescs
        );
        pAqData->mCurrentPacket += numPackets;                // 7 
    } else {
        AudioQueueStop (
            pAqData->mQueue,
            false
        );
        pAqData->mIsRunning = false; 
    }
}

Вот то, как работает этот код:

  1. Пользовательские данные, снабженные к аудио-очереди после инстанцирования, включая объект аудиофайла (типа AudioFileID) представление файла для игры, а также множество данных состояния. Посмотрите Определяют Пользовательскую Структуру для Управления состоянием.

  2. Если аудио-очередь останавливается, возвраты сразу.

  3. Переменная для содержания числа байтов аудиоданных читала из играемого файла.

  4. Инициализирует numPackets переменная с числом пакетов для чтения из играемого файла.

  5. Тесты, были ли некоторые аудиоданные получены от файла. Если так, ставит в очередь недавно заполненный буфер. В противном случае останавливает аудио-очередь.

  6. Говорит, что буфер аудио-очереди структурирует число байтов считанных данных.

  7. Постепенно увеличивает пакетный индекс согласно числу считанных пакетов.

Запишите функцию для получения размера буфера аудио-очереди воспроизведения

Audio Queue Services ожидает, что Ваше приложение укажет, что размер для аудио-очереди буферизует Вас использование. Перечисление 3-7 показывает один способ сделать это. Это получает размер буфера, достаточно большой для содержания данной продолжительности аудиоданных.

Вы вызовете это DeriveBufferSize функционируйте в своем приложении, после создания аудио-очереди воспроизведения, как предпосылка к тому, чтобы просить, чтобы аудио-очередь выделила буферы. Посмотрите Размеры Набора для Аудио-очереди Воспроизведения.

Код здесь делает две дополнительных вещи по сравнению с аналогичной функцией, Вы видели в Записи Функцию для Получения Размера буфера Аудио-очереди Записи. Для воспроизведения Вы также:

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

Перечисление 3-7  , Получающее размер буфера аудио-очереди воспроизведения

void DeriveBufferSize (
    AudioStreamBasicDescription &ASBDesc,                            // 1
    UInt32                      maxPacketSize,                       // 2
    Float64                     seconds,                             // 3
    UInt32                      *outBufferSize,                      // 4
    UInt32                      *outNumPacketsToRead                 // 5
) {
    static const int maxBufferSize = 0x50000;                        // 6
    static const int minBufferSize = 0x4000;                         // 7
 
    if (ASBDesc.mFramesPerPacket != 0) {                             // 8
        Float64 numPacketsForTime =
            ASBDesc.mSampleRate / ASBDesc.mFramesPerPacket * seconds;
        *outBufferSize = numPacketsForTime * maxPacketSize;
    } else {                                                         // 9
        *outBufferSize =
            maxBufferSize > maxPacketSize ?
                maxBufferSize : maxPacketSize;
    }
 
    if (                                                             // 10
        *outBufferSize > maxBufferSize &&
        *outBufferSize > maxPacketSize
    )
        *outBufferSize = maxBufferSize;
    else {                                                           // 11
        if (*outBufferSize < minBufferSize)
            *outBufferSize = minBufferSize;
    }
 
    *outNumPacketsToRead = *outBufferSize / maxPacketSize;           // 12
}

Вот то, как работает этот код:

  1. AudioStreamBasicDescription структура для аудио-очереди.

  2. Предполагаемый максимальный размер пакета для данных в аудиофайле Вы играете. Можно определить это значение путем вызова AudioFileGetProperty функция (объявленный в AudioFile.h заголовочный файл) со свойством ID kAudioFilePropertyPacketSizeUpperBound. Посмотрите размеры набора для аудио-очереди воспроизведения.

  3. Размер Вы указываете для каждого буфера аудио-очереди, с точки зрения секунд аудио.

  4. На выводе, размере для каждого буфера аудио-очереди, в байтах.

  5. На выводе, числе пакетов аудиоданных для чтения из файла на каждом вызове обратного вызова аудио-очереди воспроизведения.

  6. Верхняя граница для размера буфера аудио-очереди, в байтах. В этом примере верхняя граница установлена в 320 КБ. Это соответствует приблизительно пяти секундам стерео, аудио на 24 бита в частоте дискретизации 96 кГц.

  7. Нижняя граница для размера буфера аудио-очереди, в байтах. В этом примере нижняя граница установлена в 16 КБ.

  8. Для форматов аудиоданных, определяющих постоянное число кадров на пакет, получает размер буфера аудио-очереди.

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

  10. Если полученный размер буфера выше верхней границы, Вы установили, корректирует его связанное — принятие во внимание предполагаемого максимального размера пакета.

  11. Если полученный размер буфера ниже нижней границы, Вы установили, корректирует его к связанному.

  12. Вычисляет число пакетов для чтения из аудиофайла на каждом вызове обратного вызова.

Откройте аудиофайл для воспроизведения

Теперь Вы открываете аудиофайл для воспроизведения, с помощью этих трех шагов:

  1. Получите объект CFURL представление аудиофайла, который Вы хотите играть.

  2. Откройте файл.

  3. Получите формат аудиоданных файла.

Получение объекта CFURL для аудиофайла

Перечисление 3-8 демонстрирует, как получить объект CFURL для аудиофайла, который Вы хотите играть. Вы используете объект CFURL на следующем шаге, открывая файл..

Перечисление 3-8  Получая CFURL возражает для аудиофайла

CFURLRef audioFileURL =
    CFURLCreateFromFileSystemRepresentation (           // 1
        NULL,                                           // 2
        (const UInt8 *) filePath,                       // 3
        strlen (filePath),                              // 4
        false                                           // 5
    );

Вот то, как работает этот код:

  1. CFURLCreateFromFileSystemRepresentation функция, объявленная в CFURL.h заголовочный файл, создает объект CFURL представление файла для игры.

  2. Использование NULL (или kCFAllocatorDefault) использовать текущее средство выделения памяти по умолчанию.

  3. Путь файловой системы Вы хотите преобразовать в объект CFURL. В производственном коде Вы обычно получали бы значение для filePath от пользователя.

  4. Число байтов по пути файловой системы.

  5. Значение false указывает это filePath представляет файл, не каталог.

Открытие аудиофайла

Перечисление 3-9 демонстрирует, как открыть аудиофайл для воспроизведения.

Перечисление 3-9  , Открывающее аудиофайл для воспроизведения

AQPlayerState aqData;                                   // 1
 
OSStatus result =
    AudioFileOpenURL (                                  // 2
        audioFileURL,                                   // 3
        fsRdPerm,                                       // 4
        0,                                              // 5
        &aqData.mAudioFile                              // 6
    );
 
CFRelease (audioFileURL);                               // 7
 

Вот то, как работает этот код:

  1. Создает экземпляр AQPlayerState пользовательская структура (см., Определяет Пользовательскую Структуру для Управления состоянием). Вы используете этот экземпляр при открытии аудиофайла для воспроизведения как место для содержания объекта аудиофайла (типа AudioFileID) это представляет аудиофайл.

  2. AudioFileOpenURL функция, объявленная в AudioFile.h заголовочный файл, открывает файл, который Вы хотите играть.

  3. Ссылка на файл для игры.

  4. Полномочия файла, которые Вы хотите использовать с файлом, который Вы играете. Доступные полномочия определяются в Файловом менеджере File Access Permission Constants перечисление. В этом примере Вы запрашиваете разрешение считать файл.

  5. Дополнительная подсказка типа файла. Значение 0 здесь указывает, что пример не использует это средство.

  6. На выводе ссылка на аудиофайл помещается в пользовательскую структуру mAudioFile поле.

  7. Выпускает объект CFURL, создававшийся на шаге 1.

Получение формата аудиоданных файла

Перечисление 3-10 показывает, как получить формат аудиоданных файла.

Перечисление 3-10  Получая формат аудиоданных файла

UInt32 dataFormatSize = sizeof (aqData.mDataFormat);    // 1
 
AudioFileGetProperty (                                  // 2
    aqData.mAudioFile,                                  // 3
    kAudioFilePropertyDataFormat,                       // 4
    &dataFormatSize,                                    // 5
    &aqData.mDataFormat                                 // 6
);

Вот то, как работает этот код:

  1. Заставляет ожидаемый размер значения свойства использовать при запросах аудиофайла о его формате аудиоданных.

  2. AudioFileGetProperty функция, объявленная в AudioFile.h заголовочный файл, получает значение для указанного свойства в аудиофайле.

  3. Объект аудиофайла (типа AudioFileID) представление файла, формат аудиоданных которого Вы хотите получить.

  4. Свойство ID для получения значения формата данных аудиофайла.

  5. На вводе, ожидаемом размере AudioStreamBasicDescription структура, описывающая формат данных аудиофайла. На выводе, фактическом размере. Ваше приложение воспроизведения не должно использовать это значение.

  6. На выводе, полном формате аудиоданных, в форме AudioStreamBasicDescription структура, полученная из аудиофайла. Эта строка применяет формат аудиоданных файла к аудио-очереди путем хранения его в пользовательской структуре аудио-очереди.

Создайте аудио-очередь воспроизведения

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

Перечисление 3-11  , Создающее аудио-очередь воспроизведения

AudioQueueNewOutput (                                // 1
    &aqData.mDataFormat,                             // 2
    HandleOutputBuffer,                              // 3
    &aqData,                                         // 4
    CFRunLoopGetCurrent (),                          // 5
    kCFRunLoopCommonModes,                           // 6
    0,                                               // 7
    &aqData.mQueue                                   // 8
);

Вот то, как работает этот код:

  1. AudioQueueNewOutput функция создает новую аудио-очередь воспроизведения.

  2. Формат аудиоданных файла, который аудио-очередь устанавливается для игры. Посмотрите Получение Формата Аудиоданных Файла.

  3. Функция обратного вызова для использования с аудио-очередью воспроизведения. Посмотрите Запись Обратный вызов Аудио-очереди Воспроизведения.

  4. Пользовательская структура данных для аудио-очереди воспроизведения. Посмотрите Определяют Пользовательскую Структуру для Управления состоянием.

  5. Текущий цикл выполнения и тот, на который будет вызван обратный вызов воспроизведения аудио-очереди.

  6. Режимы цикла выполнения, в которых может быть вызван обратный вызов. Обычно, используйте kCFRunLoopCommonModes постоянный здесь.

  7. Зарезервированный. Должен быть 0.

  8. На выводе, недавно выделенной аудио-очереди воспроизведения.

Размеры набора для аудио-очереди воспроизведения

Затем, Вы устанавливаете некоторые размеры для аудио-очереди воспроизведения. Вы используете эти размеры при выделении буферов для аудио-очереди и прежде чем Вы начнете читать аудиофайл.

Листинги кода в этом разделе показывают, как установить:

Установка размера буфера и числа пакетов для чтения

Перечисление 3-12 демонстрирует, как использовать DeriveBufferSize функция, которую Вы записали ранее (см. Запись Функция для Получения Размера буфера Аудио-очереди Воспроизведения). Цель здесь состоит в том, чтобы установить размер, в байтах, для каждого буфера аудио-очереди, и определить число пакетов для чтения для каждого вызова обратного вызова аудио-очереди воспроизведения.

Этот код использует осторожную оценку максимального размера пакета, который Core Audio обеспечивает посредством kAudioFilePropertyPacketSizeUpperBound свойство. В большинстве случаев лучше использовать этот метод — который приблизителен, но быстр — чем занять время для чтения всего аудиофайла для получения фактического максимального размера пакета.

Перечисление 3-12  , Устанавливающее размер буфера аудио-очереди воспроизведения и число пакетов для чтения

UInt32 maxPacketSize;
UInt32 propertySize = sizeof (maxPacketSize);
AudioFileGetProperty (                               // 1
    aqData.mAudioFile,                               // 2
    kAudioFilePropertyPacketSizeUpperBound,          // 3
    &propertySize,                                   // 4
    &maxPacketSize                                   // 5
);
 
DeriveBufferSize (                                   // 6
    aqData.mDataFormat,                              // 7
    maxPacketSize,                                   // 8
    0.5,                                             // 9
    &aqData.bufferByteSize,                          // 10
    &aqData.mNumPacketsToRead                        // 11
);

Вот то, как работает этот код:

  1. AudioFileGetProperty функция, объявленная в AudioFile.h заголовочный файл, получает значение указанного свойства для аудиофайла. Здесь Вы используете его для получения консервативной верхней границы, в байтах, для размера пакетов аудиоданных в файле, который Вы хотите играть.

  2. Объект аудиофайла (типа AudioFileID) представление файла Вы хотите играть. Посмотрите Открытие Аудиофайла.

  3. Свойство ID для получения консервативной верхней границы для размера пакета в аудиофайле.

  4. На выводе, размере, в байтах, для kAudioFilePropertyPacketSizeUpperBound свойство.

  5. На выводе, консервативной верхней границе для размера пакета, в байтах, для файла Вы хотите играть.

  6. DeriveBufferSize функция, описанная в Записи Функция для Получения Размера буфера Аудио-очереди Воспроизведения, устанавливает размер буфера и много пакетов для чтения на каждом вызове обратного вызова аудио-очереди воспроизведения.

  7. Формат аудиоданных файла Вы хотите играть. Посмотрите Получение Формата Аудиоданных Файла.

  8. Предполагаемый максимальный размер пакета в аудиофайле, от строки 5 из этого перечисления.

  9. Число секунд аудио, которое должен содержать каждый буфер аудио-очереди. Один наполовину второй, как установлено здесь, обычно хороший выбор.

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

  11. На выводе, числе пакетов для чтения на каждом вызове обратного вызова аудио-очереди воспроизведения. Это значение также помещается в пользовательскую структуру для аудио-очереди.

Выделение памяти для массива описаний пакета

Теперь Вы выделяете память для массива для содержания описаний пакета для ценности одного буфера аудиоданных. Данные с постоянной скоростью передачи не используют описания пакета, таким образом, случай CBR — шаг 3 в Перечислении 3-13 — очень прост.

Перечисление 3-13  , Выделяющее память для массива описаний пакета

bool isFormatVBR = (                                       // 1
    aqData.mDataFormat.mBytesPerPacket == 0 ||
    aqData.mDataFormat.mFramesPerPacket == 0
);
 
if (isFormatVBR) {                                         // 2
    aqData.mPacketDescs =
      (AudioStreamPacketDescription*) malloc (
        aqData.mNumPacketsToRead * sizeof (AudioStreamPacketDescription)
      );
} else {                                                   // 3
    aqData.mPacketDescs = NULL;
}

Вот то, как работает этот код:

  1. Определяет, является ли форматом данных аудиофайла VBR или CBR. В данных VBR, один или оба из байтов на пакет или значений кадров на пакет является переменным, и так будет перечислен как 0 в аудио-очереди AudioStreamBasicDescription структура.

  2. Для аудиофайла, содержащего данные VBR, выделяет память для массива описаний пакета. Вычисляет память, необходимую на основе числа пакетов аудиоданных, которые будут считаны на каждом вызове обратного вызова воспроизведения. Посмотрите Размер буфера Установки и Число Пакетов для Чтения.

  3. Для аудиофайла, содержащего данные CBR, такие как линейный PCM, аудио-очередь не использует массив описаний пакета.

Установите волшебный Cookie для аудио-очереди воспроизведения

Некоторые сжатые форматы аудио, такие как MPEG 4 AAC, используют структуры для содержания аудио метаданных. Эти структуры вызывают волшебными cookie. При игре файла в таком использовании формата Audio Queue Services Вы получаете волшебный cookie от аудиофайла и добавляете его к аудио-очереди, прежде чем Вы начнете играть.

Перечисление 3-14 показывает, как получить волшебный cookie из файла и применить его к аудио-очереди. Ваш код вызвал бы эту функцию перед стартовым воспроизведением.

Перечисление 3-14  , Устанавливающее волшебный cookie для аудио-очереди воспроизведения

UInt32 cookieSize = sizeof (UInt32);                   // 1
bool couldNotGetProperty =                             // 2
    AudioFileGetPropertyInfo (                         // 3
        aqData.mAudioFile,                             // 4
        kAudioFilePropertyMagicCookieData,             // 5
        &cookieSize,                                   // 6
        NULL                                           // 7
    );
 
if (!couldNotGetProperty && cookieSize) {              // 8
    char* magicCookie =
        (char *) malloc (cookieSize);
 
    AudioFileGetProperty (                             // 9
        aqData.mAudioFile,                             // 10
        kAudioFilePropertyMagicCookieData,             // 11
        &cookieSize,                                   // 12
        magicCookie                                    // 13
    );
 
    AudioQueueSetProperty (                            // 14
        aqData.mQueue,                                 // 15
        kAudioQueueProperty_MagicCookie,               // 16
        magicCookie,                                   // 17
        cookieSize                                     // 18
    );
 
    free (magicCookie);                                // 19
}

Вот то, как работает этот код:

  1. Устанавливает предполагаемый размер для волшебных cookie-данных.

  2. Получает результат AudioFileGetPropertyInfo функция. Если успешный, эта функция возвращает значение NoErr, эквивалентный булевской переменной false.

  3. AudioFileGetPropertyInfo функция, объявленная в AudioFile.h заголовочный файл, получает размер значения указанного свойства. Вы используете это для установки размера переменной, содержащей значение свойства.

  4. Объект аудиофайла (типа AudioFileID) это представляет аудиофайл, который Вы хотите играть.

  5. Свойство ID, представляющее волшебные cookie-данные аудиофайла.

  6. На вводе, предполагаемом размере для волшебных cookie-данных. На выводе, фактическом размере.

  7. Использование NULL указать, что Вы не заботитесь о доступе для чтения-записи о свойстве.

  8. Если аудиофайл действительно содержит волшебный cookie, выделите память для содержания его.

  9. AudioFileGetProperty функция, объявленная в AudioFile.h заголовочный файл, получает значение указанного свойства. В этом случае это получает волшебный cookie аудиофайла.

  10. Объект аудиофайла (типа AudioFileID) это представляет аудиофайл, который Вы хотите играть, и чей волшебный cookie Вы получаете.

  11. Свойство ID, представляющее волшебные cookie-данные аудиофайла.

  12. На вводе, размере magicCookie переменная получила использование AudioFileGetPropertyInfo функция. На выводе, фактическом размере волшебного cookie с точки зрения числа байтов, записанных в magicCookie переменная.

  13. На выводе, волшебном cookie аудиофайла.

  14. AudioQueueSetProperty функционируйте устанавливает свойство в аудио-очереди. В этом случае это устанавливает волшебный cookie для аудио-очереди, соответствуя волшебный cookie в аудиофайле, который будет играться.

  15. Аудио-очередь, для которой Вы хотите установить волшебный cookie.

  16. Свойство ID, представляющее волшебный cookie аудио-очереди.

  17. Волшебный cookie от аудиофайла, который Вы хотите играть.

  18. Размер, в байтах, волшебного cookie.

  19. Выпускает память, выделенную для волшебного cookie.

Выделите и главные буферы аудио-очереди

Вы теперь спрашиваете аудио-очередь, которую Вы создали (в, Создают Аудио-очередь Воспроизведения) подготавливать ряд буферов аудио-очереди. Перечисление 3-15 демонстрирует, как сделать это.

  Выделение перечисления 3-15 и аудио-очередь воспламенения буферизуют для воспроизведения

aqData.mCurrentPacket = 0;                                // 1
 
for (int i = 0; i < kNumberBuffers; ++i) {                // 2
    AudioQueueAllocateBuffer (                            // 3
        aqData.mQueue,                                    // 4
        aqData.bufferByteSize,                            // 5
        &aqData.mBuffers[i]                               // 6
    );
 
    HandleOutputBuffer (                                  // 7
        &aqData,                                          // 8
        aqData.mQueue,                                    // 9
        aqData.mBuffers[i]                                // 10
    );
}

Вот то, как работает этот код:

  1. Устанавливает пакетный индекс в 0, так, чтобы, когда обратный вызов аудио-очереди начинает заполнять буферы (шаг 7), это запустилось в начале аудиофайла.

  2. Выделяет и начала ряд буферов аудио-очереди. (Вы определяете этот номер, kNumberBuffers, к 3 в определяют пользовательскую структуру для управления состоянием.)

  3. AudioQueueAllocateBuffer функция создает буфер аудио-очереди путем выделения памяти для него.

  4. Аудио-очередь, выделяющая буфер аудио-очереди.

  5. Размер, в байтах, для нового буфера аудио-очереди.

  6. На выводе добавляет новый буфер аудио-очереди к mBuffers массив в пользовательской структуре.

  7. HandleOutputBuffer функция является обратным вызовом аудио-очереди воспроизведения, который Вы записали. Посмотрите Запись Обратный вызов Аудио-очереди Воспроизведения.

  8. Пользовательская структура для аудио-очереди.

  9. Аудио-очередь, чей обратный вызов Вы вызываете.

  10. Буфер аудио-очереди, что Вы являетесь передающими к обратному вызову аудио-очереди.

Установите усиление воспроизведения аудио-очереди

Прежде чем Вы скажете аудио-очереди начинать играть, Вы устанавливаете ее усиление посредством механизма параметра аудио-очереди. Перечисление 3-16 показывает, как сделать это. Для больше на механизме параметра, посмотрите Параметры Аудио-очереди.

Перечисление 3-16  , Устанавливающее усиление воспроизведения аудио-очереди

Float32 gain = 1.0;                                       // 1
    // Optionally, allow user to override gain setting here
AudioQueueSetParameter (                                  // 2
    aqData.mQueue,                                        // 3
    kAudioQueueParam_Volume,                              // 4
    gain                                                  // 5
);

Вот то, как работает этот код:

  1. Устанавливает усиление для использования с аудио-очередью, между 0 (для тишины) и 1 (для единичного усиления).

  2. AudioQueueSetParameter функционируйте устанавливает значение параметра для аудио-очереди.

  3. Аудио-очередь, на которой Вы устанавливаете параметр.

  4. ID параметра Вы устанавливаете. kAudioQueueParam_Volume постоянный позволяет Вам установить усиление аудио-очереди.

  5. Усиление, устанавливающее это, Вы применяетесь к аудио-очереди.

Запустите и выполненный аудио-очередь

Весь предыдущий код привел к процессу игры файла. В то время как файл играет, как показано в Перечислении 3-17, это включает запуск аудио-очереди и поддержание цикла выполнения.

  Запуск перечисления 3-17 и выполнение аудио-очереди

aqData.mIsRunning = true;                          // 1
 
AudioQueueStart (                                  // 2
    aqData.mQueue,                                 // 3
    NULL                                           // 4
);
 
do {                                               // 5
    CFRunLoopRunInMode (                           // 6
        kCFRunLoopDefaultMode,                     // 7
        0.25,                                      // 8
        false                                      // 9
    );
} while (aqData.mIsRunning);
 
CFRunLoopRunInMode (                               // 10
    kCFRunLoopDefaultMode,
    1,
    false
);

Вот то, как работает этот код:

  1. Устанавливает флаг в пользовательской структуре, чтобы указать, что работает аудио-очередь.

  2. AudioQueueStart функция запускает аудио-очередь на ее собственном потоке.

  3. Аудио-очередь для запуска.

  4. Использование NULL указать, что аудио-очередь должна начать играть сразу.

  5. Опрашивает пользовательскую структуру mIsRunning поле регулярно, чтобы проверить, остановилась ли аудио-очередь.

  6. CFRunLoopRunInMode функционируйте выполняет цикл выполнения, содержащий поток аудио-очереди.

  7. Использует режим по умолчанию для цикла выполнения.

  8. Устанавливает время выполнения цикла выполнения в 0.25 секунды.

  9. Использование false указать, что цикл выполнения должен продолжаться в течение указанного полного рабочего дня.

  10. После того, как аудио-очередь остановилась, выполняет цикл выполнения немного дольше, чтобы гарантировать, что буфер аудио-очереди в настоящее время игра имеет время для окончания.

Очистите после игры

Когда Вы закончены, играя файл, избавляетесь от аудио-очереди, закрываете аудиофайл, и свободный любые остающиеся ресурсы. Перечисление 3-18 иллюстрирует эти шаги.

Перечисление 3-18  , Очищающее после игры аудиофайла

AudioQueueDispose (                            // 1
    aqData.mQueue,                             // 2
    true                                       // 3
);
 
AudioFileClose (aqData.mAudioFile);            // 4
 
free (aqData.mPacketDescs);                    // 5

Вот то, как работает этот код:

  1. AudioQueueDispose функция избавляется от аудио-очереди и всех ее ресурсов, включая ее буферы.

  2. Аудио-очередь Вы хотите избавиться.

  3. Использовать true избавляться от аудио-очереди синхронно.

  4. Закрывает игравшийся аудиофайл. AudioFileClose функция объявляется в AudioFile.h заголовочный файл.

  5. Выпускает память, использовавшуюся для содержания описаний пакета.