2010-08-04 14 views
8

Sto implementando un'applicazione VOIP che utilizza puro Java. C'è un problema di eco che si verifica quando gli utenti non usano cuffie (principalmente su laptop con microfoni incorporati).Cancellazione dell'eco acustico in Java

Cosa succede attualmente

I dadi e bulloni di l'applicazione VoIP è solo il datalines pianura di media framework di Java. In sostanza, mi piacerebbe eseguire alcune elaborazioni del segnale digitale sui dati audio prima di scriverlo all'altoparlante per l'output.

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

Come si può vedere i dati audio arrivano e vengono inseriti in un buffer. Questo è per soddisfare le connessioni di dubbia e per consentire diverse dimensioni del pacchetto. Significa anche che ho accesso a tutti i dati audio di cui ho bisogno per le fantasiose operazioni DSP prima di riprodurre i dati audio sulla linea degli altoparlanti.

Ho gestito un cancellatore di eco che funziona, tuttavia richiede un sacco di input dell'utente interattivo e mi piacerebbe avere un cancellatore di eco automatico.

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

filtri adattivi

Ho letto che adaptive filters sono la strada da percorrere. In particolare, un filtro dei minimi quadrati medi. Tuttavia, sono bloccato. La maggior parte dei codici di esempio per quanto sopra sono in C e C++ e non si traducono bene in Java.

Qualcuno ha consigli su come implementarli in Java? Altre idee sarebbero molto apprezzate. Grazie in anticipo.

+0

ho trovato guidare questo Acoustic Echo Cancellation molto, molto disponibile finora: http://www.andreadrian.de/echo_cancel/index.html –

risposta

1

Questa è una zona molto complessa e per ottenere una soluzione AEC utilizzabile lavoro avrete bisogno di fare un po 'di R & D. Tutto il bene AEC sono proprietari, e c'è un sacco più eco cancellazione di semplicemente implementando un filtro adattivo come LMS. Ti suggerisco di sviluppare il tuo algoritmo di cancellazione dell'eco inizialmente usando MATLAB (o Octave) - quando hai qualcosa che sembra funzionare abbastanza bene con le telecomms del "mondo reale", allora puoi implementare l'algoritmo in C e testarlo/valutarlo in tempo reale. Una volta che funziona, puoi usare JNI per chiamare l'implementazione C da Java.

+1

Grazie per la risposta. Ho cercato di evitare l'uso di JNI, ma sono diventato abbastanza disperato per provare qualsiasi cosa. –

+2

Potresti scoprire che devi fare cose specifiche della piattaforma nel tuo AEC, ad es. chiamate API OS/audio di basso livello, quindi potrebbe anche essere C + JNI. –

4

Nel caso in cui qualcuno fosse interessato, sono riuscito a creare un eco cancellatore funzionante, convertendo fondamentalmente il metodo Acoustic Echo Cancellationmentioned by Paul R che utilizza un algoritmo Normalized Least Means Square e alcuni filtri da C in Java. La rotta JNI è probabilmente ancora un modo migliore per andare, ma mi piace attenersi a Java puro se possibile. Vedendo come funzionano i loro filtri e la lettura di una grande quantità sui filtri sul DSP Tutor, sono riuscito a guadagnare un po 'il controllo su quanto rumore viene rimosso e come rimuovere le alte frequenze, ecc

Alcuni consigli:

  1. Ricorda cosa rimuovi da dove. Ho dovuto cambiare questo giro alcune volte.
  2. La variabile più importante di questo metodo è il tasso di convergenza. Questa è la variabile chiamata Stepsize nel codice del link qui sopra.
  3. Ho preso i singoli componenti uno alla volta, ho capito cosa hanno fatto, li hanno costruiti e li abbiamo testati separatamente.Ad esempio, ho preso il Double Talk Detector e l'ho testato per assicurarmi che funzionasse. Poi ho preso i filtri uno per uno e li ho testati su file audio per assicurarmi che funzionassero, quindi ho preso la parte quadrata media meno normalizzata e l'ho testata prima di mettere tutto insieme.

Spero che questo aiuti qualcun altro!

+2

Potresti postare la conversione del codice Java? – user489041

4

Utilizzare SpeexAEC. È open source, è scritto in C (usalo con JNI) e funziona. L'ho usato con successo in 2 diverse applicazioni VoIP e viene cancellata la maggior parte dell'eco.

+0

Ciao, per favore, guidami come cancellare echo. Sto lottando per farlo funzionare –

+0

Puoi contattarmi nella email scritta nel mio profilo. Questo non è qualcosa da riassumere in un solo post. – SirKnigget

4

Sono anni! Spero che questo è anche la classe giusta, ma ci si va:

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

Sarebbe stato meglio aggiungere questo codice alla tua risposta originale piuttosto che postare una nuova risposta. –

+0

Come integrare questo pezzo di codice con il mio progetto esistente in cui sto codificando un file raw in dati PCM e quindi il file PCM viene codificato in file .spx. Sto usando jspeex.jar per questo. –

Problemi correlati