1

我正在尝试为 Android 手机制作一个简单的音高检测应用程序。我已经让手机显示我计算的自相关值的图表,这些值存储在双精度的一维数组中。现在我需要弄清楚如何检测数组中的重复模式。这是自相关图的屏幕截图,我以稳定的音调哼唱:

哼出稳定音高时我的自相关图的屏幕截图

我尝试实现此幻灯片中给出的一维数组的递归寻峰算法:http: //courses.csail.mit.edu/6.006/spring11/lectures/lec02.pdf但我在 Android 上出现内存不足错误。

接下来,我尝试实现类似这种算法来查找二阶导数:https ://stackoverflow.com/a/3869172但是来自手机的自相关值非常不稳定,以至于它发现了太多的最小值和最大值。

我需要弄清楚如何做是对自相关数据应用某种过滤器以使其平滑,但我数学很烂,不知道该怎么做。我尝试将自相关值四舍五入到小数点后几位,但没有得到我想要的结果。

基本上我需要帮助来弄清楚如何找到重复模式的整体最大值(实际上只有第一个可能没问题)。在上面的屏幕截图中,模式是一个高峰,然后是两个较短的峰。我需要知道第二个高峰何时发生,以便我可以计算音高。

4

2 回答 2

0

您正在尝试估计样本数据中振幅峰值的频率。您可以做到这一点,而无需手动查找估计峰值然后计算频率。相反,您可以使用快速傅立叶变换,它将幅度与时间的关系图转换为频率与时间的关系图。这里有一个很好的一般概念描述http://en.wikipedia.org/wiki/Fast_Fourier_transform

...并且有几个 Java 库实现了转换,包括 Apache Commons Math - http://commons.apache.org/proper/commons-math/apidocs/org/apache/commons/math3/transform/FastFourierTransformer.html 和JTransform - https://sites.google.com/site/piotrwendykier/software/jtransforms

于 2015-03-01T14:44:12.417 回答
0

为了回答我自己的问题,这就是我最终要做的。(对不起,我花了很长时间才回到这个问题来发布答案。)

double frequency = findFrequency(lowPassFilter(signal));

private double findFrequency(double[] signal) {
    int[] signs = new int[signal.length];
    for (int i = 0; i < signal.length - 1; i++) {
        double diff = signal[i+1] - signal[i];
        if (diff < 0) {
            signs[i] = -1;
        } else if (diff == 0) {
            signs[i] = 0;
        } else {
            signs[i] = 1;
        }
    }
    int[] secondDerivatives = new int[signs.length];
    for (int i = 0; i < signs.length - 1; i++) {
        secondDerivatives[i] = signs[i+1] - signs[i];
    }
    double biggestSoFar = 0.0;
    int indexOfBiggestSoFar = 0;
    for (int i = 0; i < secondDerivatives.length; i++) {
        if (secondDerivatives[i] == -2 && signal[i] > biggestSoFar) {
            biggestSoFar = signal[i];
            indexOfBiggestSoFar = i;
        }
    }
    return 1 / (double)indexOfBiggestSoFar * AudioListener.SAMPLE_RATE;
}

private double[] lowPassFilter(double[] signal) {
    double alpha = 0.15;
    for (int i = 1; i < signal.length; i++ ) {
        signal[i] = signal[i] + alpha * (signal[i] - signal[i-1]);
    }
    return signal;
}
于 2015-10-29T08:38:28.510 回答