2012-04-09 16 views
2

Ich habe eine grundlegende Audioorecord-Audiotrack, UDP-Pakete Voice-Chat zwischen zwei Android-Geräten. Es funktioniert, aber ich habe ein schlechtes Echo. Ich versuche, das Echo mit Speex zu entfernen, das von JNI nach Android portiert wurde. Die von mir importierte Speex funktioniert, aber die Echokompensation nicht. Nativen C-Code ist:Android Speex Echokompensation Probleme

#include <jni.h> 
#include <speex/speex_echo.h> 

#define FRAME_SIZE 256 
#define FILTER_LENGTH 800 

SpeexEchoState *echoState; 

// Initialization of echo cancelation 
void Java_telefonie_voip_VoIPActivity_InitEcho(JNIEnv * env, jobject jobj) 
{ 
    echoState = speex_echo_state_init(FRAME_SIZE, FILTER_LENGTH); 
} 

// Queue the frame to soundcard for playing (receiving) 
void Java_telefonie_voip_VoIPActivity_Playback(JNIEnv * env, jobject jobj, jshortArray  inputFrame) 
{ 
    speex_echo_playback(echoState, inputFrame); 
} 

jshortArray Java_telefonie_voip_VoIPActivity_Capture(JNIEnv * env, jobject jobj,  jshortArray inputFrame) 
{ 
    jshortArray outputFrame; 

    speex_echo_capture(echoState, inputFrame, *&outputFrame); 

    return outputFrame; 
} 

// Destroing echo cancelation 
void Java_telefonie_voip_VoIPActivity_DestroyEcho(JNIEnv * env, jobject jobj) 
{ 
speex_echo_state_destroy(echoState); 
} 

Und einige wichtige Java-Code:

native void InitEcho(); 
native void DestroyEcho(); 
native void Playback(short[] inputData); // listener/receiving 
native short[] Capture(short[] inputData); // recorder/sender 
static { 
    System.loadLibrary("speex"); 
} 

public void Initialize() 
{ 
    minBufferSize = 4096; 
    try 
    { 
     InitEcho(); 
    } 
    catch (Exception e){Log.e(TAG, "jni " + e.getMessage());} 
} 

public void startRecording() 
{ 
    isStreaming = true; 
streamingThread = new Thread(new Runnable(){ 
     @Override 
     public void run() 
     { 
      try 
      { 
       audioRecorder = new AudioRecord(
         MediaRecorder.AudioSource.MIC, 
         sampleRate, 
         channelConfig, 
         audioFormat, 
         minBufferSize*10 
       ); 

       buffer = new short[minBufferSize]; 
       audioRecorder.startRecording(); 

       while(isStreaming) 
       { 
        sendPackets++; 
        minBufferSize = audioRecorder.read(buffer, 0, buffer.length);  
        // Echo cancelling 
        buffer = Capture(buffer); 

        // Send 
        dataPacket = new DatagramPacket(ShortToByteArray(buffer), buffer.length*2, receiverAddressIA, serverPort1); 
        dataSocket.send(dataPacket); 
       } 
      } 
      catch(Exception e) 
      { 
       Log.e(TAG, "2" + e.getMessage()); 
      } 
     } 
    }); 
    streamingThread.start(); 
} 

public void startListen() 
{ 
    isListening = true; 
receivingThread = new Thread(new Runnable(){ 
     @Override 
     public void run() 
     { 
      try 
      { 
       audioTrack = new AudioTrack(
         AudioManager.STREAM_VOICE_CALL, 
         sampleRate, 
         channelConfig, 
         audioFormat, 
         minBufferSize*10, 
         AudioTrack.MODE_STREAM 
       ); 

       audioTrack.play(); 

       buffer2 = new short[minBufferSize]; 

       while (isListening) 
       { 
        receivedPackets++; 
        dataPacket2 = new DatagramPacket(ShortToByteArray(buffer2), buffer2.length*2); 
        dataSocket2.receive(dataPacket2); 

        // Cancel echo 
        Playback(buffer2); 

        // Queue to soundcard 
        audioTrack.write(buffer2, 0, minBufferSize); 
       } 
      } 
      catch(Exception e) 
      { 
       Log.e(TAG, "3" + e.getMessage()); 
      } 
     } 
    }); 
    receivingThread.start(); 
} 

public short[] ByteToShortArray(byte[] byteArray) 
{ 
    short[] shortArray = new short[byteArray.length/2]; 
ByteBuffer.wrap(byteArray).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get(shortArray); 
    return shortArray; 
} 

Das Problem ist, dass, wenn ich den Recorder/Streaming-Thread starten, es zeigt mir, dass es ein Paket sendet und dann die App stürzt ohne Nachricht ab. Haben Sie Anregungen oder Ratschläge? Bitte helfen Sie mir, weil ich dieses Projekt so schnell wie möglich machen muss und ich habe hart gearbeitet und mich selbst dokumentiert, aber trotzdem will es nicht gut funktionieren. Vielen Dank!

Edit: Ich habe das

// Echo cancelling 
buffer = Capture(buffer); 

auslöst den Absturz gerade entdeckt.

+0

Ein Update zu meiner Antwort entsprechend Ihrem Update zum Absturz hinzugefügt. – SirKnigget

+0

Hallo Emil Pana hast du erfolgreich das Echo entfernt? –

+0

Haben Sie das Problem schon einmal gelöst? – aProgrammer

Antwort

1

UPDATE: In Bezug auf Absturz - Dies ist aufgrund der missbräuchlichen Verwendung von JNI. Sie können jshortarray nicht als C-Zeiger behandeln. Sie müssen die relevanten JNI-Funktionen aufrufen, um sie in einen C-Zeiger (GetShortArrayElements usw.) zu konvertieren.

In Bezug auf Echo-Canceller - Sie verwenden derzeit den internen Puffer, der etwa 2 Frames ist, und das ist vielleicht nicht genug für Sie. Androids Kontext-Umschaltung ist nicht wie auf einem PC, und Sie können 4 aufeinanderfolgende Playback-Frames erhalten -> 4 aufeinander folgende aufgenommene Frames -> und so weiter. So verlierst du Frames auf diese Weise.

Auch Android Audio-Streaming hat eine höhere Latenz als ein PC, so dass der 2-Frame-Puffer nicht ausreicht. Sie müssen Ihre eigene Pufferung implementieren und mit der Anzahl der Frames spielen, um die Sie verzögern. Der Echocanceller sieht also das Echo nach der Wiedergabe innerhalb der Grenzen seiner Filterlänge.

Ansonsten ist der beste Weg, um die Echounterdrückung zu analysieren und zu debuggen: 1. Eigentlich verstehen, wie es auf einem hohen Niveau funktioniert und was die Anforderungen sind - keine Raketenwissenschaft, können Sie alles auf dem offiziellen finden Speex Dokumente. 2. Verwenden Sie das mitgelieferte Werkzeug echo_diagnostic und untersuchen Sie die Streams in einem Tool wie Audacity, um zu sehen, wo es schief gelaufen ist.

Der Speex AEC funktioniert ziemlich gut, wenn er richtig verwendet wird, und ich habe bereits 3 verschiedene Projekte damit gemacht, es ist also nur eine Frage der Korrektur Ihrer Implementierung.

+0

Ich habe ähnliche Aufgabe auf meiner Android-App zu tun .. Ich habe erfolgreich Speex Codierung über JNI-Aufrufe implementiert .. Wenn ich jedoch die Bibliothek für FLOATING_POINT kompilieren, funktioniert es nicht. Wie stelle ich echo_cancellation in der Kompilierung FIXED_POINT zur Verfügung? – aProgrammer