I created an object that continuously reads the input from the mic but I'm having some troubles with it. I need to be able to pause and start it, so I've implemented the methods, but the problem is that, on an older phone (Galaxy Y) it works very well the first time I start it, but after I pause it and start it again it quickly crashes due to OutOfMemoryException.
#Override
public void run() {
while (mIsRunning) {
synchronized (mThread) {
while (mIsPaused) {
try {
mThread.wait();
} catch (InterruptedException e) {
}
}
}
double sum = 0;
int readSize = mRecorder.read(mBuffer, 0, mBuffer.length);
if (readSize > 0) {
short[] samples = new short[readSize];
for (int i = 0; i < readSize; i++) {
sum += mBuffer[i] * mBuffer[i];
samples[i] = mBuffer[i];
}
final int amplitude = (int) Math.sqrt((sum / readSize));
Message message = new Message();
message.arg1 = amplitude;
message.arg2 = readSize;
message.obj = samples;
message.what = 0;
if (mHandler != null)
mHandler.sendMessage(message);
}
}
}
public void start() {
if (!mIsRunning) {
mIsRunning = true;
mThread.start();
} else synchronized (mThread) {
mIsPaused = false;
mThread.notifyAll();
}
mRecorder.startRecording();
}
public void pause() {
synchronized (mThread) {
mIsPaused = true;
}
mRecorder.stop();
}
So, the first time all works well, but after I call stop() on the AudioRecord and start reading the input again I quickly run out of memory. Any ideas on how to fix this? I was thinking of just not stopping the recording and just not sending the data through the handler but I don't know. Thanks.
The solution was destroying (and releasing) the AudioRecord object on every pause and just recreating it on each start.
Related
After a particular button is clicked, I want to have it so that my audio would play fifteen times, and have the progress bar increment each time. I had also envisioned having a delay in between each time the audio plays.
What's currently happening is that all of the beeps play back to back without delay, and after that, the progress bar gets incremented right to max straight away. Using Handler somehow doesn't manage to delay the audio playing.
I'm a beginner in app development, so excuse the shoddy code:
public void click1(View view) throws IOException {
int i;
// ProgressBar1.setProgress(0);
for (i = 1; i < 16; i = i + 1)
{
int secs = 2; // Delay in seconds
Utils.delay(secs, new Utils.DelayCallback() {
#Override
public void afterDelay() throws IOException {
// Do something after delay
PlayShortAudioFileViaAudioTrack(500, 1);
ProgressBar1.incrementProgressBy(1);
}
});
}
}
Here's the delay code:
public class Utils {
public interface DelayCallback{
void afterDelay() throws IOException;
}
public static void delay(int secs, final DelayCallback delayCallback){
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
try {
delayCallback.afterDelay();
} catch (IOException e) {
e.printStackTrace();
}
}
}, secs * 1000); // afterDelay will be executed after (secs*1000) milliseconds.
}
}
The function that plays audio is
public void PlayShortAudioFileViaAudioTrack(int f, double duration) throws IOException
{ int sampleRate = 48000; // Samples per second
//double duration = 2.0;
long numFrames = (long)(duration * sampleRate);
long frameCounter = 0;
int intSize = android.media.AudioTrack.getMinBufferSize(48000, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_FLOAT);
AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, 48000, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_FLOAT, intSize, AudioTrack.MODE_STREAM);
float[] buffer = new float[intSize];
while (frameCounter < numFrames)
{
long remaining = numFrames - frameCounter;
int toWrite = (remaining > intSize) ? intSize : (int) remaining;
for (int s=0 ; s<toWrite ; s++, frameCounter++)
{
buffer[s] = (float)Math.sin(2.0 * Math.PI * f * frameCounter / sampleRate);
// buffer[1][s] = Math.sin(2.0 * Math.PI * 500 * frameCounter / sampleRate);
}
if (at!=null) {
// Write the byte array to the track
at.play();
at.write(buffer, 0, intSize, AudioTrack.WRITE_BLOCKING);
}
else
Log.d("TCAudio", "audio track is not initialised ");
}
at.stop();
at.release();
}
Changing the audiotrack mode to NON-BLOCKING from BLOCKING results in the audio just playing once, and the progress bar still shooting up to full immediately.
To solve your problem, you can use AsynkTask<> like this:
Create a subclass of AsynkTask<> in your Activity to handle the delayed action and the updates of the progressbar.
Then in your click1()-method you just have to create a new instance of your AsyncTask subclass and execute it. You can give it the number of cycles on the call of execute(). The following code should work:
...
ProgressBar1.setMax(16); // to get 16 cycles like in your example
...
public void click1(View view) throws IOException {
int max = ProgressBar1.getMax();
new MyTask().execute(max);
}
class MyTask extends AsyncTask<Integer, Integer, Void> {
#Override
protected Void doInBackground(Integer... params) {
for (int i=0 ; i <= params[0]; i++) {
try {
Thread.sleep(secs * 1000);
publishProgress(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
#Override
protected void onPreExecute() {
//do something before execution
}
#Override
protected void onProgressUpdate(Integer... values) {
//do your delayed stuff
PlayShortAudioFileViaAudioTrack(500, 1);
ProgressBar1.incrementProgressBy(1);
}
}
I am playing MIDI notes using this library as follows:
handler.postDelayed(new Runnable() {
ShortMessage message;
#Override
public void run() {
{
MidiEvent event = midiEvents.get(index[0]);
if (index[0] < midiEvents.size() - 1) {
delta = midiEvents.get(index[0] + 1).getDelta();
time[0] = timeFactor * midiEvents.get(index[0] + 1).getDelta();
mTotalMidiTime += time[0];
int noteValue;
int NOTE_STATUS;
if (event instanceof NoteOn) {
noteValue = ((NoteOn) event).getNoteValue();
NOTE_STATUS = NOTE_ON;
if (index[0] != 0) {
if (delta != 0) {
Intent intent = new Intent();
intent.setAction(Constants.SCROLL_RECYCLERVIEW);
localBroadcastManager.sendBroadcast(intent);
}
}
} else {
noteValue = ((NoteOff) event).getNoteValue();
NOTE_STATUS = NOTE_OFF;
}
try {
message = new ShortMessage(NOTE_STATUS, 2, noteValue,
127);
} catch (Exception e) {
e.printStackTrace();
}
Intent intent = new Intent();
intent.setAction(Constants.ACTION_SEEK);
localBroadcastManager.sendBroadcast(intent);
if (message != null)
recv.send(message, -1);
index[0]++;
} else {
index[0] = 0;
time[0] = 1;
mTotalMidiTime = mMinimumTime;
delta = 0;
}
handler.postDelayed(this, time[0]);
}
}
}, 0);
With each NoteOn event I am smooth scrolling a RecyclerView and updating a Seekbar using a LocalBroadcastManager
My problem is that playback is fine when UI operations are not performed but playback and UI get completely out of sync as soon as multiple MIDI notes (chords) are played in very quick succession. It would be appreciated if any performance improvements are suggested for the same. I have already tried performing the UI operations in runOnUiThread and also launching a new Handler for UI operations.
My BroadcastReceiver is as follows:
BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String actionType = intent.getAction();
switch (actionType) {
case Constants.SCROLL_RECYCLERVIEW:
Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
mNotesRecycler.smoothScrollBy(pixels, 0);
}
});
break;
case Constants.ACTION_SEEK:
Handler seekHandler = new Handler();
seekHandler.post(new Runnable() {
#Override
public void run() {
mPinchSeekBar.setSelectedCentreValue(mTotalMidiTime);
mCurrentTime.setText(timeInMinutes((int) mTotalMidiTime));
}
});
break;
}
}
};
Besides the optimizations you can do, I think the problem itself is RecyclerView's smoothScroll is more a method you call sometimes to do a fancy scroll rather than bomb it with requests that cause it to constantly recompute the running animation.
One thing you can try is to write yourself a simple scroll handler that calls mRecyclerView.scrollTo() that does the scroll without animation but I guess it's going to be more reliable.
Try something like this
Handler mHandler = new Handler();
int mPosition, mTargetPosition;
#IntRange(from = 20, to = 100)
final int INTERVAL = 50; // try with 50 ms or a little lower
Runnable mTimerScroll = new Runnable() {
public void run() {
if (mPosition != mTargetPosition) {
if (mPosition < mTargetPosition) {
mPosition += mDelta;
if (mPosition > mTargetPosition) mPosition = mTargetPosition;
}
if (mPosition > mTargetPosition) {
mPosition -= mDelta;
if (mPosition < mTargetPosition) mPosition = mTargetPosition;
}
mRecyclerView.scrollTo(mPosition, 0);
}
// repeat every 50ms
mTimerScroll.post(mRunnable, INTERVAL);
}
}
And then you start it
void startTimer() {
stop(); // prevent double start
mHandler.post(mTimerScroll);
}
void stopTimer() {
mHandler.removeCallbacks(mTimerScroll);
}
void scroll(int target) {
mTargetPosition = target;
}
void scrollBy(int pixels) {
scroll(mPosition + pixels);
}
I don't know the specifics of your app but it might work.
About optimization, you are using a lot of stuff there that can or cannot be necessary depending on the specifics of your library, etc, but if there are no different threads or services involved you could avoid the LocalBroadcast thing and all the handler.post() as everything is already on the UI thread (handler.post just posts a runnable to the thread where the handler was created, that in your case is the UI thread, so it doesn't do anything)
My project has a demand, need to constantly read the bar code data, like a commodity supermarket scanner with bar code scanning guns, then data into a keypad, but encountered a problem, for a long time continuously scanning, CPU usage will be very high, even reached 95%, I have set the thread to sleep in a loop, but failed to solve this problem.
I have been asking for this problem, but it may be too messy code, affecting everyone to read, and now simplify the code, I hope you can help me, thank you very much;
Sometimes a few hours on the CPU scan occupy too high, but sometimes a few days there. Grab logcat log found the sleep method sometimes is not executed, if not continuous execution will cause CPU use rate is too high, but I don't know why the sleep method will not perform .
private void startReceive() {
stopReceive = false;
new Thread(new Runnable() {
#Override
public void run() {
int timeout = 1000;
while (!stopReceive) {
if (mUsbDeviceConnection != null) {
try {
byte[] receiveBytes = new byte[64];
int value = mUsbDeviceConnection.bulkTransfer(mUsbEndpoint, receiveBytes,
receiveBytes.length, timeout);
if (value > 0) {
for (int i = 2; !stopReceive && i < receiveBytes.length; i++) {
byte b = receiveBytes[i];
if (b != 0) {
result += new String(new byte[]{b});
}
if (!stopReceive && !result.equals("") && result != null) {
Runtime.getRuntime().exec("input text " + result);
}
}
}
Thread.sleep(100);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}).start();
}
This seemd to be a huge thread running on the main-thread which will drastically slow down the performance of the device.
Big operations you should instead run asynchronously, which means that it will run in the background-thread and not affect the UI-thread which is the issue right now:
Here's a example of how the implementation would look like:
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
// StartReceive code..
stopReceive = false;
new Thread(new Runnable() {
#Override
public void run() {
int timeout = 1000;
while (!stopReceive) {
if (mUsbDeviceConnection != null) {
try {
byte[] receiveBytes = new byte[64];
int value = mUsbDeviceConnection.bulkTransfer(mUsbEndpoint, receiveBytes,
receiveBytes.length, timeout);
if (value > 0) {
for (int i = 2; !stopReceive && i < receiveBytes.length; i++) {
byte b = receiveBytes[i];
if (b != 0) {
result += new String(new byte[]{b});
}
if (!stopReceive && !result.equals("") && result != null) {
Runtime.getRuntime().exec("input text " + result);
}
}
}
Thread.sleep(100);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}).start();
return "Done";
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
// We're done
}
#Override
protected void onPreExecute() {
// Before starting operation
}
#Override
protected void onProgressUpdate(Void... values) {
}
}
How to start the thread:
LongOperation longOp = new LongOperation();
longOp.execute();
Read more: AsyncTask Android example
You should better look this post and try to find which method consume more system resource: https://stackoverflow.com/a/14688291/6176003
I am new to Android and I have a project that connects an android device with other device with BLE. After connecting I have mBluetoothGatt.discoverServices() and I need to call mBluetoothGatt.getServices() after onServicesDiscovered is called. For now I am using this code:
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if(status == BluetoothGatt.GATT_SUCCESS){
servicesFound = true;
Log.i("Send", String.valueOf(gatt.getServices().size()));
}
}
and this is in the button click:
public void btnTestWriteOnClick(View v){
if(mBluetoothGatt != null) {
mBluetoothGatt.discoverServices();
byte[] allBytesToSend = new byte[]{...};//Test byte array.
List<BluetoothGattService> serviceList = mBluetoothGatt.getServices();
while(!servicesFound){
}
servicesFound = false;
displayGattServicesTest(serviceList, allBytesToSend);
}
}
EDIT
My displayGattServicesTest:
private void displayGattServicesTest(List<BluetoothGattService> gattServices, byte[] allBytesToSend) {
if (gattServices == null) return;
//Sets the interval for printing.
//ChangeIntervalAndTimeout();
// Enable notification for characteristic.
EnableNotificationInFFF4(gattServices);
// Loops through available GATT Services.
for (BluetoothGattService gattService : gattServices) {
if (gattService.getUuid().toString().contains(serviceUUID)) {
for (final BluetoothGattCharacteristic characteristic : gattService.getCharacteristics()) {
if (characteristic.getUuid().toString().contains(characUUID)) {
long startTime = System.currentTimeMillis();
Log.i("Send", "===========================BEGINNING===========================");
int size = 19;
int times = allBytesToSend.length / size;
if (allBytesToSend.length > times * size) {
times++;
}
params = new byte[times][];
int tmp;
for (tmp = 0; tmp < 1000; tmp++) {
int logCount = 0;
for (int i = 0; i < allBytesToSend.length; i++) {
if(allBytesToSend.length < i + size){
size = allBytesToSend.length - i;
}
params[logCount] = new byte[size];
System.arraycopy(allBytesToSend, i, params[logCount], 0, size);
i += size - 1;
Log.i("Send", "====Sending command No " + logCount + "====");
logCount++;
}
WriteIntoPrinter t = new WriteIntoPrinter(characteristic);
t.execute(params);
try {
t.get(2000, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
t.cancel(true);
break;
}
}
Log.i("Send", "===========================DONE===========================");
Log.i("Send", "Tmp = " + tmp);
long difference = System.currentTimeMillis() - startTime;
Log.i("Send", "Time - " + (double) (difference / 1000) + " sec.");
break;
}
}
break;
}
}
}
My AsyncTask:
private class WriteIntoPrinter extends AsyncTask<byte[], Void, Void>{
BluetoothGattCharacteristic characteristic;
WriteIntoPrinter(BluetoothGattCharacteristic characteristic){
this.characteristic = characteristic;
//characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
}
#Override
protected Void doInBackground(final byte[]... params) {
for (int i = 0; i < params.length ; i++) {
Log.i("Send", "Sending - " + i + " part. Number of bytes: " + params[i].length);
characteristic.setValue(params[i]);
mBluetoothGatt.writeCharacteristic(characteristic);
final int finalI = i;
Thread t = new Thread(new Runnable() {
#Override
public void run() {
if (finalI == params.length - 1) {
Log.i("Send", "WaitingThread final - " + finalI);
while (!isWritingOnPaper) {
//SystemClock.sleep(20);
}
Log.i("Send", "WaitingThread final - " + finalI + " Done.");
}
else{
while (!isSuccessful) {
//SystemClock.sleep(20);
}
}
}
});
t.start();
try {
t.join(); // wait for thread to finish
} catch (InterruptedException e) {
e.printStackTrace();
}
isWritingOnPaper = false;
isSuccessful = false;
}
return null;
}
}
NOTE This is only a test don't mind the for loops and the new Thread in the AsyncTask. Basecally in displayGattServicesTest I am splitting a byte[] into 19 byte arrays and sending it 1000 times to test the speed.
NOTE 2 Dept Description: displayGattServicesTest take byte[] that is exactly 4*19 bytes long. After that it makes byte[4][19] and gives byte[4][19] to a AsyncTask that starts writing into the characteristic. Every time waits for onCharacteristicWrite to return true and when it writes the last [19] bytes waits for onCharacteristicChanged to return true and then writes the next byte[4][19]. This is the goal.
I do the same think when reading and writing.
This is working but I don't think this is the right way to do it. :) Is there any other way to wait onServicesDiscovered, onCharacteristicWrite and onCharacteristicChanged to finish successful.
In your btnTestWriteOnClick just show progress bar, then wait for result in onServicesDiscovered , if you got result then hide progress bar and do your stuff (call displayGattServicesTest in onServicesDiscovered), you can disable button while discovering is in progress to prevent user from clicking it and starting new discover.
Hi I've read some post with same question but can't find the exact or I must say the answer I've been looking for. Well I just want to know how I can get the playback level of the audio file that is set on the mediaplayer. I already tried the int volume_level = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); but from what I see. I only get the current volume set on my device. Well what I want to achive is to add an animation that follows with level of my audio being played. Here's my code so far:
before the call of play audio method:
audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
the playback method:
public void playAudio(String record_path) throws IOException{
if(audioPlayer!=null && mpStatus == State.Paused){
/*play from paused state*/
audioPlayer.start();
mpStatus = State.Playing;
}
else
{
/*play from start of recording*/
setMediaPlayer(record_path);
audioPlayer.start();
mpStatus = State.Playing;
}
}
and the thread:
private class playBackRunnable extends Thread {
final long start_time = System.currentTimeMillis();
public void run() {
while(chk_play.isChecked()){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
final long elapsed = System.currentTimeMillis() - start_time;
final String elapsed_time = util.getAsTime((int) elapsed);
runOnUiThread(new Runnable() {
#Override
public void run() {
int volume_level = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
int amp = (int)(volume_level * 100.f)/100;
Log.v("Volume Level", String.valueOf(amp));
if(chk_play.isChecked()){
prog_volume.setProgress(amp);
//txt_rectime.setText(elapsed_time);
if(amp <= 40 ){
prog_volume.setProgressDrawable(getResources().getDrawable(R.drawable.progress_green));
}else if(amp <= 60){
prog_volume.setProgressDrawable(getResources().getDrawable(R.drawable.progress_yellow));
}else if(amp <= 80){
prog_volume.setProgressDrawable(getResources().getDrawable(R.drawable.progress_orange));
}else {
prog_volume.setProgressDrawable(getResources().getDrawable(R.drawable.progress_red));
}
}
}
});
}
}
}
Hope someone can help me with this. Thanks in advance.
EDIT:
Added audioPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); before audioPlayer.prepare() still not working.
The only solution I know uses the Visualizer class. For convenience reason, I suggest using AudioCapture.java from KitKat live wallpaper sources, which add a data processing layer over Visualizer. The project linked above also gives some examples of uses, and here is how I use it in myself for JUnits tests :
private int getAudioOutputAmplitude(int durationInSeconds) throws InterruptedException {
AudioCapture mAudioCapture = new AudioCapture(AudioCapture.TYPE_PCM, 1024);
mAudioCapture.start();
Thread.sleep(durationInSeconds * 1000);
int [] mVizData;
mVizData = mAudioCapture.getFormattedData(1, 1);
mAudioCapture.release();
int minValue = 0;
int maxValue = 0;
for (int value:mVizData){
if (value<minValue){
minValue = value;
} else if (value>maxValue){
maxValue = value;
}
}
return maxValue-minValue;
}