ios开发之使用AudioQueue进行音频的播放

前几天钻研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:(unsigned int)length;


@end




------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------



#import "PCMDataPlayer.h"

@implementation PCMDataPlayer

{

    AudioFormatID playerAudioFormatID;

}

- (instancetype)initWithAudioFomart:(AudioFormatID)audioFormatID

{

    self = [super init];

    if (self) {

        playerAudioFormatID = audioFormatID;

        [self reset];

    }

    return self;

}


- (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 = (__bridge PCMDataPlayer*)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

    static const int maxBufferSize = 0x1000; // limit size to 4K 4096

    static const 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__);


    [self stop];


    sysnLock = [[NSLock alloc] 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, (__bridge void * _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:(unsigned int)length

{

    NSLog(@"%s",__func__);


    if (audioQueue == nil || ![self checkBufferHasUsed]) {

        [self reset];

        Float32 gain=1.0;

        //设置音量

        AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, gain);

       OSStatus error = AudioQueueStart(audioQueue, NULL);

        if(error!=noErr) {

            NSLog(@"error:%d",error);

            return;

        }

    }


    [sysnLock lock];


    AudioQueueBufferRef audioQueueBuffer = NULL;


    //获取可用buffer

    while (true) {

        audioQueueBuffer = [self getNotUsedBuffer];

        if (audioQueueBuffer != NULL) {

            break;

        }

    }

    

    memcpy(audioQueueBuffer->mAudioData, pcmData, length);//pcmdata按长度length赋值给audioQueueBuffer->mAudioData,内存拷贝

    audioQueueBuffer->mAudioDataByteSize = length;

    AudioQueueEnqueueBuffer(audioQueue, audioQueueBuffer, 0, NULL);


    [sysnLock unlock];

}


- (BOOL)checkBufferHasUsed

{

    NSLog(@"%s",__func__);


    for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {

        if (YES == audioQueueUsed[i]) {

            return YES;

        }

    }

    NSLog(@"PCMDataPlayer 播放中断............");

    return NO;

}


- (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);

            return audioQueueBuffers[i];

        }

    }

    return NULL;

}


- (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;

        }

    }

}


/*更多关于AudioQueue的使用以及函数讲解,请参照官方文档*/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值