2010-08-04 6 views
8

Ich implementiere eine VOIP-Anwendung, die reines Java verwendet. Es gibt ein Echo-Problem, das auftritt, wenn Benutzer keine Headsets verwenden (hauptsächlich bei Laptops mit integrierten Mikrofonen).Akustische Echokompensation in Java

Was zur Zeit geschieht

die Schrauben und Muttern der VoIP-Anwendung ist nur die einfachen Datenleitungen von Java Media Framework. Im Wesentlichen möchte ich eine digitale Signalverarbeitung an Audiodaten vornehmen, bevor ich sie zur Ausgabe an den Lautsprecher schreibe.

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

Wie Sie sehen können, kommen die Audiodaten an und werden in einen Puffer eingereiht. Dies dient dazu, dubiose Verbindungen zu berücksichtigen und unterschiedliche Paketgrößen zu ermöglichen. Es bedeutet auch, dass ich auf so viele Audiodaten zugreifen kann, wie ich für irgendwelche ausgefallenen DSP-Operationen benötige, bevor ich die Audiodaten an die Lautsprecherlinie abspiele.

Ich habe einen Echokompensator, der funktioniert, aber es erfordert eine Menge interaktiver Benutzereingaben und ich möchte einen automatischen Echokompensator haben.

Manuelles Echokompensators

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 Filter

ich gelesen habe, dass adaptive filters der Weg zu gehen. Insbesondere ein Least-Mean-Squares-Filter. Ich stecke jedoch fest. Die meisten Beispiele für die obigen Beispiele sind in C und C++ und sie lassen sich nicht gut in Java übersetzen.

Hat jemand Tipps, wie man sie in Java implementiert? Alle anderen Ideen würden auch sehr geschätzt werden. Danke im Voraus.

+0

ich dieses Acoustic Echo Cancellation sehr Führer finden, der sehr hilfreich bisher: http://www.andreadrian.de/echo_cancel/index.html –

Antwort

1

Dies ist ein sehr komplexer Bereich und eine nutzbare AEC Lösung zu erhalten, arbeiten Sie ein viel mehr ziemlich viel R & D. Alle tun das gute AECs sind urheberrechtlich geschützt, und es gibt müssen Stornierung Echo als einfach einen adaptiven Filter wie LMS implementieren. Ich schlage vor, dass Sie Ihren Echokompensationsalgorithmus zunächst mit MATLAB (oder Octave) entwickeln - wenn Sie etwas haben, das mit "echten" Telecomms recht gut zu funktionieren scheint, dann können Sie den Algorithmus in C implementieren und in Echtzeit testen/evaluieren. Sobald dies funktioniert, können Sie JNI verwenden, um die C-Implementierung von Java aus aufzurufen.

+1

Danke für die Antwort. Ich habe versucht, die Verwendung von JNI zu vermeiden, bin aber verzweifelt genug, etwas zu versuchen. –

+2

Sie können feststellen, dass Sie plattformspezifische Daten in Ihrem AEC, z. Low-Level-OS/Audio-API-Aufrufe, so könnte es auch C + JNI sein. –

4

Falls jemand interessiert ist, habe ich es geschafft, einen fairen, funktionierenden Echokompensator zu bauen, indem ich die Acoustic Echo Cancellation Methode mentioned by Paul R, die einen Normalized Least Means Square Algorithmus und einige Filter von C verwendet, in Java umwandelt. Die JNI-Route ist wahrscheinlich immer noch ein besserer Weg, aber ich bleibe lieber bei reinem Java, wenn irgend möglich. Wenn wir sehen, wie sie ihre Filter arbeiten und sehr viel auf Filter auf DSP Tutor Lesung bis schaffte ich es über eine gewisse Kontrolle zu gewinnen, wie viel Lärm wird entfernt und wie hohe Frequenzen entfernen usw.

Einige Tipps:

  1. Denken Sie daran, was Sie von wo entfernen. Ich musste das ein paar Mal umschalten.
  2. Die wichtigste Variable dieser Methode ist die Konvergenzrate. Dies ist die Variable namens Stepsize im obigen Link-Code.
  3. Ich nahm die einzelnen Komponenten nacheinander, fand heraus, was sie taten, baute sie und testete sie separat.Zum Beispiel habe ich den Double Talk Detector genommen und getestet, um sicherzustellen, dass er funktioniert. Dann nahm ich die Filter nacheinander und testete sie auf Audiodateien, um sicherzustellen, dass sie funktionierten, dann nahm ich das normalisierte Quadrat und teste es, bevor ich alles zusammenfasste.

Hoffe, das hilft jemand anderem!

+2

Können Sie eine Java-Konvertierung des Codes posten? – user489041

4

Verwenden Sie die SpeexAEC. Es ist Open Source, es ist in C geschrieben (benutze es mit JNI), und es funktioniert. Ich habe es erfolgreich in 2 verschiedenen VoIP-Anwendungen verwendet und es wird die meisten Echo abgebrochen.

+0

hallo können Sie mir bitte helfen, wie Echo zu kündigen. Ich kämpfe um es zu arbeiten –

+0

Sie können mich in der E-Mail in meinem Profil geschrieben kontaktieren. Dies ist nicht etwas in einem Post zusammenzufassen. – SirKnigget

4

Es ist schon ewig her! Hoffe, das ist auch die richtige Klasse, aber Sie dorthin gehen:

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

Sie wären besser dran gewesen, diesen Code zu Ihrer ursprünglichen Antwort hinzuzufügen, als eine neue Antwort zu posten. –

+0

Wie integriere ich diesen Teil des Codes mit meinem bestehenden Projekt, in dem ich eine Raw-Datei zu PCM-Daten kodiere und dann die PCM-Datei in .spx-Datei codiert wird. Ich verwende jspeex.jar dafür. –