前几天钻研IOS音频技术的时候接触到了AudioUnit与AudioQueue两个系统的播放库,前面一个博客写了关于AudioUnit的使用方法,这次发一篇关于AudioQueue的使用方法,AudioQueue的使用相对于AudioUnit来说比较简单一点,可以直接定义目标buffer的大小,对于一般的音频数据的处理没有任何问题,下面贴出一个自己写的AudioQueue的播放器的代码
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#define QUEUE_BUFFER_SIZE 3//队列缓冲个数
#define MIN_SIZE_PER_FRAME 1280//每帧最小数据长度
@interface PCMDataPlayer : NSObject {
AudioStreamBasicDescription audioDescription;///音频参数
AudioQueueRef audioQueue;//音频播放队列
AudioQueueBufferRef audioQueueBuffers[QUEUE_BUFFER_SIZE];//音频缓存
BOOL audioQueueUsed[QUEUE_BUFFER_SIZE];
UInt32 mNumPacketsToRead;
UInt32 bufferByteSize;
NSLock* sysnLock;
}
- (instancetype)initWithAudioFomart:(AudioFormatID)audioFormatID;
/*!
* @author 15-02-10 16:02:27
*
* @brief 重置播放器
*
* @since v1.0
*/
- (void)reset;
/*!
* @author 15-02-10 17:02:52
*
* @brief 停止播放
*
* @since v1.0
*/
- (void)stop;
/*!
* @author 15-02-10 16:02:56
*
* @brief 播放PCM数据
*
* @param pcmData pcm字节数据
*
* @since v1.0
*/
- (void)play:(void*)pcmData length:(unsignedint)length;
@end
#import "PCMDataPlayer.h"
@implementation PCMDataPlayer
{
AudioFormatID playerAudioFormatID;
}
- (instancetype)initWithAudioFomart:(AudioFormatID)audioFormatID
{
self = [superinit];
if (self) {
playerAudioFormatID = audioFormatID;
[selfreset];
}
returnself;
}
- (void)dealloc
{
NSLog(@"%s",__func__);
if (audioQueue !=nil) {
AudioQueueStop(audioQueue,true);
}
audioQueue =nil;
sysnLock =nil;
NSLog(@"PCMDataPlayer dealloc...");
}
static void AudioPlayerAQInputCallback(void* inUserData,AudioQueueRef outQ, AudioQueueBufferRef outQB)
{
NSLog(@"%s",__func__);
PCMDataPlayer* player = (__bridgePCMDataPlayer*)inUserData;
[player playerCallback:outQB];
}
void CalculateBytesForTime1 (AudioStreamBasicDescription inDesc,UInt32 inMaxPacketSize, Float64 inSeconds, UInt32 *outBufferSize,UInt32 *outNumPackets)
{
NSLog(@"%s",__func__);
// we only use time here as a guideline
// we're really trying to get somewhere between 16K and 64K buffers, but not allocate too much if we don't need it
staticconst int maxBufferSize =0x1000; // limit size to 4K 4096
staticconst int minBufferSize =0x400; // limit size to 1K 1024
if (inDesc.mFramesPerPacket) {
Float64 numPacketsForTime = inDesc.mSampleRate / inDesc.mFramesPerPacket * inSeconds;
*outBufferSize = numPacketsForTime * inMaxPacketSize;
} else {
// if frames per packet is zero, then the codec has no predictable packet == time
// so we can't tailor this (we don't know how many Packets represent a time period
// we'll just return a default buffer size
*outBufferSize = maxBufferSize > inMaxPacketSize ? maxBufferSize : inMaxPacketSize;
}
// we're going to limit our size to our default
if (*outBufferSize > maxBufferSize && *outBufferSize > inMaxPacketSize)
*outBufferSize = maxBufferSize;
else {
// also make sure we're not too small - we don't want to go the disk for too small chunks
if (*outBufferSize < minBufferSize)
*outBufferSize = minBufferSize;
}
*outNumPackets = *outBufferSize / inMaxPacketSize;
}
- (void)reset
{
NSLog(@"%s",__func__);
[selfstop];
sysnLock = [[NSLockalloc] init];
//设置音频参数
audioDescription.mSampleRate =8000; //采样率
audioDescription.mFormatID =kAudioFormatLinearPCM;
audioDescription.mFormatFlags =kLinearPCMFormatFlagIsSignedInteger |kAudioFormatFlagIsPacked;;
audioDescription.mChannelsPerFrame =1; ///单声道
audioDescription.mFramesPerPacket =1; //每一个packet一侦数据
audioDescription.mBitsPerChannel =16; // 0 for compressed format
audioDescription.mBytesPerFrame =audioDescription.mChannelsPerFrame * (audioDescription.mBitsPerChannel/8);
audioDescription.mBytesPerPacket =audioDescription.mBytesPerFrame;
audioDescription.mReserved =0;
OSStatus error =AudioQueueNewOutput(&audioDescription,AudioPlayerAQInputCallback, (__bridgevoid * _Nullable)self,nil, nil,0, &audioQueue);//使用player的内部线程播放
if(error!=noErr) {
NSLog(@"error:%d",error);
return;
}
UInt32 maxPacketSize =1;
UInt32 size =sizeof(maxPacketSize);
AudioQueueGetProperty(audioQueue,kAudioQueueProperty_MaximumOutputPacketSize, &size, &maxPacketSize);
CalculateBytesForTime1 (audioDescription, maxPacketSize,0.01, &bufferByteSize, &mNumPacketsToRead);
bool isFormatVBR = (audioDescription.mBytesPerPacket ==0 || audioDescription.mFramesPerPacket ==0);
//初始化音频缓冲区
for (int i =0; i < QUEUE_BUFFER_SIZE; i++) {
error = AudioQueueAllocateBufferWithPacketDescriptions(audioQueue,MIN_SIZE_PER_FRAME, (isFormatVBR ?mNumPacketsToRead : 0), &audioQueueBuffers[i]);
if(error!=noErr) {
NSLog(@"Audio Queue alloc buffer error %d %d",i,(int)error);
return;
}
}
UInt32 value =kAudioQueueHardwareCodecPolicy_UseSoftwareOnly;
error = AudioQueueSetProperty(audioQueue,kAudioQueueProperty_HardwareCodecPolicy, &value,sizeof(value));
if(error!=noErr) {
NSLog(@"software code not use");
}
Float32 gain=1.0;
AudioQueueSetParameter(audioQueue,kAudioQueueParam_Volume, gain);
}
- (void)stop
{
NSLog(@"%s",__func__);
if (audioQueue !=nil) {
AudioQueueStop(audioQueue,true);
AudioQueueReset(audioQueue);
}
audioQueue =nil;
sysnLock =nil;
}
- (void)play:(void*)pcmData length:(unsignedint)length
{
NSLog(@"%s",__func__);
if (audioQueue ==nil || ![selfcheckBufferHasUsed]) {
[selfreset];
Float32 gain=1.0;
//设置音量
AudioQueueSetParameter(audioQueue,kAudioQueueParam_Volume, gain);
OSStatus error =AudioQueueStart(audioQueue,NULL);
if(error!=noErr) {
NSLog(@"error:%d",error);
return;
}
}
[sysnLocklock];
AudioQueueBufferRef audioQueueBuffer =NULL;
//获取可用buffer
while (true) {
audioQueueBuffer = [selfgetNotUsedBuffer];
if (audioQueueBuffer !=NULL) {
break;
}
}
memcpy(audioQueueBuffer->mAudioData, pcmData, length);//将pcmdata按长度length赋值给audioQueueBuffer->mAudioData,内存拷贝
audioQueueBuffer->mAudioDataByteSize = length;
AudioQueueEnqueueBuffer(audioQueue, audioQueueBuffer,0, NULL);
[sysnLockunlock];
}
- (BOOL)checkBufferHasUsed
{
NSLog(@"%s",__func__);
for (int i =0; i < QUEUE_BUFFER_SIZE; i++) {
if (YES ==audioQueueUsed[i]) {
returnYES;
}
}
NSLog(@"PCMDataPlayer播放中断............");
returnNO;
}
- (AudioQueueBufferRef)getNotUsedBuffer
{
NSLog(@"%s",__func__);
for (int i =0; i < QUEUE_BUFFER_SIZE; i++) {
if (NO ==audioQueueUsed[i]) {
audioQueueUsed[i] =YES;
// NSLog(@"PCMDataPlayer play buffer index:%d", i);
returnaudioQueueBuffers[i];
}
}
returnNULL;
}
- (void)playerCallback:(AudioQueueBufferRef)outQB
{
NSLog(@"%s",__func__);
for (int i =0; i < QUEUE_BUFFER_SIZE; i++) {
if (outQB ==audioQueueBuffers[i]) {
//清空缓存
// outQB->mAudioDataByteSize = 1;
// Byte* byte = (Byte *)outQB->mAudioData;
// byte = 0;
// AudioQueueFreeBuffer(audioQueue, outQB);
// outQB = nil;
audioQueueUsed[i] =NO;
}
}
}