使用AUHAL音频单元

时间:2016-01-11 05:38:24

标签: macos core-audio audiounit

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

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

   // 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");

这里我使用一些缓冲区来保存来自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);
      }
   }

为了检查数据是否真的合理,我渲染音频单元,然后记录每个回调中每组帧(4096)的前4个字节。原因是要检查这些值是否与进入麦克风的内容保持一致。当我在麦克风中说话时,我注意到这个记忆位置的记录值对应于输入。所以事情似乎在这方面起作用:

// 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并得到类似的结果?

4 个答案:

答案 0 :(得分:1)

为了简单和保持主题,似乎使用AUHAL音频单元将字节写入音频文件,我将尽量不讨论看似过于复杂的假设或在本回复的范围内,过于宽泛,因此难以跟踪和调试。

  1. 为了让事情有效,正如问题所述,人们不需要 AUGraph 。单个 HAL 音频组件可以胜任这项工作。

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

  3. 为简单起见,我假设每帧一个输入通道, Float32 数据格式,无格式转换。

  4. 假设所有内容都已正确声明并初始化(文档,教科书,教程和示例代码中都有详细介绍),以下30行plain-C渲染回调单个AU < strong> kAudioUnitSubType_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中低级音频处理的先决条件。希望这可以提供帮助。

答案 1 :(得分:0)

如果您可以确认同步文件写入是您的问题,则可以使用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);
}

答案 2 :(得分:0)

我认为您的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有问题,因为当描述没有意义时你永远不会得到有意义的错误,所以总是值得仔细检查。

答案 3 :(得分:0)

您正在格式请求中设置(32位)浮点标志:

GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, 512, 512,
                GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, bufer);

但WAVE文件通常包含16位整数样本。将32位浮点样本写入16位整数音频文件通常会产生垃圾。