Технические вопросы и ответы QA1636

Аудио-очередь - цикличное выполнение сжатое аудио

Q: Как я беспрепятственно циклично выполняюсь, AAC сжал аудио с помощью Аудио-очереди?

A: Сводка

Беспрепятственно цикличное выполнение, сжатый аудиофайл требует трех данных, связанных с тем, как были сжаты аудио носители:

  1. Число тихих демонстрационных кадров (известный как кадры воспламенения) добавило к передней стороне закодированных аудиоданных.

  2. Число дополнения демонстрационных кадров (известный как кадры остатка) добавило до конца закодированных аудиоданных.

  3. Количество пакетов аудиоданных, указывающее общее количество пакетов аудиоданных, содержится в файле.

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

Фон

Для анализа, на что эта информация похожа в сжатом аудиофайле, создают закодированный Формат аудио Огрызка AAC (.caf) файл с afconvert, тогда используйте afinfo распечатать некоторые подробные данные о файле.

% afconvert -d aac -f caff /System/Library/Sounds/Submarine.aiff /tmp/output.caf
 
% afinfo /tmp/output.caf
 
File: /tmp/output.caf
File type ID: caff
Data format: 2 ch,  44100 Hz, 'aac ' (0x00000000) 0 bits/channel, 0 bytes/packet, 1024 frames/packet, 0 bytes/frame
                no channel layout.
estimated duration: 0.975 sec
audio bytes: 10541
audio packets: 45
audio 42998 valid frames + 2112 priming + 970 remainder = 46080

Что означают числа?

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

Если Вы берете общее количество выборок (46080), вычитаете воспламенение и дополняющий демонстрационные кадры (2112, и 970 соответственно) Вас оставляют с 42 998 допустимыми выборками (46080 - 2112 - 970 = 42998). Можно подтвердить, что 42998 исходная длина Submarine.aiff при помощи afinfo снова.

% afinfo /System/Library/Sounds/Submarine.aiff
 
File: /System/Library/Sounds/Submarine.aiff
File type ID: AIFF
Data format: 2 ch,  44100 Hz, 'lpcm' (0x0000000E) 16-bit big-endian signed integer
                no channel layout.
estimated duration: 0.975 sec
audio bytes: 171992
audio packets: 42998

Как только Вы имеете точное число воспламенения и кадров остатка вместе с количеством пакетов для закодированного потока битов, и помните эту информацию при постановке в очередь буферов, сплошное цикличное выполнение с Аудио-очередью становится довольно прямым усилием.

Чтобы декодировать вышеупомянутое, AAC сжал поток битов и вывести то же число выборок; Ставьте в очередь все пакеты, которые Вы имеете к аудио-очереди, урезаете 2 112 демонстрационных кадров от запуска, урезаете 970 демонстрационных кадров от последнего пакета, и Вы будете полностью декодировать сжатый поток битов точно к последней выборке. Как только Вы ставили в очередь все пакеты, сбрасывали Ваше текущее количество пакетов и запускаете снова, таким образом, цикличное выполнение аудио.

Подробные данные

Со всеми числами из пути и так как мы теперь знаем о кадрах воспламенения, кадрах остатка и количестве пакетов - где мы получаем эти значения от?

Структура AudioFilePacketTableInfo

Получите Кадры Воспламенения и значения Кадров Остатка путем вызова AudioFileGetProperty выяснение kAudioFilePropertyPacketTableInfo свойство как показано в Перечислении 1. Храните их для использования в выходном обратном вызове.

<AudioToolbox/AudioFile.h>
 
/*!
    @struct     AudioFilePacketTableInfo
    @abstract   This contains information about the number of valid frames in a file and where they begin and
                end.
    @discussion Some data formats may have packets whose contents are not completely valid, but represent
                priming or remainder frames that are not meant to be played. For example a file with 100
                packets of AAC is nominally 1024 * 100 = 102400 frames of data. However the first 2112 frames
                of that may be priming frames and there may be some number of remainder frames added to pad
                out to a full packet of 1024 frames. The priming and remainder frames should be discarded. The
                total number of packets in the file times the frames per packet (or counting each packet's
                frames individually for a variable frames per packet format) minus mPrimingFrames, minus
                mRemainderFrames, should equal mNumberValidFrames.
    @field      mNumberValidFrames the number of valid frames in the file.
    @field      mPrimingFrames the number of invalid frames at the beginning of the file.
    @field      mRemainderFrames the number of invalid frames at the end of the file.
*/
struct AudioFilePacketTableInfo
{
        SInt64  mNumberValidFrames;
        SInt32  mPrimingFrames;
        SInt32  mRemainderFrames;
};
typedef struct AudioFilePacketTableInfo AudioFilePacketTableInfo;

Перечисление 1  

AudioFilePacketTableInfo thePacketTableInfo;
 
UInt32 size = sizeof(thePacketTableInfo);
AudioFileGetProperty(myAudioFileID, kAudioFilePropertyPacketTableInfo, &size, &thePacketTableInfo);
 
myAudioLooper->mPrimingFrames = thePacketTableInfo.mPrimingFrames;
myAudioLooper->mRemainderFrames = thePacketTableInfo.mRemainderFrames;

Количество пакетов

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

Количество пакетов Аудиоданных является a UInt64 значение, указывающее число пакетов аудиоданных в файле. Храните это для использования в выходном обратном вызове.

Перечисление 2  

UInt32 size = sizeof(myAudioLooper->mAudioFilePacketCount);
 
AudioFileGetProperty(myAudioFileID, kAudioFileStreamProperty_AudioDataPacketCount,
                                    &size, &myAudioLooper->mAudioFilePacketCount);

Постановка в очередь аудио пакетов

Постановка в очередь сделана с помощью AudioQueueEnqueueBufferWithParameters API, удобно обеспечивающий возможность обрезки. Используйте значение кадров воспламенения для inTrimFramesAtStart параметр как требуется и использование остаток структурируют значение для inTrimFramesAtEnd параметр как требуется.

AudioQueueEnqueueBufferWithParameters(AudioQueueRef                        inAQ,
                                      AudioQueueBufferRef                  inBuffer,
                                      UInt32                               inNumPacketDescs,
                                      const AudioStreamPacketDescription * inPacketDescs,
                                      UInt32                               inTrimFramesAtStart,
                                      UInt32                               inTrimFramesAtEnd,
                                      UInt32                               inNumParamValues,
                                      const AudioQueueParameterEvent *     inParamValues,
                                      const AudioTimeStamp *               inStartTime,
                                      AudioTimeStamp *                     outActualStartTime)
 
Assigns a buffer to an audio queue for playback, providing parameters and start time information.
 
You can exert some control of the buffer queue by using this function. You can assign audio queue settings that are in effect carried by an audio queue buffer as you enqueue it. Hence, these changes only take effect when an audio queue buffer begins playing.
 
This function queues a buffer for playback only, not for recording. Audio queues for recording have no parameters, do not support variable-bit-rate (VBR) formats (which might require trimming), and have a different way to handle timing. When queued for playback, the buffer must contain the audio data to be played back. See AudioQueueEnqueueBuffer for details on queuing a buffer for recording.
 
Parameters:
 
inAQ - The audio queue associated with the buffer.
 
inBuffer - The buffer to be played from.
 
inNumPacketDescs - The number of packet descriptions pointed to by the inPacketDescs parameter. Required only
                   for variable-bit-rate (VBR) audio formats. Pass 0 if no packet descriptions are required.
 
inPacketDescs - A pointer to an array of audio stream packet descriptions. Required only for VBR audio
                formats. Pass NULL if no packet descriptions are required.
 
inTrimFramesAtStart - The number of priming frames to skip at the start of the buffer.
 
inTrimFramesAtEnd - The number of frames to skip at the end of the buffer.
 
inNumParamValues - The number of parameter values pointed to by the inParamValues parameter.
 
inParamValues - An array of parameter values(In Mac OS X v10.5, there is only one parameter,
                kAudioQueueParam_Volume.) These values are set before buffer playback and cannot be
                changed while the buffer is playing. How accurately changes in parameters can be
                scheduled depends on the size of the buffer. If there are no parameters to set
                (inNumParamValues = 0), pass NULL.
 
inStartTime - A pointer to a structure containing the desired start time for playing the buffer. If
               you specify the time using the mSampleTime field of the AudioTimeStamp structure, the
               sample time is relative to the time the queue started. If you pass NULL for the start
               time, the buffer starts immediately after the previously queued buffer, or as soon as
               possible if no buffers are queued ahead of it. Buffers are played in the order they are
               queued. If multiple buffers are queued, their times must be in ascending order or NULL;
               otherwise, an error occurs. The start time indicates when the actual audio data in the
               buffer is to be played (that is, the trim frames are not counted).
 
outActualStartTime - On return, points to an AudioTimeStamp structure indicating when the buffer will
                     actually play.

Реализация выходного обратного вызова аудио-очереди

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

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

  Пример перечисления 3 выходного обратного вызова цикличного выполнения

void AQLooper::AQBufferCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inCompleteAQBuffer)
{
    AQLooper *THIS = (AQLooper *)inUserData;
 
    if (THIS->mIsDone) return;
 
    UInt32 numBytes;
    UInt32 nPackets = THIS->GetNumPacketsToRead(); // generally enough for a half second of audio based on the
                                                   // format -- see CalculateBytesForTime() from aqplay.cpp
 
    OSStatus result = AudioFileReadPackets(THIS->GetAudioFileID(),
                                           false,
                                           &numBytes,
                                           inCompleteAQBuffer->mPacketDescriptions,
                                           THIS->GetCurrentPacket(),
                                           &nPackets,
                                           inCompleteAQBuffer->mAudioData);
    if (result) printf("AudioFileReadPackets failed: %d", result);
 
    if (nPackets > 0) {
        UInt32 trimFramesAtStart = 0;
        UInt32 trimFramesAtEnd = 0;
 
        inCompleteAQBuffer->mAudioDataByteSize = numBytes;
        inCompleteAQBuffer->mPacketDescriptionCount = nPackets;
 
        if (THIS->mCurrentPacket == 0) {
            // at the beginning -- need to trim priming frames
            trimFramesAtStart = THIS->mPrimingFrames;
        }
 
        THIS->mCurrentPacket = (THIS->GetCurrentPacket() + nPackets);
 
        if (THIS->mCurrentPacket == THIS->mAudioFilePacketCount) {
            // at the end -- need to trim remainder frames
            inTrimFramesAtEnd = THIS->mRemainderFrames;
 
            // reset read from the beginning again
            THIS->mCurrentPacket = 0;
        }
 
        result = AudioQueueEnqueueBufferWithParameters(inAQ,
                                                       inCompleteAQBuffer,
                                                       0,
                                                       NULL,
                                                       trimFramesAtStart,
                                                       trimFramesAtEnd,
                                                       0, NULL, NULL, NULL);
        if (result) printf("AudioQueueEnqueueBufferWithParameters failed: %d", result);
    }
}

Ссылки:



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


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

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

20.02.2009

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