2012-04-09 44 views
2

私は基本的なaudiorecord-audiotrackを持っています。udpパケットは2台のAndroidデバイス間で音声チャットを行います。それは動作しますが、私は悪いエコーを持っています。私はJNIによってアンドロイドに移植されたSpeexを使ってエコーを除去しようとしています。私がインポートしたspeexは動作しますが、エコーキャンセルは動作しません。ネイティブCコードです:Android Speexエコーキャンセルの問題

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

、重要なJavaコードの一部:

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

問題は、私はレコーダー/ストリーミングスレッドを起動したとき、それはその後、一つのパッケージを送信していることを私に示していることですアプリはメッセージなしでクラッシュします。あなたはどんな懇願やアドバイスもありますか?私はこのプロジェクトをできるだけ早く行う必要があるので私を助けてください。私は懸命に働いて、自分自身を文書化しましたが、それでもうまくいきたいとは思いません。ありがとうございました!

編集:私はちょうど

// Echo cancelling 
buffer = Capture(buffer); 

がクラッシュをトリガしていることを発見しました。

+0

クラッシュ時の更新内容に応じて、自分の回答に更新情報を追加しました。 – SirKnigget

+0

こんにちはエミールパナはうまくエコーを削除しましたか? –

+0

問題を解決したことはありますか? – aProgrammer

答えて

1

UPDATE:クラッシュについて - これはJNIの誤使用によるものです。 jshortarrayをCのポインタとして扱うことはできません。関連するJNI関数を呼び出してCポインタ(GetShortArrayElementsなど)に変換する必要があります。

エコーキャンセラーについて - 現在、約2フレームの内部バッファーを使用していますが、これでは不十分かもしれません。 Androidのコンテキスト切り替えはPCのようなものではなく、連続した4つの再生フレーム - > 4つの連続した記録フレーム - >というようになります。だからあなたはそのようにフレームを失う。

また、AndroidオーディオストリーミングはPCよりもレイテンシが長いため、2フレームバッファでは不十分です。あなた自身のバッファリングを実装し、遅らせるフレームの量で再生する必要があるので、エコーキャンセラは、フィルタ長の境界内で再生した後にエコーを表示します。

エコーキャンセラーを分析してデバッグするには、 が最適です。1.実際にどのように高レベルで動作しているのか、要件は何か - ロケット科学ではなく、公式Speexドキュメント。 2.提供されているecho_diagnosticツールを使用し、Audacityなどのツールでストリームを調べて、どこが間違っているかを確認します。

Speex AECは正しく使用するとうまく動作します。すでに3つの異なるプロジェクトを使用していますので、実装を修正するだけです。

+0

私はAndroidアプリケーションで同様の作業をしています.JNI呼び出しでSpeexエンコーディングを正常に実装しました。しかし、FLOATING_POINTのライブラリをコンパイルするとうまくいきません。 FIXED_POINTコンパイルでecho_cancellationをどうやって提供するのですか? – aProgrammer