【iOS录音与播放】实现利用音频队…

11 篇文章 0 订阅

转自 http://www.cnblogs.com/anjohnlv/p/3383908.html

都说iOS最恶心的部分是流媒体,其中恶心的恶心之处更在即时语音。

所以我们先不谈即时语音,研究一下,iOS中声音采集与播放的实现。

要在iOS设备上实现录音和播放功能,苹果提供了简单的做法,那就是利用AVAudioRecorder和AVAudioPlayer。度娘大多数也是如此。但是这种方法有很大的局限性。单说说这种做法:录音,首先得设置录音文件路径,然后录音数据直接写入了文件。播放也是首先给出文件路径,等到音频整个加载完成了,才能开始播放。这相当不灵活。

我的做法是利用音频队列AudioQueue,将声音暂存至缓冲区,然后从缓冲区取出音频数据,进行播放。

声音采集:

使用AudioQueue框架以队列的形式处理音频数据。因此使用时需要给队列分配缓存空间,由回调(Callback)函数完成向队列缓存读写音频数据的功能。

一个Recording Audio Queue,包括Buffer(缓冲器)组成的BufferQueue(缓冲队列),以及一个Callback(回调)。实现主要步骤为:

  1. 设置音频的参数

  2. 准备并启动声音采集的音频队列

  3. 在回调函数中处理采集到的音频Buffer,在这里是暂存在了一个Byte数组里,提供给播放端使用


Record.h

#import

#import

#import

#import "AudioConstant.h"

 

// use Audio Queue

 

typedef struct AQCallbackStruct

{

    AudioStreamBasicDescriptionmDataFormat;

    AudioQueueRef              queue;

    AudioQueueBufferRef        mBuffers[kNumberBuffers];

    AudioFileID                outputFile;

     

    unsignedlong               frameSize;

    long long                   recPtr;

    int                         run;

     

} AQCallbackStruct;

 

 

@interface Record : NSObject

{

    AQCallbackStructaqc;

    AudioFileTypeIDfileFormat;

    long audioDataLength;

    ByteaudioByte[999999];

    long audioDataIndex;

}

- (id) init;

- (void) start;

- (void) stop;

- (void) pause;

- (Byte *) getBytes;

- (void) processAudioBuffer:(AudioQueueBufferRef) bufferwithQueue:(AudioQueueRef) queue;

 

@property (nonatomic, assign) AQCallbackStructaqc;

@property (nonatomic, assign)long audioDataLength;

@end


Record.mm

#import "Record.h"

 

@implementation Record

@synthesize aqc;

@synthesize audioDataLength;

 

static void AQInputCallback(void                   *inUserData,

                             AudioQueueRef         inAudioQueue,

                             AudioQueueBufferRef   inBuffer,

                             const AudioTimeStamp  * inStartTime,

                             unsignedlong          inNumPackets,

                             const AudioStreamPacketDescription* inPacketDesc)

{

     

    Record* engine = (__bridge Record *) inUserData;

    if (inNumPackets> 0)

    {

        [engineprocessAudioBuffer:inBuffer withQueue:inAudioQueue];

    }

     

    if (engine.aqc.run)

    {

        AudioQueueEnqueueBuffer(engine.aqc.queue,inBuffer, 0, NULL);

    }

}

 

- (id) init

{

    self =[super init];

    if (self)

    {

        aqc.mDataFormat.mSampleRate= kSamplingRate;

        aqc.mDataFormat.mFormatID= kAudioFormatLinearPCM;

        aqc.mDataFormat.mFormatFlags= kLinearPCMFormatFlagIsSignedInteger|kLinearPCMFormatFlagIsPacked;

        aqc.mDataFormat.mFramesPerPacket= 1;

        aqc.mDataFormat.mChannelsPerFrame= kNumberChannels;

        aqc.mDataFormat.mBitsPerChannel= kBitsPerChannels;

        aqc.mDataFormat.mBytesPerPacket= kBytesPerFrame;

        aqc.mDataFormat.mBytesPerFrame= kBytesPerFrame;

        aqc.frameSize= kFrameSize;

         

        AudioQueueNewInput(&aqc.mDataFormat,AQInputCallback, (__bridge void *)(self), NULL,kCFRunLoopCommonModes,0, &aqc.queue);

         

        for (int i=0;i

        {

            AudioQueueAllocateBuffer(aqc.queue,aqc.frameSize, &aqc.mBuffers[i]);

            AudioQueueEnqueueBuffer(aqc.queue,aqc.mBuffers[i], 0, NULL);

        }

        aqc.recPtr= 0;

        aqc.run= 1;

    }

    audioDataIndex= 0;

    return self;

}

 

- (void) dealloc

{

    AudioQueueStop(aqc.queue,true);

    aqc.run= 0;

    AudioQueueDispose(aqc.queue,true);

}

 

- (void) start

{

    AudioQueueStart(aqc.queue,NULL);

}

 

- (void) stop

{

    AudioQueueStop(aqc.queue,true);

}

 

- (void) pause

{

    AudioQueuePause(aqc.queue);

}

 

- (Byte *)getBytes

{

    return audioByte;

}

 

- (void) processAudioBuffer:(AudioQueueBufferRef) bufferwithQueue:(AudioQueueRef) queue

{

    NSLog(@"processAudioData:%ld", buffer->mAudioDataByteSize);

    //处理data:忘记oc怎么copy内存了,于是采用的C++代码,记得把类后缀改为.mm。同Play

    memcpy(audioByte+audioDataIndex,buffer->mAudioData, buffer->mAudioDataByteSize);

    audioDataIndex+=buffer->mAudioDataByteSize;

    audioDataLength= audioDataIndex;

}

 

@end

声音播放:

同采集一样,播放主要步骤如下:

  1. 设置音频参数(需和采集时设置参数一样)

  2. 取得缓存的音频Buffer

  3. 准备并启动声音播放的音频队列

  4. 在回调函数中处理Buffer


Play.h

#import

#import

 

#import "AudioConstant.h"

 

@interface Play : NSObject

{

    //音频参数

    AudioStreamBasicDescriptionaudioDescription;

    //音频播放队列

    AudioQueueRefaudioQueue;

    //音频缓存

    AudioQueueBufferRefaudioQueueBuffers[QUEUE_BUFFER_SIZE];

}

 

-(void)Play:(Byte *)audioByte Length:(long)len;

 

@end

 

Play.mm

 

#import "Play.h"

 

@interface Play()

{

    Byte*audioByte;

    long audioDataIndex;

    long audioDataLength;

}

@end

 

@implementation Play

 

//回调函数(Callback)的实现

static void BufferCallback(void *inUserData,AudioQueueRefinAQ,AudioQueueBufferRef buffer){

     

    NSLog(@"processAudioData:%u", (unsigned int)buffer->mAudioDataByteSize);

     

    Play*player=(__bridge Play*)inUserData;

     

    [playerFillBuffer:inAQ queueBuffer:buffer];

}

 

//缓存数据读取方法的实现

-(void)FillBuffer:(AudioQueueRef)queuequeueBuffer:(AudioQueueBufferRef)buffer

{

    if(audioDataIndex+ EVERY_READ_LENGTH < audioDataLength)

    {

        memcpy(buffer->mAudioData,audioByte+audioDataIndex, EVERY_READ_LENGTH);

        audioDataIndex+= EVERY_READ_LENGTH;

        buffer->mAudioDataByteSize=EVERY_READ_LENGTH;

        AudioQueueEnqueueBuffer(queue,buffer, 0, NULL);

    }

     

}

 

-(void)SetAudioFormat

{

    ///设置音频参数

    audioDescription.mSampleRate = kSamplingRate;//采样率

    audioDescription.mFormatID   = kAudioFormatLinearPCM;

    audioDescription.mFormatFlags= kAudioFormatFlagIsSignedInteger;//|kAudioFormatFlagIsNonInterleaved;

    audioDescription.mChannelsPerFrame= kNumberChannels;

    audioDescription.mFramesPerPacket = 1;//每一个packet一侦数据

    audioDescription.mBitsPerChannel  =kBitsPerChannels;//av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)*8;//每个采样点16bit量化

    audioDescription.mBytesPerFrame   = kBytesPerFrame;

    audioDescription.mBytesPerPacket  = kBytesPerFrame;

     

    [self CreateAudioQueue];

}

 

-(void)CreateAudioQueue

{

    [self Cleanup];

    //使用player的内部线程播

    AudioQueueNewOutput(&audioDescription,BufferCallback, (__bridge void *)(self), nil, nil,0, &audioQueue);

    if(audioQueue)

    {

        添加buffer区

        for(int i=0;i

        {

            int result=  AudioQueueAllocateBuffer(audioQueue,EVERY_READ_LENGTH, &audioQueueBuffers[i]);

            ///创建buffer区,MIN_SIZE_PER_FRAME为每一侦所需要的最小的大小,该大小应该比每次往buffer里写的最大的一次还大

            NSLog(@"AudioQueueAllocateBufferi = %d,result = %d",i,result);

        }

    }

}

 

-(void)Cleanup

{

    if(audioQueue)

    {

        NSLog(@"ReleaseAudioQueueNewOutput");

         

        [self Stop];

        for(int i=0;i < QUEUE_BUFFER_SIZE; i++)

        {

            AudioQueueFreeBuffer(audioQueue,audioQueueBuffers[i]);

            audioQueueBuffers[i]= nil;

        }

        audioQueue= nil;

    }

}

 

-(void)Stop

{

    NSLog(@"AudioPlayer Stop");

     

    AudioQueueFlush(audioQueue);

    AudioQueueReset(audioQueue);

    AudioQueueStop(audioQueue,TRUE);

}

 

-(void)Play:(Byte *)byte Length:(long)len

{

    [self Stop];

    audioByte= byte;

    audioDataLength= len;

     

    NSLog(@"AudioPlay Start >>>>>");

     

    [self SetAudioFormat];

     

    AudioQueueReset(audioQueue);

    audioDataIndex= 0;

    for(int i=0;i

    {

        [self FillBuffer:audioQueuequeueBuffer:audioQueueBuffers[i]];

    }

    AudioQueueStart(audioQueue,NULL);

}

 

@end

以上,实现了通过内存缓存,声音的采集和播放,包括了声音采集,暂停,结束,播放等主要流程。

PS:由于本人水品有限加之这方面资料较少,只跑通了正常流程,暂时没做异常处理。采集的声音Buffer限定大小每次只有十来秒钟的样子,这个留给需要的人自己去优化了。

demo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值