蘋果ios音訊的回聲消除處理
阿新 • • 發佈:2019-01-27
工業上的聲音處理中,回聲消除是一個重要的話題,重要性不亞於噪聲消除、人聲放大、自動增益等,尤其是在VoIP功能上,回聲消除是每一個做VoIP功能團隊的必修課。QQ、Skype等等,回聲消除的效果是一個重要的考查指標。
具體的回聲消除演算法比較複雜,我現在還沒有研究的很明白。簡單來說,就是在即將播放出來的聲音中,將回聲的那部分減去。其中一個關鍵,是如何估計回聲大小,這需要用到自適應演算法。研究不透,多說無益。有興趣的同學可以一起學習。
-
將聲音輸出route到speaker,這樣聲音比較大,回聲明顯:
AVAudioSession* session = [AVAudioSession sharedInstance]; [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil]; [session setActive:YES error:nil];
-
初始化一個AUGraph,建立一個AUNode,並將之新增到graph上。一般來說,溝通麥克風/揚聲器的AUNode,其型別應該是RemoteIO,但是RemoteIO不帶回聲消除功能,VoiceProcessingIO型別的才帶。
AudioComponentDescription inputcd = {0}; inputcd.componentType = kAudioUnitType_Output; //inputcd.componentSubType = kAudioUnitSubType_RemoteIO; //we can access the system's echo cancellation by using kAudioUnitSubType_VoiceProcessingIO subtype inputcd.componentSubType = kAudioUnitSubType_VoiceProcessingIO; inputcd.componentManufacturer = kAudioUnitManufacturer_Apple;
-
//Open input of the bus 1(input mic) UInt32 enableFlag = 1; CheckError(AudioUnitSetProperty(myStruct->remoteIOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableFlag, sizeof(enableFlag)), "Open input of bus 1 failed"); //Open output of bus 0(output speaker) CheckError(AudioUnitSetProperty(myStruct->remoteIOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableFlag, sizeof(enableFlag)), "Open output of bus 0 failed"); //Set up stream format for input and output streamFormat.mFormatID = kAudioFormatLinearPCM; streamFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; streamFormat.mSampleRate = 44100; streamFormat.mFramesPerPacket = 1; streamFormat.mBytesPerFrame = 2; streamFormat.mBytesPerPacket = 2; streamFormat.mBitsPerChannel = 16; streamFormat.mChannelsPerFrame = 1; CheckError(AudioUnitSetProperty(myStruct->remoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(streamFormat)), "kAudioUnitProperty_StreamFormat of bus 0 failed"); CheckError(AudioUnitSetProperty(myStruct->remoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &streamFormat, sizeof(streamFormat)), "kAudioUnitProperty_StreamFormat of bus 1 failed"); //Set up input callback AURenderCallbackStruct input; input.inputProc = InputCallback; input.inputProcRefCon = myStruct; CheckError(AudioUnitSetProperty(myStruct->remoteIOUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0,//input mic &input, sizeof(input)), "kAudioUnitProperty_SetRenderCallback failed");
-
在回撥函式inputCallback中,用
AudioUnitRender()
函式獲取麥克風的聲音,存在一個bufferList中。這個bufferList是一個ring結構,儲存最新的聲音,然後播放舊聲音。這樣,聲音的輸入和輸出之間,就有了0.5s(可調節)左右的延遲,形成了明顯的回聲。 -
給回聲消除新增一個開關。VoiceProcessingIO有一個屬性可用來開啟/關閉回聲消除功能:
kAUVoiceIOProperty_BypassVoiceProcessing
UInt32 echoCancellation; UInt32 size = sizeof(echoCancellation); CheckError(AudioUnitGetProperty(myStruct.remoteIOUnit, kAUVoiceIOProperty_BypassVoiceProcessing, kAudioUnitScope_Global, 0, &echoCancellation, &size), "kAUVoiceIOProperty_BypassVoiceProcessing failed");
-
現在可以開始graph了:
CheckError(AUGraphInitialize(graph), "AUGraphInitialize failed"); CheckError(AUGraphStart(graph), "AUGraphStart failed");
在示例中,有一個簡單的開關按鈕,可以明顯感覺到開啟/關閉回聲消除的區別。
在實測中,打開回聲消除功能時,仍然能聽到一點點的回聲,不過很小,一般情況下足夠使用了。