Акустическое эхоподавление в Java
Я реализую приложение VOIP, которое использует чистую Java. Существует проблема эха, которая возникает, когда пользователи не используют гарнитуры (в основном на ноутбуках со встроенными микрофонами).
Что в настоящее время происходит
Основные черты приложения 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 ответа
Сколько лет сколько зим! Надеюсь, что это даже правильный класс, но вы идете:
/**
* 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;
}
В случае, если кому-то интересно, мне удалось создать честный, работающий эхоподавитель, в основном, преобразовав метод акустического эхоподавления, упомянутый Полом Р., который использует алгоритм нормализованного квадрата наименьшего значения и несколько фильтров из С в Java. JNI-маршрут, вероятно, все еще лучше, но мне нравится придерживаться чистой Java, если это вообще возможно. Видя, как работают их фильтры, и много читая о фильтрах в DSP Tutor, мне удалось получить некоторый контроль над тем, сколько шума удаляется, как удалять высокие частоты и т. Д.
Несколько советов:
- Имейте в виду, что вы удалите откуда. Я должен был изменить это несколько раз.
- Наиболее важной переменной этого метода является скорость сходимости. Эта переменная называется Stepsize в приведенном выше коде ссылки.
- Я брал отдельные компоненты по одному, выяснял, что они делают, собирал их и тестировал их по отдельности. Например, я взял детектор двойного разговора и проверил его на работоспособность. Затем я взял фильтры один за другим и проверил их на аудио-файлах, чтобы убедиться, что они работают, затем я взял нормализованную часть наименьших средних и проверил ее перед тем, как собрать все вместе.
Надеюсь, это поможет кому-то еще!
Это очень сложная область, и чтобы получить работоспособное решение AEC, вам потребуется немало исследований и разработок. Все хорошие AEC являются проприетарными, и эхоподавление гораздо больше, чем просто применение адаптивного фильтра, такого как LMS. Я предлагаю вам разработать свой алгоритм эхоподавления первоначально с использованием MATLAB (или Octave) - когда у вас есть что-то, что, по-видимому, работает достаточно хорошо с телекоммуникациями "реального мира", тогда вы можете реализовать алгоритм на C и протестировать / оценить его в режиме реального времени. Как только это сработает, вы можете использовать JNI для вызова реализации C из Java.