使用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渲染回调在单个AU的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中低级音频处理的先决条件。 希望这可以帮助。
如果您可以确认同步文件写入是您的问题,则可以使用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