Xamarin.iOS 上的回声消除 (AEC)

Acoustic Echo Cancellation (AEC) on Xamarin.iOS

我目前正在从事基于Xamarin.iOS 平台的跨平台Voip 应用开发。我确实在 iOS 上搜索了 AEC 实施,但大多数主题都与 Objective-C 相关。 我现在已经实现的是:我可以使用Audiotoolbox(音频队列)从麦克风获取输入的原始声音数据并通过套接字将其发送出去。但是在使用其他设备进行测试时,我在 phone 上遇到了非常清晰的回声。这是代码:

private void SetupInputQueue()
    {
        inputQueue = new InputAudioQueue(audioStreamBasicDescription);
        //sendbuffer initialization
        heading = Encoding.ASCII.GetBytes("msg ");
        sendBuffer = new byte[CountAudioBuffers][];
        for (int i = 0; i < CountAudioBuffers; i++)
        {
            sendBuffer[i] = new byte[516];
            for (int j = 0; j < heading.Length; j++)
            {
                sendBuffer[i][j] = heading[j];
            }
        }

        for (int count = 0; count < CountAudioBuffers; count++)
        {
            IntPtr bufferpointer;
            inputQueue.AllocateBuffer(AudioBufferLength, out bufferpointer);
            inputQueue.EnqueueBuffer(bufferpointer, AudioBufferLength, null);
        }
        inputQueue.InputCompleted += HandleInputCompleted;
    }

    private void HandleInputCompleted(object sender, InputCompletedEventArgs e)
    {
        unsafe
        {
            byte* shortPtr = (byte*)e.UnsafeBuffer->AudioData;
            for (int count = heading.Length; count < sendBuffer[sendOutIndex].Length; count++)
            {
                sendBuffer[sendOutIndex][count] = *shortPtr;
                shortPtr++;
            }
        }
        socket.SendTo(sendBuffer[sendOutIndex], master);
        this.inputQueue.EnqueueBuffer(e.IntPtrBuffer, AudioBufferLength, null);
        sendOutIndex = (sendOutIndex + 1) % CountAudioBuffers;
    }

根据AEC on OSX using AudioQueue,我了解到我应该将原始声音数据传递给I/O单元(音频单元?)的提示。但是由于Xamarin.iOS(c#)中缺少示例,我无法详细了解如何实现这个过程。任何熟悉 Xamarin 平台上的 Voip 应用程序开发的人都可以给我一些例子来学习吗?非常感谢对此的任何帮助或提示。

(Nov, 21, 2018)我找到的一些相关帖子:Record audio with audio unit Audio unit Callbacks An audio unit example

我建议分析您系统中回声的性质(例如 test the echo path)。也许它超出了内置回声消除的功能。

我们终于想出了如何在 Xamarin.iOS 平台上打开 iOS 内置 AEC。 文档 here 有助于更好地了解 Audio Unit 的细节(即使该文档适用于 iOS 原生开发环境)和工作机制。如果你想精确控制每一个音频单元,了解函数中的参数,这个文档是必读的。

工作 here 为在音频单元上进行实验提供了一个非常好的起点。我在这个 post 的第 2 步中做了一些修改。关键部分是我们必须使用AudioTypeOutput.VoiceProcessingIO,这会打开AEC。

public void prepareAudioUnit()
    {

        var _audioComponent =   AudioComponent.FindComponent(AudioTypeOutput.VoiceProcessingIO);

        audioUnit = new AudioUnit.AudioUnit(_audioComponent);

        audioUnit.SetEnableIO(true,
            AudioUnitScopeType.Input,
            1 // Remote Input
        );

        // setting audio format
        audioUnit.SetAudioFormat(audioStreamBasicDesc,
            AudioUnitScopeType.Output,
            1
        );                                    

        audioUnit.SetInputCallback(input_CallBack, AudioUnitScopeType.Input, 1);

        audioUnit.SetRenderCallback(render_CallBack, AudioUnitScopeType.Global, 0);


        audioUnit.Initialize();
        audioUnit.Start();
    }