| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- //
- // JCHATRawAudioDataPlayer.m
- // OggSpeex
- //
- // Created by Jiang Chuncheng on 6/25/13.
- // Copyright (c) 2013 Sense Force. All rights reserved.
- //
- #import "JCHATRawAudioDataPlayer.h"
- #import "JChatConstants.h"
- @interface JCHATRawAudioDataPlayer ()
- - (void)prepare;
- - (void)initAudio;
- - (void)readPCMAndPlay:(AudioQueueRef)outQ buffer:(AudioQueueBufferRef)outQB;
- - (int)checkUsedQueueBuffer:(AudioQueueBufferRef) qbuf;
- - (void)putEmptyBuffer:(AudioQueueBufferRef)buffer;
- - (void)removeEmptyBuffer:(AudioQueueBufferRef)buffer;
- @end
- @implementation JCHATRawAudioDataPlayer
- @synthesize isDataInputOver;
- - (id)init {
- self = [super init];
- if (self) {
- mPcmData = [NSMutableData data];
- readedBytes = 0;
- pcmDataBuffer = malloc(EVERY_READ_LENGTH);
- synlock = [[NSLock alloc] init];
- emptyAudioQueueBufferIndexs = [NSMutableArray arrayWithCapacity:QUEUE_BUFFER_SIZE];
- }
- return self;
- }
- - (void)prepare {
- isDataInputOver = NO;
- readedBytes = 0;
- [emptyAudioQueueBufferIndexs removeAllObjects];
- }
- - (void)startPlay {
- [self initAudio];
- [self prepare];
- OSStatus status = AudioQueueStart(audioQueue, NULL);
- if (status) {
- [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_PLAY_OVER object:nil];
- return;
- }
- for(int i=0;i<QUEUE_BUFFER_SIZE;i++) {
- [self readPCMAndPlay:audioQueue buffer:audioQueueBuffers[i]];
- }
- /*
- audioQueue使用的是驱动回调方式,即通过AudioQueueEnqueueBuffer(outQ, outQB, 0, NULL);传入一个buff去播放,播放完buffer区后通过回调通知用户,
- 用户得到通知后再重新初始化buff去播放,周而复始,当然,可以使用多个buff提高效率(测试发现使用单个buff会小卡)
- */
- }
- - (void)stopPlay {
- AudioQueueStop(audioQueue, YES);
- AudioQueueDispose(audioQueue, YES);
- }
- - (void)initAudio {
- ///设置音频参数
- audioDescription.mSampleRate = 8000;//采样率
- audioDescription.mFormatID = kAudioFormatLinearPCM;
- audioDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
- audioDescription.mChannelsPerFrame = 1;///单声道
- audioDescription.mFramesPerPacket = 1;//每一个packet一侦数据
- audioDescription.mBitsPerChannel = 16;//每个采样点16bit量化
- audioDescription.mBytesPerFrame = (audioDescription.mBitsPerChannel/8) * audioDescription.mChannelsPerFrame;
- audioDescription.mBytesPerPacket = audioDescription.mBytesPerFrame ;
- ///创建一个新的从audioqueue到硬件层的通道
- // AudioQueueNewOutput(&audioDescription, AudioPlayerAQInputCallback, self, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &audioQueue);///使用当前线程播
- AudioQueueNewOutput(&audioDescription, AudioPlayerAQInputCallback, (__bridge void *)(self), nil, nil, 0, &audioQueue);//使用player的内部线程播
-
- ////添加buffer区
- for(int i=0;i<QUEUE_BUFFER_SIZE;i++) {
- int result = AudioQueueAllocateBuffer(audioQueue, MIN_SIZE_PER_FRAME, &audioQueueBuffers[i]);///创建buffer区,MIN_SIZE_PER_FRAME为每一侦所需要的最小的大小,该大小应该比每次往buffer里写的最大的一次还大
- JPIMLog(@"AudioQueueAllocateBuffer i = %d,result = %d",i,result);
- }
- }
- - (void)inputNewDataFromBuffer:(Byte *)buffer size:(int)bufferSize {
- [synlock lock];
- [mPcmData appendBytes:buffer length:bufferSize];
- [synlock unlock];
-
- if ([emptyAudioQueueBufferIndexs count] > 0) { //如果有空闲的audio queue buffer就尝试填入
- [self readPCMAndPlay:audioQueue buffer:audioQueueBuffers[[[emptyAudioQueueBufferIndexs objectAtIndex:0] intValue]]];
- }
-
- }
- - (void)setIsDataInputOver:(BOOL)dataInputOver {
- isDataInputOver = dataInputOver;
- if (dataInputOver) {
- if ([emptyAudioQueueBufferIndexs count] == QUEUE_BUFFER_SIZE) {
- JPIMLog(@"audio queue play over");
- [self stopPlay];
- [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_PLAY_OVER object:nil];
- }
- }
- }
- - (void)readPCMAndPlay:(AudioQueueRef)outQ buffer:(AudioQueueBufferRef)outQB
- {
- [synlock lock];
-
- NSUInteger lengthLeft = [mPcmData length] - readedBytes; //能够读取的数据长度
- if ((lengthLeft < EVERY_READ_LENGTH) && ( ! isDataInputOver)) { //数据不足以填充queue buffer
- [self putEmptyBuffer:outQB];
- }
- else {
- if ([emptyAudioQueueBufferIndexs count] > 0) {
- if (lengthLeft == 0 && isDataInputOver) {
- //所有数据都输入并且读取完了
- if ([emptyAudioQueueBufferIndexs count] == QUEUE_BUFFER_SIZE) {
- NSLog(@"audio queue play over");
- [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_PLAY_OVER object:nil];
- }
- } else {
- [self removeEmptyBuffer:outQB];
- NSUInteger readLength = (lengthLeft > EVERY_READ_LENGTH) ? EVERY_READ_LENGTH : lengthLeft;
- outQB->mAudioDataByteSize = (int)readLength;
- Byte *audioData = (Byte *)outQB->mAudioData;
- memcpy(audioData, [mPcmData bytes] + readedBytes, readLength);
- readedBytes += readLength;
- /*
- 将创建的buffer区添加到audioqueue里播放
- AudioQueueBufferRef用来缓存待播放的数据区,AudioQueueBufferRef有两个比较重要的参数,AudioQueueBufferRef->mAudioDataByteSize用来指示数据区大小,AudioQueueBufferRef->mAudioData用来保存数据区
- */
- AudioQueueEnqueueBuffer(outQ, outQB, 0, NULL);
- }
-
- }
- }
- [synlock unlock];
- }
- - (void)putEmptyBuffer:(AudioQueueBufferRef)buffer {
- BOOL isInArray = NO;
- int indexValue = [self checkUsedQueueBuffer:buffer];
- for (NSNumber *index in emptyAudioQueueBufferIndexs) {
- if ([index intValue] == indexValue) {
- isInArray = YES;
- }
- }
- if ( ! isInArray) {
- [emptyAudioQueueBufferIndexs addObject:[NSNumber numberWithInt:indexValue]];
- }
- }
- - (void)removeEmptyBuffer:(AudioQueueBufferRef)buffer {
- int indexValue = [self checkUsedQueueBuffer:buffer];
- for (NSNumber *index in emptyAudioQueueBufferIndexs) {
- if ([index intValue] == indexValue) {
- [emptyAudioQueueBufferIndexs removeObject:index];
- return;
- }
- }
- }
- - (int)checkUsedQueueBuffer:(AudioQueueBufferRef) qbuf {
- int bufferIndex = 0;
- if(qbuf == audioQueueBuffers[0]) {
- bufferIndex = 0;
- }
- if(qbuf == audioQueueBuffers[1]) {
- bufferIndex = 1;
- }
- if(qbuf == audioQueueBuffers[2]) {
- bufferIndex = 2;
- }
- if(qbuf == audioQueueBuffers[3]) {
- bufferIndex = 3;
- }
- return bufferIndex;
- }
- - (void)dealloc {
- free(pcmDataBuffer);
- }
- #pragma mark -
- #pragma mark player call back
- /*
- 试了下其实可以不用静态函数,但是c写法的函数内是无法调用[self ***]这种格式的写法,所以还是用静态函数通过void *input来获取原类指针
- 这个回调存在的意义是为了重用缓冲buffer区,当通过AudioQueueEnqueueBuffer(outQ, outQB, 0, NULL);函数放入queue里面的音频文件播放完以后,通过这个函数通知
- 调用者,这样可以重新再使用回调传回的AudioQueueBufferRef
- */
- static void AudioPlayerAQInputCallback(void *input, AudioQueueRef outQ, AudioQueueBufferRef outQB) {
- JCHATRawAudioDataPlayer *player = (__bridge JCHATRawAudioDataPlayer *)input;
- [player putEmptyBuffer:outQB];
- [player readPCMAndPlay:outQ buffer:outQB];
- }
- @end
|