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);
来源:https://stackoverflow.com/questions/35554937/how-to-trigger-vibration-on-sound-input