2016-09-06 16 views
2

我正在从事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,并通过转换器运行通过扬声器播放,这是工作没有问题。

只是无法弄清楚为什么转换器失败并且没有返回任何数据。

有没有人有这种类型的问题的经验?

回答

1

UPDATE
所以你调用AUGraphStart其他地方,但ULAW转换器拒绝为你:(你可以另一种速率转换器添加到图形或干脆让vpio单位做它做的一般速率转换给你的。更改此代码

ioASBDin.mSampleRate = currentSampleRate; // change me to 8000Hz 
ioASBDout.mSampleRate = currentSampleRate; // delete me, I'm ignored 

status = AudioUnitSetProperty(ioSendUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &ioASBDin, sizeof(ioASBDin)); 

ioASBDin.mSampleRate = streamAudioFormat.mSampleRate; // a.k.a 8000Hz 

status = AudioUnitSetProperty(ioSendUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &ioASBDin, sizeof(ioASBDin)); 

将使整个图形做8kHz的,给你非空ioData缓冲区:

AudioUnitGraph 0xCA51000: 
    Member Nodes: 
    node 1: 'auou' 'vpio' 'appl', instance 0x7b5bb320 O I 
    node 2: 'aufc' 'conv' 'appl', instance 0x7c878d50 O I 
    node 3: 'aufc' 'conv' 'appl', instance 0x7c875eb0 O I 
Connections: 
    node 1 bus 1 => node 2 bus 0 [ 1 ch, 8000 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, 8000 Hz, 'lpcm' (0x0000000C) 16-bit little-endian signed integer] 
CurrentState: 
    mLastUpdateError=0, eventsToProcess=F, isInitialized=T, isRunning=T (1) 

老答案

您需要

  1. AUGraphStart您的图形
  2. 更改ULAW mSampleRate至11025,22050或44100

,那么你将看到非nul l在kAudioUnitRenderAction_PostRender阶段的ioData。

转换为8kHz甚至16kHz ulaw看起来像一个音频转换器应该能够做的事情。我不知道为什么它不起作用,但是当你将采样率设置为除点2以外的任何值时,ulaw转换器会报告kAUGraphErr_CannotDoInCurrentContext(-10863)错误,这对我来说没有意义。

+0

对AUGraphStart的调用是在代码的另一部分完成的,因此图形正在运行。显然,我需要它被转换为8kHz,所以改变其他任何东西都不会起作用。 – DRourke

+0

明显是相对的。查看更新后的答案。 –

+0

将vpio设备上的采样率更改为8KHz确实有效。你会认为它使用的是conv单元使用的相同的转换逻辑,但是一定是不同的。谢谢你的帮助! – DRourke

相关问题