美文网首页
iOS录音实时抛出buffer,同时转码成WAV

iOS录音实时抛出buffer,同时转码成WAV

作者: 小程故事多又多 | 来源:发表于2019-07-14 00:59 被阅读0次

    最近在做一个录音插件,要求实时获取PCMBuffer,分析buffer ,并且同时转换成wav 文件

    设置录音相关信息

    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:nil];
    [[AVAudioSession sharedInstance] setActive:YES error:nil];
    
    AudioStreamBasicDescription  streamDes;
    // 采样率
    streamDes.mSampleRate = sampleRate;
    // PCM 格式
    streamDes.mFormatID = kAudioFormatLinearPCM;
    streamDes.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
    // 1:单声道;2:立体声
    streamDes.mChannelsPerFrame = nChannel;
    // 语音每采样点占用位数
    streamDes.mBitsPerChannel = 16;
    //每桢的bytes数
    streamDes.mBytesPerFrame = (streamDes.mBitsPerChannel / 8)  * streamDes.mChannelsPerFrame;
    //每个数据包下的桢数,即每个数据包里面有多少桢
    streamDes.mFramesPerPacket = 1;
    //每个数据包的bytes总数,每桢的bytes数*每个数据包的桢数
    streamDes.mBytesPerPacket = streamDes.mBytesPerFrame * streamDes.mFramesPerPacket;
    

    输出

    AudioQueueNewInput(&streamDes, InputCallback,(__bridge void * _Nullable)(self), nil, nil, 0, &recordQueue);
    for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
            AudioQueueAllocateBuffer(recordQueue, buffersize * 2, rBuffer+i);
            AudioQueueEnqueueBuffer(recordQueue, rBuffer[i], 0, nil);
     }
    

    启动录音

      AudioQueueStart(recordQueue, 0);
    

    拿到BUffer回调

    void InputCallback(
                       void * __nullable               inUserData,
                       AudioQueueRef                   inAQ,
                       AudioQueueBufferRef             inBuffer,
                       const AudioTimeStamp *          inStartTime,
                       UInt32                          inNumberPacketDescriptions,
                       const AudioStreamPacketDescription * __nullable inPacketDescs)
    {
            Recorder * recorder = (__bridge Recorder*)(inUserData);
    //统计buffer数量
            recorder-> bufferIndex ++;
            //写入WAV
            if (recorder->isToWAV) {
                dispatch_queue_t queue = dispatch_queue_create("WAVQueue", DISPATCH_QUEUE_SERIAL);
                dispatch_sync(queue, ^{
                    [recorder PCMtoWAV:inBuffer IONumPackets:inNumberPacketDescriptions tag:recorder->bufferIndex];
                    NSLog(@"Towav%@ ",[NSThread currentThread]);
                });
            }
    
     //抛出Buffer 进行测试
            if (recorder->isPictchDetector){
                dispatch_queue_t queue = dispatch_queue_create("PictchQueue", DISPATCH_QUEUE_SERIAL);
                dispatch_sync(queue, ^{
                    [recorder pictchDetector:inBuffer->mAudioData tag:recorder->bufferIndex];
                });
            }
            NSLog(@"InputCallback  %d",recorder-> bufferIndex);
            AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nil);
    }
    

    //pcm 转 Wav
    wav 就是在PCM的基础上加上对应的文件头 本编的重点在于 根据动态来的BUffer 实时拼接在File尾部 同时修改 wav 的文件头
    fseek 用来移动file的光标 fwrite 用来在对应的光标位置写入对应的数据

    WavFile = fopen([fileName cStringUsingEncoding:1], "wb");
    
    - (void)PCMtoWAV:(AudioQueueBufferRef)buffer IONumPackets:(UInt32)ionumpackets tag:(NSInteger)tag{
        wavDatalen += ionumpackets * 2;
        //wav头data
        NSData * data = [self GetWavheadData:wavDatalen+80 totalDataLen:wavDatalen longSampleRate:sampleRate  channels:nChannel byteRate:byteRate];
       
    //写入头文件  光标移至头部
        fseek(WavFile, 0, 0);
        fwrite([data bytes], 44 * sizeof(Byte), 1, WavFile);
    //写入Pcm data 光标移至尾部
        fseek(WavFile, 0, 2);
        fwrite(buffer->mAudioData, ionumpackets * sizeof(Byte), 2, WavFile);    
    }
    

    拼接的wav头文件 重点在于传入自己录音的配置参数与对应的data长度 生成头文件

    - (NSData*)GetWavheadData:(NSInteger)totalAudioLen totalDataLen:(NSInteger)totalDataLen longSampleRate:(NSInteger)longSampleRate channels:(NSInteger)channels byteRate:(NSInteger)byteRate{
        
        Byte  header[44];
        //4byte,资源交换文件标志:RIFF
        header[0] = 'R';  // RIFF/WAVE header
        header[1] = 'I';
        header[2] = 'F';
        header[3] = 'F';
        //4byte,从下个地址到文件结尾的总字节数
        header[4] = (Byte) (totalDataLen & 0xff);  //file-size (equals file-size - 8)
        header[5] = (Byte) ((totalDataLen >> 8) & 0xff);
        header[6] = (Byte) ((totalDataLen >> 16) & 0xff);
        header[7] = (Byte) ((totalDataLen >> 24) & 0xff);
        //4byte,wav文件标志:WAVE
        header[8] = 'W';  // Mark it as type "WAVE"
        header[9] = 'A';
        header[10] = 'V';
        header[11] = 'E';
        //4byte,波形文件标志:FMT(最后一位空格符)
        header[12] = 'f';  // Mark the format section 'fmt ' chunk
        header[13] = 'm';
        header[14] = 't';
        header[15] = ' ';
        //4byte,音频属性
        header[16] = 16;   // 4 bytes: size of 'fmt ' chunk, Length of format data.  Always 16
        header[17] = 0;
        header[18] = 0;
        header[19] = 0;
        //2byte,格式种类(1-线性pcm-WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM)
        header[20] = 1;  // format = 1 ,Wave type PCM
        header[21] = 0;
        //2byte,通道数
        header[22] = (Byte) channels;  // channels
        header[23] = 0;
        //4byte,采样率
        header[24] = (Byte) (longSampleRate & 0xff);
        header[25] = (Byte) ((longSampleRate >> 8) & 0xff);
        header[26] = (Byte) ((longSampleRate >> 16) & 0xff);
        header[27] = (Byte) ((longSampleRate >> 24) & 0xff);
        //4byte 传输速率,Byte率=采样频率*音频通道数*每次采样得到的样本位数/8,00005622H,也就是22050Byte/s=11025*1*16/8。
        header[28] = (Byte) (byteRate & 0xff);
        header[29] = (Byte) ((byteRate >> 8) & 0xff);
        header[30] = (Byte) ((byteRate >> 16) & 0xff);
        header[31] = (Byte) ((byteRate >> 24) & 0xff);
        //2byte   一个采样多声道数据块大小,块对齐=通道数*每次采样得到的样本位数/8,0002H,也就是2=1*16/8
        header[32] = (Byte) (channels * 16 / 8);
        header[33] = 0;
        //2byte,采样精度-PCM位宽
        header[34] = 16; // bits per sample
        header[35] = 0;
        //4byte,数据标志:data
        header[36] = 'd'; //"data" marker
        header[37] = 'a';
        header[38] = 't';
        header[39] = 'a';
        //4byte,从下个地址到文件结尾的总字节数,即除了wav header以外的pcm data length(纯音频数据)
        header[40] = (Byte) (totalAudioLen & 0xff);  //data-size (equals file-size - 44).
        header[41] = (Byte) ((totalAudioLen >> 8) & 0xff);
        header[42] = (Byte) ((totalAudioLen >> 16) & 0xff);
        header[43] = (Byte) ((totalAudioLen >> 24) & 0xff);
        
        return [[NSData alloc] initWithBytes:header length:44];;
    }
    

    相关文章

      网友评论

          本文标题:iOS录音实时抛出buffer,同时转码成WAV

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