音频

几种播放音频文件的方式(六) —— 音频队列服务(Audio Q

2017-12-28  本文已影响657人  刀客传奇

版本记录

版本号 时间
V1.0 2017.12.27

前言

ios系统中有很多方式可以播放音频文件,这里我们就详细的说明下播放音乐文件的原理和实例。感兴趣的可以看我写的上面几篇。
1. 几种播放音频文件的方式(一) —— 播放本地音乐
2. 几种播放音频文件的方式(二) —— 音效播放
3. 几种播放音频文件的方式(三) —— 网络音乐播放
4. 几种播放音频文件的方式(四) —— 音频队列服务(Audio Queue Services)(一)
5. 几种播放音频文件的方式(五) —— 音频队列服务(Audio Queue Services)简介(二)

About Audio Queues - 关于音频队列

在本章中,您将了解音频队列的功能,体系结构和内部工作原理。 您将了解音频队列audio queues,音频队列缓冲区audio queue buffers以及音频队列用于录制或回放的回调函数callback。 您还可以了解有关音频队列状态和参数的信息。 到本章结束时,您将获得有效使用此技术所需的概念性理解。


What Is an Audio Queue? - 音频队列是什么?

音频队列Audio Queue是用于在iOS或Mac OS X中录制或播放音频的软件对象。它由AudioQueue.h头文件中声明的AudioQueueRef不透明数据类型表示。

音频队列的工作是:

您可以将音频队列与其他Core Audio接口以及相对较少量的自定义代码一起使用,以在应用程序中创建完整的数字音频录制或播放解决方案。


Audio Queue Architecture - 音频队列结构

下面我们就看一下音频队列的结构。

所有音频队列具有相同的总体结构,由以下部分组成:

结构的改变取决于音频队列是用于记录还是回放。 不同点在于音频队列如何连接其输入和输出,以及回调函数的作用。

Audio Queues for Recording - 音频队列用于记录

使用AudioQueueNewInput函数创建的录制音频队列的结构如图1-1所示。

Figure 1-1 A recording audio queue

记录音频队列的输入侧通常连接到外部音频硬件,如麦克风。例如,在iOS中,音频来自用户内置麦克风或耳机麦克风所连接的设备。在Mac OS X的默认情况下,音频来自系统偏好设置中由用户设置的系统的默认音频输入设备。

录制音频队列的输出端使用您编写的回调函数。在录制到磁盘时,回调会将新音频数据的缓冲区(从音频队列接收)写入音频文件。但是,记录音频队列可以以其他方式使用。例如,您的回调可以将音频数据直接提供给您的应用程序,而不是将其写入磁盘。

您将在 The Recording Audio Queue Callback Function中了解有关此回调的更多信息。

每个音频队列(无论是用于记录还是播放)都有一个或多个音频队列缓冲区audio queue buffers。这些缓冲区按照一个称为缓冲队列buffer queue的特定顺序排列。在图中,音频队列缓冲区按照它们被填充的顺序进行编号 - 这与它们被交给回调的顺序是相同的。您将了解音频队列如何在The Buffer Queue and Enqueuing中使用其缓冲区buffers

Audio Queues for Playback - 音频队列回放

播放音频队列(使用AudioQueueNewOutput函数创建)的结构如图1-2所示。

Figure 1-2 A playback audio queue

在回放音频队列中,回调位于输入端。 该回调负责从磁盘(或其他来源)获取音频数据,并将其交给音频队列。 回放回调还会告诉音频队列在没有更多数据可以播放时停止播放。 您将在The Playback Audio Queue Callback Function中了解有关此回调的更多信息。

回放音频队列的输出通常连接到外部音频硬件,如扬声器。 在iOS中,音频会转到用户选择的设备上,例如接收器或耳机。 在Mac OS X的默认情况下,音频将转到系统偏好设置中由用户设置的系统的默认音频输出设备。


Audio Queue Buffers - 音频队列缓冲

音频队列缓冲区是AudioQueue.h头文件中声明的AudioQueueBuffer类型的数据结构:

typedef struct AudioQueueBuffer {
    const UInt32   mAudioDataBytesCapacity;
    void *const    mAudioData;
    UInt32         mAudioDataByteSize;
    void           *mUserData;
} AudioQueueBuffer;
typedef AudioQueueBuffer *AudioQueueBufferRef;

在代码清单中突出显示的mAudioData字段指向缓冲区本身:一块内存,用作正在播放或录音的音频数据的瞬时块的容器。其他字段中的信息有助于音频队列audio queue管理缓冲区buffer

音频队列可以使用任意数量的缓冲区。你的应用程序指定多少。一个典型的数字是三个。这使得一个用于写入磁盘,而另一个正在填充新的音频数据。如果需要补偿磁盘I / O延迟等问题,那么第三个缓冲区就有用了。Figure 1-3说明了这一点。

音频队列为其缓冲区执行内存管理。

这提高了您添加到应用程序的录制和播放功能的稳健性。它还有助于优化资源使用。

有关AudioQueueBuffer数据结构的完整说明,请参阅Audio Queue Services Reference


The Buffer Queue and Enqueuing - 缓冲队列和入队

缓冲队列就是给音频队列,实际上是音频队列服务,他们的名字的对象。 您在Audio Queue Architecture中认识了缓冲区队列 —— 一个有序的缓冲区列表。 在这里,您将了解到音频队列对象与回调函数在录制或回放过程中如何管理缓冲区队列。 尤其是,您了解入队enqueuing,将音频队列缓冲区添加到缓冲区队列。 无论您正在执行录制还是回放,入队都是回调执行的任务。

1. The Recording Process - 录音过程

录制时,一个音频队列缓冲区正在填充从输入设备(如麦克风)获取的音频数据。 缓冲区队列中剩余的缓冲区排列在当前缓冲区后面,等待依次填充音频数据。

音频队列按已获取音频数据的填充缓冲区的顺序将它们传递给您的回调。 图1-3说明了使用音频队列时录制的工作原理。

Figure 1-3 The recording process

在图1-3的步骤1中,开始记录。 音频队列用获取的数据填充缓冲区。

在第2步中,第一个缓冲区已被填充。 音频队列调用回调函数,将其交给完整的缓冲区(缓冲区1)。 回调(步骤3)将缓冲区的内容写入音频文件。 同时,音频队列用新获取的数据填充另一个缓冲区(缓冲区2)。

在第4步中,回调将刚刚写入磁盘的缓冲区(缓冲区1)排入队列,以便再次进行填充。 音频队列再次调用回调(步骤5),把下一个完整的缓冲区(缓冲区2)交给它。 回调(第6步)将此缓冲区的内容写入音频文件。 这种循环稳定状态一直持续到用户停止记录。

2. The Playback Process - 播放过程

播放时,一个音频队列缓冲区正在发送到输出设备,如扬声器。 缓冲区队列中剩余的缓冲区排列在当前缓冲区后面,等待依次播放。

音频队列按照播放的顺序将播放的音频数据缓冲区传递到您的回调。 回调会将新的音频数据读入缓冲区,然后将其排入队列。 图1-4说明了使用音频队列时的播放方式。

Figure 1-4 The playback process

在图1-4的步骤1中,应用程序启动播放音频队列。 应用程序为每个音频队列缓冲区调用一次回调函数,将其填充并添加到缓冲区队列中。 启动确保播放可以在您的应用程序调用AudioQueueStart函数时立即启动(步骤2)。

在步骤3中,音频队列发送第一个缓冲区(缓冲区1)输出。

一旦第一个缓冲器被播放,回放音频队列就进入循环稳定状态。 音频队列开始播放下一个缓冲区(缓冲区2,步骤4)并调用回调(步骤5),将刚刚播放的缓冲区(缓冲区1)传送给它。 回调(第6步)从音频文件中填充缓冲区,然后将其排入队列进行回放。

3. Controlling the Playback Process - 控制播放过程

音频队列缓冲区总是以它们入队的顺序播放。 但是,音频队列服务使用AudioQueueEnqueueBufferWithParameters函数为播放过程提供了一些控制。 这个功能可以让你:

有关设置回放增益的更多信息,请参阅Audio Queue Parameters。 有关AudioQueueEnqueueBufferWithParameters函数的完整说明,请参阅 Audio Queue Services Reference


The Audio Queue Callback Function - 音频队列回调函数

通常,使用音频队列服务的大部分编程工作包括编写音频队列回调函数。

在录制或回放过程中,音频队列回调函数由拥有它的音频队列重复调用。 两次调用之间的时间取决于音频队列缓冲区的容量,一般范围从半秒到几秒。

无论是用于记录还是回放,音频队列回调的一个责任是将音频队列缓冲audio queue buffers返回到缓冲区队列buffer queue。 回调函数使用AudioQueueEnqueueBuffer函数将缓冲区添加到缓冲区队列的末尾。 对于播放,如果需要更多控制,则可以使用AudioQueueEnqueueBufferWithParameters函数,如Controlling the Playback Process中所述。

1. The Recording Audio Queue Callback Function - 录制音频队列回调函数

本节介绍将音频录制到磁盘文件的常见情况而写的回调。 以下是AudioQueue.h头文件中声明的录音音频队列回调的原型:

AudioQueueInputCallback (
    void                               *inUserData,
    AudioQueueRef                      inAQ,
    AudioQueueBufferRef                inBuffer,
    const AudioTimeStamp               *inStartTime,
    UInt32                             inNumberPacketDescriptions,
    const AudioStreamPacketDescription *inPacketDescs
);

记录音频队列在调用回调函数时提供了回调函数将下一组音频数据写入音频文件的所有内容:

有关录音callback的更多信息,请参阅本文档中的Recording Audio,并参阅Audio Queue Services Reference

2. The Playback Audio Queue Callback Function - 播放音频回调函数

本节介绍您为从磁盘文件播放音频的常见情况而写的回调。 以下是AudioQueue.h头文件中声明的回放音频队列回调的原型:

AudioQueueOutputCallback (
    void                  *inUserData,
    AudioQueueRef         inAQ,
    AudioQueueBufferRef   inBuffer
);

回放音频队列在调用回调函数时提供回调需要从音频文件读取下一组音频数据的内容:

如果您的应用程序正在播放VBR数据,则回调callback需要获取正在读取的音频数据的数据包信息。它通过调用AudioFile.h头文件中声明AudioFileReadPackets函数来完成此操作。该回调然后将包信息放置在自定义数据结构中以使其可用于回放音频队列。

有关回放回调callback的更多信息,请参阅本文档中的Playing Audio,并参阅Audio Queue Services Reference


Using Codecs and Audio Data Formats - 使用编解码器和音频数据格式

音频队列服务使用编解码器(音频数据编码/解码组件)根据需要在音频格式之间进行转换。 您的录制或回放应用程序可以使用任何已安装编解码器的音频格式。 您不需要编写自定义代码来处理各种音频格式。 具体来说,你的回调不需要知道数据格式。

这里就是如何工作的。 每个音频队列都有一个音频数据格式,以AudioStreamBasicDescription结构表示。 当您在结构体的mFormatID字段中指定格式时,音频队列使用适当的编解码器。 然后指定采样率和通道数,这就是它的全部。 您将看到在Recording AudioPlaying Audio中设置音频数据格式的示例。

录制音频队列使用已安装的编解码器,如图1-5所示。

Figure 1-5 Audio format conversion during recording

在图1-5的第1步中,你的应用程序告诉一个音频队列开始记录,并告诉它要使用的数据格式。 在步骤2中,音频队列获取新的音频数据,并使用编解码器根据您指定的格式进行转换。 音频队列然后调用回调函数,将缓冲区中包含适当格式的音频数据。 在第3步中,您的回调会将格式化的音频数据写入磁盘。 同样,你的回调不需要知道数据格式。

播放音频队列使用已安装的编解码器,如图1-6所示。

Figure 1-6 Audio format conversion during playback

在图1-6的第1步中,你的应用程序通知一个音频队列开始播放,并且还告诉它要播放的音频文件中包含的数据格式。在步骤2中,音频队列调用您的回调,它从音频文件中读取数据。回调将原始格式的数据转交给音频队列。在步骤3中,音频队列使用适当的编解码器,然后将音频发送到目的地。

音频队列可以使用任何已安装的编解码器,无论是原生的Mac OS X还是由第三方提供。要指定要使用的编解码器,请将其四个字符的编码ID提供给音频队列的AudioStreamBasicDescription结构。您将在Recording Audio中看到这个例子。

Mac OS X包含各种各样的音频编码解码器,如CoreAudioTypes.h头文件中的格式ID枚举所示,以及 Core Audio Data Types Reference中所述。您可以使用Audio Toolbox Framework中的AudioFormat.h头文件中的接口来确定系统上可用的编解码器。您可以使用Fiendishthngs应用程序在系统上显示编解码器,可在http://developer.apple.com/samplecode/Fiendishthngs/上以示例代码的形式获得。


Audio Queue Control and State - 音频队列控制和状态

音频队列在创建和销毁之间有一个生命周期。你的应用程序管理这个生命周期,并使用AudioQueue.h头文件中声明的六个函数来控制音频队列的状态:

您可以在同步或异步模式下使用AudioQueueStop功能:

有关每个功能的完整说明,请参阅Audio Queue Services Reference,其中包括关于音频队列的同步停止和异步停止的更多信息。


Audio Queue Parameters - 音频队列参数

音频队列具有可调参数设置称为parameters。每个参数都有一个枚举常量作为键,一个浮点数作为其值。参数通常用于回放,而不是记录。

在Mac OS X v10.5中,唯一可用的音频队列参数用于增益。此参数的值是使用kAudioQueueParam_Volume常量设置或检索的,其可用范围从0.0到单位增益为1.0。

您的应用程序可以通过两种方式设置音频队列参数

在这两种情况下,音频队列的参数设置都会一直有效,直到您更改它们。

您可以随时使用AudioQueueGetParameter函数访问音频队列的当前参数值。有关获取和设置参数值的函数的完整说明,请参阅Audio Queue Services Reference

后记

未完,待续~~~

上一篇 下一篇

猜你喜欢

热点阅读