【问题标题】:Acoustic echo cancellation in JavaJava 中的回声消除
【发布时间】:2011-03-25 02:34:43
【问题描述】:

我正在实现一个使用纯 Java 的 VOIP 应用程序。当用户不使用耳机时会出现回声问题(主要是在带有内置麦克风的笔记本电脑上)。

目前发生的情况

VOIP 应用程序的具体细节只是 Java 媒体框架的普通数据线。本质上,我想在将音频数据写入扬声器进行输出之前对音频数据进行一些数字信号处理。

  public synchronized void addAudioData(byte[] ayAudioData)
  {
    m_oBuffer.enqueue(ayAudioData);
    this.notify();
  }

如您所见,音频数据到达并在缓冲区中排队。这是为了迎合狡猾的连接并允许不同的数据包大小。这也意味着在我将音频数据播放到扬声器线路之前,我可以访问任何需要的音频数据以进行任何花哨的 DSP 操作。

我已经管理了一个可以工作的回声消除器,但是它需要大量交互式用户输入,我想要一个自动回声消除器。

手动回声消除器

public static byte[] removeEcho(int iDelaySamples, float fDecay, byte[] aySamples)
  {
    m_awDelayBuffer = new short[iDelaySamples];
    m_aySamples = new byte[aySamples.length];
    m_fDecay = (float) fDecay;
    System.out.println("Removing echo");
    m_iDelayIndex = 0;

    System.out.println("Sample length:\t" + aySamples.length);
    for (int i = 0; i < aySamples.length; i += 2)
    {
      // update the sample
      short wOldSample = getSample(aySamples, i);

      // remove the echo
      short wNewSample = (short) (wOldSample - fDecay * m_awDelayBuffer[m_iDelayIndex]);
      setSample(m_aySamples, i, wNewSample);

      // update the delay buffer
      m_awDelayBuffer[m_iDelayIndex] = wNewSample;
      m_iDelayIndex++;

      if (m_iDelayIndex == m_awDelayBuffer.length)
      {
        m_iDelayIndex = 0;
      }
    }

    return m_aySamples;
  }

自适应过滤器

我已经读到adaptive filters 是要走的路。具体来说,最小均方滤波器。但是,我被困住了。上面的大多数示例代码都是用 C 和 C++ 编写的,它们不能很好地翻译成 Java。

有人对如何在 Java 中实现它们有建议吗?任何其他想法也将不胜感激。提前致谢。

【问题讨论】:

标签: java echo signal-processing javasound cancellation


【解决方案1】:

使用SpeexAEC。它是开源的,它是用 C 语言编写的(与 JNI 一起使用),并且可以正常工作。 我已在 2 个不同的 VoIP 应用程序中成功使用它,并且它消除了大部分回声。

【讨论】:

  • 嗨,你能指导我如何取消回声。我正在努力让它工作
  • 您可以通过写在我个人资料中的电子邮件与我联系。这不是一篇文章可以总结的。
  • @SirKnigget 如何安装 Speex?
【解决方案2】:

如果有人感兴趣,我通过将使用标准化最小均方算法和一些 C 语言过滤器的 Acoustic Echo Cancellation 方法 mentioned by Paul R 基本转换为 Java,设法构建了一个公平、有效的回声消除器。 JNI 路线可能仍然是更好的选择,但如果可能的话,我喜欢坚持使用纯 Java。通过查看他们的过滤器如何工作并在DSP Tutor 上阅读大量有关过滤器的信息,我设法控制了去除多少噪音以及如何去除高频等。

一些提示:

  1. 记住从哪里删除的内容。我不得不切换几次。
  2. 这种方法最重要的变量是收敛速度。这是上面链接代码中名为 Stepsize 的变量。
  3. 我一次拿一个单独的组件,弄清楚它们的作用,构建它们并分别测试它们。例如,我使用了 Double Talk Detector 并对其进行了测试以确保其正常工作。然后我一个一个地提取过滤器并在音频文件上对其进行测试以确保它们有效,然后我提取了归一化的最小均方部分并对其进行了测试,然后再将它们放在一起。

希望这对其他人有帮助!

【讨论】:

    【解决方案3】:

    已经很久了!希望这甚至是正确的课程,但你去吧:

    /**
     * This filter performs a pre-whitening Normalised Least Means Square on an
     * array of bytes. This does the actual echo cancelling.
     * 
     * Echo cancellation occurs with the following formula:
     * 
     * e = d - X' * W
     * 
     * e represents the echo-free signal. d represents the actual microphone signal
     * with the echo. X' is the transpose of the loudspeaker signal. W is an array
     * of adaptive weights.
     * 
     */
    public class cNormalisedLeastMeansSquareFilter
      implements IFilter
    {
      private byte[] m_ayEchoFreeSignal;// e
      private byte[] m_ayEchoSignal;// d
      private byte[] m_ayTransposeOfSpeakerSignal;// X'
      private double[] m_adWeights;// W
    
      /**
       * The transpose and the weights need to be updated before applying the filter
       * to an echo signal again.
       * 
       * @param ayEchoSignal
       * @param ayTransposeOfSpeakerSignal
       * @param adWeights
       */
      public cNormalisedLeastMeansSquareFilter(byte[] ayEchoSignal, byte[] ayTransposeOfSpeakerSignal, double[] adWeights)
      {
        m_ayEchoSignal = ayEchoSignal;
        m_ayTransposeOfSpeakerSignal = ayTransposeOfSpeakerSignal;
        m_adWeights = adWeights;
      }
    
      @Override
      public byte[] applyFilter(byte[] ayAudioBytes)
      {
        // e = d - X' * W
        m_ayEchoFreeSignal = new byte[ayAudioBytes.length];
        for (int i = 0; i < m_ayEchoFreeSignal.length; ++i)
        {
          m_ayEchoFreeSignal[i] = (byte) (m_ayEchoSignal[i] - m_ayTransposeOfSpeakerSignal[i] * m_adWeights[i]);
        }
        return m_ayEchoFreeSignal;
      }
    

    【讨论】:

    • 您最好将此代码添加到原始答案中,而不是发布新答案。
    • 如何将这段代码与我现有的项目集成,在该项目中,我将原始文件编码为 PCM 数据,然后将 PCM 文件编码为 .spx 文件。我为此使用 jspeex.jar。
    【解决方案4】:

    这是一个非常复杂的领域,要使可用的 AEC 解决方案发挥作用,您需要进行大量的研发工作。所有好的 AEC 都是专有的,而且回声消除还有很多功能,而不仅仅是实现 LMS 等自适应滤波器。我建议您最初使用 MATLAB(或 Octave)开发您的回声消除算法 - 当您有一些看起来与“现实世界”电信相当好的东西时,您可以在 C 中实现该算法并实时测试/评估它。一旦这工作正常,您就可以使用 JNI 从 Java 调用 C 实现。

    【讨论】:

    • 感谢您的回复。我一直在努力避免使用 JNI,但我已经绝望地尝试任何事情。
    • 您可能会发现您需要在 AEC 中执行特定于平台的操作,例如低级操作系统/音频 API 调用,所以它也可能是 C + JNI。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-20
    • 2011-08-31
    • 1970-01-01
    • 1970-01-01
    • 2020-10-10
    相关资源
    最近更新 更多