How to trigger Vibration on Sound Input?
Asked Answered
S

4

8

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?

Stansbury answered 22/2, 2016 at 13:28 Comment(0)
F
6

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:

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.

Faunie answered 28/2, 2016 at 12:13 Comment(2)
Hello, This sounds great but I am unable to find a way to start recording on the button click, would you suggest, a button onclicklistener method inside the AsyncTask and then the protected Void doInBackground(Void... params inside it?Stansbury
Hi @MomoPomo I edited my answer with an usage example. Hope it helps.Faunie
Y
1

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.

Yesman answered 3/3, 2016 at 14:8 Comment(2)
You can use setOutputFile("/dev/null") on the MediaRecorder to avoid the file output. Also, for just detecting if there is noise, the media recorder would do just fine by checking the sound amplitude over a small period of time.Faunie
Your answer is great, but as I need to develop android and also several other beginners, it would have been great if only android implementations were introduced, however for taking the logic its excellent, still I had the logic already what I needed was syntax to implement it. Also for learning purposes its not necessary to change the used classes without implementing one thoroughly at first.Stansbury
C
0

Try this:

Btn1.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            v.post(new Runnable() {

                @Override
                public void run() {

                    vibrate.vibrate(800);
                }
            });

        }
    });
Carpetbag answered 22/2, 2016 at 13:40 Comment(1)
Thank you for your reply, but I need to make the phone vibrate when there is a sound, not on click, I already am able to do that. Please read the question again.Stansbury
D
0

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);
Dunnage answered 28/2, 2016 at 11:59 Comment(1)
Would you please explain the code, I am new to android and not familiar with all the concepts.Stansbury

© 2022 - 2024 — McMap. All rights reserved.