2012-10-05 18 views
6

मैं एंड्रॉइड-टू-एंड्रॉइड वीओआईपी (लाउडस्पीकर) ऐप को अपने ऑडियो रिकॉर्ड और ऑडियोट्रैक क्लास का उपयोग करके, एनडीके के माध्यम से स्पीक्स के साथ इको रद्दीकरण करने के लिए बना रहा हूं। मैं स्पीक्स के speex_echo_cancellation() फ़ंक्शन से डेटा को सफलतापूर्वक पास और पुनर्प्राप्त करने में सक्षम था, लेकिन गूंज बनी हुई है।स्पीक्स इको रद्दीकरण कॉन्फ़िगरेशन

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

इन:

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

} 

यहाँ प्रासंगिक NDK/JNI कोड है:

यहाँ प्रासंगिक एंड्रॉयड धागा कोड है कि रिकॉर्डिंग है/भेजने और प्राप्त करने/खेलने ऑडियो है कोड ठीक चलाता है और ऑडियो डेटा निश्चित रूप से भेजा/प्राप्त/संसाधित/एंड्रॉइड-टू-एंड्रॉइड से खेला जाता है। 8000 हर्ट्ज और 2000bytes/1000shorts के पैकेट आकार की ऑडियो नमूना दर को देखते हुए, मैंने पाया है कि खेला गया ऑडियो चिकनी होने के लिए 1000 का फ्रेम आकार आवश्यक है। फिल्टर लम्बाई का अधिक मूल्य (स्पीक्स दस्तावेज़ के अनुसार उर्फ ​​पूंछ की लंबाई) चलती है, लेकिन ऐसा लगता है कि गूंज हटाने पर कोई प्रभाव नहीं पड़ता है।

क्या कोई मुझे पर्याप्त एईसी समझता है ताकि मुझे स्पीक्स को कार्यान्वित करने या कॉन्फ़िगर करने पर कुछ पॉइंटर्स प्रदान किए जा सकें? पढ़ने के लिए धन्यवाद।

+0

मुझे भी इसी तरह की समस्या है। क्या आपको अपनी समस्या का कोई समाधान मिला? – aProgrammer

+0

हाय क्या आपको समस्या का समाधान मिला? धन्यवाद – SoH

उत्तर

2

आपका कोड सही है, लेकिन देशी कोड में कुछ कमी है, मैं init विधि में संशोधन किया और गूंज रद्द होने के बाद Speex preprocess जोड़ा है, तो अपने कोड अच्छी तरह से काम (मैं खिड़कियों में करने की कोशिश की) यहाँ मूल कोड है

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

आप स्पीक्स लाइब्रेरी के स्रोत (http://www.speex.org/downloads/)

+0

त्रुटि: EchoCanceller_jniHeader.h: ऐसी कोई फ़ाइल या निर्देशिका नहीं – EvilThinker

2

में एन्कोडिंग, डिकोडिंग, इको रद्दीकरण जैसे उपयोगी नमूने पा सकते हैं क्या आप दूर-अंत सिग्नल को सही तरीके से संरेखित कर रहे हैं (जिसे आप आरईवी कहते हैं) और अंत सिग्नल के पास (जिसे आप रिकॉर्ड कहते हैं)? हमेशा कुछ प्लेबैक/रिकॉर्ड विलंबता होती है जिसके लिए जिम्मेदार होना आवश्यक है। यह आमतौर पर कुछ निश्चित अवधि के लिए एक अंगूठी बफर में दूर-अंत सिग्नल की बफरिंग की आवश्यकता होती है। पीसी पर यह आमतौर पर लगभग 50 - 120 मिमी होता है। एंड्रॉइड पर मुझे संदेह है कि यह बहुत अधिक है। शायद 150 - 400 एमएमएस की सीमा में। मैं एईसी अभिसरण तक अपने दूर-अंत बफर के आकार को समायोजित करने और आकार को समायोजित करने के साथ 100 एमएमएस ताइलेथेंथ का उपयोग करने की सलाह दूंगा। इन परिवर्तनों से एईसी को प्रीप्रोसेसर को शामिल करने के स्वतंत्र रूप से अभिसरण करने की अनुमति देनी चाहिए, जिसकी आवश्यकता यहां नहीं है।

संबंधित मुद्दे