有关于苹果的音频《Audio Queue Services》

简介

Audio �Queue 服务提供了一种更低开销,更简单直接的方式,用于iOS和MacOSX中录制和播放音频。这是一种相对会更好用的更值得推荐的音频播放、录制方式。
Audio Queue Services 可以让你录制和播放以下的格式:
1、PCM 格式;
2、苹果平台所支持的所有音频格式;

这里大概包括

   kAudioFormatLinearPCM               = 'lpcm',
    kAudioFormatAC3                     = 'ac-3',
    kAudioFormat60958AC3                = 'cac3',
    kAudioFormatAppleIMA4               = 'ima4',
    kAudioFormatMPEG4AAC                = 'aac ',
    kAudioFormatMPEG4CELP               = 'celp',
    kAudioFormatMPEG4HVXC               = 'hvxc',
    kAudioFormatMPEG4TwinVQ             = 'twvq',
    kAudioFormatMACE3                   = 'MAC3',
    kAudioFormatMACE6                   = 'MAC6',
    kAudioFormatULaw                    = 'ulaw',
    kAudioFormatALaw                    = 'alaw',
    kAudioFormatQDesign                 = 'QDMC',
    kAudioFormatQDesign2                = 'QDM2',
    kAudioFormatQUALCOMM                = 'Qclp',
    kAudioFormatMPEGLayer1              = '.mp1',
    kAudioFormatMPEGLayer2              = '.mp2',
    kAudioFormatMPEGLayer3              = '.mp3',
    kAudioFormatTimeCode                = 'time',
    kAudioFormatMIDIStream              = 'midi',
    kAudioFormatParameterValueStream    = 'apvs',
    kAudioFormatAppleLossless           = 'alac',
    kAudioFormatMPEG4AAC_HE             = 'aach',
    kAudioFormatMPEG4AAC_LD             = 'aacl',
    kAudioFormatMPEG4AAC_ELD            = 'aace',
    kAudioFormatMPEG4AAC_ELD_SBR        = 'aacf',
    kAudioFormatMPEG4AAC_ELD_V2         = 'aacg',    
    kAudioFormatMPEG4AAC_HE_V2          = 'aacp',
    kAudioFormatMPEG4AAC_Spatial        = 'aacs',
    kAudioFormatAMR                     = 'samr',
    kAudioFormatAMR_WB                  = 'sawb',
    kAudioFormatAudible                 = 'AUDB',
    kAudioFormatiLBC                    = 'ilbc',
    kAudioFormatDVIIntelIMA             = 0x6D730011,
    kAudioFormatMicrosoftGSM            = 0x6D730031,
    kAudioFormatAES3                    = 'aes3',
    kAudioFormatEnhancedAC3             = 'ec-3'

Audio Queue Services是比较高级的框架,它可以让你的程序直接使用硬件录制或播放音频,不需要更多的了解硬件接口,也可以让你在使用编解码器的过程中,无须再去理解编解码器的工作原理。
Audio Queue 会支持一些比较高级的特性,它提供了精确的的时间控制,用来支持定时播放和同步。这就可以让多组的音频队列同时播放,并实现音视频同步。(这个在做游戏声效的时候非常好用)

Note: Audio Queue Services 是基于M�ac OSX 的Sound Manager 优化出来的,它增加了一些新的特性,比方说:同步。因为Sound Manager 已经在Mac OSXv10.5 被弃用了,而且他不支持64位应用程序,所以苹果才有意的推出Audio Queue Services 给开发人员使用以及为SoundManager做替换。

Audio Queue Services 是一个纯C的接口,你可以在Cocoa 应用以及在Mac OSX上一样的使用,为了帮助Audio Queue Service类的使用,可通过Core Audio 框架中的C++类来简化实现,但是并非一定得使用这两个东西的。
Audio Queue Services包括了以下的内容:
About Audio Queues 描述了音频队列的功能,架构以及内部的工作原理。
Recording Audio 介绍如何进行录音。
Playing Audio 介绍了如何进行播放。

关于Audio Queues

这里会讲述关于音频队列的功能、架构以及内部工作过程。接下来会引入音频队列、音频队列缓冲以及音频队列的回调方法来进行录音或播放音频。你需要了解音频队列的状态以及相关的设置参数。
Audio queue 是iOS和Mac OS X通用的一个录音和播放音频的软件对象,它由AudioQueue.h头文件中声明的AudioQueueRef数据类型表示。
一个AudioQueue会执行以下的内容:
1、连接音频采集硬件模块
2.、管理音频采集所需的内存
3、根据需求采用压缩音频格式以及选择相关编解码器
4、录音和音频播放的中转站
你可以通过使用Core Audio 相关接口,以及少量的代码来创建相关的Audio Queue 来进行录音或者音频回放。

音频队列的架构

所有的音频队列都具有相同的通用架构,主要包括以下内容:
1、一组音频队列缓冲区audio queue buffers,作为音频数据的临时存储;
2、一个音频队列buffer queue,用于控制音频队列缓冲的数据;
3、一个音频队列回调函数,可用于写入、发送等操作。
这个架构决定了音频队列是用于录制还是播放,会体现音频队列的输入、输出以及回调函数的差异。

用于录音的音频队列

使用AudioQueueNewInput可创建一个用于录音的音频队列,其工作过程如下图所示:

recording_architecture

录音音频队列的输出端是通过回调函数实现的,当回调从音频队列接收到新的音频数据时,就可以从缓冲区读取音频数据写到磁盘中,或者发送出去。

总的来说就是通过麦克风采集音频数据,通过队列的形式,回调给上层使用。

用于播放的音频队列

使用AudioQueueNewOutput可创建一个用于播放的音频队列,其工作过程如下图所示:

playback_architecture

在播放队列中,回调函数是作为一个输入端,它从磁盘或者其他地方获取到音频数据,然后将其闯入到音频队列,通过队列缓冲播放。

音频队列缓冲区

Audio queue buffer 是AudioQueue.h类里面声明的AudioQueueBuffer类型的结构体。

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

其中mAudioData属性是指向缓冲区本身(一个正在播放或者录音的音频数据的临时缓存内存块)。其他字段中的信息是用于Audio Queue管理Buffer的。
开发者可以随意定制音频队列的缓冲区数量,一般我们使用三个就满足了。这样就可以让一个用来读,一个用来写入,另一个就可以用来缓存新的音频数据。

音频队列对其缓冲区的内存管理
1、当调用AudioQueueAllocateBuffer函数时,音频队列会分配一个缓冲区。
2、当通过调用AudioQueueDispose函数释放音频队列时,队列释放其缓冲区。

这里值得一提的是,无论录音操作还是音频播放操作,音频队列的入队和出队都是在回调函数实现的。

设置音频队列缓冲区大小

DeriveBufferSize (                               
    aqData.mQueue,                               // 当前设置缓冲区大小的音频队列
    aqData.mDataFormat,                          // 当前录制的文件的音频数据格式。
    0.5,                                         // 每个音频队列缓冲区应该保持的音频的秒数
    &aqData.bufferByteSize                       // 在输出时,每个音频队列缓冲区的大小(以字节为单位)。
);

录音过程

1、开始录音后,音频数据从设备麦克风采集音频数据然后填入音频队列缓冲器中;
2、当第一个缓冲区满了,音频队列就回调给上层;
3、把回调的音频数据写入磁盘中;
4、回调完成后的缓冲区会继续被音频队列重新使用;
5、音频队列传递完整缓冲区然后回调并填充另一个缓冲区;
整体过程如下图所示:

recording_callback_function

录音音频队列的回调声明

static void HandleInputBuffer (
    void                                *aqData,             // 1
    AudioQueueRef                       inAQ,                // 2
    AudioQueueBufferRef                 inBuffer,            // 3
    const AudioTimeStamp                *inStartTime,        // 4
    UInt32                              inNumPackets,        // 5
    const AudioStreamPacketDescription  *inPacketDesc        // 6
)

1、通常aqData时报喊音频队列的状态数据的自定义数据;
2、拥有当前回调的音频队列。
3、音频队列缓冲区包含要录制的传入音频数据。
4、音频队列缓冲区中第一个采样的采样时间(简单记录是不需要的)。
5、AudioStreamPacketDescription 参数中的数据包数,当值是0时表示CBR数据。
6、编码器为缓冲区中的数据包生成对应的压缩音频数据格式描述内容。

static void HandleInputBuffer (
    void                                 *aqData,
    AudioQueueRef                        inAQ,
    AudioQueueBufferRef                  inBuffer,
    const AudioTimeStamp                 *inStartTime,
    UInt32                               inNumPackets,
    const AudioStreamPacketDescription   *inPacketDesc
) {
    AQRecorderState *pAqData = (AQRecorderState *) aqData;               // 1在实例化时提供给音频队列对象的定制结构,包括表示要记录的音频文件的音频文件对象以及各种状态数据。
 
    if (inNumPackets == 0 &&                                             // 2 如果音频队列缓冲区包含CBR数据,则计算缓冲区中的数据包数。 该数字等于缓冲区中数据的总字节数除以(常数)每个数据包的字节数。 对于VBR数据,音频队列在调用回调时提供缓冲区中的数据包数。
          pAqData->mDataFormat.mBytesPerPacket != 0)
       inNumPackets =
           inBuffer->mAudioDataByteSize / pAqData->mDataFormat.mBytesPerPacket;
 
    if (AudioFileWritePackets (                                          // 3 将缓冲区的内容写入音频数据文件。
            pAqData->mAudioFile,
            false,
            inBuffer->mAudioDataByteSize,
            inPacketDesc,
            pAqData->mCurrentPacket,
            &inNumPackets,
            inBuffer->mAudioData
        ) == noErr) {
            pAqData->mCurrentPacket += inNumPackets;                     // 4 如果成功写入音频数据,则将音频数据文件的数据包索引增加到准备写入下一个缓冲区的音频数据。
    }
   if (pAqData->mIsRunning == 0)                                         // 5 如果音频队列已停止,则返回
      return;
 
    AudioQueueEnqueueBuffer (                                            // 6 将内容刚被写入音频文件的音频队列缓冲区排队。
        pAqData->mQueue,
        inBuffer,
        0,
        NULL
    );
}

设置用于录制的音频格式

AQRecorderState aqData;                                       // 1创建AQRecorderState自定义结构的实例
 
aqData.mDataFormat.mFormatID         = kAudioFormatLinearPCM; // 2 将音频数据格式类型定义为线性PCM
aqData.mDataFormat.mSampleRate       = 44100.0;               // 3 将采样率定义为44.1 kHz。
aqData.mDataFormat.mChannelsPerFrame = 2;                     // 4将通道数定义为2。
aqData.mDataFormat.mBitsPerChannel   = 16;                    // 5 将每个通道的位深度定义为16。
aqData.mDataFormat.mBytesPerPacket   =                        // 6将每个数据包的字节数和每帧的字节数定义为4(即每个通道2个byte)
   aqData.mDataFormat.mBytesPerFrame =
      aqData.mDataFormat.mChannelsPerFrame * sizeof (SInt16);
aqData.mDataFormat.mFramesPerPacket  = 1;                     // 7 将每个数据包的帧数定义为1
 
AudioFileTypeID fileType             = kAudioFileAIFFType;    // 8 将文件类型定义为AIFF,请参阅AudioFile.h头文件中的音频文件类型枚举,以获取可用文件类型的完整列表。
aqData.mDataFormat.mFormatFlags =                             // 9 设置指定文件类型所需的格式标志。
    kLinearPCMFormatFlagIsBigEndian
    | kLinearPCMFormatFlagIsSignedInteger
    | kLinearPCMFormatFlagIsPacked;

开始录音、停止录音
aqData.mCurrentPacket = 0;                           // 1
aqData.mIsRunning = true;                            // 2
 //开始录音
AudioQueueStart (                                    // 3
    aqData.mQueue,                                   // 4
    NULL                                             // 5
);
// 停止录音
AudioQueueStop (                                     // 6
    aqData.mQueue,                                   // 7
    true                                             // 8
);
 
aqData.mIsRunning = false;    

后续会继续更新音频播放相关的内容以及音频转码内容

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容