Android AEC 不会生成有用的声音

发布于 2024-12-15 16:32:28 字数 8016 浏览 2 评论 0原文

我正在为 VoIP 应用程序进行一些声学回声消除实验,声音让我抓狂。我想做的很简单:我之前已经录制过声音。现在我将播放该声音,并在播放时录制另一个声音,这是全双工 VoIP 场景的真实情况。我使用 MedaiPlayer 来播放声音,使用 MediaRecorder 来录制新声音。下面是该类的完整代码,修改自 Android SoundRecorder Sample。重要的一点是, mPlayer.setAudioStreamType(AudioManager.MODE_IN_COMMUNICATION); 对于 Android 文档,“在通信音频模式下。建立音频/视频聊天或 VoIP 呼叫。”和 mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION); 对于 Android 文档,“针对 VoIP 等语音通信进行调整的麦克风音频源。例如,它将利用回声消除或自动增益控制(如果可用)。如果不应用语音处理,则其行为类似于默认值。”这是非常有希望的。 但如果我播放扬声器发出的任何声音,我几乎什么也录不到,或者只录到奇怪的声音。由于我是这里的新用户,所以无法附加录音的声波,但第一个是正常的录音,具有正常的声波。第二次录音是在播放第一次录音的同时进行录音,几乎没有任何内容,没有声波。如果扬声器有任何活动,Android 似乎会关闭麦克风。我尝试了 MediaRecorder 和 MediaPlayer 的不同可能性,但没有用。在 Android 中实现回声消除的正确方法是什么?我在Sony Tablet S上试用并使用Android 3.0 SDK进行开发。 提前致谢。

package com.kadir.sample;
import android.app.Activity;
import android.widget.LinearLayout;
import android.widget.Toast;
import android.os.Bundle;
import android.os.Environment;
import android.view.ViewGroup;
import android.widget.Button;
import android.view.View;
import android.content.Context;
import android.util.Log;
import android.media.AudioManager;
import android.media.MediaRecorder;
import android.media.MediaPlayer;

import java.io.IOException;


public class AudioRecordTest extends Activity
{
    private static final String LOG_TAG = "AudioRecordTest";
    private static String mFileName = null;
    private static String mFileNameConst = null;

    private RecordButton mRecordButton = null;
    private MediaRecorder mRecorder = null;

    private PlayButton   mPlayButton = null;
    private MediaPlayer   mPlayer = null;
    private PlayConstButton mPlayConstButton = null;

    private void onRecord(boolean start) {
        if (start) {
            startRecording();
        } else {
            stopRecording();
        }
    }

    private void onPlay(boolean start) {
        if (start) {
            startPlaying();
        } else {
            stopPlaying();
        }
    }

    private void onPlayConst(boolean start) {
        if (start) {
            startConstPlaying();
        } else {
            stopConstPlaying();
        }
    }

    private void startPlaying() {
        mPlayer = new MediaPlayer();
        try {
            mPlayer.setDataSource(mFileName);
            mPlayer.prepare();
            mPlayer.start();
        } catch (IOException e) {
            Toast.makeText(this, "prepare() failed", Toast.LENGTH_LONG).show();
            Log.e(LOG_TAG, "prepare() failed");
        }
    }

    private void stopPlaying() {
        mPlayer.release();
        mPlayer = null;
    }

    private void startConstPlaying() {
        mPlayer = new MediaPlayer();
        try {
            mPlayer.setDataSource(mFileNameConst);
            mPlayer.setAudioStreamType(AudioManager.MODE_IN_COMMUNICATION);
            mPlayer.prepare();
            mPlayer.start();
        } catch (IOException e) {
            Toast.makeText(this, "prepare() failed", Toast.LENGTH_LONG).show();
            Log.e(LOG_TAG, "prepare() failed");
    }
}

private void stopConstPlaying() {
        mPlayer.release();
        mPlayer = null;
    }

    private void startRecording() {
        mRecorder = new MediaRecorder();
        mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION);
        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
        mRecorder.setOutputFile(mFileName);
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

        try {
            mRecorder.prepare();
        } catch (IOException e) {
            Toast.makeText(this, "startRecording() failed", Toast.LENGTH_LONG).show();
            Log.e(LOG_TAG, "prepare() failed");
        }

        mRecorder.start();
    }

    private void stopRecording() {
        mRecorder.stop();
        mRecorder.release();
        mRecorder = null;
    }

    class RecordButton extends Button {
        boolean mStartRecording = true;

        OnClickListener clicker = new OnClickListener() {
            public void onClick(View v) {
                onRecord(mStartRecording);
                if (mStartRecording) {
                    setText("Stop recording");
                } else {
                    setText("Start recording");
                }
                mStartRecording = !mStartRecording;
            }
        };

        public RecordButton(Context ctx) {
            super(ctx);
            setText("Start recording");
            setOnClickListener(clicker);
        }
    }

    class PlayButton extends Button {
        boolean mStartPlaying = true;

        OnClickListener clicker = new OnClickListener() {
            public void onClick(View v) {
                onPlay(mStartPlaying);
                if (mStartPlaying) {
                    setText("Stop playing");
                } else {
                    setText("Start playing");
                }
                mStartPlaying = !mStartPlaying;
            }
        };

        public PlayButton(Context ctx) {
            super(ctx);
            setText("Start playing");
            setOnClickListener(clicker);
        }
    }

    class PlayConstButton extends Button {
        boolean mStartPlaying = true;

        OnClickListener clicker = new OnClickListener() {
            public void onClick(View v) {
                onPlayConst(mStartPlaying);
                if (mStartPlaying) {
                    setText("Stop Constant playing");
                } else {
                    setText("Start Constant playing");
                }
                mStartPlaying = !mStartPlaying;
            }
        };

        public PlayConstButton(Context ctx) {
            super(ctx);
            setText("Start Constant playing");
            setOnClickListener(clicker);
        }
    }

    public AudioRecordTest() {
        mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();
        mFileName += "/audiorecordtest.3gp";
        mFileNameConst = Environment.getExternalStorageDirectory().getAbsolutePath();
        mFileNameConst += "/constant.3gp";
    }

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        LinearLayout ll = new LinearLayout(this);
        mRecordButton = new RecordButton(this);
        ll.addView(mRecordButton,
            new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT,
                0));
        mPlayButton = new PlayButton(this);
        ll.addView(mPlayButton,
            new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT,
                0));
        mPlayConstButton = new PlayConstButton(this);
        ll.addView(mPlayConstButton,
            new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT,
                0));
        setContentView(ll);
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mRecorder != null) {
            mRecorder.release();
            mRecorder = null;
        }

        if (mPlayer != null) {
            mPlayer.release();
            mPlayer = null;
        }
    }
}

更新: 我进行了一些研发,如下: 我更改 MediaRecorder 和 MediaPlayer 参数。对于每个值,我都会自己录制,并且在录制过程中,我开始了另一场比赛。然后完成录音并听我刚刚录制的内容。对于 MediaRecorder,我尝试了这些值: MediaRecorder.AudioSource.DEFAULT、MediaRecorder.AudioSource.MIC、MediaRecorder.AudioSource.VOICE_CALL、MediaRecorder.AudioSource.VOICE_COMMUNICATION、MediaRecorder.AudioSource.VOICE_DOWNLINK、MediaRecorder.AudioSource.VOICE_RECOGNITION、MediaRecorder.AudioSource.VOICE_UPLINK 对于 MediaPlayer,我尝试了这些值: AudioManager.MODE_NORMAL、AudioManager.MODE_CURRENT、AudioManager.MODE_IN_CALL、AudioManager.MODE_IN_COMMUNICATION、 AudioManager.STREAM_VOICE_CALL,AudioManager.STREAM_MUSIC。 但无论我尝试什么,我总是要么沉默,要么纯粹的噪音。我认为 MediaRecorder 和 MediaPlayer 类不足以用于 VoIP。 Android 的声音系统对于我这样的初学者来说有点奇怪。

I am having some Accoustic Echo Cancellation experiments for a VoIP application and sound drives me mad. What I am trying to do is simple: I had recorded a sound before. Now I will play that sound and while playing, record another sound, which is the real case for a full duplex VoIP scenario. I use MedaiPlayer for playing sound and MediaRecorder for recording new sound. Below is the full code of the class, modified from Android SoundRecorder Sample. The important points are,
mPlayer.setAudioStreamType(AudioManager.MODE_IN_COMMUNICATION);
for Android docs say "In communication audio mode. An audio/video chat or VoIP call is established." and
mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION);
for Android docs say "Microphone audio source tuned for voice communications such as VoIP. It will for instance take advantage of echo cancellation or automatic gain control if available. It otherwise behaves like DEFAULT if no voice processing is applied." and this is so promising.
But if I play anything coming out from speakers, I record almost nothing or only weird noises. I can't attach sound waves of recordings as I am a new user here, but the first one is a normal recording, which has normal sound waves. Second recording is the recording while playing the first one, which contains almost nothing, no sound waves. Android seems to turn mic off if there is any activity in speaker. I tried different possibilities for MediaRecorder and MediaPlayer, but no use. What is the correct way of implementing Accoustic Echo Cancellation in Android? I tried on Sony Tablet S and developed using Android 3.0 SDK.
Thanks in advance.

package com.kadir.sample;
import android.app.Activity;
import android.widget.LinearLayout;
import android.widget.Toast;
import android.os.Bundle;
import android.os.Environment;
import android.view.ViewGroup;
import android.widget.Button;
import android.view.View;
import android.content.Context;
import android.util.Log;
import android.media.AudioManager;
import android.media.MediaRecorder;
import android.media.MediaPlayer;

import java.io.IOException;


public class AudioRecordTest extends Activity
{
    private static final String LOG_TAG = "AudioRecordTest";
    private static String mFileName = null;
    private static String mFileNameConst = null;

    private RecordButton mRecordButton = null;
    private MediaRecorder mRecorder = null;

    private PlayButton   mPlayButton = null;
    private MediaPlayer   mPlayer = null;
    private PlayConstButton mPlayConstButton = null;

    private void onRecord(boolean start) {
        if (start) {
            startRecording();
        } else {
            stopRecording();
        }
    }

    private void onPlay(boolean start) {
        if (start) {
            startPlaying();
        } else {
            stopPlaying();
        }
    }

    private void onPlayConst(boolean start) {
        if (start) {
            startConstPlaying();
        } else {
            stopConstPlaying();
        }
    }

    private void startPlaying() {
        mPlayer = new MediaPlayer();
        try {
            mPlayer.setDataSource(mFileName);
            mPlayer.prepare();
            mPlayer.start();
        } catch (IOException e) {
            Toast.makeText(this, "prepare() failed", Toast.LENGTH_LONG).show();
            Log.e(LOG_TAG, "prepare() failed");
        }
    }

    private void stopPlaying() {
        mPlayer.release();
        mPlayer = null;
    }

    private void startConstPlaying() {
        mPlayer = new MediaPlayer();
        try {
            mPlayer.setDataSource(mFileNameConst);
            mPlayer.setAudioStreamType(AudioManager.MODE_IN_COMMUNICATION);
            mPlayer.prepare();
            mPlayer.start();
        } catch (IOException e) {
            Toast.makeText(this, "prepare() failed", Toast.LENGTH_LONG).show();
            Log.e(LOG_TAG, "prepare() failed");
    }
}

private void stopConstPlaying() {
        mPlayer.release();
        mPlayer = null;
    }

    private void startRecording() {
        mRecorder = new MediaRecorder();
        mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION);
        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
        mRecorder.setOutputFile(mFileName);
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

        try {
            mRecorder.prepare();
        } catch (IOException e) {
            Toast.makeText(this, "startRecording() failed", Toast.LENGTH_LONG).show();
            Log.e(LOG_TAG, "prepare() failed");
        }

        mRecorder.start();
    }

    private void stopRecording() {
        mRecorder.stop();
        mRecorder.release();
        mRecorder = null;
    }

    class RecordButton extends Button {
        boolean mStartRecording = true;

        OnClickListener clicker = new OnClickListener() {
            public void onClick(View v) {
                onRecord(mStartRecording);
                if (mStartRecording) {
                    setText("Stop recording");
                } else {
                    setText("Start recording");
                }
                mStartRecording = !mStartRecording;
            }
        };

        public RecordButton(Context ctx) {
            super(ctx);
            setText("Start recording");
            setOnClickListener(clicker);
        }
    }

    class PlayButton extends Button {
        boolean mStartPlaying = true;

        OnClickListener clicker = new OnClickListener() {
            public void onClick(View v) {
                onPlay(mStartPlaying);
                if (mStartPlaying) {
                    setText("Stop playing");
                } else {
                    setText("Start playing");
                }
                mStartPlaying = !mStartPlaying;
            }
        };

        public PlayButton(Context ctx) {
            super(ctx);
            setText("Start playing");
            setOnClickListener(clicker);
        }
    }

    class PlayConstButton extends Button {
        boolean mStartPlaying = true;

        OnClickListener clicker = new OnClickListener() {
            public void onClick(View v) {
                onPlayConst(mStartPlaying);
                if (mStartPlaying) {
                    setText("Stop Constant playing");
                } else {
                    setText("Start Constant playing");
                }
                mStartPlaying = !mStartPlaying;
            }
        };

        public PlayConstButton(Context ctx) {
            super(ctx);
            setText("Start Constant playing");
            setOnClickListener(clicker);
        }
    }

    public AudioRecordTest() {
        mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();
        mFileName += "/audiorecordtest.3gp";
        mFileNameConst = Environment.getExternalStorageDirectory().getAbsolutePath();
        mFileNameConst += "/constant.3gp";
    }

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        LinearLayout ll = new LinearLayout(this);
        mRecordButton = new RecordButton(this);
        ll.addView(mRecordButton,
            new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT,
                0));
        mPlayButton = new PlayButton(this);
        ll.addView(mPlayButton,
            new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT,
                0));
        mPlayConstButton = new PlayConstButton(this);
        ll.addView(mPlayConstButton,
            new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT,
                0));
        setContentView(ll);
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mRecorder != null) {
            mRecorder.release();
            mRecorder = null;
        }

        if (mPlayer != null) {
            mPlayer.release();
            mPlayer = null;
        }
    }
}

UPDATE:
I had a little R&D as follows:
I change MediaRecorder and MediaPlayer parameters. For each value, I recorded myself, and during recording, I started another play. Then finished recording and listened what I've just recorded. For MediaRecorder, I tried these values:
MediaRecorder.AudioSource.DEFAULT,MediaRecorder.AudioSource.MIC, MediaRecorder.AudioSource.VOICE_CALL,MediaRecorder.AudioSource.VOICE_COMMUNICATION,MediaRecorder.AudioSource.VOICE_DOWNLINK,MediaRecorder.AudioSource.VOICE_RECOGNITION,MediaRecorder.AudioSource.VOICE_UPLINK
For MediaPlayer, I tried these values:
AudioManager.MODE_NORMAL, AudioManager.MODE_CURRENT,AudioManager.MODE_IN_CALL,AudioManager.MODE_IN_COMMUNICATION,
AudioManager.STREAM_VOICE_CALL,AudioManager.STREAM_MUSIC.
But whatever I tried, I always had either silence or pure noise. I think MediaRecorder and MediaPlayer classes are not sufficient for VoIP. And Android's sound system is a little bit weird for a beginner like me.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

等数载,海棠开 2024-12-22 16:32:28

我部分明白了。我使用的是搭载 Android 3.2 的索尼 Tablet S。我在装有 Android 2.3 的 Archos 70 上尝试了相同的程序。当我在演奏另一个东西的同时录制一个东西时,两种声音都会被记录下来。这意味着我有一个可以应用 AEC 的声音。在索尼,录音只不过是噪音。现在有两种可能性:要么是 Sony Tablet S 的麦克风实现有问题(顺便说一句,GTalk 在 Sony 上运行得很好,但在 Android 中实现可能有些特殊),要么是 Android 3.2 的麦克风实现有问题。

I partially figured out. I was using Sony Tablet S with Android 3.2. I tried same program on Archos 70 with Android 2.3. When I record something while playing another thing, both sounds are recorded. That means I have a sound to apply AEC. On Sony, recording was nothing but noises. Now there are two possibilities: Either Sony Tablet S has a problem with microphone implementation (by the way, GTalk works perfect on Sony, but it can be something special as implemented in Android) or Android 3.2 has a problem with microphone implementation.

辞慾 2024-12-22 16:32:28

Android 中的回声消除在很多情况下效果不佳。我认为这与回声尾较长有关。我知道有第三方算法可以解决这个问题。谷歌搜索回声消除软件并验证该软件是否支持长回声尾。

The Echo cancellation in Android does not work very well in many cases. I think it is related to long echo tail. I know there are third party algorithms for this problem. Google for echo cancellation software and verify that the software supports long echo tail.

青衫负雪 2024-12-22 16:32:28

为什么将 MediaRecorder 用于流应用程序?请改用 AudioRecorder。

我目前正在使用类似的用例,并且 AudioRecorder 工作完美。

Why use a MediaRecorder for a streaming application? Use AudioRecorder instead.

I am currently playing with a similar use case and the AudioRecorder works flawlessly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文