我正在从事iOS项目,需要从麦克风捕获输入并将其转换为ULaw(发送数据流)。我正在使用带有转换器节点的AUGraph来完成此操作。该图创建成功并初始化,但是在我的渲染通知回调中,即使认为inNumberFrame包含值93,ioData缓冲区也始终包含NULL。我认为它可能有一些由于格式转换器缓冲区的大小不正确,但我可以了解正在发生的事情。AUGraph FormatConverter(AUConverter)渲染通知包含NULL ioData缓冲区
下面是代码:
OSStatus status;
// ************************** DEFINE AUDIO STREAM FORMATS ******************************
double currentSampleRate;
currentSampleRate = [[AVAudioSession sharedInstance] sampleRate];
// Describe stream format
AudioStreamBasicDescription streamAudioFormat = {0};
streamAudioFormat.mSampleRate = 8000.00;
streamAudioFormat.mFormatID = kAudioFormatULaw;
streamAudioFormat.mFormatFlags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
streamAudioFormat.mFramesPerPacket = 1;
streamAudioFormat.mChannelsPerFrame = 1;
streamAudioFormat.mBitsPerChannel = 8;
streamAudioFormat.mBytesPerPacket = 1;
streamAudioFormat.mBytesPerFrame = streamAudioFormat.mBytesPerPacket * streamAudioFormat.mFramesPerPacket;
// ************************** SETUP SEND AUDIO ******************************
AUNode ioSendNode;
AUNode convertToULAWNode;
AUNode convertToLPCMNode;
AudioUnit convertToULAWUnit;
AudioUnit convertToLPCMUnit;
status = NewAUGraph(&singleChannelSendGraph);
if (status != noErr)
{
NSLog(@"Unable to create send audio graph.");
return;
}
AudioComponentDescription ioDesc = {0};
ioDesc.componentType = kAudioUnitType_Output;
ioDesc.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
ioDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
ioDesc.componentFlags = 0;
ioDesc.componentFlagsMask = 0;
status = AUGraphAddNode(singleChannelSendGraph, &ioDesc, &ioSendNode);
if (status != noErr)
{
NSLog(@"Unable to add IO node.");
return;
}
AudioComponentDescription converterDesc = {0};
converterDesc.componentType = kAudioUnitType_FormatConverter;
converterDesc.componentSubType = kAudioUnitSubType_AUConverter;
converterDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
converterDesc.componentFlags = 0;
converterDesc.componentFlagsMask = 0;
status = AUGraphAddNode(singleChannelSendGraph, &converterDesc, &convertToULAWNode);
if (status != noErr)
{
NSLog(@"Unable to add ULAW converter node.");
return;
}
status = AUGraphAddNode(singleChannelSendGraph, &converterDesc, &convertToLPCMNode);
if (status != noErr)
{
NSLog(@"Unable to add LPCM converter node.");
return;
}
status = AUGraphOpen(singleChannelSendGraph);
if (status != noErr)
{
return;
}
// get the io audio unit
status = AUGraphNodeInfo(singleChannelSendGraph, ioSendNode, NULL, &ioSendUnit);
if (status != noErr)
{
NSLog(@"Unable to get IO unit.");
return;
}
UInt32 enableInput = 1;
status = AudioUnitSetProperty (ioSendUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
1, // microphone bus
&enableInput,
sizeof (enableInput)
);
if (status != noErr)
{
return;
}
UInt32 sizeASBD = sizeof(AudioStreamBasicDescription);
AudioStreamBasicDescription ioASBDin;
AudioStreamBasicDescription ioASBDout;
status = AudioUnitGetProperty(ioSendUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &ioASBDin, &sizeASBD);
if (status != noErr)
{
NSLog(@"Unable to get IO stream input format.");
return;
}
status = AudioUnitGetProperty(ioSendUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &ioASBDout, &sizeASBD);
if (status != noErr)
{
NSLog(@"Unable to get IO stream output format.");
return;
}
ioASBDin.mSampleRate = currentSampleRate;
ioASBDout.mSampleRate = currentSampleRate;
status = AudioUnitSetProperty(ioSendUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &ioASBDin, sizeof(ioASBDin));
if (status != noErr)
{
NSLog(@"Unable to set IO stream output format.");
return;
}
status = AudioUnitSetProperty(ioSendUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &ioASBDin, sizeof(ioASBDin));
if (status != noErr)
{
NSLog(@"Unable to set IO stream input format.");
return;
}
// get the converter audio unit
status = AUGraphNodeInfo(singleChannelSendGraph, convertToULAWNode, NULL, &convertToULAWUnit);
if (status != noErr)
{
NSLog(@"Unable to get ULAW converter unit.");
return;
}
status = AudioUnitSetProperty(convertToULAWUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &ioASBDin, sizeof(ioASBDin));
if (status != noErr)
{
NSLog(@"Unable to set ULAW stream input format.");
return;
}
status = AudioUnitSetProperty(convertToULAWUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamAudioFormat, sizeof(streamAudioFormat));
if (status != noErr)
{
NSLog(@"Unable to set ULAW stream output format.");
return;
}
// get the converter audio unit
status = AUGraphNodeInfo(singleChannelSendGraph, convertToLPCMNode, NULL, &convertToLPCMUnit);
if (status != noErr)
{
NSLog(@"Unable to get LPCM converter unit.");
return;
}
status = AudioUnitSetProperty(convertToLPCMUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamAudioFormat, sizeof(streamAudioFormat));
if (status != noErr)
{
NSLog(@"Unable to set LPCM stream input format.");
return;
}
status = AudioUnitSetProperty(convertToLPCMUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &ioASBDin, sizeof(ioASBDin));
if (status != noErr)
{
NSLog(@"Unable to set LPCM stream output format.");
return;
}
status = AUGraphConnectNodeInput(singleChannelSendGraph, ioSendNode, 1, convertToULAWNode, 0);
if (status != noErr)
{
NSLog(@"Unable to set ULAW node input.");
return;
}
status = AUGraphConnectNodeInput(singleChannelSendGraph, convertToULAWNode, 0, convertToLPCMNode, 0);
if (status != noErr)
{
NSLog(@"Unable to set LPCM node input.");
return;
}
status = AUGraphConnectNodeInput(singleChannelSendGraph, convertToLPCMNode, 0, ioSendNode, 0);
if (status != noErr)
{
NSLog(@"Unable to set IO node input.");
return;
}
status = AudioUnitAddRenderNotify(convertToULAWUnit, &outputULAWCallback, (__bridge void*)self);
if (status != noErr)
{
NSLog(@"Unable to add ULAW render notify.");
return;
}
status = AUGraphInitialize(singleChannelSendGraph);
if (status != noErr)
{
NSLog(@"Unable to initialize send graph.");
return;
}
CAShow (singleChannelSendGraph);
}
和图形节点被初始化为:
Member Nodes:
node 1: 'auou' 'vpio' 'appl', instance 0x7fd5faf8fac0 O I
node 2: 'aufc' 'conv' 'appl', instance 0x7fd5fad05420 O I
node 3: 'aufc' 'conv' 'appl', instance 0x7fd5fad05810 O I
Connections:
node 1 bus 1 => node 2 bus 0 [ 1 ch, 44100 Hz, 'lpcm' (0x0000000C) 16-bit little-endian signed integer]
node 2 bus 0 => node 3 bus 0 [ 1 ch, 8000 Hz, 'ulaw' (0x0000000C) 8 bits/channel, 1 bytes/packet, 1 frames/packet, 1 bytes/frame]
node 3 bus 0 => node 1 bus 0 [ 1 ch, 44100 Hz, 'lpcm' (0x0000000C) 16-bit little-endian signed integer]
而呈现通知回调:
static OSStatus outputULAWCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
AudioManager *audioManager = (__bridge AudioManager*)inRefCon;
if ((*ioActionFlags) & kAudioUnitRenderAction_PostRender)
{
if (!audioManager.mute && ioData->mBuffers[0].mData != NULL)
{
TPCircularBufferProduceBytes(audioManager.activeChannel == 0 ? audioManager.channel1StreamOutBufferPtr : audioManager.channel2StreamOutBufferPtr,
ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize);
// do not want to playback our audio into local speaker
SilenceData(ioData);
}
}
return noErr;
}
注意:如果我直接将麦克风输入发送到输出(跳过转换器节点),我确实听到输出,所以我知道AUGraph正在工作。
我有一个接收AUGraph安装程序接收来自流的ULaw,并通过转换器运行通过扬声器播放,这是工作没有问题。
只是无法弄清楚为什么转换器失败并且没有返回任何数据。
有没有人有这种类型的问题的经验?
对AUGraphStart的调用是在代码的另一部分完成的,因此图形正在运行。显然,我需要它被转换为8kHz,所以改变其他任何东西都不会起作用。 – DRourke
明显是相对的。查看更新后的答案。 –
将vpio设备上的采样率更改为8KHz确实有效。你会认为它使用的是conv单元使用的相同的转换逻辑,但是一定是不同的。谢谢你的帮助! – DRourke