Reputation: 113
I've been having a fairly annoying problem with a video chat app I'm developing, and that's the issue of audio echoing. I am at best a rank amateur at this, but the project I'm working on requires at least fully functional audio communication. Video turned out to be a lot easier than I originally anticipated. The intended structure is eventually a thread taking input and another playing output on the same phone, for developing this, I've made two small apps that take in mic input on one phone, and send it via Datagram socket to the other. The phones in question are LG Optimus L7-2 running Android 4.1.2 and Alcatel Idol Mini (I think it's also advertized as Onetouch or some such.) running Android 4.2.2. The code that transfers audio works perfectly, with minimal background noise (I'm guessing thanks to my choice of input as well as the post processing), however, as long as the two phones are close enough, I get a rather alarming echo, which is only made worse if I dare attempt to put input/output in the same app at the same time.
After my initial attempts at somehow filtering it out failed (AcousticEchoCanceler seems to help less than NoiseSupressor, and AutomaticGainControl seems to do more damage than good), I've done a bit of reading but found nothing that could help. I am at this point rather confused as I can't seem to shake the feeling that I'm missing something obvious, and that it shouldn't be THAT complicated to set up.
I'm in addition putting up the base code I'm using for the audio recording/playing.
The recorder segment
package com.example.audiotest;
import java.io.IOException;
import java.io.InputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.media.audiofx.AcousticEchoCanceler;
import android.media.audiofx.AutomaticGainControl;
import android.media.audiofx.NoiseSuppressor;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private Button startButton,stopButton;
public byte[] buffer;
public static DatagramSocket socket;
private int port=50005;
AudioRecord recorder;
private int sampleRate = 22050;
private int channelConfig = AudioFormat.CHANNEL_IN_MONO;
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private int minBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
private boolean status = true;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startButton = (Button) findViewById (R.id.start_button);
stopButton = (Button) findViewById (R.id.stop_button);
startButton.setOnClickListener (startListener);
stopButton.setOnClickListener (stopListener);
Log.v("AudioPlayerApp","minBufSize: " + minBufSize);
//minBufSize += 2048;
minBufSize = 4096;
System.out.println("minBufSize: " + minBufSize);
}
private final OnClickListener stopListener = new OnClickListener() {
@Override
public void onClick(View arg0) {
status = false;
recorder.release();
Log.d("VS","Recorder released");
}
};
private final OnClickListener startListener = new OnClickListener() {
@Override
public void onClick(View arg0) {
status = true;
startStreaming();
}
};
public void startStreaming() {
Thread streamThread = new Thread(new Runnable() {
@Override
public void run() {
try {
DatagramSocket socket = new DatagramSocket();
Log.d("AudioPlayerApp"", "Socket Created");
minBufSize = 4096;
byte[] buffer = new byte[minBufSize];
Log.d("AudioPlayerApp","Buffer created of size " + minBufSize);
DatagramPacket packet;
final InetAddress destination = InetAddress.getByName("192.168.0.13");
recorder = new AudioRecord(MediaRecorder.AudioSource.VOICE_RECOGNITION,sampleRate,channelConfig,audioFormat,minBufSize);
AcousticEchoCanceler canceler = AcousticEchoCanceler.create(recorder.getAudioSessionId());
NoiseSuppressor ns = NoiseSuppressor.create(recorder.getAudioSessionId());
AutomaticGainControl agc = AutomaticGainControl.create(recorder.getAudioSessionId());
canceler.setEnabled(true);
ns.setEnabled(true);
//agc.setEnabled(true);
recorder.startRecording();
while(status == true) {
//reading data from MIC into buffer
minBufSize = recorder.read(buffer, 0, buffer.length);
//putting buffer in the packet
packet = new DatagramPacket (buffer,buffer.length,destination,port);
socket.send(packet);
}
} catch(UnknownHostException e) {
Log.e("AudioPlayerApp", "UnknownHostException");
} catch (IOException e) {
e.printStackTrace();
Log.e("AudioPlayerApp", "IOException");
}
}
});
streamThread.start();
}
}
And the player segment.
package com.test.playsound;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
public class MainActivity extends Activity {
static int port = 50005;
static String address = "";
static int sampleRate = 22050;
private boolean running = true;
private AudioTrack audioTrack;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.v("Player", "Init complete");
openPlaySocket();
}
private void openPlaySocket() {
// TODO Auto-generated method stub
Thread t = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
Log.v("AudioPlayerApp", "Opening socket");
DatagramSocket sSock = new DatagramSocket(port);
byte[] output = new byte[4096];
Log.v("AudioPlayerApp", "Generating AudioTrack");
int minBufferSize = AudioTrack.getMinBufferSize(sampleRate,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRate, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize,
AudioTrack.MODE_STREAM);
DatagramPacket receivePacket = new DatagramPacket(output,
output.length);
//Log.v("AudioPlayerApp", "Playing AudioTrack");
audioTrack.play();
while (running) {
//Log.v("AudioPlayerApp", "Waiting Packet");
sSock.receive(receivePacket);
Log.v("AudioPlayerApp","REcieved packet");
//Log.v("AudioPlayerApp", "Packet recieved");
try {
//Log.v("AudioPlayerApp", "writing data to audioTrack");
audioTrack.write(receivePacket.getData(), 0,
receivePacket.getData().length);
} catch (Exception e) {
Log.v("AudioPlayerApp",
"Failed to write audio: " + e.getMessage());
}
}
/*Log.v("AudioPlayerApp","Opening socket");
ServerSocket sSock = new ServerSocket(port);
Socket sock = sSock.accept();
Log.v("AudioPlayerApp","Socket opened "+port);
*/
} catch (Exception e) {
// TODO: handle exception
Log.v("AudioPlayerApp", "Error: " + e.getMessage());
}
}
});
Log.v("Player", "Starting thread");
t.start();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
I'm aware it contains bad practices (such as not checking whether the device in question has support for certain things, or releasing resources and such) however, this was in an effort to start testing out and fixing the echo as fast as possible. I've confirmed that both phones have access to AcousticEchoCanceller, NoiseSupression, recording rights, internet rights, and aside for the fact that AudioFormat.VOICECOMMUNICATION causes my AudioRecord to crash, I've had no other problems.
I'm looking for any ideas or advice on the subject, as I'm quite frankly stumped. What can be done to fix the issue of echoing while recording and playing voice?
Upvotes: 5
Views: 6483
Reputation: 7439
AcousticEchoCanceler.isAvailable()
check and return trueUpvotes: 1
Reputation: 31
the AcousticEchoCanceler class is for canceling or removing the audio played by speaker and captured by microphone of the same device and there is small delay between play and capture.
the AcousticEchoCanceler class can not remove the echo caused by placing two phone near each other because of long and variable nature of echo delay of echo path.
Upvotes: 1