【问题标题】:Android: Finding fundamental frequency of audio inputAndroid:查找音频输入的基频
【发布时间】:2014-11-25 01:37:53
【问题描述】:

一段时间以来,我一直在努力寻找最佳解决方案来计算使用 AudioRecord 实时捕获的样本的基频。

我查看了一些关于 SO 的示例: This one, and this one 是对我帮助最大的问题,但我仍然不完全理解它们将如何工作以找到基本频率。所以我正在寻找的是更详细的解释,说明我需要做什么才能找到具有样本的基频。

所以,我创建了一个 AudioRecord:

micData = new AudioRecord(audioSource, sampleRate, channel, encoding, bufferSize);
data = new short[bufferSize];

然后开始听:

micData.startRecording();    
sample = micData.read(data,0,bufferSize);

我了解如何创建一个 Complex 数组,但我不知道 FFT.java 中的哪些方法我可以使用 的值来创建这些复数,以及哪个是返回峰值的方法频率。

【问题讨论】:

  • 究竟是什么你不明白?音高估计是一个很大的研究课题。你在记录什么,你的准确性要求是什么?
  • 对不起,不准确,我仍然不明白你是如何得到基频的正在播放)在 FFT 数组中。
  • 谷歌“音高检测或音高估计”。有许多关于各种技术的研究论文(参见:music-ir.org/mirex/wiki/MIREX_HOME)。
  • 您应该在您的问题中添加 Java 标签,因为它与 Android 相关。否则代码块不会得到语法高亮...
  • 如果你强烈坚持使用 FFT,你需要做的是: 1. 用样本值填充复数数组的实部 2. 调用你的 FFT 计算方法,然后你有虚部也是。 3. 将每个点的大小计算为 Re^2 + Im^2 (如果您想要 DB 中的度量,您需要另外计算 10*log() ) 4. 现在您的结果数组是 SamplingFreq/numOfSamples 给出的频谱你频率点之间的距离。对于结果数组中的索引 i,频率为 (i+1)*SamplingFreq/numOfSamples。

标签: java audio fft frequency analysis


【解决方案1】:

阅读您的问题,我发现您还不确定是否要使用 FFT。这很好,因为我不建议只使用 FFT。保持在时域中,使用自相关或 AMDF,如果您想要更准确的结果,则可以使用 FFT 作为附加组件。

这是我用于计算基频的 Java 代码。我写了 cmets 是因为你说你还是不明白这个过程。

public double getPitchInSampleRange(AudioSamples as, int start, int end) throws Exception {
    //If your sound is musical note/voice you need to limit the results because it wouldn't be above 4500Hz or bellow 20Hz
    int nLowPeriodInSamples = (int) as.getSamplingRate() / 4500;
    int nHiPeriodInSamples = (int) as.getSamplingRate() / 20;

    //I get my sample values from my AudioSamples class. You can get them from wherever you want
    double[] samples = Arrays.copyOfRange((as.getSamplesChannelSegregated()[0]), start, end);
    if(samples.length < nHiPeriodInSamples) throw new Exception("Not enough samples");

    //Since we're looking the periodicity in samples, in our case it won't be more than the difference in sample numbers
    double[] results = new double[nHiPeriodInSamples - nLowPeriodInSamples];

    //Now you iterate the time lag
    for(int period = nLowPeriodInSamples; period < nHiPeriodInSamples; period++) {
        double sum = 0;
        //Autocorrelation is multiplication of the original and time lagged signal values
        for(int i = 0; i < samples.length - period; i++) {
            sum += samples[i]*samples[i + period];
        }
        //find the average value of the sum
        double mean = sum / (double)samples.length;
        //and put it into results as a value for some time lag. 
        //You subtract the nLowPeriodInSamples for the index to start from 0.
        results[period - nLowPeriodInSamples] = mean;
    }
    //Now, it is obvious that the mean will be highest for time lag equal to the periodicity of the signal because in that case
    //most of the positive values will be multiplied with other positive and most of the negative values will be multiplied with other
    //negative resulting again as positive numbers and the sum will be high positive number. For example, in the other case, for let's say half period
    //autocorrelation will multiply negative with positive values resulting as negatives and you will get low value for the sum.        
    double fBestValue = Double.MIN_VALUE;
    int nBestIndex = -1; //the index is the time lag
    //So
    //The autocorrelation is highest at the periodicity of the signal
    //The periodicity of the signal can be transformed to frequency
    for(int i = 0; i < results.length; i++) {
        if(results[i] > fBestValue) {
            nBestIndex = i; 
            fBestValue = results[i]; 
        }
    }
    //Convert the period in samples to frequency and you got yourself a fundamental frequency of a sound
    double res = as.getSamplingRate() / (nBestIndex + nLowPeriodInSamples)

    return res;
}

您还需要了解的是,自相关方法中存在常见的八度音阶错误,尤其是在信号中有噪声的情况下。根据我的经验,钢琴声音或吉他都不是问题。错误很少见。但人声可能是……

【讨论】:

  • 非常感谢,这真的很有帮助。在您的代码中,nLowPeriodInSamples 和 nHiPeriodInSamples 不能直接设置为 4500 和 20 吗?在双数组(样本)中,我可以使用 AudioRecord,对吗?另外,bestIndices[i] 到底是什么?
  • No.... 20 和 4500 是以赫兹为单位的频率。由于自相关是时域中的方法,您需要将频率限制转换为样本周期中的时间限制
  • 对,对不起,这很明显。另外,在双数组(samples)上,我可以按原样使用 AudioRecord 对象,还是需要将其转换为数组?
  • 如您所愿...您只需要访问样本值,将它们相乘并将它们放入结果数组中。如果您不想使用 double[] 示例数组,请稍微转换代码并使用您的类...
  • 谢谢,好的。还有一件事,我看到您使用 for() 来测试最佳值,但是 bestIndices[i] 来自哪里?
猜你喜欢
  • 1970-01-01
  • 2012-01-09
  • 2014-05-06
  • 2016-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-17
相关资源
最近更新 更多