我正在尝试从我的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并得到类似的结果?
答案 0 :(得分:1)
为了简单和保持主题,似乎使用AUHAL音频单元将字节写入音频文件,我将尽量不讨论看似过于复杂的假设或在本回复的范围内,过于宽泛,因此难以跟踪和调试。
为了让事情有效,正如问题所述,人们不需要 AUGraph 。单个 HAL 音频组件可以胜任这项工作。
使用ExtAudioFileWriteAsync()将线性PCM写入文件时,如果正在编写字节或数据包或AFAIK,则无关紧要。它只能从bufferList中写入inNumberFrames成员元素。
为简单起见,我假设每帧一个输入通道, Float32 数据格式,无格式转换。
假设所有内容都已正确声明并初始化(文档,教科书,教程和示例代码中都有详细介绍),以下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位整数音频文件通常会产生垃圾。