在mac系统中使用AudioQueue播放音频数据

        在MAC中使用AudioQueue可以很方便的播放音频数据。音频的数据格式可以通过AudioStreamBasicDescription来设置。

dhsAudioQueue.h文件:


#ifndef dhsAudioQueue_h
#define dhsAudioQueue_h

@interface dhsAudioQueue : NSObject

- (void)configureAudioQueue;
- (void)startPlayWithDataProvider:(DataProvider*)dataProvider;
- (void)stopPlay;

@end

#endif /* dhsAudioQueue_h */

dhsAudioQueue.m文件:

#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import "dhsAudioQueue.h"


static const int kNumberBuffers = 3;

@interface dhsAudioQueue()

@property (nonatomic, assign) BOOL isRunning;

@end


@implementation dhsAudioQueue {
    AudioStreamBasicDescription mDataFormat;
    AudioQueueRef mQueue;
    AudioQueueBufferRef mBuffers[kNumberBuffers];
    int mBufferSize;
    bool mIsRunning;
    bool mIsInitialized;
    AudioInformation mAudioInformation;
    AVFrame *mFrame;
}
                                      

- (instancetype)init {
    self = [super init];
    
    mIsInitialized = false;
    mFrame = av_frame_alloc();
    
    return self;
}

- (void)dealloc {
    av_frame_unref(mFrame);
    av_frame_free(&mFrame);
}

- (void)configureAudioQueue
{
    mDataFormat.mSampleRate = mAudioInformation.samplerate;
    mDataFormat.mFormatID = kAudioFormatLinearPCM;
    mDataFormat.mChannelsPerFrame = mAudioInformation.channels;
    mDataFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    mDataFormat.mBitsPerChannel = mAudioInformation.bitsPerChannel;
    mDataFormat.mBytesPerFrame = mAudioInformation.bytesPerSample;
    mDataFormat.mFramesPerPacket = 1;
    mDataFormat.mBytesPerPacket = mDataFormat.mFramesPerPacket * mDataFormat.mBytesPerFrame;
    mDataFormat.mReserved = 0;
    
    OSStatus status = AudioQueueNewOutput(&mDataFormat,
                                          audioPlayCallback,
                                          (__bridge void *)self,
                                          CFRunLoopGetCurrent(),
                                          kCFRunLoopCommonModes,
                                          0,
                                          &mQueue);

    if(status != noErr)
    {
        NSLog(@"audio queue init failed!\n");
        return;
    }
    
    status = AudioQueueAddPropertyListener(mQueue, 
                                           kAudioQueueProperty_IsRunning, 
                                           AudioQueuePropertyListenerProc, 
                                           (__bridge void*)self);
    
    UInt32 size = sizeof(mDataFormat);
    status = AudioQueueGetProperty(mQueue, kAudioQueueProperty_StreamDescription, &mDataFormat, &size);
    
    status = AudioQueueSetParameter(mQueue, kAudioQueueParam_Volume, 1.0);
    if(status != noErr)
    {
        return;
    }
    
    for(int i=0; i != kNumberBuffers; i++) {
        status = AudioQueueAllocateBuffer(mQueue, mAudioInformation.bufferSize, &mBuffers[i]);
        if(status != noErr)
        {
            return;
        }
    }
    
}

- (void)startPlayWithDataProvider:(DataProvider*)dataProvider
{
    if(!mIsInitialized)
    {
        mDataProvider = dataProvider;
        mDataProvider->initAudioInformation(&mAudioInformation);
        [self configureAudioQueue];
        
        for(int i=0; i<kNumberBuffers; i++) {
            memset(mBuffers[i]->mAudioData, 0, mAudioInformation.bufferSize);
            mBuffers[i]->mAudioDataByteSize = mAudioInformation.bufferSize;
            AudioQueueEnqueueBuffer(mQueue, mBuffers[i], 0, NULL);
        }
        
        OSStatus status = AudioQueueStart(mQueue, NULL);
        if(status != noErr)
        {
            NSLog(@"Audio queue start failed!\n");
        }
        
        mIsInitialized = true;
    }
}

- (void)stopPlay
{
    AudioQueueStop(mQueue, true);
    
    for(int i=0; i<kNumberBuffers; i++) {
        AudioQueueFreeBuffer(mQueue, mBuffers[i]);
    }
    
    AudioQueueDispose(mQueue, true);
    
    mIsInitialized = false;
}

#pragma mark - callback
static void audioPlayCallback(void* aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
    dhsAudioQueue* dhsAudioQueue = (__bridge dhsAudioQueue*)aqData;
    DataProvider* dataProvider = dhsAudioQueue->mDataProvider;
    
    if(dataProvider)
    {
        if(dataProvider->getAudioData(dhsAudioQueue->mFrame))
        {
            [dhsAudioQueue dataTranslate:dataProvider data:dhsAudioQueue->mFrame queueBuffer:inBuffer];
            
            AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
            
            av_frame_unref(dhsAudioQueue->mFrame);
        }
        else {
            AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
        }
    }
}

static void AudioQueuePropertyListenerProc(void* inUserData, AudioQueueRef inQA, AudioQueuePropertyID inID) {
    dhsAudioQueue* playerAudioQueue = (__bridge dhsAudioQueue*)inUserData;
    
    if(playerAudioQueue == NULL)
        return;
    
    UInt32 isRunning = 0;
    UInt32 size = sizeof(isRunning);
    OSStatus err = AudioQueueGetProperty(inQA, kAudioQueueProperty_IsRunning, &isRunning, &size);
    if(err) {
        playerAudioQueue->mIsRunning = NO;
    } else {
        playerAudioQueue->mIsRunning = isRunning;
    }
}

@end