使用AUHAL音频单元将字节写入音频文件

我正尝试从我的macbook(内置麦克风)的默认输入设备获得的声音输入中创建一个wav文件。 但是,作为原始数据导入到大胆的结果文件是完整的垃圾。

首先我初始化音频文件参考,以便稍后在音频单元输入回调中写入它。

   // struct contains audiofileID as member
   MyAUGraphPlayer player = {0};
   player.startingByte = 0;

   // describe a PCM format for audio file
   AudioStreamBasicDescription format =  { 0 };
   format.mBytesPerFrame = 2;
   format.mBytesPerPacket = 2;
   format.mChannelsPerFrame = 1;
   format.mBitsPerChannel = 16;
   format.mFramesPerPacket = 1;
   format.mFormatFlags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsFloat;
   format.mFormatID = kAudioFormatLinearPCM;

   CFURLRef myFileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR("./test.wav"), kCFURLPOSIXPathStyle, false);
   //CFShow (myFileURL);
   CheckError(AudioFileCreateWithURL(myFileURL,
                                     kAudioFileWAVEType,
                                     &format,
                                     kAudioFileFlags_EraseFile,
                                     &player.recordFile), "AudioFileCreateWithURL failed");

在这里我malloc一些缓冲区来保存来自AUHAL单元的音频数据。

UInt32 bufferSizeFrames = 0;
   propertySize = sizeof(UInt32);
   CheckError (AudioUnitGetProperty(player->inputUnit,
                                    kAudioDevicePropertyBufferFrameSize,
                                    kAudioUnitScope_Global,
                                    0,
                                    &bufferSizeFrames,
                                    &propertySize), "Couldn't get buffer frame size from input unit");
   UInt32 bufferSizeBytes = bufferSizeFrames * sizeof(Float32);

   printf("buffer num of frames  %i", bufferSizeFrames);

   if (player->streamFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) {

      int offset = offsetof(AudioBufferList, mBuffers[0]);
      int sizeOfAB = sizeof(AudioBuffer);
      int chNum = player->streamFormat.mChannelsPerFrame;

      int inputBufferSize = offset + sizeOfAB * chNum;

      //malloc buffer lists
      player->inputBuffer = (AudioBufferList *)malloc(inputBufferSize);
      player->inputBuffer->mNumberBuffers = chNum;

      for (UInt32 i = 0; i < chNum ; i++) {
         player->inputBuffer->mBuffers[i].mNumberChannels = 1;
         player->inputBuffer->mBuffers[i].mDataByteSize = bufferSizeBytes;
         player->inputBuffer->mBuffers[i].mData = malloc(bufferSizeBytes);
      }
   }

为了检查数据实际上是否合理,我渲染音频单元,并在每个回调中记录每组帧的前4个字节(4096)。 原因是要检查这些值是否与话筒中的内容保持一致。 正如我在麦克风中谈到的那样,我注意到这个存储器位置上的注销值与输入相对应。 所以看起来事情在这方面起作用:

// render into our buffer
    OSStatus inputProcErr = noErr;
    inputProcErr = AudioUnitRender(player->inputUnit,
                                   ioActionFlags,
                                   inTimeStamp,
                                   inBusNumber,
                                   inNumberFrames,
                                   player->inputBuffer);
    // copy from our buffer to ring buffer

   Float32 someDataL = *(Float32*)(player->inputBuffer->mBuffers[0].mData);
   printf("L2 input: % 1.7f n",someDataL);

最后,在输入回调中,我将音频字节写入文件。

 UInt32 numOfBytes = 4096*player->streamFormat.mBytesPerFrame;

   AudioFileWriteBytes(player->recordFile,
                       FALSE,
                       player->startingByte,
                       &numOfBytes,
                       &ioData[0].mBuffers[0].mData);

   player->startingByte += numOfBytes;

所以我还没有弄清楚为什么这些数据会出现令人毛骨悚然,失真或根本不存在的情况。 有一件事是,最终的音频文件大约只要我实际记录。 (点击返回停止音频单元并关闭音频文件)。

我不确定接下来要看什么。 有没有人试图从AUHAL回调中写入audiofile并得到类似的结果?


为了简单起见和保持主题,这似乎是使用AUHAL音频单元将音频文件写入音频文件 ,我会尽量不要讨论看起来过于复杂或过于宽泛,因此难以追踪和调试的假设。这个答复的范围。

  • 正如问题中提到的,为了使事情有效,人们不需要AUGraph 。 一个HAL音频组件完成这项工作。

  • 当使用ExtAudioFileWriteAsync()将线性PCM写入文件时,如果正在写入字节数据包或AFAIK,则无关紧要。 它只能从bufferList中写入inNumberFrames成员元素。

  • 为了简单起见,我假设每帧一个输入通道, Float32数据格式,没有格式转换。

  • 在假定一切都被正确声明和初始化的情况下(这在文档,教科书,教程和示例代码中都有很好的介绍),下面的30行plain-C渲染回调在单个AUkAudioUnitSubType_HALOutput上完成了工作,我相信它可以变得更简单:

    static OSStatus inputRenderProc (void *                    inRefCon,
                                AudioUnitRenderActionFlags *   ioActionFlags,
                                const AudioTimeStamp *         inTimeStamp,
                                UInt32                         inBusNumber,
                                UInt32                        inNumberFrames,
                                AudioBufferList *              ioData) 
    {
    Float64 recordedTime = inTimeStamp->mSampleTime/kSampleRate;  
    Float32 samples[inNumberFrames]; 
    memset (&samples, 0, sizeof (samples));
    AudioBufferList bufferList;
    bufferList.mNumberBuffers = 1;
    bufferList.mBuffers[0].mData = samples;
    bufferList.mBuffers[0].mNumberChannels = 1;
    bufferList.mBuffers[0].mDataByteSize = inNumberFrames*sizeof(Float32);
    
    myPlayer* player = ( myPlayer *)inRefCon;
    
    CheckError(AudioUnitRender(player->mAudioUnit,
                             ioActionFlags, 
                             inTimeStamp, 
                             kInputBus, 
                             inNumberFrames, 
                             &bufferList),
               "Couldn't render from device");
    
    // Write samples from bufferList into file
    ExtAudioFileWriteAsync(player->mAudioFileRef, inNumberFrames, &bufferList);        
    return noErr;     
    }
    

    额外的用户级提示是创建并选择包含内置麦克风和音频MIDI Setup.app中的内置输出的所谓聚合设备 ,以便正确制作单个HAL AU渲染。

    一旦确定这样一个简单的代码的行为如预期,可以构建更复杂的程序,包括图形,但图形不是OSX中低级音频处理的先决条件。 希望这可以帮助。


    如果您可以确认同步文件写入是您的问题,则可以使用GCD异步写入文件。 它是一个队列,所以它保留了订单。 它一次处理一件事。 您还可以查看剩余的物品数量,完成时间等。

    dispatch_queue_t fileWritingQueue;
    
    
    -(void) setup
    {
        // be sure to initialize this 
        fileWritingQueue = dispatch_queue_create("myQueue", NULL);
    }
    
    
    YourInputCallaback
    {
    
    
        // replace this with your synchronous file writer
        dispatch_async(fileWritingQueue, ^{
    
            // write file chunk here
    
       });
    
    
    }
    
    -(void) cleanUp
    {
        dispatch_release(fileWritingQueue);
    }
    

    我认为你的AudioStreamBasicDescription存在问题:你有:

    format.mBytesPerFrame = 2;
    format.mBytesPerPacket = 2;
    format.mChannelsPerFrame = 1;
    format.mBitsPerChannel = 16;
    format.mFramesPerPacket = 1;
    format.mFormatFlags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsFloat
    

    但你应该有:

    format.mBytesPerFrame = format.mBytesPerPacket = 4;
    

    format.mBitsPerChannel=4
    

    当使用浮游物。 我记得那些AudioStreamBasicDescription有问题,因为当描述没有意义时,你永远不会得到有意义的错误,所以它总是值得仔细检查。

    链接地址: http://www.djcxy.com/p/60189.html

    上一篇: Writing bytes to audio file using AUHAL audio unit

    下一篇: CoreAudio Enumerating HAL AudioID's Are Inconsistent