2

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.

skaffman
  • 398,947
  • 96
  • 818
  • 769
Kadir San
  • 78
  • 1
  • 7
  • You're setting mPlayer.setAudioStreamType(AudioManager.MODE_IN_COMMUNICATION);. That's not a correct parameter value for that call. You should be using a STREAM_ constant – Philip Pearl Feb 26 '13 at 11:06

3 Answers3

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.

Kadir San
  • 78
  • 1
  • 7
  • 2
    I tried contacting Sony Support. And they only care about closing my case. They replied: "Since GTalk is working fine, contact with developer of application that malfunctions". I am the developer of the application that malfunctions! Enough of dealing with Sony, I guess. – Kadir San Nov 21 '11 at 06:56
0

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.

Jim
  • 326
  • 1
  • 4
  • Thank you for your response. But I am afraid your explanations are not relevant to my question. My real problem is, as stated in original post, if something goes out of speaker, Android turns microphone gain down. Nothing comes from microphone. I am asking how to disable this feature, if it is a feature. There may be some other classess than MediaRecorder whichkeeps microphone on. My code contains nothing about AEC, because I don't get any sound to apply AEC. – Kadir San Nov 18 '11 at 10:56
  • Thank you for the clarifications. I suggest to open the recording with a different constant and NOT VOICE_COMMUNICATION. The VOICE_COMMUNICATION activates all the VoIP features that might be disturbing you. – Jim Nov 18 '11 at 15:01
  • Thanks. I'll try other parameters and report the results. – Kadir San Nov 18 '11 at 17:44
  • I tried other parameters and explain results in message's UPDATE. – Kadir San Nov 18 '11 at 19:54
  • Thank you for the update. I suggest you try using the class "AudioRecord" – Jim Nov 19 '11 at 17:38
0

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.

user
  • 86,916
  • 18
  • 197
  • 190
Neeraj
  • 63
  • 1
  • 5
  • Neeraj> Any idea about this related issue? http://stackoverflow.com/questions/33062660/android-what-audio-mode-should-be-set-to-send-receive-voice-between-devices – Jasper Oct 18 '15 at 23:20