iOS 通过Audio Queue播放音频数据

这是一篇干货,是直接应用的关于audio queue 播放

先看下面一张运行时的流程原理图:

playback_callback_function_2x.png

上图中的步骤:
1、准备播放的音频队列,为每个音频队列缓冲区(Buffer)进行数据填充;
2、当启用AudioQueueStart时,即刻进行播放数据;
3、将队列里第一个缓冲的buffer发送到音频输出区
4、播放队列进入循环模式,音频队列可以进行下一个的音频缓冲区播放
5、回调告诉上层缓冲的buffer已被使用了,然后可以进行下一次的缓冲
6、待上一个已被播放了的音频buffer释放后再次填充buffer

1、设置宏定义

#define MIN_SIZE_PER_FRAME 2000
#define QUEUE_BUFFER_NUM 3

2、定义相关的属性

    AudioQueueRef audioQueue;
    AudioStreamBasicDescription _audioDescription;
    AudioQueueBufferRef audioQueueBuffers[QUEUE_BUFFER_NUM];
    BOOL audioqueueBufferUsed[QUEUE_BUFFER_NUM];
    NSLock *sysLock;
    NSMutableData *tempData;
    OSStatus osState;

3、初始化音频播放的参数

  _audioDescription.mSampleRate = 8000;//采样率
            _audioDescription.mFormatID = kAudioFormatLinearPCM;//音频格式
            _audioDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;//保存音频数据的方式的说明
            _audioDescription.mChannelsPerFrame = 1; //1单声道 2双声道
            _audioDescription.mFramesPerPacket = 1;//每一个packet指定只有一帧数据,每个数据包的帧数
            _audioDescription.mBitsPerChannel = 16;//采样点,语音每个采样点的占用位数
            _audioDescription.mBytesPerFrame = (_audioDescription.mBitsPerChannel/8) * _audioDescription.mChannelsPerFrame;
            _audioDescription.mBytesPerPacket = _audioDescription.mBytesPerFrame * _audioDescription.mFramesPerPacket;
 AudioQueueNewOutput(&_audioDescription, AudioPlayerAQInputCallback, (__bridge void*)self, NULL, kCFRunLoopCommonModes, 0, &audioQueue);
        //设置AudioSessionInitialize


        // 设置音量
        AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1.0);
        
        //初始化缓冲区
        for (int i = 0; i < QUEUE_BUFFER_NUM; i++) {
            audioqueueBufferUsed[i] = false;
            osState = AudioQueueAllocateBuffer(audioQueue, MIN_SIZE_PER_FRAME, &audioQueueBuffers[i]);
            printf("第 %d 个AudioQueueAllocateBuffer 初始化结果 %d (0表示成功)\n", i + 1, osState);
        }
        
        osState = AudioQueueStart(audioQueue, NULL);
        if (osState != noErr) {
            NSLog(@"Audio Queue Start Failed");
        }

4、设置相关的回调函数,用于设置数据的回调

// 回调回来把buffer状态设为未使用
static void AudioPlayerAQInputCallback(void* inUserData,AudioQueueRef audioQueueRef, AudioQueueBufferRef audioQueueBufferRef) {
    
    ECAudioQueuePlayer* player = (__bridge ECAudioQueuePlayer*)inUserData;
    
    [player resetBufferState:audioQueueRef and:audioQueueBufferRef];
}

- (void)resetBufferState:(AudioQueueRef)audioQueueRef and:(AudioQueueBufferRef)audioQueueBufferRef {
    
    for (int i = 0; i < QUEUE_BUFFER_NUM; i++) {
        // 将这个buffer设为未使用
        if (audioQueueBufferRef == audioQueueBuffers[i]) {
            audioqueueBufferUsed[i] = false;
        }
    }
}

6、输入播放数据

-(void)playWithData:(NSData *)data{
    [sysLock lock];
    tempData = nil;
    tempData = [NSMutableData new];
    [tempData appendData:data];
    NSUInteger len = tempData.length;
    Byte *bytes = (Byte *)malloc(len);
    [tempData getBytes:bytes length:len];
    int i = 0;
    while (true) {
        if (!audioqueueBufferUsed[i]) {
            audioqueueBufferUsed[i] = true;
            break;
        }else{
            i++;
            if (i>=QUEUE_BUFFER_NUM) {
                i = 0;
            }
        }
    }
    
    audioQueueBuffers[i]->mAudioDataByteSize = (unsigned int)len;
    memcpy(audioQueueBuffers[i]->mAudioData, bytes, len);
    
    free(bytes);
    AudioQueueEnqueueBuffer(audioQueue, audioQueueBuffers[i], 0, NULL);
    [sysLock unlock];

}

你可能感兴趣的:(iOS 通过Audio Queue播放音频数据)