1
$\begingroup$

I've whipped together a small example that tries to use the Harmonic Product Spectrum Algorithm to extract the pitch of a simple sine wave. I'm not sure about my implementation and if i understood everything completely, that's why i'm hoping for some helping hand here.

The code is here: https://gist.github.com/akuehntopf/4da9bced2cb88cfa2d19 (You'll need JTransform and JavaFX to compile, though).

Please see what happens with the data during the processing using the following graphs. Note: I'm using a sine wave with 160Hz and a sample rate of 8000. The duration of the sound data is 1 second.

enter image description here

First the sine wave is displayed (see first image).

Second, i'm applying a hamming window to the data (second image)

Then i calculate the power spectrum of the data as per

private float[] powerSpectrum(float[] window) {
    float[] powerSpectrum = new float[window.length];
    float[] fftBuffer = new float[window.length * 2 + 1];
    System.arraycopy(window, 0, fftBuffer, 0, window.length);
    FloatFFT_1D fft = new FloatFFT_1D(window.length);
    fft.realForward(fftBuffer);

    for (int i=0; i < fftBuffer.length / 2 - 1; i++) {
        float real = fftBuffer[2*i];
        float imag = fftBuffer[2*i+1];

        powerSpectrum[i] = (float)Math.sqrt(real*real + imag * imag); 
    }

    return powerSpectrum;
}

Which gives me the spectrum in the third graph.

Observe that the peak is approximately at the right spot already, but we go for HPS, so continue (i'm not really sure about everything from here on):

Next, i'm multiplying the signal with its compressed form several times (with increasing compression)...

// 4. Compress
float[] spectrumCopy = new float[spectrum.length];
System.arraycopy(spectrum, 0, spectrumCopy, 0, spectrum.length);        
for (int compression = 2; compression < 4; compression++) {
    for (int i = 1; i < spectrum.length; i++) {
        spectrum[i] = spectrum[i] * getCompressedSample(spectrumCopy, 1, compression, i);
    }
}

it uses the function

private float getCompressedSample(float[] buffer, int offset, int compression, int loc) {
    if (offset + loc * compression < buffer.length) {
        return buffer[offset + loc * compression];
    }
    return 0;
}

i found it somewhere on the interwebs. But my understanding is that we compress the power spectrum n times while taking only every 2, then only every 3 and so on samples from the power spectrum. The original spectrum is then multiplied with each of those compressed spectra.

The result i get is the fourth graph

Observe that the peaks have changed.

From my understanding the next step would be to find the bin with the highest peak, interpolate (i use quadratic interpolation) and recalculate the pitch frequency using this formula:

private float getFrequencyForIndex(int index, int size, int rate) {
    float freq = (float)index * (float)rate / (float)size;
    return freq;
}

However this gives me wrong results. I'm pretty much stuck. Any help with this topic would be very much appreciated! Thanks in advance!

For the next step i'm hoping to be able to extract pitch frequency data from a musical instrument (guitar/ukulele), but of course first things first :-)

$\endgroup$
1
  • $\begingroup$ sine waves don't have harmonics $\endgroup$
    – endolith
    Aug 21, 2015 at 20:43

2 Answers 2

3
$\begingroup$

The Harmonic Product Spectrum pitch estimation method, using pure multiplication weighting, only works well for signals that have a full set of harmonics with sufficient magnitudes. Before using HPS, one should either validate that the signal in question meets that criteria, or add a suitably large non-zero floor to all harmonic magnitudes being multiplied together. Otherwise a pure sinewave with zero harmonics will simply disappear from the result after being multiplied by a zero magnitude.

Guitar and ukulele sounds usually have a far richer set of harmonics than test sine waves.

$\endgroup$
1
$\begingroup$

Sorry, but it looks like you might not be noticing the important difference between a 'frequency' of vibration and a musical 'pitch'.

A 'pitch' is not a single vibration, such as a sine wave, but is a composite of multiple sound vibrations occurring at different mathematically related frequencies. The elements of this composite of vibrations at differing frequencies are referred to as harmonics or partials. For instance, if we press the Middle C key on the piano, the individual frequencies of the composite's harmonics will start at 261.6 Hz as the fundamental frequency, 523 Hz would be the 2nd Harmonic, 785 Hz would be the 3rd Harmonic, 1046 Hz would be the 4th Harmonic, etc. The later harmonics are integer multiples of the fundamental frequency, 261.6 Hz ( ex: 2 x 261.6 = 523, 3 x 261.6 = 785, 4 x 261.6 = 1046 ).

Below is the image of a Logarithmic DFT for 3 seconds of a guitar solo on a polyphonic mp3 recording. It shows how the harmonics appear for individual notes on a guitar, while playing a solo. For each note on this Logarithmic DFT we can see its multiple harmonics extending vertically, because each harmonic will have the same time-width. enter image description here

This Wikipedia article gives a good background into the concept of 'pitch' as it pertains to music. https://en.wikipedia.org/wiki/Transcription_(music)#Pitch_detection

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.