如何在声音输入时触发振动?

8

我正在尝试创建一个安卓应用程序,在该应用程序中过滤掉一种特定频率的蜂鸣声并使手机振动。

我从手机的MIC中获取输入,并使用MediaRecorder类,通过使用这个类,我可以记录、保存和播放输入。现在我需要我的手机在有蜂鸣声或任何声音时进行震动。

输入是通过线缆连接到手机的耳机插孔,所以我知道只有一个频率被输入。

我有一个按钮,点击它会开始录制。我的清单文件已经具有振动和录制权限。

record.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    isRecording=true;
                    myAudioRecorder.prepare();
                    myAudioRecorder.start();
...
}

我尝试在互联网上搜索,并发现了类似的问题在这里,但我无法找到任何正确的答案。
然而,我可以通过点击另一个按钮使手机振动,以下是代码片段:
 Vibrator vibrate;
    vibrate = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);

        Btn1.setOnClickListener(new View.OnClickListener()

                                {
                                    @Override
                                    public void onClick(View v) {
                                       vibrate.vibrate(800);
                                    }
                                }

我尝试在`recorder.start()`函数中调用振动器,但即使没有声音,这也会使手机振动。 我还试图从这个问题寻求帮助,因此当没有声音时,手机不应该振动,但我感到困惑,我有点理解应该有一个布尔值,在有声音时变为true并使手机振动,但我无法将这种逻辑放入代码中。请告诉我在这种情况下我该怎么做以及我应该朝哪个方向搜索?
更新:我找到了这个用于显示输入声音幅度的进度条的教程,它可以正常工作,并且我尝试让手机在缓冲区中有一些值时振动,现在它甚至在幅度为零时也会振动,我猜这是因为每次振动都会发出噪音,导致手机振动。 由于`java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()`,我无法通过TOAST检查该函数。是否有任何建议?
4个回答

6

针对您的主要问题,也许您可以检查声音的振幅,只有在达到最低阈值时才进行振动。类似这样:

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;
    }
} 

如需了解有关声音级别检测的更多信息,请参阅以下内容:

关于异常 java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare(),这是因为 Toast 需要在应用程序的主线程上运行。如果您的 Thread 代码(例如 AsyncTask)位于一个 Activity 内部,您可以尝试以下方法:

runOnUiThread(new Runnable() {
        @Override
        public void run() {
            //Call your Toast here
        }
    });

否则,你需要以某种方式将方法的结论传递给Activity,以便它运行Toast
编辑:
如果你想从一个Button中使用它,你可以在ActivityonCreate()调用中设置它的OnClickListener并在那里执行AsyncTask。例如:
@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[]{});
        }
    });
}

在生产代码中使用之前,我建议您先了解AsyncTask的工作原理


你好,这听起来很不错,但我找不到在按钮点击时开始录制的方法,你会建议在AsyncTask中添加一个按钮onclicklistener方法,然后在其中使用protected Void doInBackground(Void... params)吗? - Momo Pomo
嗨@MomoPomo,我编辑了我的答案并附上了一个使用示例。希望它有所帮助。 - Ricardo

1
你想对音频进行采样并立即分析。
MediaRecorder似乎过于高级,它只能捕获到文件中。你可能想使用AudioRecorder,因为它可以直接访问输入样本。
为了检测特定的音调,你可以在输入样本上使用Goertzel算法。这是我多年前做的一个C++实现,可以作为示例。
为了检测任何声音超过一定阈值,你可以在输入样本上使用均方根分析,并在响度达到你的阈值时触发它。这是一个Python示例,用于对来自麦克风的响亮噪音做出反应。

您可以在MediaRecorder上使用 setOutputFile("/dev/null") 来避免文件输出。此外,仅仅检测是否有噪音,媒体记录器通过检查一小段时间内的声音振幅就可以胜任。 - Ricardo
你的回答很好,但是我需要开发Android和其他一些初学者,如果只介绍Android实现就更好了。不过,对于理解逻辑来说,它仍然很棒。我已经有了所需的逻辑,需要的是实现它的语法。此外,为了学习目的,在没有彻底实现一个类之前,没有必要更改使用的类。 - Momo Pomo

0

你可以尝试这个:

    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);

请您能否解释一下这段代码,我是新手,对Android的所有概念都不太熟悉。 - Momo Pomo

0
尝试这个:
Btn1.setOnClickListener(new View.OnClickListener() {

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

                @Override
                public void run() {

                    vibrate.vibrate(800);
                }
            });

        }
    });

谢谢你的回复,但我需要在有声音时让电话震动,而不是在点击时,我已经能够做到这一点。请再次阅读问题。 - Momo Pomo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接