2012-10-05 33 views
6

Tôi đang tạo ứng dụng VoIP (loa) Android-to-Android bằng cách sử dụng lớp AudioRecord và AudioTrack, cùng với Speex qua NDK để hủy bỏ tiếng vang. Tôi đã có thể chuyển thành công và lấy dữ liệu từ hàm speex_echo_cancellation() của Speex, nhưng tiếng vang vẫn còn.Cấu hình hủy bỏ tiếng vọng Speex

Đây là liên quan android đang thread đang ghi âm/gửi và nhận/chơi âm thanh:

//constructor 
public MyThread(DatagramSocket socket, int frameSize, int filterLength){ 
    this.socket = socket; 
    nativeMethod_initEchoState(frameSize, filterLength); 
} 

public void run(){ 

    short[] audioShorts, recvShorts, recordedShorts, filteredShorts; 
    byte[] audioBytes, recvBytes; 
    int shortsRead; 
    DatagramPacket packet; 

    //initialize recorder and player 
    int samplingRate = 8000; 
    int managerBufferSize = 2000; 
    AudioTrack player = new AudioTrack(AudioManager.STREAM_MUSIC, samplingRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, managerBufferSize, AudioTrack.MODE_STREAM); 
    recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, samplingRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, managerBufferSize); 
    recorder.startRecording(); 
    player.play(); 

    //record first packet 
    audioShorts = new short[1000]; 
    shortsRead = recorder.read(audioShorts, 0, audioShorts.length); 

    //convert shorts to bytes to send 
    audioBytes = new byte[shortsRead*2]; 
    ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(audioShorts); 

    //send bytes 
    packet = new DatagramPacket(audioBytes, audioBytes.length); 
    socket.send(packet); 

    while (!this.isInterrupted()){ 

    //recieve packet/bytes (received audio data should have echo cancelled already) 
    recvBytes = new byte[2000]; 
    packet = new DatagramPacket(recvBytes, recvBytes.length); 
    socket.receive(packet); 

    //convert bytes to shorts 
    recvShorts = new short[packet.getLength()/2]; 
    ByteBuffer.wrap(packet.getData(), 0, packet.getLength()).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(recvShorts); 

    //play shorts 
    player.write(recvShorts, 0, recvShorts.length); 

    //record shorts 
    recordedShorts = new short[1000]; 
    shortsRead = recorder.read(recordedShorts, 0, recordedShorts.length); 

    //send played and recorded shorts into speex, 
    //returning audio data with the echo removed 
    filteredShorts = nativeMethod_speexEchoCancel(recordedShorts, recvShorts); 

    //convert filtered shorts to bytes 
    audioBytes = new byte[shortsRead*2]; 
    ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(filteredShorts); 

    //send off bytes 
    packet = new DatagramPacket(audioBytes, audioBytes.length); 
    socket.send(packet);     

    }//end of while loop 

} 

Đây là có liên quan đang NDK/JNI:

void nativeMethod_initEchoState(JNIEnv *env, jobject jobj, jint frameSize, jint filterLength){ 
    echo_state = speex_echo_state_init(frameSize, filterLength); 
} 

jshortArray nativeMethod_speexEchoCancel(JNIEnv *env, jobject jObj, jshortArray input_frame, jshortArray echo_frame){ 

    //create native shorts from java shorts 
    jshort *native_input_frame = (*env)->GetShortArrayElements(env, input_frame, NULL); 
    jshort *native_echo_frame = (*env)->GetShortArrayElements(env, echo_frame, NULL); 

    //allocate memory for output data 
    jint length = (*env)->GetArrayLength(env, input_frame); 
    jshortArray temp = (*env)->NewShortArray(env, length); 
    jshort *native_output_frame = (*env)->GetShortArrayElements(env, temp, 0); 

    //call echo cancellation 
    speex_echo_cancellation(echo_state, native_input_frame, native_echo_frame, native_output_frame); 

    //convert native output to java layer output 
    jshortArray output_shorts = (*env)->NewShortArray(env, length); 
    (*env)->SetShortArrayRegion(env, output_shorts, 0, length, native_output_frame); 

    //cleanup and return 
    (*env)->ReleaseShortArrayElements(env, input_frame, native_input_frame, 0); 
    (*env)->ReleaseShortArrayElements(env, echo_frame, native_echo_frame, 0); 
    (*env)->ReleaseShortArrayElements(env, temp, native_output_frame, 0); 
    return output_shorts; 
} 

Những mã chạy tốt và dữ liệu âm thanh chắc chắn được gửi/nhận/xử lý/phát từ android-to-android. Với tốc độ mẫu âm thanh 8000 Hz và kích thước gói 2000bytes/1000shorts, tôi đã nhận thấy rằng một frameSize 1000 là cần thiết để âm thanh phát được mượt mà. Hầu hết giá trị của filterLength (còn gọi là chiều dài đuôi theo Speex doc) sẽ chạy, nhưng dường như không có tác dụng gì đối với việc loại bỏ echo.

Có ai hiểu đủ AEC để cung cấp cho tôi một số gợi ý về việc triển khai hoặc định cấu hình Speex không? Cảm ơn vì đã đọc.

+0

Tôi cũng có vấn đề tương tự. Bạn có giải pháp nào cho vấn đề của mình không? – aProgrammer

+0

Hi u tìm thấy một giải pháp cho vấn đề? Cảm ơn lỗi – SoH

Trả lời

2

Mã của bạn là đúng, nhưng thiếu một cái gì đó trong mã nguồn gốc, tôi sửa đổi phương pháp init và thêm Speex preprocess sau khi hủy bỏ tiếng vang, sau đó mã của bạn làm việc tốt (Tôi đã thử ở cửa sổ) Đây là Mã Native

#include <jni.h> 
#include "speex/speex_echo.h" 
#include "speex/speex_preprocess.h" 
#include "EchoCanceller_jniHeader.h" 
SpeexEchoState *st; 
SpeexPreprocessState *den; 

JNIEXPORT void JNICALL Java_speex_EchoCanceller_open 
    (JNIEnv *env, jobject jObj, jint jSampleRate, jint jBufSize, jint jTotalSize) 
{ 
    //init 
    int sampleRate=jSampleRate; 
    st = speex_echo_state_init(jBufSize, jTotalSize); 
    den = speex_preprocess_state_init(jBufSize, sampleRate); 
    speex_echo_ctl(st, SPEEX_ECHO_SET_SAMPLING_RATE, &sampleRate); 
    speex_preprocess_ctl(den, SPEEX_PREPROCESS_SET_ECHO_STATE, st); 
} 

JNIEXPORT jshortArray JNICALL Java_speex_EchoCanceller_process 
    (JNIEnv * env, jobject jObj, jshortArray input_frame, jshortArray echo_frame) 
{ 
    //create native shorts from java shorts 
    jshort *native_input_frame = (*env)->GetShortArrayElements(env, input_frame, NULL); 
    jshort *native_echo_frame = (*env)->GetShortArrayElements(env, echo_frame, NULL); 

    //allocate memory for output data 
    jint length = (*env)->GetArrayLength(env, input_frame); 
    jshortArray temp = (*env)->NewShortArray(env, length); 
    jshort *native_output_frame = (*env)->GetShortArrayElements(env, temp, 0); 

    //call echo cancellation 
    speex_echo_cancellation(st, native_input_frame, native_echo_frame, native_output_frame); 
    //preprocess output frame 
    speex_preprocess_run(den, native_output_frame); 

    //convert native output to java layer output 
    jshortArray output_shorts = (*env)->NewShortArray(env, length); 
    (*env)->SetShortArrayRegion(env, output_shorts, 0, length, native_output_frame); 

    //cleanup and return 
    (*env)->ReleaseShortArrayElements(env, input_frame, native_input_frame, 0); 
    (*env)->ReleaseShortArrayElements(env, echo_frame, native_echo_frame, 0); 
    (*env)->ReleaseShortArrayElements(env, temp, native_output_frame, 0); 

    return output_shorts; 
} 

JNIEXPORT void JNICALL Java_speex_EchoCanceller_close 
    (JNIEnv *env, jobject jObj) 
{ 
    //close 
    speex_echo_state_destroy(st); 
    speex_preprocess_state_destroy(den); 
} 

Bạn có thể tìm thấy các mẫu hữu ích như Mã hóa, Giải mã, Hủy bỏ Echo trong thư viện của thư viện độc lập (http://www.speex.org/downloads/)

+0

: EchoCanceller_jniHeader.h: Không có tệp hoặc thư mục nào như vậy – EvilThinker

2

Bạn có sắp xếp đúng tín hiệu ở xa hay không (những gì bạn gọi là recv) và gần tín hiệu kết thúc (những gì bạn gọi hồ sơ)? Luôn luôn có một số độ trễ phát lại/ghi lại cần được tính toán. Điều này thường yêu cầu đệm tín hiệu đầu cuối trong bộ đệm vòng trong một khoảng thời gian nhất định. Trên PC, thường là khoảng 50 - 120ms. Trên Android tôi nghi ngờ nó cao hơn nhiều. Có lẽ trong khoảng 150 - 400ms. Tôi sẽ khuyên bạn nên sử dụng một chiều dài 100ms với speex và điều chỉnh kích thước của bộ đệm xa của bạn cho đến khi AEC hội tụ. Những thay đổi này sẽ cho phép AEC hội tụ, độc lập với sự bao gồm của bộ tiền xử lý, mà không cần thiết ở đây.

Các vấn đề liên quan