JCHATRawAudioDataPlayer.m 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. //
  2. // JCHATRawAudioDataPlayer.m
  3. // OggSpeex
  4. //
  5. // Created by Jiang Chuncheng on 6/25/13.
  6. // Copyright (c) 2013 Sense Force. All rights reserved.
  7. //
  8. #import "JCHATRawAudioDataPlayer.h"
  9. #import "JChatConstants.h"
  10. @interface JCHATRawAudioDataPlayer ()
  11. - (void)prepare;
  12. - (void)initAudio;
  13. - (void)readPCMAndPlay:(AudioQueueRef)outQ buffer:(AudioQueueBufferRef)outQB;
  14. - (int)checkUsedQueueBuffer:(AudioQueueBufferRef) qbuf;
  15. - (void)putEmptyBuffer:(AudioQueueBufferRef)buffer;
  16. - (void)removeEmptyBuffer:(AudioQueueBufferRef)buffer;
  17. @end
  18. @implementation JCHATRawAudioDataPlayer
  19. @synthesize isDataInputOver;
  20. - (id)init {
  21. self = [super init];
  22. if (self) {
  23. mPcmData = [NSMutableData data];
  24. readedBytes = 0;
  25. pcmDataBuffer = malloc(EVERY_READ_LENGTH);
  26. synlock = [[NSLock alloc] init];
  27. emptyAudioQueueBufferIndexs = [NSMutableArray arrayWithCapacity:QUEUE_BUFFER_SIZE];
  28. }
  29. return self;
  30. }
  31. - (void)prepare {
  32. isDataInputOver = NO;
  33. readedBytes = 0;
  34. [emptyAudioQueueBufferIndexs removeAllObjects];
  35. }
  36. - (void)startPlay {
  37. [self initAudio];
  38. [self prepare];
  39. OSStatus status = AudioQueueStart(audioQueue, NULL);
  40. if (status) {
  41. [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_PLAY_OVER object:nil];
  42. return;
  43. }
  44. for(int i=0;i<QUEUE_BUFFER_SIZE;i++) {
  45. [self readPCMAndPlay:audioQueue buffer:audioQueueBuffers[i]];
  46. }
  47. /*
  48. audioQueue使用的是驱动回调方式,即通过AudioQueueEnqueueBuffer(outQ, outQB, 0, NULL);传入一个buff去播放,播放完buffer区后通过回调通知用户,
  49. 用户得到通知后再重新初始化buff去播放,周而复始,当然,可以使用多个buff提高效率(测试发现使用单个buff会小卡)
  50. */
  51. }
  52. - (void)stopPlay {
  53. AudioQueueStop(audioQueue, YES);
  54. AudioQueueDispose(audioQueue, YES);
  55. }
  56. - (void)initAudio {
  57. ///设置音频参数
  58. audioDescription.mSampleRate = 8000;//采样率
  59. audioDescription.mFormatID = kAudioFormatLinearPCM;
  60. audioDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
  61. audioDescription.mChannelsPerFrame = 1;///单声道
  62. audioDescription.mFramesPerPacket = 1;//每一个packet一侦数据
  63. audioDescription.mBitsPerChannel = 16;//每个采样点16bit量化
  64. audioDescription.mBytesPerFrame = (audioDescription.mBitsPerChannel/8) * audioDescription.mChannelsPerFrame;
  65. audioDescription.mBytesPerPacket = audioDescription.mBytesPerFrame ;
  66. ///创建一个新的从audioqueue到硬件层的通道
  67. // AudioQueueNewOutput(&audioDescription, AudioPlayerAQInputCallback, self, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &audioQueue);///使用当前线程播
  68. AudioQueueNewOutput(&audioDescription, AudioPlayerAQInputCallback, (__bridge void *)(self), nil, nil, 0, &audioQueue);//使用player的内部线程播
  69. ////添加buffer区
  70. for(int i=0;i<QUEUE_BUFFER_SIZE;i++) {
  71. int result = AudioQueueAllocateBuffer(audioQueue, MIN_SIZE_PER_FRAME, &audioQueueBuffers[i]);///创建buffer区,MIN_SIZE_PER_FRAME为每一侦所需要的最小的大小,该大小应该比每次往buffer里写的最大的一次还大
  72. JPIMLog(@"AudioQueueAllocateBuffer i = %d,result = %d",i,result);
  73. }
  74. }
  75. - (void)inputNewDataFromBuffer:(Byte *)buffer size:(int)bufferSize {
  76. [synlock lock];
  77. [mPcmData appendBytes:buffer length:bufferSize];
  78. [synlock unlock];
  79. if ([emptyAudioQueueBufferIndexs count] > 0) { //如果有空闲的audio queue buffer就尝试填入
  80. [self readPCMAndPlay:audioQueue buffer:audioQueueBuffers[[[emptyAudioQueueBufferIndexs objectAtIndex:0] intValue]]];
  81. }
  82. }
  83. - (void)setIsDataInputOver:(BOOL)dataInputOver {
  84. isDataInputOver = dataInputOver;
  85. if (dataInputOver) {
  86. if ([emptyAudioQueueBufferIndexs count] == QUEUE_BUFFER_SIZE) {
  87. JPIMLog(@"audio queue play over");
  88. [self stopPlay];
  89. [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_PLAY_OVER object:nil];
  90. }
  91. }
  92. }
  93. - (void)readPCMAndPlay:(AudioQueueRef)outQ buffer:(AudioQueueBufferRef)outQB
  94. {
  95. [synlock lock];
  96. NSUInteger lengthLeft = [mPcmData length] - readedBytes; //能够读取的数据长度
  97. if ((lengthLeft < EVERY_READ_LENGTH) && ( ! isDataInputOver)) { //数据不足以填充queue buffer
  98. [self putEmptyBuffer:outQB];
  99. }
  100. else {
  101. if ([emptyAudioQueueBufferIndexs count] > 0) {
  102. if (lengthLeft == 0 && isDataInputOver) {
  103. //所有数据都输入并且读取完了
  104. if ([emptyAudioQueueBufferIndexs count] == QUEUE_BUFFER_SIZE) {
  105. NSLog(@"audio queue play over");
  106. [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_PLAY_OVER object:nil];
  107. }
  108. } else {
  109. [self removeEmptyBuffer:outQB];
  110. NSUInteger readLength = (lengthLeft > EVERY_READ_LENGTH) ? EVERY_READ_LENGTH : lengthLeft;
  111. outQB->mAudioDataByteSize = (int)readLength;
  112. Byte *audioData = (Byte *)outQB->mAudioData;
  113. memcpy(audioData, [mPcmData bytes] + readedBytes, readLength);
  114. readedBytes += readLength;
  115. /*
  116. 将创建的buffer区添加到audioqueue里播放
  117. AudioQueueBufferRef用来缓存待播放的数据区,AudioQueueBufferRef有两个比较重要的参数,AudioQueueBufferRef->mAudioDataByteSize用来指示数据区大小,AudioQueueBufferRef->mAudioData用来保存数据区
  118. */
  119. AudioQueueEnqueueBuffer(outQ, outQB, 0, NULL);
  120. }
  121. }
  122. }
  123. [synlock unlock];
  124. }
  125. - (void)putEmptyBuffer:(AudioQueueBufferRef)buffer {
  126. BOOL isInArray = NO;
  127. int indexValue = [self checkUsedQueueBuffer:buffer];
  128. for (NSNumber *index in emptyAudioQueueBufferIndexs) {
  129. if ([index intValue] == indexValue) {
  130. isInArray = YES;
  131. }
  132. }
  133. if ( ! isInArray) {
  134. [emptyAudioQueueBufferIndexs addObject:[NSNumber numberWithInt:indexValue]];
  135. }
  136. }
  137. - (void)removeEmptyBuffer:(AudioQueueBufferRef)buffer {
  138. int indexValue = [self checkUsedQueueBuffer:buffer];
  139. for (NSNumber *index in emptyAudioQueueBufferIndexs) {
  140. if ([index intValue] == indexValue) {
  141. [emptyAudioQueueBufferIndexs removeObject:index];
  142. return;
  143. }
  144. }
  145. }
  146. - (int)checkUsedQueueBuffer:(AudioQueueBufferRef) qbuf {
  147. int bufferIndex = 0;
  148. if(qbuf == audioQueueBuffers[0]) {
  149. bufferIndex = 0;
  150. }
  151. if(qbuf == audioQueueBuffers[1]) {
  152. bufferIndex = 1;
  153. }
  154. if(qbuf == audioQueueBuffers[2]) {
  155. bufferIndex = 2;
  156. }
  157. if(qbuf == audioQueueBuffers[3]) {
  158. bufferIndex = 3;
  159. }
  160. return bufferIndex;
  161. }
  162. - (void)dealloc {
  163. free(pcmDataBuffer);
  164. }
  165. #pragma mark -
  166. #pragma mark player call back
  167. /*
  168. 试了下其实可以不用静态函数,但是c写法的函数内是无法调用[self ***]这种格式的写法,所以还是用静态函数通过void *input来获取原类指针
  169. 这个回调存在的意义是为了重用缓冲buffer区,当通过AudioQueueEnqueueBuffer(outQ, outQB, 0, NULL);函数放入queue里面的音频文件播放完以后,通过这个函数通知
  170. 调用者,这样可以重新再使用回调传回的AudioQueueBufferRef
  171. */
  172. static void AudioPlayerAQInputCallback(void *input, AudioQueueRef outQ, AudioQueueBufferRef outQB) {
  173. JCHATRawAudioDataPlayer *player = (__bridge JCHATRawAudioDataPlayer *)input;
  174. [player putEmptyBuffer:outQB];
  175. [player readPCMAndPlay:outQ buffer:outQB];
  176. }
  177. @end