9

我正在实现一个使用纯 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;
  }

自适应过滤器

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

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

4

4 回答 4

5

已经好多年了!希望这甚至是正确的课程,但是您去了:

/**
 * 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;
  }
于 2011-04-13T06:12:52.657 回答
4

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

于 2011-03-09T14:21:26.193 回答
4

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

一些技巧:

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

希望这对其他人有帮助!

于 2010-09-23T08:48:38.687 回答
1

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

于 2010-08-04T06:45:04.667 回答