2016-09-06 19 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

相關問題