我正在实现一个使用纯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中实现它们的建议?任何其他想法也将不胜感激。提前致谢。
答案 0 :(得分:4)
如果有人感兴趣,我设法通过基本上将使用规范化最小均方算法的Acoustic Echo Cancellation方法mentioned by Paul R和从C中的一些过滤器转换为Java来构建一个公平的,有效的回声消除器。 JNI路线可能仍然是一个更好的方法,但我喜欢坚持使用纯Java。通过了解他们的滤波器如何工作并在DSP Tutor上读取大量滤波器,我设法控制了多少噪声消除以及如何消除高频等。
一些提示:
希望这有助于其他人!
答案 1 :(得分:4)
答案 2 :(得分:4)
已经很久了!希望这甚至是正确的课程,但是你去了:
/**
* 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;
}
答案 3 :(得分:1)
这是一个非常复杂的领域,为了获得可用的AEC解决方案,你需要做很多R&amp; D.所有优秀的AEC都是专有的,并且除了实现LMS之类的自适应滤波器之外,回声消除还有 lot 。我建议你最初使用MATLAB(或Octave)开发回声消除算法 - 当你有一些看起来与“真实世界”电信相当合理的东西时,你可以用C实现算法并实时测试/评估它。一旦这个工作,您可以使用JNI从Java调用C实现。