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

时间:2021-04-09 16:33:07

前几天钻研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;

        }

    }

}


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