Android AcousticEchoCanceler does not cancel out echo - android

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?

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.

You need to leverage the build in echo cancellation at the hardware level, check if AcousticEchoCanceler.isAvailable() check and return true
Then you can try the following combinations from here on SO

Related

How to run a service in background forever irrespective of phone in sleep mode or running any other app?

I want to run my service forever in the background but it stops after sometime I checked every solution on Youtube and Internet but I didn't get the answer,I tried every solution like using START_STICKY in onStartCommand() or using onTaskRemoved() method but it did not work.Any help would be appreciated.
This is my TheService class code. `
`
package apphub.secretapp;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.os.SystemClock;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Random;
/**
* Created by as on 12/24/2017.
*/
public class TheService extends Service implements
MediaRecorder.OnInfoListener {
String AudioSavePathInDevice = null;
MediaRecorder mediaRecorder ;
Random random ;
String RandomAudioFileName = "ABCDEFGHIJKLMNOP";
public static final int RequestPermissionCode = 1;
MediaPlayer mediaPlayer ;
private MediaRecorder mRecorder;
private long mStartTime;
//setting maximum file size to be recorded
private long Audio_MAX_FILE_SIZE = 1000000;//1Mb
private int[] amplitudes = new int[100];
private int i = 0;
private File mOutputFile;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent,flags,startId);
Toast.makeText(this, "Service Started", Toast.LENGTH_SHORT).show();
return START_STICKY;
}
private void startRecording() {
mRecorder = new MediaRecorder();
mRecorder.setOnInfoListener(this);
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setMaxFileSize(Audio_MAX_FILE_SIZE);
mRecorder.setOutputFormat
(MediaRecorder.OutputFormat.MPEG_4);
Toast.makeText(this, "Recording started", Toast.LENGTH_SHORT).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
{
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC);
mRecorder.setAudioEncodingBitRate(48000);
} else {
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setAudioEncodingBitRate(64000);
}
mRecorder.setAudioSamplingRate(16000);
mOutputFile = getOutputFile();
mOutputFile.getParentFile().mkdirs();
mRecorder.setOutputFile(mOutputFile.getAbsolutePath());
try {
mRecorder.prepare();
mRecorder.start();
mStartTime = SystemClock.elapsedRealtime();
} catch (IOException e) {
}
}
protected void stopRecording(boolean saveFile) {
mRecorder.stop();
mRecorder.release();
mRecorder = null;
mStartTime = 0;
if (!saveFile && mOutputFile != null) {
mOutputFile.delete();
}
// to stop the service by itself
}
private File getOutputFile() {
SimpleDateFormat dateFormat = new SimpleDateFormat
("yyyyMMdd_HHmmssSSS", Locale.US);
return new File(Environment.getExternalStorageDirectory().getAbsolutePath().toString()
+ "/Voice Recorder/RECORDING_"
+ dateFormat.format(new Date())
+ ".m4a");
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
Intent i =new Intent(getApplicationContext(),this.getClass());
i.setPackage(getPackageName());
startService(i);
super.onTaskRemoved(rootIntent);
}
#Override
public void onInfo(MediaRecorder mr, int what, int extra) {
if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
getOutputFile();
startRecording();
}
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
startRecording();
}
}
`
The simple answer is: You can't! Android is an OS created for mobile devices. Mobile devices are small battery operated computers with constrained memory. With that in mind the OS will kill your service whenever it needs memory.
In further on latest versions of the OS (specially Nougat and Oreo), those limitations are being imposed more heavily to give extra battery to users.
Any tricks, hacks and work-around you find online are just that, tricks and hacks. They might work in certain conditions or certain devices for a little bit, but you still won't have your service running forever, specially not on latest Androids.
The best scenario to try to have your Service run for as much as possible is to do two things:
return START_STICKY (like you're already doing). This indicates to the OS that you would like your Service to run for as long as possible, but there are zero guarantees that it will.
Use a foreground service. Call the methods startForeground(int, Notification) with a notification to show on the device notification panel. This will bring your process to a foreground state and allow it to stay for a bit longer, but again, no guarantees. PS.: Remember to remove the notification on your service onDestroy.

Receiving data via Bluetooth and viewing it on app screen

I am currently building an application that will connect to a device via bluetooth and receive data from it. Currently i am able to connect with the paired devices, but can't seem to find any good resources on how to receive data from that device. I have attached 2 images of the application, the first one; is the main menu. In there you'll see a button that says "connect to TANGO", in which i have put in all the code below. That allows me to connect to any paired devices. Also in the main menu, there's a "Translate" button, and that's were i am trying to receive data in, and view them, but so far i haven't been able to do so.
Here's the code for the "connect to TANGO" connection.java
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Set;
public class connection extends AppCompatActivity {
Button b1,b2,b3,b4;
private BluetoothAdapter BA;
private Set<BluetoothDevice>pairedDevices;
ListView lv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_connection);
b1 = (Button)findViewById(R.id.button);
b2=(Button)findViewById(R.id.button2);
b3=(Button)findViewById(R.id.button3);
b4=(Button)findViewById(R.id.button4);
BA = BluetoothAdapter.getDefaultAdapter();
lv = (ListView)findViewById(R.id.listView);
}
public void on(View v){
if (!BA.isEnabled()) {
Intent turnOn = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(turnOn, 0);
Toast.makeText(getApplicationContext(), "Turned on",Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getApplicationContext(), "Already on", Toast.LENGTH_LONG).show();
}
}
public void off(View v){
if (BA.disable());
Toast.makeText(getApplicationContext(), "Turned off" ,Toast.LENGTH_LONG).show();
}
public void visible(View v){
Intent getVisible = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
startActivityForResult(getVisible, 0);
}
public void list(View v){
pairedDevices = BA.getBondedDevices();
ArrayList list = new ArrayList();
for(BluetoothDevice bt : pairedDevices) list.add(bt.getName());
Toast.makeText(getApplicationContext(), "Showing Paired Devices",Toast.LENGTH_SHORT).show();
final ArrayAdapter adapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1, list);
lv.setAdapter(adapter);
}
}
When you have a reference to a paired BluetoothDevice you need to create a socket and listen to it. Simplified example follows:
Create a BluetoothSocket and connect to it:
BluetoothSocket socket = device.createRfcommSocketToServiceRecord(<your-device>.getUuids()[0].getUuid());
socket.connect();
Listen to the socket (Get data from the device)
InputStream inStream = socket.getInputStream();
while (inStream.available() > 0) {
inStream.read(); // <-- data from device
}
Write to the socket (Send data to the device)
OutputStream outStream = socket.getOutputStream();
byte[] bytes = <some-data>
outStream.write(bytes);
For more detailed information, with examples, see the docs
#krekle would this code work.
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class display extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display);
}
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private android.os.Handler mHandler;
public display (BluetoothSocket socket, Handler mHandler) {
mmSocket = socket;
this.mHandler = mHandler;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI activity
int MESSAGE_READ = 0;
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
break;
}
}
}
/* Call this from the main activity to send data to the remote device */
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
} catch (IOException e) { }
}
/* Call this from the main activity to shutdown the connection */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
i only had to change "ConnectThread" to "display" and "Thread" to "AppCompatActivity" just to match with the activities that i already made before.

Send Android TextToSpeech to just one stereo channel

On Android, I want to play TextToSpeech output through only one sound channel (think Shoulder Angel). To do this, I am currently using tts.synthesizeToFile(), and then playing back the dynamically-created file using the MediaPlayer. I use mediaPlayer.setVolume(0.0f, 1.0f) to play the audio through only one channel.
My working code is below.
My question is: is there a more direct way of playing TTS output through a single channel?
Using TextToSpeech to synthesize the file is time-consuming, and using MediaPlayer to play it back uses more resources than strictly necessary. I want this to be responsive and to work on low-end devices, so being kind to the CPU is important.
MainActivity.java
package com.example.pantts;
import android.app.Activity;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.speech.tts.TextToSpeech;
import android.os.Bundle;
import android.speech.tts.UtteranceProgressListener;
import android.util.Log;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Locale;
public class MainActivity extends Activity implements TextToSpeech.OnInitListener {
private TextToSpeech tts;
private String toSpeak = "Hello, right ear!";
private static final String FILE_ID = "file";
private HashMap<String, String> hashMap = new HashMap<String, String>();
private String filename;
private TextToSpeech tts;
private MediaPlayer mediaPlayer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
filename = getFilesDir() + "/" + "tts.wav";
Log.d("LOG", "file: " + filename);
// /data/data/com.example.pantts/files/tts.wav
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
tts = new TextToSpeech(this, this);
tts.setOnUtteranceProgressListener(mProgressListener);
}
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
tts.setLanguage(Locale.UK);
hashMap.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, FILE_ID);
// Using deprecated call for API 20 and earlier
tts.synthesizeToFile(toSpeak, hashMap, filename);
Log.d("LOG", "synthesizeToFile queued");
}
}
private UtteranceProgressListener mProgressListener =
new UtteranceProgressListener() {
#Override
public void onStart(String utteranceId) {
Log.d("LOG", "synthesizeToFile onStart " + utteranceId);
}
#Override
public void onError(String utteranceId) {
Log.d("LOG", "synthesizeToFile onError " + utteranceId);
}
#Override
public void onDone(String utteranceId) {
if (utteranceId.equals(FILE_ID)) { // Thanks to Hoan Nguyen for correcting this
Log.d("LOG", "synthesizeToFile onDone " + utteranceId);
try {
File ttsFile = new File(filename);
FileInputStream inputStream = new FileInputStream(ttsFile);
FileDescriptor fileDescriptor = inputStream.getFD();
mediaPlayer.reset();
mediaPlayer.setDataSource(fileDescriptor);
inputStream.close();
mediaPlayer.prepare();
mediaPlayer.setVolume(0.0f, 1.0f); // right channel only
mediaPlayer.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
}
There is nothing wrong with the synthesize, it is the comparison that is wrong. It should be
if (utteranceId.equals(FILE_ID))

Android Stream Audio to Java Server

I am developing an Android app, the user can speak and the app will send the voice to the computer java server. I have used the following codes but it seems that it does not work. I use my IPV4 address and codes from here
Stream Live Android Audio to Server
The errors are when I run this app, it tells me it has stopped working, however there are other errors, could anyone help me to get it fixed? Thank you.
1) I create a android project and put the codes in the MainActivity.
2) I create a java project and place it in a class.
3) I run the server.
4) I plug in my android device and run it on the device.
5) When I press start (to start recording), it doesn't work.
My Android App:
package com.example.mictest;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
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 = 44100;
private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
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);
minBufSize += 2048;
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("VS", "Socket Created");
byte[] buffer = new byte[minBufSize];
Log.d("VS","Buffer created of size " + minBufSize);
DatagramPacket packet;
final InetAddress destination = InetAddress.getByName("172.20.129.255");
Log.d("VS", "Address retrieved");
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,sampleRate,channelConfig,audioFormat,minBufSize*10);
Log.d("VS", "Recorder initialized");
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);
Log.d("VS", "MinBufferSize: " +minBufSize);
}
} catch(UnknownHostException e) {
Log.e("VS", "UnknownHostException");
} catch (IOException e) {
e.printStackTrace();
Log.e("VS", "IOException");
}
}
});
streamThread.start();
}
}
My Java Server:
package com.datagram;
import java.io.ByteArrayInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.SourceDataLine;
class Server {
AudioInputStream audioInputStream;
static AudioInputStream ais;
static AudioFormat format;
static boolean status = true;
static int port = 50005;
static int sampleRate = 44100;
public static void main(String args[]) throws Exception {
DatagramSocket serverSocket = new DatagramSocket(50005);
/**
* Formula for lag = (byte_size/sample_rate)*2
* Byte size 9728 will produce ~ 0.45 seconds of lag. Voice slightly broken.
* Byte size 1400 will produce ~ 0.06 seconds of lag. Voice extremely broken.
* Byte size 4000 will produce ~ 0.18 seconds of lag. Voice slightly more broken then 9728.
*/
byte[] receiveData = new byte[4000];
format = new AudioFormat(sampleRate, 16, 1, true, false);
while (status == true) {
DatagramPacket receivePacket = new DatagramPacket(receiveData,
receiveData.length);
serverSocket.receive(receivePacket);
ByteArrayInputStream baiss = new ByteArrayInputStream(
receivePacket.getData());
ais = new AudioInputStream(baiss, format, receivePacket.getLength());
toSpeaker(receivePacket.getData());
}
}
public static void toSpeaker(byte soundbytes[]) {
try {
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, format);
SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
sourceDataLine.open(format);
FloatControl volumeControl = (FloatControl) sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
volumeControl.setValue(100.0f);
sourceDataLine.start();
sourceDataLine.open(format);
sourceDataLine.start();
System.out.println("format? :" + sourceDataLine.getFormat());
sourceDataLine.write(soundbytes, 0, soundbytes.length);
System.out.println(soundbytes.toString());
sourceDataLine.drain();
sourceDataLine.close();
} catch (Exception e) {
System.out.println("Not working in speakers...");
e.printStackTrace();
}
}
}
I think it has something to do with your code in the while-loop
while(status == true) {
//YOU WANT TO RECORD IN YOUR BUFFERSIZE?
//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);
Log.d("VS", "MinBufferSize: " +minBufSize);
}

Android AudioRecord - Won't Initialize 2nd time

Hej, im currently trying to get AudioRecord to work. Because I need it in a bigger project. But it seems to mess up a lot.
I have been trying alot of things, so I went back to basic when I traced this bug.
I am using my Samsung Galaxy S as my debugdevice.
My problem is, first time after a reboot of my device I can initialize the AudioRecord object I create without problems.
But the second time I run it, it won't initialize the AudioRecord object.
I have tried several frequencies, fyi.
Here is my code:
package android.audiorecordtest;
import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class AudioRecordTest extends Activity {
int frequency;
AudioRecord audRec;
TextView txtVw;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
txtVw = (TextView) findViewById(R.id.txtVw);
frequency=8000;
int bufferSize=(AudioRecord.getMinBufferSize(frequency, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT))*2;
if (bufferSize>0) {
audRec = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
int status = audRec.getState();
if (status == AudioRecord.STATE_INITIALIZED) {
txtVw.setText("Initialized" + frequency);
} else {
txtVw.setText("Not Initialized i=" + frequency);
}
}
After a few hours of looking through logcat information i found this event
02-28 10:46:37.048: DEBUG/dalvikvm(4477): GC_EXPLICIT freed 1801 objects / 98944 bytes in 97ms
02-28 10:46:37.048: VERBOSE/AudioRecord(4477): stop
Which seems to "release the native hold on the AudioRecord.
So i tried doing an override of finalize with my Audiorecord object.release(). This didnt work though.. Anyone have any idea?
I was able to reproduce your problem (on a Samsung phone). I added an onDestroy() method releasing the record:
#Override
public void onDestroy() {
super.onDestroy();
System.out.println("OnDestroy");
audRec.release();
}
After adding this, the audioRecord seems to initialize correctly every time the activity is started.
I had the same problem, usually the audRec.release() helps indeed, but if you need to stop and start several times the following code is more robust. Plus, I had an issue that the recording took place in a separate thread and Android sometimes kills threads when running for a long time. So take a look at this code, it makes sure the recording is held even when the other thread is dead and upon the following audRec.start() it stops and releases:
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
public class RecorderSingleton {
private static final int FREQUENCY = 16000;
public static RecorderSingleton instance = new RecorderSingleton();
private AudioRecord recordInstance = null;
private int bufferSize;
private RecorderSingleton() {
bufferSize = AudioRecord.getMinBufferSize(FREQUENCY, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
}
public boolean init() {
recordInstance = new AudioRecord(MediaRecorder.AudioSource.MIC, FREQUENCY, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
if (recordInstance.getState() == AudioRecord.STATE_UNINITIALIZED) {
return false;
}
return true;
}
public int getBufferSize() {
return bufferSize;
}
public boolean start() {
if (recordInstance != null && recordInstance.getState() != AudioRecord.STATE_UNINITIALIZED) {
if (recordInstance.getRecordingState() != AudioRecord.RECORDSTATE_STOPPED) {
recordInstance.stop();
}
recordInstance.release();
}
if (!init()) {
return false;
}
recordInstance.startRecording();
return true;
}
public int read(short[] tempBuffer) {
if (recordInstance == null) {
return AudioRecord.ERROR_INVALID_OPERATION;
}
int ret = recordInstance.read(tempBuffer, 0, bufferSize);
return ret;
}
public void stop() {
if (recordInstance == null) {
return;
}
recordInstance.stop();
recordInstance.release();
}
}
Then if you have a recorder thread you can use it as follows:
import android.media.AudioRecord;
public class Recorder implements Runnable {
private int requiredSamples;
private int takenSamples = 0;
private boolean cancelled = false;
public void run() {
// We're important...
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
int bufferRead = 0;
int bufferSize = RecorderSingleton.instance.getBufferSize();
short[] tempBuffer = new short[bufferSize];
if (!RecorderSingleton.instance.start()) {
return;
}
try {
Log.d(RoomieConstants.LOG_TAG, "Recorder Started");
while (takenSamples < requiredSamples && !cancelled) {
bufferRead = RecorderSingleton.instance.read(tempBuffer);
if (bufferRead == AudioRecord.ERROR_INVALID_OPERATION) {
throw new IllegalStateException("read() returned AudioRecord.ERROR_INVALID_OPERATION");
} else if (bufferRead == AudioRecord.ERROR_BAD_VALUE) {
throw new IllegalStateException("read() returned AudioRecord.ERROR_BAD_VALUE");
}
takenSamples += bufferRead;
// do something with the samples ...
// ...
// ...
}
} finally {
// Close resources...
stop();
}
}
public void stop() {
RecorderSingleton.instance.stop();
}
public void cancel() {
cancelled = true;
}
}
To Answer my own question, the only way i found it doable to use AudioRecord, is to never have it as an global variable, dont know why, but it seems it won't let you release the resources of the instance correctly if you do so.
You should try to call audRec.stop() to release the resource.
My AudioRecord didn't initialize because it was static

Categories

Resources