2010-08-04 19 views
8

Estoy implementando una aplicación VOIP que usa Java puro. Hay un problema de eco que ocurre cuando los usuarios no usan auriculares (principalmente en computadoras portátiles con micrófonos incorporados).Cancelación de eco acústico en Java

Lo que sucede actualmente

Los aspectos prácticos de la aplicación VoIP es sólo las líneas de datos claros del marco de medios de Java. Esencialmente, me gustaría realizar un procesamiento de señal digital en datos de audio antes de escribirlo en el altavoz para su salida.

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

Como puede ver, los datos de audio llegan y se colocan en un búfer. Esto es para abastecer conexiones poco confiables y para permitir diferentes tamaños de paquetes. También significa que tengo acceso a la mayor cantidad de datos de audio que necesito para cualquier operación elegante de DSP antes de reproducir los datos de audio en la línea de altavoces.

He conseguido un cancelador de eco que hace el trabajo, sin embargo, requiere una gran cantidad de entrada de usuario interactiva y me gustaría tener un cancelador de eco automática.

Manual cancelador de eco

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; 
    } 

filtros adaptativos

He leído que adaptive filters son el camino a seguir. Específicamente, un filtro de Mínimos Cuadrados Medios. Sin embargo, estoy atascado. La mayoría del código de muestra para lo anterior está en C y C++ y no se traducen bien en Java.

¿Alguien tiene consejos sobre cómo implementarlos en Java? Cualquier otra idea también sería muy apreciada. Gracias por adelantado.

+0

yo encontramos este cancelación de eco acústico guía muy, muy útiles hasta el momento: http://www.andreadrian.de/echo_cancel/index.html –

Respuesta

1

Esta es un área muy compleja y para obtener una solución de AEC útil de trabajo que tendrá que hacer un poco de R & D. Todo el bien AEC son propiedad, y hay mucho más a la cancelación de eco simplemente implementando un filtro adaptativo como LMS. Le sugiero que desarrolle su algoritmo de cancelación de eco inicialmente usando MATLAB (u Octave): cuando tiene algo que parece funcionar razonablemente bien con las telecomunicaciones del "mundo real", entonces puede implementar el algoritmo en C y probarlo/evaluarlo en tiempo real. Una vez que esto esté funcionando, puede usar JNI para llamar a la implementación de C desde Java.

+1

Gracias por la respuesta. He estado tratando de evitar el uso de JNI, pero estoy lo suficientemente desesperado como para intentar cualquier cosa. –

+2

Es posible que tenga que hacer cosas específicas de la plataforma en su AEC, p. Ej. llamadas de bajo nivel de OS/API de audio, por lo que bien podría ser C + JNI. –

4

En caso de que alguien esté interesado, logré construir un compensador de eco justo y funcional básicamente convirtiendo el método Acoustic Echo Cancellationmentioned by Paul R que usa un algoritmo Normalizado de mínimos cuadrados mínimos y algunos filtros de C a Java. La ruta JNI probablemente sea aún mejor, pero me gusta apegarme a Java puro si es posible. Al ver cómo funcionan sus filtros y leer una gran cantidad de filtros en DSP Tutor, me las arreglé para ganar algo de control sobre la cantidad de ruido se hace eliminado y cómo eliminar las altas frecuencias, etc.

Algunos consejos:

  1. Tenga en cuenta lo que quita de donde. Tuve que cambiar esto varias veces.
  2. La variable más importante de este método es la tasa de convergencia. Esta es la variable llamada Stepize en el código del enlace anterior.
  3. Tomé los componentes individuales uno a la vez, descubrí lo que hicieron, los construí y los probé por separado.Por ejemplo, tomé Double Talk Detector y lo probé para asegurarme de que funcionaba. Luego tomé los filtros uno por uno y los probé en archivos de audio para garantizar que funcionasen, luego tomé la parte normalizada de mínimos cuadrados y la probé antes de armarla todo.

Espero que esto ayude a alguien más!

+2

¿Podría publicar la conversión de Java del código? – user489041

4

Utilice SpeexAEC. Es de código abierto, está escrito en C (lo usa con JNI) y funciona. Lo he usado con éxito en 2 aplicaciones diferentes de VoIP y se cancela la mayor parte del eco.

+0

hola puedes por favor guíame cómo cancelar echo. Estoy luchando para hacerlo funcionar –

+0

Puedes contactarme en el correo electrónico escrito en mi perfil. Esto no es algo para resumir en una publicación. – SirKnigget

4

¡Han pasado años! Espero que esto es aún la clase correcta, pero hay que ir:

/** 
* 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

Habría sido mejor agregar este código a su respuesta original que publicar una nueva respuesta. –

+0

¿Cómo integro esta pieza de código con mi proyecto existente en el que estoy codificando un archivo sin formato para datos PCM y luego el archivo PCM se está codificando en un archivo .spx? Estoy usando jspeex.jar para esto. –

Cuestiones relacionadas