android AcousticEchoCanceler doesn't seem to work on most devices - java

I am building an application which requires the mic to cancel any sound coming from the speaker. It seems that this issue is almost a conspiracy on-line as others with the exact same problem were never responded to for an extended duration.
Android's native hardware accelerated AcousticEchoCanceler does not seem to work on most devices. Tests where made on many devices and the ones that seemed to work Include Nexus 5, and Moto X while almost all Samsung devices tested could not remove background sound.Note: All phones tested return true for AcousticEchoCanceler.isAvailable()
However, there must be a solution since applications such as Skype or WhatsApp seem to cancel sounds outside their app context, i.e. a call is on speaker and the Microphone cancels any feedback received.
This simplified recording app records sound to a file and plays it later when play is clicked.
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>
AndroidManfist.xml permissions
<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"/>
To Verify if the device works simply play something in the background and then click Start Recording record a small sector and then click Stop Recording at this point click Play Back and check if you hear the background sound. If you can hear the background sound then AEC is not working.
But why is this inconsistency occurring, or how do I achieve echo cancellation (I am already using WebRTC within my app for noise cancellation within my apps context)
Any help would be appreciated !

I was having the same problem on my S6 device. I played with a variety of settings and found a set that seem to enable AEC. The differences between my and your setup seem to be:
16k sample rate
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
For others, I'm not sure precisely what settings are needed to get AEC working. I do know that my same app with
48k sample rate
NO audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
NO android.permission.MODIFY_AUDIO_SETTINGS
does not successfully AEC.

Problem:
AudioTrack audioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC, sampleRateInHz,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSizeInBytes,
AudioTrack.MODE_STREAM);
should be:
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()

Related

AsyncTask kills istelf without any error message while debugging

I'm having a very weird problem with AsyncTask which I use to download a zip file in my android application. It was working flawlessly until I decided to use strings.xml resource for every string linked to this task.
when I click on the download button inside my app, the progressbar of the AsyncTask shows for a second or less then dismisses itself and the task goes to the onPostExecute() state.
I tried debuging the app on my test device and there is no error about the task. I even added some stubs with Log.d tag, I've included the logcat results:
275-15524/xmc.androidexpert35.com.xtrememusicchecker D/ANDRO_ASYNC: path set
2019-04-04 20:19:22.484 15275-15524/xmc.androidexpert35.com.xtrememusicchecker D/ANDRO_ASYNC: Try block
2019-04-04 20:19:22.487 15275-15524/xmc.androidexpert35.com.xtrememusicchecker D/ANDRO_ASYNC: file url got
2019-04-04 20:19:22.490 15275-15524/xmc.androidexpert35.com.xtrememusicchecker D/ANDRO_ASYNC: opening connection
2019-04-04 20:19:22.515 495-528/? D/SurfaceFlinger: duplicate layer name: changing xmc.androidexpert35.com.xtrememusicchecker/xmc.androidexpert35.com.xtrememusicchecker.SettingsActivity to xmc.androidexpert35.com.xtrememusicchecker/xmc.androidexpert35.com.xtrememusicchecker.SettingsActivity#1
2019-04-04 20:19:22.585 495-794/? D/SurfaceFlinger: duplicate layer name: changing Surface(name=cafcbf6 xmc.androidexpert35.com.xtrememusicchecker/xmc.androidexpert35.com.xtrememusicchecker.SettingsActivity)/#0x3604cd - animation-leash to Surface(name=cafcbf6 xmc.androidexpert35.com.xtrememusicchecker/xmc.androidexpert35.com.xtrememusicchecker.SettingsActivity)/#0x3604cd - animation-leash#1
2019-04-04 20:19:22.614 15275-15275/xmc.androidexpert35.com.xtrememusicchecker I/ViewRootImpl: CPU Rendering VSync enable = true
2019-04-04 20:19:22.672 495-528/? W/SurfaceFlinger: Attempting to set client state on removed layer: xmc.androidexpert35.com.xtrememusicchecker/xmc.androidexpert35.com.xtrememusicchecker.SettingsActivity#1
2019-04-04 20:19:22.672 495-528/? W/SurfaceFlinger: Attempting to destroy on removed layer: xmc.androidexpert35.com.xtrememusicchecker/xmc.androidexpert35.com.xtrememusicchecker.SettingsActivity#1
2019-04-04 20:19:24.758 15275-15314/xmc.androidexpert35.com.xtrememusicchecker D/FA: Logging event (FE): user_engagement(_e), Bundle[{firebase_event_origin(_o)=auto, engagement_time_msec(_et)=3635, firebase_screen_class(_sc)=SettingsActivity, firebase_screen_id(_si)=-6495914915605520780}]
2019-04-04 20:19:26.125 829-3715/? W/NotificationService: Toast already killed. pkg=xmc.androidexpert35.com.xtrememusicchecker callback=android.app.ITransientNotification$Stub$Proxy#3b51651
This is my AsyncTask code, if anyone can help me find the issue? or suggest a useful debug solution to discover it?
Thanks, any help is very appreciated!
public class DownloadFile extends AsyncTask<String, String, String> {
private static String file_url;
private Context context;
private ProgressDialog dialog;
private String dialogString;
private File path;
private String xmpath;
private boolean canceled = false;
public DownloadFile(Context cxt) {
context = cxt;
dialog = new ProgressDialog(context);
}
#Override
protected void onPreExecute() {
dialog.setMessage(context.getString(R.string.xm_downloading));
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setCancelable(false);
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.xm_cancel), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
path.delete();
canceled = true;
dialog.dismiss();
}
});
dialog.show();
super.onPreExecute();
}
#Override
protected String doInBackground(String... aurl) {
int count;
if (SettingsActivity.isMagisk){
file_url = "http://androidexpert35developer.altervista.org/Xtrememusic-versions/XTREMEMusic_MAGISK_OFICIAL_By_androidexpert35.zip";
path= new File(Environment.getExternalStorageDirectory() + "/XTREMEMusic_Download/XTREMEMusic_Magisk.zip");
}else{
file_url = "http://androidexpert35developer.altervista.org/Xtrememusic-versions/XTREMEMusic_OFFICIAL_By_androidexpert35.zip";
path = new File(Environment.getExternalStorageDirectory() + "/XTREMEMusic_Download/XTREMEMusic.zip");
Log.d("ANDRO_ASYNC","path set");
}
try {
Log.d("ANDRO_ASYNC","Try block");
URL url = new URL(file_url);
Log.d("ANDRO_ASYNC","file url got");
URLConnection conexion = url.openConnection();
Log.d("ANDRO_ASYNC","opening connection");
conexion.connect();
Log.d("ANDRO_ASYNC","Connected");
int lenghtOfFile = conexion.getContentLength();
InputStream is = url.openStream();
File testDirectory = new File(Environment.getExternalStorageDirectory() + "/XTREMEMusic_Download");
Log.d("ANDRO_ASYNC","making directory");
if (!testDirectory.exists()) {
testDirectory.mkdir();
}
FileOutputStream fos;
Log.d("ANDRO_ASYNC","Stream");
if(SettingsActivity.isMagisk) {
fos = new FileOutputStream(testDirectory + "/" + ("XTREMEMusic_Magisk") + ".zip");
Log.d("ANDRO_ASYNC","Downloading");
}else{
fos = new FileOutputStream(testDirectory + "/" + ("XTREMEMusic") + ".zip");
Log.d("ANDRO_ASYNC","Downloading");
}
byte data[] = new byte[1024];
long total = 0;
int progress = 0;
while ((count = is.read(data)) != -1) {
total += count;
int progress_temp = (int) total * 100 / lenghtOfFile;
publishProgress(""+ progress_temp);
if (progress_temp % 10 == 0 && progress != progress_temp) {
progress = progress_temp;
}
fos.write(data, 0, count);
}
is.close();
fos.close();
} catch (Exception e) {}
return null;
}
protected void onProgressUpdate(String... progress) {
Log.d("ANDRO_ASYNC",progress[0]);
dialog.setProgress(Integer.parseInt(progress[0]));
}
#Override
protected void onPostExecute(String unused) {
dialog.dismiss();
if(SettingsActivity.isInstall) {
installer();
}else if (canceled) {
Toast.makeText(context, R.string.xm_cancelled, Toast.LENGTH_LONG).show();
} else{
xmpath = path.toString();
Toast.makeText(context, context.getString(R.string.xm_downloaded, xmpath), Toast.LENGTH_LONG).show();
}
}

Android: Check if the mic is receiving any sound

I am new to Android, and i am developing an audiometer on Android Studio. One of the steps is to see if the mic is receiving a sound that i am sending from the earphones, to check if they are working properly.
I am doing both things in the same activity, sending sound, and checking if there is any sound coming in.
I was able to send a tone of 1kHz of frequency for 2 seconds, using AudioTrack class, and the next step is to check if the mic is receiving something near that frequency. Since i wasn't able to even make the mic work, i am lowering my goals to just check if the microphone is receiving anything.
I've checked several links and none helped me, or because i am not familiar with android or because it wasn't what i needed, among others:
Detect sound level, How to detect when a user stops talking into the microphone and Detect 'Whistle' sound in android
I've already put the permissions on the Manifest:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
And my CalibrationActivity.java is:
import android.content.Intent;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import java.io.IOException;
public class CalibrationActivity extends AppCompatActivity {
private MediaRecorder myRecorder;
private String outputFile = null;
private final int duration = 2; // seconds
private final int sampleRate = 4000;
private final int numSamples = duration * sampleRate;
private final double sample[] = new double[numSamples];
private final double freqOfTone = 1000; // hz
private final byte generatedSnd[] = new byte[2 * numSamples];
Handler handler = new Handler();
int getAmplitude = myRecorder.getMaxAmplitude();
public int result(){
if (getAmplitude != 0) {
return 1;
}else {
return 0;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_calibration);
outputFile = Environment.getExternalStorageDirectory().
getAbsolutePath() + "/teste.3gpp";
myRecorder = new MediaRecorder();
myRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
myRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
myRecorder.setAudioEncoder(MediaRecorder.OutputFormat.AMR_NB);
myRecorder.setOutputFile(outputFile);
Intent intent;
if(result()==1){
intent = new Intent(this, FirstTestActivity.class);
}else{
intent = new Intent(this, End1Activity.class);
}
}
void start_recording() {
try {
myRecorder.prepare();
myRecorder.start();
} catch (IllegalStateException e) {
// start:it is called before prepare()
// prepare: it is called after start() or before setOutputFormat()
e.printStackTrace();
} catch (IOException e) {
// prepare() fails
e.printStackTrace();
}
}
void stop_recording(){
try {
myRecorder.stop();
myRecorder.release();
myRecorder = null;
} catch (IllegalStateException e) {
// it is called before start()
e.printStackTrace();
} catch (RuntimeException e) {
// no valid audio/video data has been received
e.printStackTrace();
}
}
#Override
protected void onResume() {
super.onResume();
// Use a new tread as this can take a while
final Thread thread = new Thread(new Runnable() {
public void run() {
genTone();
handler.post(new Runnable() {
public void run() {
playSound();
}
});
}
});
thread.start();
start_recording();
SystemClock.sleep(3000);
stop_recording();
}
void genTone() {
// fill out the array
for (int i = 0; i < numSamples; ++i) {
sample[i] = Math.sin(2 * Math.PI * i / (sampleRate / freqOfTone));
}
// convert to 16 bit pcm sound array
// assumes the sample buffer is normalised.
int idx = 0;
for (final double dVal : sample) {
// scale to maximum amplitude
final short val = (short) ((dVal * 32767));
// in 16 bit wav PCM, first byte is the low order byte
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
}
void playSound() {
final AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRate, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, generatedSnd.length,
AudioTrack.MODE_STATIC);
audioTrack.write(generatedSnd, 0, generatedSnd.length);
audioTrack.play();
}
}
I wrote that based on examples that i found online, mostly from here, here and here, so there are a few parts of the code that i don't really understand.
The idea here is to send the sound from the earphones, and the user will be informed to put their earphones close to the mic. Then, the code should let the mic recording for 3 seconds and then check if the amplitude of the sound is different from 0, if that is the case, the application then goes to FirstTestActivity, else, to End1Activity. But once i try running the code, the aplication suddenly crashes, and i don't know why. I've been working on that for several weeks and i could not find a solution that probably is pretty simple. Thanks on advance.
Based on your lack on knowledge on the subject it may be useful for you to just use a library instead like the one here

Android mediaplayer play file wihle writing to file

I have some mp3 files on a cloud service.The links are like that https://dns/mp3filename.mp3?dl=1. I can play the files streaming with Vlc media player and I can write the bytes in files in Java. But when I try to play the links in Android media player some times it plays some times I get error(1,-1004) that is media_error_io.
Streaming code:
mp.setDataSource(link);
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.prepareAsync();
mp.setWakeMode(ctx, PowerManager.PARTIAL_WAKE_LOCK);
mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
I have been looking for a library but I could not found one. I just came with the idea about download the file and play it while downloading, but the media player only reads the first bytes I give it to play and called setOnCompletionListener even if the file have been completely downloaded.
That code is in a thread and it's for download the file
try {
File cacheDir = new File(ctx.getCacheDir().getPath()+"/"+"mp3s");
cacheDir.mkdir();
File tempFile = File.createTempFile("mp3" + num, ".mp3", cacheDir);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(tempFile));
HttpURLConnection httpURLConnection = (HttpURLConnection) new URL(lien).openConnection();
httpURLConnection.setDoInput(true);
BufferedInputStream bufferedInputStream = new BufferedInputStream(httpURLConnection.getInputStream());
byte bytes[] = new byte[1048576];
int len = 0;
int nbre = 0;
int current = 0;
while ((len = bufferedInputStream.read(bytes))!=-1){
bufferedOutputStream.write(bytes,0,len);
bufferedOutputStream.flush();
nbre+= len;
current += len;
onLoadingListener.onLoading(current, httpURLConnection.getContentLength());
if(nbre >= 524288){
nbre = 0;
onReadyListener.onReady(tempFile);
}
}
bufferedOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
onReadyListener.onReady(tempFile); is callback to start the media player like that.
playerMediaDownloader.setOnReadyListener(new PlayerMediaDownloader.OnReadyListener() {
#Override
public void onReady(final File file) {
path = file.getPath();
try {
if(!playing) {
Log.d(getClass().getSimpleName(), "li ready");
mp.setDataSource(file.getPath());
mp.prepare();
mp.start();
Log.d(Player.this.getClass().getSimpleName(), "pos:"+mp.getCurrentPosition());
playing = true;
for (OnTimeChanged l : onTimeChangeds) {
l.onTimeChanged(mp.getCurrentPosition(), mp.getDuration());
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
The media player finished to play the file when it reach the 524288 bytes even if the file has been completely downloaded.
I came with another solution that is set the file file again and play it and it worked but, the sound cut a bit and it is not pretty like that.
mp.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
int currentPosition = mp.getCurrentPosition();
mp.reset();
try {
Player.this.mp.setDataSource(path);
mp.prepare();
mp.seekTo(currentPosition);
mp.start();
} catch (Exception e) {
e.printStackTrace();
}
Log.d(getClass().getSimpleName(), "fini jwe:"+mp.getCurrentPosition());
}
});
Do you have a better solution to help me make it works fine please, like playing the file asynchronously?

Audio not clear while streaming between a java class and android activity

I have an android activity, which connects to a java class and sends data packets to it in form of sockets. The class receives the sound packets and throws them to PC speakers. The code is working excellently, but there is a constant jitter/ interruption while the sound is played in PC speakers.
The android activity:
public class SendActivity extends Activity {
private Button startButton, stopButton;
public byte[] buffer;
public static DatagramSocket socket;
private int port = 50005;
AudioRecord recorder;
private int sampleRate = 8000;
#SuppressWarnings("deprecation")
private int channelConfig = AudioFormat.CHANNEL_IN_MONO;
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
int minBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig,
audioFormat);
private boolean status = true;
int bufferSizeInBytes;
int bufferSizeInShorts;
int shortsRead;
short audioBuffer[];
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_send);
startButton = (Button) findViewById(R.id.start_button);
stopButton = (Button) findViewById(R.id.stop_button);
startButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
status = true;
startStreaming();
}
});
stopButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
status = false;
recorder.release();
Log.d("VS", "Recorder released");
}
});
minBufSize += 5120;
System.out.println("minBufSize: " + minBufSize);
}
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;
//machine's IP
final InetAddress destination = InetAddress
.getByName("192.168.1.20");
Log.d("VS", "Address retrieved");
recorder = new AudioRecord(MediaRecorder.AudioSource.VOICE_RECOGNITION,
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);
System.out.println("MinBufferSize: " + minBufSize);
}
} catch (UnknownHostException e) {
Log.e("VS", "UnknownHostException");
} catch (IOException e) {
e.printStackTrace();
Log.e("VS", "IOException");
}
}
});
streamThread.start();
}
}
The android layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".SendActivity" >
<Button
android:id="#+id/stop_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/start_button"
android:layout_alignBottom="#+id/start_button"
android:layout_toRightOf="#+id/start_button"
android:text="Stop" />
<Button
android:id="#+id/start_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="79dp"
android:layout_marginTop="163dp"
android:text="Start" />
</RelativeLayout>
Android Manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.audiostreamsample"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >
</uses-permission>
<uses-permission android:name="android.permission.INTERNET" >
</uses-permission>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" >
</uses-permission>
<uses-permission android:name="android.permission.READ_PHONE_STATE" >
</uses-permission>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.example.audiostreamsample.SendActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
The class to receive the data packets and throw them to the PC speakers:
class Server {
AudioInputStream audioInputStream;
static AudioInputStream ais;
static AudioFormat format;
static boolean status = true;
static int port = 50005;
static int sampleRate = 8000;
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[5000];
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(6.0206f);
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();
}
}
}
If you want to test the app in your IDE, then simply create two different projects, one for the android app and one for the server class.
In the android app just add the IP of your machine and run the app on a device, the mobile and the computer should belong to the same network. Please execute the server class as a java application.
The jitter will be prominent and irritating but the voices will be more or less clear. Please suggest me what to do to get a clearer output.
You need to have some coded support for actual streaming.
There's a little more to consider than just sending datagrams and hoping for the best.
Real networks are not perfect.
Delay: packets take time
Jitter : the time a packet takes in flight is not constant
Dropped packets: sometimes they don't make it.
Reordering : sometimes packets arrive in a different order to the sending.
You should read up on simple media streaming protocols like RTP and perhaps use a library that provides RTP to both ends. RTP commonly sits atop UDP.
TCP streaming for audio can be less helpful than UDP/RTP , as you'd have to turn off Nagling.
You will at a minimum need a small buffer at the receiver end to prevent buffer empty causing sound dropouts.

Download multiple file using AsyncTask?

I am trying to download multiple file using AsyncTask.
I start each download in one AsyncTask with progress bar in notification bar but i face many problems.
If i download 4 or 5 files at the same time the one or more files
interrupted without any reason.
If i download 4 or 5 images the one or more images corrupted.
this is the code i used.
private class DownloadFile extends AsyncTask<String, Integer, Long>
{
int nID = 0;
int nDownloadCounter = 0;
public DownloadFile(String sFileName, int nNotificationID) {
this.nID = nNotificationID;
this.NotificationName = sFileName;
this.nDownloadCounter = 0;
}
protected void onPreExecute(){
this.sDownloadPath = sFilePathWithSubDir;
notification = new Notification(R.drawable.app_icon, "Download File", System.currentTimeMillis());
RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.custom_notification);
Intent notificationIntent = new Intent();
File file = new File(this.sDownloadPath + this.NotificationName);
sMediaDataType = GSUtilities.sGetFileMIMEType(this.NotificationName);
notificationIntent.setAction(android.content.Intent.ACTION_VIEW);
notificationIntent.setDataAndType(Uri.fromFile(file), sMediaDataType);
PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);
contentView.setTextViewText(R.id.status_percent, "0%");
contentView.setTextViewText(R.id.status_fileName, this.NotificationName);
contentView.setProgressBar(R.id.status_progress, 100, 0, false);
notification.contentView = contentView;
notification.contentIntent = contentIntent;
mNotificationManager.notify(nID, notification);
}
protected Long doInBackground(String... urls) {
long retData = 0;
OutputStream fosXSPDatabse = null;
InputStream inServerResponse = null;
URLConnection oURLConnection = null;
URL oURL = null;
try
{
oURL = new URL(oFileManager.sExpiryLink);
oURLConnection = oURL.openConnection();
if (!(oURLConnection instanceof HttpURLConnection))
throw new IOException("Not an HTTP connection");
oURLConnection.connect();
inServerResponse = new BufferedInputStream(oURL.openStream());
if (inServerResponse != null)
{
File fDirectory = new File(oFileManager.sAppFlesDirectory);
if (!fDirectory.exists())
{
if (!fDirectory.mkdir())
{}
}
fosXSPDatabse = new FileOutputStream(oFileManager.sAppFlesDirectory + "/" + oFileInfo.getFileName());
byte data[] = new byte[BUFFER_SIZE];
int nCount = 0;
long lTotalDownloaded = 0;
int nTotalSize = oURLConnection.getContentLength();
while ((nCount = inServerResponse.read(data)) != -1)
{
Log.d(String.valueOf(nID) + " - DoInBackground", String.valueOf(dNotificationbarProgress));
nDownloadCounter ++;
lTotalDownloaded += nCount;
dNotificationbarProgress = lTotalDownloaded * 100.0 / nTotalSize;
if (this.nDownloadCounter == 20 || dNotificationbarProgress == 100) {
publishProgress(Integer.parseInt(new DecimalFormat("#.##").format(dNotificationbarProgress).split("\\.")[0]));
nDownloadCounter = 0;
}
fosXSPDatabse.write(data, 0, nCount);
}
inServerResponse.close();
fosXSPDatabse.flush();
fosXSPDatabse.close();
}
}
catch (MalformedURLException e)
{}
catch (IOException e)
{}
catch (Exception e)
{}
finally
{
try
{
inServerResponse.close();
fosXSPDatabse.flush();
fosXSPDatabse.close();
}
catch (IOException e)
{}
}
return retData;
}
protected void onProgressUpdate(Integer... progress) {
try
{
Log.d(String.valueOf(nID),"onProgressUpdate");
notification.contentView.setProgressBar(R.id.status_progress, 100, progress[0], false);
notification.contentView.setTextViewText(R.id.status_fileName, this.NotificationName);
notification.contentView.setTextViewText(R.id.status_percent, String.valueOf(progress[0]) + "%");
Intent notificationIntent = new Intent();
File file = new File(this.sDownloadPath + this.NotificationName);
sMediaDataType = GSUtilities.sGetFileMIMEType(this.NotificationName);
notificationIntent.setAction(android.content.Intent.ACTION_VIEW);
notificationIntent.setDataAndType(Uri.fromFile(file), sMediaDataType);
PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);
notification.contentIntent = contentIntent;
mNotificationManager.notify(this.nID, notification);
}
catch(Exception e)
{}
}
protected void onPostExecute(Long result) {
notification.contentView.setTextViewText(R.id.status_fileName, "Successfully installed " + this.NotificationName);
notification.contentView.setTextViewText(R.id.status_percent, "100%");
mNotificationManager.notify(this.nID, notification);
}
}
First, it's never a good idea to silently ignore Exceptions. You should at minumum log them to logcat. It think this is the problem.
Second, you're closing the streams multiple times.
Where are you calling the AsyncTask from?
If it's from an activity and the user navigates away from it, that might be terminated any time the OS wants to free up memory.
If it's from a service, that could be terminated too, but at least it is restarted later. If the service was started from a different process, such as a Sync Adapter, you should also post the PreExecute and PublishProgress and PostExecute code to the UI thread with the application looper handler as a message.
In adition, try limiting the number of concurrent downloads to max 3. Otherwise, an OutOfMemory might occur. You can do this with a BlockingQueue. If concurrency is not important, consider using an IntentService instead of an AsyncTask.
The Best way to handle download multiple files is to use the Service.

Categories

Resources