基于AudioQueue PCM音频播放器

gitHub 链接地址:https://github.com/WangMing1998/AudioQueuePlayer

1.pcm播放器主要实现类,更具体的请参考Demo

import "WMPlayer.h"

import

import

import

define MIN_SIZE_PER_FRAME 2000 //每个包的大小

define QUEUE_BUFFER_SIZE 3 //缓冲器个数

@interface WMPlayer(){
AudioQueueRef audioQueue; // 音频播放队列
AudioStreamBasicDescription _format; // 音频格式
AudioQueueBufferRef audioQueueBuffers[QUEUE_BUFFER_SIZE]; // 音频缓存
BOOL audioQueueBufferUsed[QUEUE_BUFFER_SIZE]; // 判断音频缓存是否在使用
NSLock *sysnLock; // 同步锁
NSMutableData *tempData; // 缓存数据
OSStatus osState; // 播放器状态
}
@end

@implementation WMPlayer
+(void)initialize{
NSError *error = nil;
AVAudioSession *session = [AVAudioSession sharedInstance];
BOOL ret = [session setCategory:AVAudioSessionCategoryPlayback error:&error];
if (!ret) {
NSLog(@"设置失败");
return;
}
//启用audio session
ret = [session setActive:YES error:&error];
if (!ret){
NSLog(@"启动失败");
return;
}
}

/**
初始化播放器参数
@param sampleRate 音频采样率
@param channels 音频声道数 1位单声道
@param bitsPerChannel 每个采样点的量化数,一般为8或16
@param volume 音量
@return player

*/
-(instancetype)initSampleRate:(Float64)sampleRate
ChannelsNumber:(UInt32)channels
BitsPerChannel:(UInt32)bitsPerChannel
volume:(CGFloat)volume{
if(self = [super init]){
_sampleRate = sampleRate;
_channels = channels;
_volume = volume;
_bitsPerChannel = bitsPerChannel;
[self resetSetting];
}
return self;
}

-(void)resetSetting{
[self stopWithInImmediat:YES];
sysnLock = [[NSLock alloc] init];
_format.mSampleRate = _sampleRate;
_format.mFormatID = kAudioFormatLinearPCM;
_format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger|kAudioFormatFlagIsPacked;
_format.mFramesPerPacket = 1;
_format.mChannelsPerFrame =_channels;
_format.mBitsPerChannel = _bitsPerChannel;//
_format.mBytesPerFrame = (_format.mBitsPerChannel/8) * _format.mChannelsPerFrame;
_format.mBytesPerPacket = _format.mBytesPerFrame * _format.mFramesPerPacket;

// 使用player的内部线程播放,新建输出
osState = AudioQueueNewOutput(&_format, AudioPlayerAQInputCallback, (__bridge void * _Nullable)(self), NULL,NULL, 0, &audioQueue);
if(osState != noErr){
    NSLog(@"AudioQueueNewOutput Fail");
}

NSLog(@"\n采样率:%.f\n通道数:%u\n采样位数:%u\n",_format.mSampleRate,(unsigned int)_format.mChannelsPerFrame,(unsigned int)_format.mBitsPerChannel);

// 设置音量
osState = AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume,_volume);
if(osState != noErr){
    NSLog(@"set Volume Fail");
}
// //初始化音频缓冲区--audioQueueBuffers为结构体数组
for(int i = 0; i < QUEUE_BUFFER_SIZE;i++){
    int result = AudioQueueAllocateBuffer(audioQueue,MIN_SIZE_PER_FRAME, &audioQueueBuffers[i]);
    NSLog(@"AudioQueueAllocateBuffer i = %d,result = %d", i, result);
}

NSLog(@"PCMDataPlayer reset");

}

/**
播放完buffer回调,把buffer状态设为未使用

@param inUserData
@param audioQueueRef 当前播放队列
@param audioQueueBufferRef 已经使用过的buffer
/
static void AudioPlayerAQInputCallback(void
inUserData,AudioQueueRef audioQueueRef, AudioQueueBufferRef audioQueueBufferRef) {
NSLog(@"processAudioData :%u", (unsigned int)audioQueueBufferRef->mAudioDataByteSize);
WMPlayer player = (__bridge WMPlayer)inUserData;
[player playerCallback:audioQueueBufferRef];
}

  • (void)playerCallback:(AudioQueueBufferRef)outQB
    {
    NSLog(@"---消费了buffer:%d---\r\n",outQB->mAudioDataByteSize);
    for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
    if (outQB == audioQueueBuffers[i]) {
    audioQueueBufferUsed[i] = NO;
    }
    }
    }

/**
停止播放

@param inImmediate 是否立刻停止
*/

  • (void)stopWithInImmediat:(BOOL)inImmediate{
    if(audioQueue != nil){
    AudioQueueStop(audioQueue, inImmediate);
    AudioQueueReset(audioQueue);
    audioQueue = nil;
    }
    }

-(void)playWithData:(NSData *)data length:(UInt32)length{
if(audioQueue == nil || ![self checkBufferHasUsed]){
[self resetSetting];
AudioQueueStart(audioQueue,NULL);
}

[sysnLock lock];// 上锁

// data = [self changeMonoToStereo:data muteLeft:YES];
AudioQueueBufferRef audioQueueBuffer = NULL;
while(true){// 取出音频缓存数组
audioQueueBuffer = [self getNotUsedBuffer];
if(audioQueueBuffer != NULL){
break;
}
}
// 将数据填充到缓存数组并加入队列
audioQueueBuffer->mAudioDataByteSize = length;// 填充数据长度
Byte *audioData = audioQueueBuffer->mAudioData;// 数据指针指向填充的数据
memcpy(audioData,[data bytes],length);// 给audioData赋值即是给audioQueueBuffer赋值
AudioQueueEnqueueBuffer(audioQueue,audioQueueBuffer,0, NULL);// 填充buffer,系统自动播放

[sysnLock unlock];

}

  • (AudioQueueBufferRef)getNotUsedBuffer
    {
    for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
    if (NO == audioQueueBufferUsed[i]) {
    audioQueueBufferUsed[i] = YES;
    NSLog(@"PCMDataPlayer play buffer index:%d", i);
    return audioQueueBuffers[i];
    }
    }
    return NULL;
    }

  • (BOOL)checkBufferHasUsed
    {
    for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
    if (YES == audioQueueBufferUsed[i]) {
    return YES;
    }
    }
    NSLog(@"Player 播放中断............");
    return NO;
    }

/**
声音播放出现问题的时候 重置播放器
*/

  • (void)resetPlay{
    if(audioQueue != nil){
    AudioQueueReset(audioQueue);
    }
    }

  • (void)dealloc
    {
    if (audioQueue != nil) {
    AudioQueueStop(audioQueue, true);
    }
    audioQueue = nil;
    sysnLock = nil;
    NSLog(@"WMPlayer dealloc...");
    }

pragma mark 单声道转双声道,静音其中一声道

/**
双通道转单通道,将其中一通道填0即是静音

@param srcData 输入数据
@param muteleft 禁止通道
@return 新的音频数据
*/

  • (NSData *) changeMonoToStereo:(NSData ) srcData muteLeft:(BOOL )muteleft
    {
    NSMutableData * mdata = [[NSMutableData alloc] initWithCapacity:srcData.length
    2];
    Byte muteData[2] ={0x00,0x00};
    Byte * srcDatap= (Byte *)srcData.bytes;
    for (long i=0;i if (muteleft) {
    [mdata appendBytes:muteData length:2];
    [mdata appendBytes:srcDatap length:2];
    }
    else{
    [mdata appendBytes:srcDatap length:2];
    [mdata appendBytes:muteData length:2];
    }
    srcDatap +=2;
    }
    return [mdata subdataWithRange:NSMakeRange(0, mdata.length)];
    }

pragma mark getter dataQueue

-(NSMutableData *)dataQueue{
if(_dataQueue == nil){
_dataQueue = [NSMutableData data];
}
return _dataQueue;
}

// 添加播放数据。配合下面方法使用,用于FIFO播放音频流
-(void)addBufferWithData:(NSData *)buffer{
NSLog(@"添加buffer,length = %ld",buffer.length);
@synchronized(self.dataQueue)
{
[self.dataQueue appendData:buffer];
NSLog(@"commandLstLength:%ld\n",self.dataQueue.length);

}

}

// 每次读取多大的数据块
-(NSData *)readBufferWithSize:(NSInteger)length isEof:(BOOL *) eof
{
*eof = NO;
@synchronized(self.dataQueue)
{
NSMutableData * data = [[NSMutableData alloc] init];
Byte replacesample = 0x00;
NSInteger minlength = length > _dataQueue.length ? _dataQueue.length:length;
if (_dataQueue.length > 0) {

        [data appendData:[_dataQueue subdataWithRange:NSMakeRange(0, minlength)]];
        NSLog(@"队列长度:%ld",_dataQueue.length);
        [_dataQueue replaceBytesInRange:NSMakeRange(0,minlength) withBytes:NULL length:0];
    }

// long rewindSamplesCount = (length - minlength)/2;
// if(minlength != length)
// {
// // for(NSInteger i=0;i< length - minlength;i++)
// // [data appendBytes:&replacesample length:1];
// int16_t posiSample = 10000;
// int16_t negeSample = -10000;
// for(NSInteger i=0;i // {
// if(i%2 == 0){
// [data appendBytes:&posiSample length:2];
// }else{
// [data appendBytes:&negeSample length:2];
// }
// }
// }

    return data;
}

}

@end

  1. 测试代码,主要PCM音频格式必须跟设置的一样,否则会出现变声或无法播放现象
    player = [[WMPlayer alloc] initSampleRate:8000 ChannelsNumber:1 BitsPerChannel:8 volume:1.0];
    NSString *path = [[NSBundle mainBundle] pathForResource:@"8000K-8bit-1channels.pcm" ofType:nil];
    NSData *data = [NSData dataWithContentsOfFile:path];
    [player addBufferWithData:data];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(60 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    player = nil;
    player = [[WMPlayer alloc] initSampleRate:8000 ChannelsNumber:1 BitsPerChannel:16 volume:1.0];
    NSString *path = [[NSBundle mainBundle] pathForResource:@"8000K-16bit-1channels.pcm" ofType:nil];
    NSData *data = [NSData dataWithContentsOfFile:path];
    [player addBufferWithData:data];
    });

3.如有任何问题或更好的建议跟我联系哦。
QQ:286241793

你可能感兴趣的:(基于AudioQueue PCM音频播放器)