安卓,实时音高和音量检测

3

我正在编写一个应用程序,需要监听麦克风并提供实时的音量和音高输出。我已经解决了音高识别的问题。我已经进行了大量fft的研究,并找到了Android库TarsosDSP,使得监听音高变得非常简单:

AudioDispatcher dispatcher = 
        AudioDispatcherFactory.fromDefaultMicrophone(22050,1024,0);
PitchDetectionHandler pdh = new PitchDetectionHandler() {
    @Override
    public void handlePitch(PitchDetectionResult res, AudioEvent e){
        final float pitchInHz = res.getPitch();
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                processPitch(pitchInHz);
            }
        });
    }
};
AudioProcessor pitchProcessor = new PitchProcessor(PitchEstimationAlgorithm.FFT_YIN, 22050, 1024, pdh);
dispatcher.addAudioProcessor(pitchProcessor);

Thread audioThread = new Thread(dispatcher, "Audio Thread");
audioThread.start();

我已经学会如何使用内置的android.getMaxAmplitude()方法进行振幅检测。

但我的问题是,我无论如何都无法同时进行两者。问题在于你似乎不能运行多个麦克风实例。例如,如果您尝试在单独的线程上运行两个不同的实时录音,则会出现问题。我已经搜索整个互联网,试图找到一些示例代码来帮助我入手,但我找不到任何东西。有人曾经做过类似的事情吗?

编辑 我已经发现可以使用Pitchdetectionhandler中的AudioEvent。 audioevent.getbytebuffer()返回一个带有以字节为单位的音频数据的字节数组,根据文档:https://0110.be/releases/TarsosDSP/TarsosDSP-latest/TarsosDSP-latest-Documentation/

如果我没有弄错,当转换为short[]时,最高值就是最高幅度对吗?

但:

final byte[] audioBytes = e.getByteBuffer();
 short[] shortArray = new short[audioBytes.length];
         for (int index = 0; index < audioBytes.length; index++) {
                    shortArray[index] = (short) audioBytes[index];
                            float item = shortArray[index];
                               if (item > amp){
                                        amp = item;
                                    }
                                }

在这种情况下,AMP始终返回127。而且这种方法在实时环境中并不真正有效?
所以还有三个问题。如果我的基本思路是正确的,那么为什么它总是返回127?我该如何在实时环境中使用它?
- 列表项

我不使用Android API,但是pitchInHz = res.getPitch();后面也可以跟着.getMaxAmplitude();代码吗? - VC.One
不好意思,它不能这样做,但如果可以的话那将是非常棒的。 - cas
1个回答

3
我找到了解决方案。你可以使用audioEvent.getFloatBuffer()方法,然后将该缓冲区通过一些FFT方法运行,然后可以从缓冲区中提取振幅值。该缓冲区非常小,因此我最终从正在运行的缓冲区中获取了最大振幅,这样可以让你获得多次每秒的振幅读数,并且对我来说足够实时。 编辑 示例:
 public void handlePitch(PitchDetectionResult result, final AudioEvent e) {

                        final float pitchInHz = result.getPitch();
                        final float[] amplitudes = new float[e.getBufferSize()];

                        new Thread(new Runnable() {
                            public void run() {
                                if (pitchInHz > pitch) {
                                    pitch = pitchInHz;
                                }

                                float[] audioFloatBuffer = e.getFloatBuffer();
                                float[] transformBuffer = new float[e.getBufferSize() * 2];
                                FFT fft = new FFT(e.getBufferSize());
                                System.arraycopy(audioFloatBuffer, 0, transformBuffer, 0, audioFloatBuffer.length);
                                fft.forwardTransform(transformBuffer);
                                fft.modulus(transformBuffer, amplitudes);


                                for (int index = 0; index < amplitudes.length; index++) {
                                    if (amplitudes[index] > amp) {
                                        amp = amplitudes[index];
                                    }
                                }
                            }
                        }).start();
                    }

你能发布你的解决方案吗?对我们来说非常有用,因为我们大多数人都在寻找检测实时振幅的方法。 - Nana Ghartey
哇,没想到这篇旧帖子仍然受到关注,我已经更新了答案并附上了我使用的解决方案。请记住,这是我第一次尝试做这样的事情,之后也没有做过类似的事情。我不知道这是否是一个好方法,但它对我有效。 - cas

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