美文网首页
iOS Audio Queues获取音频数据并写入文件

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

作者: Good_Citizen | 来源:发表于2019-12-12 15:03 被阅读0次

    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获取音频数据并写入文件

          本文链接:https://www.haomeiwen.com/subject/nnhlgctx.html