Harmonic Product Spectrum doesn't return correct frequency

Viewed 18

I have some code here, but the software seems to spit out incorrect values for some notes. For example, it tells me my frequency is "947" for a lower note and "775" for a slightly higher note. From there though, it seems to generally work. Why could this be?

Here's my code:

public static void main(final String[] args) throws Exception {
    final AudioFormat format = new AudioFormat(
            AudioFormat.Encoding.PCM_SIGNED,
            44100,
            16,
            1,
            2,
            44100,
            false
    );

    final DataLine.Info info = new DataLine.Info(
            TargetDataLine.class,
            format
    );

    final TargetDataLine targetLine = (TargetDataLine) AudioSystem.getLine(info);
    targetLine.open();
    targetLine.start();

    final AudioInputStream audioInputStream = new AudioInputStream(targetLine);

    final byte[] buf = new byte[1024];
    final int numberOfSamples = buf.length / format.getFrameSize();
    final JavaFFT fft = new JavaFFT(numberOfSamples);

    while (true) {
        audioInputStream.read(buf);

        final float[] samples = decode(buf, format);
        final float[][] transformed = fft.transform(samples);
        final float[] real = transformed[0];
        final float[] imaginary = transformed[1];
        final double[] magnitudes = toMagnitudes(real, imaginary);

        // do something with magnitudes

        double[] hps = HPS(magnitudes);
        int maxIndex = maxIndex(hps);

        int freq = (int) (maxIndex * format.getSampleRate() / numberOfSamples);

        System.out.println(freq);
    }
}


private static final int HPS_ORDER = 2;

// Harmonic Product Spectrum
private static double[] HPS(double[] magnitudes) {
    double[] hps = new double[magnitudes.length];

    int hpsLength = magnitudes.length / (HPS_ORDER+1);
    for (int i = 0; i < hps.length; i++) {
        if (i < hpsLength) {
            hps[i] = magnitudes[i];
        } else {
            hps[i] = Float.NEGATIVE_INFINITY;
        }
    }

    for (int i = 1; i <= HPS_ORDER; i++) {
        int dsFactor = i+1;
        for (int j = 0; j < hpsLength; j++) {
            float avg = 0;
            for (int k = 0; k < dsFactor; k++) {
                avg += magnitudes[j*dsFactor + k];
            }
            hps[j] += avg / dsFactor;
        }
    }

    return hps;
}

I could be missing something very obvious, but I don't see it. If anyone can offer assistance, it would be greatly appreciated!

0 Answers
Related