iOS Audio Queues获取音频数据并写入文件

AudioQueue的工作模式

在使用AudioQueue之前首先必须理解其工作模式,它之所以这么命名是因为在其内部有一套缓冲队列(Buffer Queue)的机制。在AudioQueue启动之后需要通过AudioQueueAllocateBuffer生成若干个AudioQueueBufferRef结构,这些Buffer将用来存储即将要播放的音频数据,并且这些Buffer是受生成他们的AudioQueue实例管理的,内存空间也已经被分配(按照Allocate方法的参数),当AudioQueue被Dispose时这些Buffer也会随之被销毁。

当有音频数据需要被播放时首先需要被memcpy到AudioQueueBufferRef的mAudioData中(mAudioData所指向的内存已经被分配,之前AudioQueueAllocateBuffer所做的工作),并给mAudioDataByteSize字段赋值传入的数据大小。完成之后需要调用AudioQueueEnqueueBuffer把存有音频数据的Buffer插入到AudioQueue内置的Buffer队列中。在Buffer队列中有buffer存在的情况下调用AudioQueueStart,此时AudioQueue就回按照Enqueue顺序逐个使用Buffer队列中的buffer进行播放,每当一个Buffer使用完毕之后就会从Buffer队列中被移除并且在使用者指定的RunLoop上触发一个回调来告诉使用者,某个AudioQueueBufferRef对象已经使用完成,你可以继续重用这个对象来存储后面的音频数据。如此循环往复音频数据就会被逐个播放直到结束。

这里封装了一个c++文件类,对外提供两个接口

    bool startCollect();  //开启录制

    void stopCollect();  //停止录制

对内的方法和成员

    AudioQueueRef mQueue;

    AudioQueueBufferRef mBuffers;  //装载采集到的音频数据

    AudioStreamBasicDescription mRecordFormat;   //音频数据输入格式结构体(下面有对应的介绍)

    bool m_bAudioPlayFlag;                

    PGThread m_SendThread; 

    NSMutableArray *m_inputArray;

    void SetupAudioFormat(UInt32 inFormatID);  //配置音频数据输出格式参数

    static void MyInputBufferHandler(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,                                                    const AudioTimeStamp *inStartTime, UInt32 inNumPackets,  const AudioStreamPacketDescription* inPacketDesc);  //采集到音频数据的回到方法

structAudioStreamBasicDescription{

    Float64 mSampleRate;// 采样率 :Hz

    AudioFormatID mFormatID;// 采样数据的类型,PCM,AAC等

    AudioFormatFlags mFormatFlags;// 每种格式特定的标志,无损编码 ,0表示没有

    UInt32 mBytesPerPacket;// 一个数据包中的字节数

    UInt32 mFramesPerPacket;// 一个数据包中的帧数,每个packet的帧数。如果是未压缩的音频数据,值是1。动态帧率格式,这个值是一个较大的固定数字,比如说AAC的1024。如果是动态大小帧数(比如Ogg格式)设置为0。

    UInt32 mBytesPerFrame;// 每一帧中的字节数

    UInt32 mChannelsPerFrame;// 每一帧数据中的通道数,单声道为1,立体声为2

    UInt32 mBitsPerChannel;// 每个通道中的位数,1byte = 8bit

    UInt32 mReserved;// 8字节对齐,填0

};typedefstructAudioStreamBasicDescription AudioStreamBasicDescription;

对外构造方法中初始化相关数据

AudioInput::AudioInput()

{

    m_bAudioPlayFlag = false;  //是否开启录制

    m_inputArray= [[NSMutableArray alloc] init];  //存储采集到的音频数据NSData

//创建线程来发送采集到的数据到设备端(相当于服务端),写入到文件就不需要这里

    GThread::ThreadFunc runSendFunction(bind(&AudioInput::send,this));

    m_SendThread=new GThread(runSendFunction,"sendThread");

    m_SendThread->setPalpitationTime(50);

}

开始录制

bool AudioInput::startCollect()

{

    m_SendThread->start();  //启动发送数据线程

    SetupAudioFormat(kAudioFormatLinearPCM);   //配置音频数据输出格式参数

    OSStatusstate =AudioQueueNewInput(&mRecordFormat,AudioInput::MyInputBufferHandler,this,NULL,NULL,0, &mQueue);  //开始采集,传入音频参数、回调方法以及音频采集队列

    if(state !=noErr){

        return false;

    }

    intbufferByteSize =5*1024;

    state =AudioQueueAllocateBuffer(mQueue, bufferByteSize, &mBuffers);

    if(state !=noErr){

        return false;

    }

    state =AudioQueueEnqueueBuffer(mQueue, mBuffers, 0, NULL);

    if(state !=noErr){

        return false;

    }

    state =AudioQueueStart(mQueue,NULL);

    if(state !=noErr){

        return false;

    }

    m_bAudioPlayFlag = true;

    return true;

}

配置音频输出参数

void AudioInput::SetupAudioFormat(UInt32inFormatID)

{

    memset(&mRecordFormat, 0, sizeof(mRecordFormat));


UInt32 size = sizeof(mRecordFormat.mSampleRate);

AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate, &size, &mRecordFormat.mSampleRate);

    mRecordFormat.mSampleRate=8000;

size =sizeof(mRecordFormat.mChannelsPerFrame);

AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputNumberChannels,  &size, &mRecordFormat.mChannelsPerFrame);

mRecordFormat.mFormatID= inFormatID;

if (inFormatID == kAudioFormatLinearPCM){

// if we want pcm, default to signed 16-bit little-endian

mRecordFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;

mRecordFormat.mBitsPerChannel = 16;

mRecordFormat.mBytesPerPacket = mRecordFormat.mBytesPerFrame = (mRecordFormat.mBitsPerChannel / 8) * mRecordFormat.mChannelsPerFrame;

mRecordFormat.mFramesPerPacket = 1;

}

}

采集数据后的回调方法

void AudioInput::MyInputBufferHandler(void*inUserData,AudioQueueRefinAQ,AudioQueueBufferRefinBuffer,

                                      constAudioTimeStamp*inStartTime,UInt32inNumPackets,

                                      constAudioStreamPacketDescription*inPacketDesc)

{

    AudioInput*aqr = (AudioInput*)inUserData;   //当前this

    if(inNumPackets >0){

       NSData*data = [NSData dataWithBytes:(char*)inBuffer->mAudioDatalength:inBuffer->mAudioDataByteSize];

        [handle seekToEndOfFile];

         [handlewriteData:data];  //将data数据写入到文件中

         [handlecloseFile];

        [m_inputArray addObject:data];   // 放到数组中,以便发送数据线程从数组中获取

    }

    AudioQueueEnqueueBuffer(inAQ, inBuffer,0,NULL);

}

创建文件

       staticNSString*aPath =nil;

      aPath = [NSString stringWithFormat:@"%@/Documents/%@",NSHomeDirectory(),@"test.pcm"];

            if(![fileMfileExistsAtPath:aPath]) {

                [fileM createFileAtPath:aPath contents:nil attributes:nil];

            }

            handle = [NSFileHandle fileHandleForWritingAtPath:aPath];

发送数据线程

int AudioInput::send()

{

    if (m_inputArray.count != 0 && m_bAudioPlayFlag)

    {

        NSData*data = [m_inputArray objectAtIndex:0];

        [m_inputArray removeObject:data];

//        发送数据到设备端

//        。。。。。。。。

    }

    return0;

}

停止录制

void AudioInput::stopCollect()

{

    if(!m_bAudioPlayFlag){

        return;

    }

    m_SendThread->stop();

    m_bAudioPlayFlag = false;

    AudioQueueStop(mQueue, false);

    AudioQueueDispose(mQueue, false);

}

析构方法

AudioInput::~AudioInput()

{

        stopCollect();    

}

写入到文件的pcm数据可以通过Cool Edit Pro工具来播放

你可能感兴趣的:(iOS Audio Queues获取音频数据并写入文件)