android上的AcousticEchoCanceler在大多数设备上似乎无法正常工作。

9
我正在开发一个应用程序,需要麦克风消除扬声器发出的任何声音。在线上看来,这个问题似乎几乎是一个阴谋,因为其他遇到同样问题的人长时间以来都没有得到回复。
Android的本地硬件加速AcousticEchoCanceler在大多数设备上似乎无法正常工作。在许多设备上进行了测试,似乎可以正常工作的包括Nexus 5和Moto X,而几乎所有经过测试的三星设备都无法消除背景噪音。注意:所有测试的手机返回AcousticEchoCanceler.isAvailable()的值都为true。
然而,必须有解决方案,因为像Skype或WhatsApp这样的应用程序似乎会消除应用程序环境外的声音,即扬声器上有通话,麦克风会消除收到的任何反馈声音。
这个简化的录音应用程序将声音记录到文件中,并在单击播放按钮后播放它。
MainActivity.java
public class MainActivity extends Activity {

Button startRec, stopRec, playBack;
int minBufferSizeIn;
AudioRecord audioRecord;
short[] audioData;
Boolean recording;
int sampleRateInHz = 48000;
private String TAG = "TAG";

/**
 * Called when the activity is first created.
 */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    startRec = (Button) findViewById(R.id.startrec);
    stopRec = (Button) findViewById(R.id.stoprec);
    playBack = (Button) findViewById(R.id.playback);

    startRec.setOnClickListener(startRecOnClickListener);
    stopRec.setOnClickListener(stopRecOnClickListener);
    playBack.setOnClickListener(playBackOnClickListener);
    playBack.setEnabled(false);
    startRec.setEnabled(true);
    stopRec.setEnabled(false);

    minBufferSizeIn = AudioRecord.getMinBufferSize(sampleRateInHz,
            AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT);

    audioData = new short[minBufferSizeIn];

    audioRecord = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION,
            sampleRateInHz,
            AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT,
            minBufferSizeIn);

}

OnClickListener startRecOnClickListener
        = new OnClickListener() {

    @Override
    public void onClick(View arg0) {
        playBack.setEnabled(false);
        startRec.setEnabled(false);
        stopRec.setEnabled(true);
        Thread recordThread = new Thread(new Runnable() {

            @Override
            public void run() {
                recording = true;
                startRecord();
            }

        });

        recordThread.start();

    }
};


OnClickListener stopRecOnClickListener
        = new OnClickListener() {

    @Override
    public void onClick(View arg0) {
        playBack.setEnabled(true);
        startRec.setEnabled(false);
        stopRec.setEnabled(false);
        recording = false;
    }
};



OnClickListener playBackOnClickListener
        = new OnClickListener() {

    @Override
    public void onClick(View v) {
        playBack.setEnabled(false);
        startRec.setEnabled(true);
        stopRec.setEnabled(false);
        playRecord();
    }

};


@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void startRecord() {
    File file = new File(Environment.getExternalStorageDirectory(), "test.pcm");

    try {
        FileOutputStream outputStream = new FileOutputStream(file);
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
        DataOutputStream dataOutputStream = new DataOutputStream(bufferedOutputStream);

        NoiseSuppressor ns;
        AcousticEchoCanceler aec;

        if (NoiseSuppressor.isAvailable()) {
            ns = NoiseSuppressor.create(audioRecord.getAudioSessionId());
            if (ns != null) {
                ns.setEnabled(true);
            } else {
                Log.e(TAG, "AudioInput: NoiseSuppressor is null and not enabled");
            }
        }

        if (AcousticEchoCanceler.isAvailable()) {
            aec = AcousticEchoCanceler.create(audioRecord.getAudioSessionId());
            if (aec != null) {
                aec.setEnabled(true);
            } else {
                Log.e(TAG, "AudioInput: AcousticEchoCanceler is null and not enabled");
            }
        }
        audioRecord.startRecording();

        while (recording) {
            int numberOfShort = audioRecord.read(audioData, 0, minBufferSizeIn);
            for (int i = 0; i < numberOfShort; i++) {
                dataOutputStream.writeShort(audioData[i]);
            }
        }

        audioRecord.stop();
        dataOutputStream.close();

    } catch (IOException e) {
        e.printStackTrace();
    }
}

void playRecord() {
    File file = new File(Environment.getExternalStorageDirectory(), "test.pcm");

    int shortSizeInBytes = Short.SIZE / Byte.SIZE;

    int bufferSizeInBytes = (int) (file.length() / shortSizeInBytes);
    short[] audioData = new short[bufferSizeInBytes];

    try {
        FileInputStream inputStream = new FileInputStream(file);
        BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
        DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);

        int i = 0;
        while (dataInputStream.available() > 0) {
            audioData[i] = dataInputStream.readShort();
            i++;
        }

        dataInputStream.close();

        AudioTrack audioTrack = new AudioTrack(
                AudioManager.STREAM_MUSIC, sampleRateInHz,
                AudioFormat.CHANNEL_OUT_MONO,
                AudioFormat.ENCODING_PCM_16BIT,
                bufferSizeInBytes,
                AudioTrack.MODE_STREAM);
        while(audioTrack.getState() != AudioTrack.STATE_INITIALIZED){

        }
        audioTrack.play();
        audioTrack.write(audioData, 0, bufferSizeInBytes);


    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          android:orientation="vertical" >

<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello_world" />
<Button
    android:id="@+id/startrec"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Start Recording Test" />
<Button
    android:id="@+id/stoprec"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Stop Recording" />
<Button
    android:id="@+id/playback"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Play Back" />

</LinearLayout>

AndroidManifest.xml 权限

    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

要验证设备是否正常工作,只需在后台播放一些东西,然后单击“开始录制”记录一个小部分,然后单击“停止录制”,此时单击“回放”,检查是否可以听到背景声音。如果你能听到背景声音,则说明AEC未起作用。 但是为什么会出现这种不一致情况,或者我该如何实现回声消除(我已经在我的应用程序中使用WebRTC进行上下文噪声消除)

任何帮助将不胜感激!


你解决了这个问题吗? - Mohit Dholakia
2个回答

4
我在我的S6设备上遇到了同样的问题。我尝试了各种设置,找到了一组似乎能够启用AEC的设置。我和你的设置之间的差异似乎是:
  • 16k采样率
  • audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
对于其他人,我不确定需要哪些设置才能使AEC正常工作。我知道,如果设置为:
  • 48k采样率
  • NO audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
  • NO android.permission.MODIFY_AUDIO_SETTINGS
则无法成功实现AEC。

0
Problem:
AudioTrack audioTrack = new AudioTrack(
    AudioManager.STREAM_MUSIC, sampleRateInHz,
    AudioFormat.CHANNEL_OUT_MONO,
    AudioFormat.ENCODING_PCM_16BIT,
    bufferSizeInBytes,
    AudioTrack.MODE_STREAM);

应该是:

应该是:

AudioTrack audioTrack = new AudioTrack(
    AudioManager.STREAM_MUSIC, sampleRateInHz,
    AudioFormat.CHANNEL_OUT_MONO,
    AudioFormat.ENCODING_PCM_16BIT,
    bufferSizeInBytes,
    AudioTrack.MODE_STREAM
    sessionId); // this param is important, which is audioRecord. getAudioSessionId()

这对我没用。它破坏了我试图创建的任何“AudioEffect”的创建: E/AudioEffect: set(): AudioFlinger无法创建效果,状态:-22 - Roman Samoilenko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接