Hi I am using android with java. I have set up a very simple button which when held down records audio and when released stops recording. I have two questions:
When I run the following implementation of my idea, I get runtime a warning mediarecorder went away with unhandled events every time the button is released. I can't find what is causing this! I see that this has been answered previously on this forum many years ago with the suggestion to add mediaRecorder.update(), but this does not address why the warning is occurring. What does it mean by unhandled events and what could be causing it? I have done nothing different I can see than in the documentation, other than using an onTouchListener...
Second, should I be wary of user's being able to switch on and off the button very rapidly - could this cause runtime problems and should I take steps to guard against this?
The relevant code I use is more-or-less this:
public void set() {
View.OnTouchListener recordOnTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
if (requestMultiplePermissions(Permissions).granted) {
audioSetup();
recordAudio();
}
return true;
case MotionEvent.ACTION_UP:
stopAudio();
return true;
default:
return false;
}
}
binding.addnewvocabRecordVocab.setOnTouchListener(recordOnTouchListener)
}
where
private void audioSetup() {
File filedir = new File(filepath);
if (!filedir.exists()) {filedir.mkdirs();
file = new File(filepath,filename);
if (file.exists()) { file.delete();}
}
public void recordAudio () {
isRecording = true;
if (mediaRecorder != null) {
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;
}
try {
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mediaRecorder.setOutputFile(file);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mediaRecorder.prepare();
} catch (Exception e) {
e.printStackTrace();
}
mediaRecorder.start();
}
public void stopAudio () {
if (isRecording) {
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;
isRecording = false;
}
}
This isn't a full answer to my own question, and the question still stands. But after playing with this a while, I have learnt a few lessons.
If the user taps the record button for a second, then mediarecorder.stop() will produce an error if there is not sufficient data recorded. See this. So if one wants to prevent the app from crashing, one needs to wrap mediarecorder.stop() in some catch - as the discussion in the link advises us. In fact, on some shortish taps, the stop method seems to take rather a long time (well over a second), so it might be worth considering disabling the button just before and after the stop method is called.
Another problem with fast repeated tapping is that it seems to keep adding to the main thread queue, which is probably inadvisable. I have found that using an executor thread with submit is a nice way of dealing with this. Schematically we can
public class audioLibrary() {
//or could go directly in main code
ExecutorService executor;
public void onStartRecord() {
if (executor == null) {
this.executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
//check isRecording and record stuff
});
}
}
public void onStopRecord() {
if (executor != null) {
executor.submit(() -> {
//check isRecording and shut down mediarecorder
//together with catch for onStop.
});
executor.shutdown();
}
}
}
Clearly, one must be a little careful to make sure the thread really does always get shut down.
Curiously, I do not get the `unhandled events' when I do this now...
Maybe someone could expand on this and comment on my code above to see whether they agree with the gist of it, whether it is necessary and on possible improvements.
Related
I am trying to create an android application where I filter one specific frequency of a beep and make the phone vibrate.
I am taking input from the MIC of mobile and using MediaRecorder class, by using this class, I can record, save and play the input. Now I need my mobile to vibrate whenever there is a beep/or any sound.
The input is given by a wire to the Headphone jack of the mobile so I know that there is only one frequency being input.
I have a button, Clicking which starts recording.
I have Permissions to vibrate and record in my manifest file already.
record.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
isRecording=true;
myAudioRecorder.prepare();
myAudioRecorder.start();
...
}
I also tried to search the internet and found kind of the similar question here but I am unable to find any correct answer.
However, I can make the phone vibrate on clicking another button and here is the snipt of code,
Vibrator vibrate;
vibrate = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
Btn1.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v) {
vibrate.vibrate(800);
}
}
I tried calling a Vibrator inside recorder.start(); function but this makes the phone vibrate even when there is no sound anymore.
I also tried getting help from this question so whenever there is silence, the phone should not vibrate, but I am getting confused, I somehow understand that there should be a Boolean which gets true when there is sound and make the phone vibrate, but I am unable to put this logic into code.
Please let me know what can I do in this context and which direction should I be searching in?
UPDATE
I found this toturial for showing the progress bar with amplitude of input sound, it works fine and I tried to make the phone vibrate when there is some value in buffer, Now it vibrates even when the amplitude is zero, I guess thats because of the fact that every vibration makes noise which leads the phone to vibrate. I am unable to check the function via TOAST because of java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare(). Is there any suggestion?
For your main problem, maybe you can check for the amplitude of the sound, and only vibrate if a minimum threshold has been reached. Something like this:
private class DetectAmplitude extends AsyncTask<Void, Void, Void> {
private MediaRecorder mRecorder = null;
private final static int MAX_AMPLITUDE = 32768;
//TODO: Investigate what is the ideal value for this parameter
private final static int MINIMUM_REQUIRED_AVERAGE = 5000;
#Override
protected Void doInBackground(Void... params) {
Boolean soundStarted = true;
if (mRecorder == null) {
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mRecorder.setOutputFile("/dev/null");
try {
mRecorder.prepare();
} catch (IllegalStateException e) {
soundStarted = false;
Log.e(TAG, "Could not detect background noise. Error preparing recorder: " + e.getMessage());
} catch (IOException e) {
soundStarted = false;
Log.e(TAG, "Could not detect background noise. Error preparing recorder: " + e.getMessage());
}
try {
mRecorder.start();
} catch (RuntimeException e) {
Log.e(TAG, "Could not detect background noise. Error starting recorder: " + e.getMessage());
soundStarted = false;
mRecorder.release();
mRecorder = null;
}
}
if (soundStarted) {
// Compute a simple average of the amplitude over one
// second
int nMeasures = 100;
int sumAmpli = 0;
mRecorder.getMaxAmplitude(); // First call returns 0
int n = 0;
for (int i = 0; i < nMeasures; i++) {
if (mRecorder != null) {
int maxAmpli = mRecorder.getMaxAmplitude();
if (maxAmpli > 0) {
sumAmpli += maxAmpli;
n++;
}
} else {
return null;
}
try {
Thread.sleep(1000 / nMeasures);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
mRecorder.stop();
mRecorder.release();
mRecorder = null;
final float avgAmpli = (float) sumAmpli / n;
if (avgAmpli > MINIMUM_REQUIRED_AVERAGE) {
//TODO: Vibrate the device here
}
}
return null;
}
}
For more information regarding the detection of sound level, please refer to the following:
android: detect sound level
What does Android's getMaxAmplitude() function for the MediaRecorder actually give me?
Regarding the exception java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare(), that is happening because the Toast needs to run on the main thread of your app. If your Thread code (like an AsyncTask) is inside an Activity, you can try the following:
runOnUiThread(new Runnable() {
#Override
public void run() {
//Call your Toast here
}
});
Otherwise, you need to somehow pass the conclusion of your method to the Activity for it to run the Toast.
EDIT:
If you want to use this from a Button, you could set its OnClickListener on your Activity's onCreate() call and execute the AsyncTask there. For example:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout);
Button button = (Button)findViewById(R.id.your_button_id);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new DetectAmplitude().execute(new Void[]{});
}
});
}
I suggest you take a look at how AsyncTask works before using this in production code.
You want to sample the audio, and analyze it immediately.
MediaRecorder seems to high level for this, it only captures to file. You probably want to use AudioRecorder instead, as it gives direct access to the input samples.
In order to detect a specific tone, you can use the Goertzel algorithm on the input samples. Here is a C++ implementation I did years ago that could serve as an example.
In order to detect any sound over a certain threshold, you can use Root Mean Square analysis on the input samples and make it trigger once the loudness reaches your threshold. Here is a Python example that reacts to loud noises from a microphone.
Try this:
Btn1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
v.post(new Runnable() {
#Override
public void run() {
vibrate.vibrate(800);
}
});
}
});
You can try this:
Handler handler;
Runnable r;
handler = new Handler();
r = new Runnable() {
public void run() {
Vibrator vib = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
vib.vibrate(500);
handler.postDelayed(r, 1000);
}
};
handler.post(r);
I am working on a video recording camera APP. App crashes if camera is stopped right after starting it maybe because of video size very less. I want to activate stop button only if video size is greater than 1 sec. But problem is I cannot find Current Time and Start time correctly. Finding the difference of two time factors will help in implementing 2 sec Check. Need Help please.
private void onClickActions(View v)
{
float tt = start_time /10000000000000f;
float ct = ((System.currentTimeMillis() ) /10000000000000f);
Log.d("Before stoping S-Time ",tt+"");
Log.d("Before stoping C-Time ",ct+"");
if (recording && tt>=2.0f)
{
Log.d("After Stopping = ",tt+"");
// stop recording and release camera
mediaRecorder.stop(); // stop the recording
recording = false;
rec.setVisibility(View.INVISIBLE);
start_time = 0;
}
//remove time code to initial revert
if(v.getId()== start.getId() && ((CameraPreview.recordHappy || CameraPreview.recordSad))) {
prepareMediaRecorder();
recording = true;
mediaRecorder.start();
consent = true;
happyRecorded=true;
stop.setClickable(true);
start.setClickable(false);
if (AndroidVideoCaptureExample.iV.getVisibility()==View.VISIBLE)
AndroidVideoCaptureExample.iV.setVisibility(View.INVISIBLE);
//AndroidVideoCaptureExample.capture.setText("RECORDING STARTED!");
rec.setVisibility(View.VISIBLE);
start_time = (int)(System.currentTimeMillis());
//Toast.makeText(myContext, "You are being recorded now!", Toast.LENGTH_LONG);
}
if(v.getId()== stop.getId() && consent==true && recording==false) {
if((!CameraPreview.recordHappy && CameraPreview.recordSad))
{
releaseMediaRecorder(); // release the MediaRecorder object
Intent intent = new Intent();
intent.setClass(AndroidVideoCaptureExample.this, consentActivity.class);
startActivity(intent);
finish();
}
else {
CameraPreview.recordHappy = false;
CameraPreview.recordSad = true;
stop.setClickable(false);
start.setClickable(true);
recording = false;
AndroidVideoCaptureExample.capture.setText("Record Neutral Moment");
rec.setVisibility(View.INVISIBLE);
}
}
}
I think you might be overengineering a simple thing. You don't really need to count record time unless you are showing it on the UI. If you want to disable the button, simply disable it just before starting the recording, then use Handler to re-enable after 2 seconds:
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
// enable stop button
}
},2000);
However, I would argue that's not a very good user experience. If you look at cameras like Google Camera, you can stop it immediately after starting, it just won't record anything. To achieve this, you need to catch the RuntimeException when calling mediaRecorder.stop(), then check and clean up the generated file. If it's empty then delete it and don't throw an error to the UI.
What i have:
I have implemented three MediaPlayer.Objects in my App.
All Three are created using a thread:
protected void onResume() {
// Threads
mTT1 = new TrackThread(this, R.raw.audiofile1, 1, mHandler);
mTT2 = new TrackThread(this, R.raw.audiofile2, 2, mHandler);
mTT3 = new TrackThread(this, R.raw.audiofile3, 3, mHandler);
// start thread
mTT1.start();
mTT2.start();
mTT3.start();
super.onResume();
}
"simplified" Code in the Thread for creating:
public class TrackThread extends Thread implements OnPreparedListener {
...
...
...
public void run() {
super.run();
try {
mMp.setDataSource(afd.getFileDescriptor(),
afd.getStartOffset(), afd.getDeclaredLength());
mMp.prepare();
} catch (IllegalArgumentException | IllegalStateException
| IOException e) {
Log.e(TAG, "Unable to play audio queue do to exception: "
+ e.getMessage(), e);
}
}
As I read in several Tutorials the "prepare()" methode takes a little bit of time to finish. Therefore i implemented a "Waiting loop" which waits until all MPs are prepared and created.
When "prepare and create" are done i enable the Start button and i want to start all 3 Mediaplayers SIMULTANEOUSLY.
I again use a Thread for dooing so:
public void onClick(View v) {
// Button 1
if (mBtn.getId() == v.getId()) {
mTT1.startMusic();
mTT2.startMusic();
mTT3.startMusic();
}
Code in the thread:
public class TrackThread extends Thread implements OnPreparedListener {
...
...
...
// start
public void startMusic() {
if (mMp == null)
return;
mMp.start();
}
Please note that the code above is not the full code, but it should be enough to define my problem.
What i want, My problem:
All MPs should play their Music in Sync, unfortunately sometimes when i start the music, there is a time delay between them.
The MPs must start at the exact same time as the 3Audio-files must be played simultaneously (and exactly in sync)
What i have already tried:
+) using SoundPool: My Audio-files are to big(5Megabyte and larger) for SoundPool
+) seekTo(msec): i wanted to seek every MP to a Specific time: eg.: 0, but this did not solve the problem.
+) to reach more Programmers i also asked this question on: coderanch.com
I hope somebody can help me!
Thanks in advance
The bottleneck here will certainly be preparing the mediaplayers to play. The Android framework provides an asynchronous method to perform this loading, and so with a bit of synchronization code you should be able to get these audio sources to play at roughly the same time. To keep from sound artifacting, you'll want less than 10ms of latency.
Initialize an atomic counter, C, to the number of things to load.
Use the prepareAsync() functions within MediaPlayer to prepare all three. Immediately after calling prepareAsync, supply a listener using setOnPreparedListener(listener).
Inside this listener, decrement C and check the value. If the value is greater than 0, wait on an object using the java object .wait() function. If the value is equal to 0, call notifyAll() on the object to wake up all of the other mediaplayer prepared-listener callback threads.
public void startMediaPlayers(List<MediaPlayer> mediaPlayers) {
private AtomicInteger counter = new AtomicInteger(mediaPlayers.size());
Object barrier = new Object();
/* start off all media players */
for (MediaPlayer player : mediaPlayers) {
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(final MediaPlayer mediaPlayer) {
int value = counter.decrementAndGet();
if (value == 0) {
// all media players are done loading.
// wake up all the ones that are asleep
barrier.notifyAll();
} else {
while (value > 0) {
try {
// wait for everyone else to load
barrier.wait();
} catch (InterruptedException e) {
// ignore
}
}
}
mediaPlayer.start();
callback.success(true);
}
player.prepareAsync();
}
}
As nobody could help me I found a solution on my own. MediaPlayer did not fulfill my requirements but Android JETPlayer in combination with JETCreator did.
CAUTION: Installing Python for using JETCreator is very tricky, therfore
follow this tutorial. And be careful with the versions of python and wxpython, not all versions support the JETCreator.
I used:
Python Version 2.5.4 (python-2.5.4.msi)
wxPython 2.8 (wxPython2.8-win32-unicode-2.8.7.1-py25.exe)
For those who do not know how to implement the Jetplayer watch this video
(at min.5 he starts with programming the Jetplayer).
Unfortunately I do not speak French so I just followed the code which worked for me.
Using Android JETCreator you can create your own JET Files and use them as your resource.
Useful links:
Demo data
Manual
Code/class
for last three weeks I have worked on a Media Player in Android.I am trying to find a solution of how can I make my Media Player to change the song when it's already playing one.
Here is my Listener on the RecyclerView
musicList.addOnItemTouchListener(
new RecyclerItemClickListener(getApplicationContext(), new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, final int position) {
currentPosition = position;
if(!mediaPlayer.isPlaying()){
musicThread.start();
} else {
mediaPlayer.reset();
}
}
})
);
}
and my Thread is this:
final Thread musicThread = new Thread(new Runnable(){
#Override
public void run() {
try {
URL = getMusicURL(myDataset[currentPosition]);
try {
mediaPlayer.setDataSource(URL);
//mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.prepareAsync(); // prepare async to not block main thread
} catch (IOException e) {
e.printStackTrace();
Log.i("TEST","Eroare: "+e.getMessage());
}
} catch (StorageApiException e) {
e.printStackTrace();
Log.i("TEST","Eroare: "+e.getMessage());
}
}
});
I think you have a mess. First of all, you dont need a thread to play music, the own mediaplayer API does it for you when you call mediaPlayer.start(). However, you have to care about the time it takes to prepare the data source if you are for example streaming online music. For this, just use mediaPlayer.prepareAsync() and register a callback. When it has finished preparing, you can automatically start playing or do whatever you want.
If you want to change the data source, just follow the automaton map that you can find in MediaPlayer docs. Essentially, when the user selects another track, you register the call in your button listener, then reset the mediaPlayer, and recall all prepare, start... cycle again. By the way, it is advised to deploy all your mediaplayer code into a service so that it can keep playing even though the user has closed your activity.
I'm currently going through the 'Building A Camera App' tutorial -http://developer.android.com/guide/topics/media/camera.html#custom-camera
As someone relatively new to android, I find it a bit confusing / unclear at times.
I'm trying to understand where this code is supposed to go:
private boolean isRecording = false;
// Add a listener to the Capture button
Button captureButton = (Button) findViewById(id.button_capture);
captureButton.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
if (isRecording) {
// stop recording and release camera
mMediaRecorder.stop(); // stop the recording
releaseMediaRecorder(); // release the MediaRecorder object
mCamera.lock(); // take camera access back from MediaRecorder
// inform the user that recording has stopped
setCaptureButtonText("Capture");
isRecording = false;
} else {
// initialize video camera
if (prepareVideoRecorder()) {
// Camera is available and unlocked, MediaRecorder is prepared,
// now you can start recording
mMediaRecorder.start();
// inform the user that recording has started
setCaptureButtonText("Stop");
isRecording = true;
} else {
// prepare didn't work, release the camera
releaseMediaRecorder();
// inform user
}
}
}
}
);
Can this be anywhere within the top level class, or is this supposed to be inside one of the provided methods or inner classes?
No matter where I put this code it's causing errors telling me to add or remove '}', but I'm sure I must just have it in the wrong place, since I'm sure google's code is fine.
Any help much appreciated!
This code belongs in the activity that loads the layout containing the "button_capture" Button. One you find that activity, out this inside of the onCreate() method.
No matter where I put this code it's causing errors telling me to add or remove '}'
This is simply a matter of getting your braces paired up properly.