Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to trigger Vibration on Sound Input?

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?

like image 649
Momo Pomo Avatar asked Feb 22 '16 13:02

Momo Pomo


People also ask

What is vibration in audio?

When we hear something, we are sensing the vibrations in the air. The number of vibrations per second is known as the frequency, measured in Hertz (1 Hz = 1 vibration per second). These vibrations enter the outer ear and cause the eardrum to vibrate too.


1 Answers

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.

like image 97
Ricardo Avatar answered Oct 14 '22 05:10

Ricardo