题记:对上一节的补充,iOS端PCM音频的播放,后续有统一的SDL音频播放,本节可跳过。
PCM简易播放
流程简介
AudioToolbox是一个用于音频处理的框架,提供了录制、播放和流解析的接口。AudioToolbox支持多种音频播放,如短时音效(AudioServices)、本地音乐(AVAudioPlayer)、音频会话(AVAudioSession)、音频队列(AudioQueue)。
本文主要播放上一章的音频流,简单介绍数据流播放,播放流程如图(官网)
简单来说
- AudioQueue播放Buffer队列中的数据
- 队列中Buffer播放完时,会通过callback通知补充数据
代码解析
AudioQueue可以播放各种音频格式,包括AAC数据,Demo主要播放PCM数据。
设置音频数据格式
在使用Audio Queue之前,需要配置音频数据的格式,使用AudioStreamBasicDescription结构体来描述,包括采样率、位深度、声道数量等。具体参数:
- mSampleRate:采样率,每秒的样本数。常见的采样率有16000Hz、32000Hz、44100Hz等。
- mFormatID:数据格式,AAC、PCM等
- mFormatFlags:音频数据的方式的说明,如可以根据大端字节序或小端字节序,浮点数或整数以及不同体位去保存数据
- mBytesPerPacket:每个数据包的字节数。对于固定比特率的音频数据
mFramesPerPacket * mBytesPerFrame;对于可变比特率的音频数据,这个值应设为0 - mFramesPerPacket:每个数据包中的帧数。对于线性PCM格式,这个值通常为1;对于压缩格式,这个值可能大于1。
- mBytesPerFrame:每帧的字节数
声道数*位深度。当一帧中不包含一个采样通道时(如压缩格式),这个值为0。 - mChannelsPerFrame:声道数。单声道音频1;立体声音频为2
- mBitsPerChannel:每个声道的位深度,常见的位深度有8位、16位、24位等。
- mReserved:保留字段
初始化Audio Queue
使用AudioQueueNewOutput函数创建音频输出队列,指定音频数据的格式、回调函数以及用户数据等。参数:
- inFormat:上文中的格式描述
- inCallbackProc:当AudioQueue已使用完一个缓冲区时通知用户的回调函数,用户填充音频数据
- inUserData:传入的数据指针,用于callback中传递对象 callback会透传这个void *(C风格)
- inCallbackRunLoop:指明回调事件发生在哪个RunLoop之中,假设传递NULL,表示在AudioQueue所在的线程上运行该回调事件。
- inCallbackRunLoopMode:指明回调事件发生的RunLoop的模式,传递NULL相当于kCFRunLoopCommonModes
- outAQ:创建的AudioQueue的实例,传入指针地址
分配音频缓冲区
AudioQueueAllocateBuffer为音频队列缓冲区分配内存,即图中的红色区域(一般3个buffer)
- inAQ:AudioQueue实例,上文中创建的对象
- inBufferByteSize:开辟的缓冲区的大小,这个大小需要用户指定,过小会造成播放不连贯,过大会造成缓冲时间较大
- outBuffer:开辟的缓冲区
控制Audio Queue
- AudioQueueStart 启动音频。启动后,音频队列将开始读取数据进行播放
- AudioQueuePause 暂停播放
- AudioQueueStop 停止播放
- AudioQueueSetParameter 设置参数,如音量
返回OSStatus结果,为noErr(0)表示成功
实现回调函数
AudioQueueNewOutput中设置的inCallbackProc回调函数,参数解析:
- inUserData
AudioQueueNewOutput中传入void *指针,因为函数是全局的,一般用于传递对象。 - audioQueueRef 是操作AudioQueue的句柄或引用
- audioQueueBufferRef 需要填充的buffer地址
当音频数据播放完成时,回调函数会被触发,此时可以在回调函数中执行相应的处理逻辑,如更新播放状态、释放缓冲区等。Demo中
注意 AudioQueue可以直接播放AAC数据,Demo中是为了展示播放流程,后续有SDL的播放
其他
此外,如果需要更精细地控制音频队列的运行状态,可以使用AudioQueueAddPropertyListener函数为音频队列添加属性监听器。当音频队列的某些属性(如播放状态、缓冲区数量等)发生变化时,会触发监听器回调函数。