在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