I am currently working on a school project involving modelling sound recordings of piano notes. I have used the periodogram function to graph my recording and would like to find the peaks of this graph.

I have read about the FindPeaks function, however, I do not understand how to apply this to the periodogram.

In case it helps, this is the code I’ve been using:

c1 = Import[“C:\\Users\\annak\\Documents\\Sound recordings\\c1_1.wav”]

Periodogram[{c1}, PlotRange -> {{0, 6000}, Automatic}, Frame -> True,

ImageSize -> Medium, PlotLegends -> {Middle C}]

=================

=================

1 Answer

1

=================

From the docs of Periodogram:

plots the squared magnitude of the discrete Fourier transform (power spectrum) of list.

We can perform the same transform manually to make all steps transparent. I will take Middle C note from here. We need to know the sample rate and sample length, so we can scale frequency axis appropriately. You can find more on scaling of FFT here.

spectrum[soundFile_] :=

Module[{data, options, rate, signal, length, fft, freq, result},

{data, options} =

Import[soundFile, #] & /@ {“Data”, “Options”};

rate = “SampleRate” /. options;

signal = First@data;

length = Length@signal;

fft = Normalize@Abs@Fourier@signal;

freq = N@rate/length Range[0, length – 1];

result = Transpose@{freq, fft}

];

sndC = spectrum[

“https://upload.wikimedia.org/wikipedia/commons/b/b9/Audio_\

Frequency_tone%2C_Middle_C%2C_C4%2C_261.63hz.ogg”];

ListPlot[sndC, PlotRange -> {{0, 1000}, Full}, Joined -> True,

GridLines -> Automatic, Frame -> True, PlotLabel -> “Pure C”]

You can see that the peak is at ~260.

You can find it precisely with:

sndC[[#]] & @@@ FindPeaks[sndC[[All, 2]], 0, 0.1]

(* {{261.5, 0.635423}, {43838.5, 0.635423}} *)

And the actual number,as the file name says, is 261.63 Hz.

Keep in mind that the second peak is an alias, so you don’t want to count it.

If you try to apply this technique to a real recording, that will be not ideal due to noise and higher harmonics of your tone, you will need to play with FindPeaks parameters to achieve good results. Some of peak detection ideas are described in Cutting away points that are not resonances.

I tried to record a piano sound with my phone and here is what I got.

When I play Middle C

When I play Middle C and E simultaneously.

If we define our own peak finder:

findPeaks[snd_, cutoff_] := #[[All, 1]] &@ Select[#, (#[[1]] < cutoff) &] &@ (snd[[#]] & @@@FindPeaks[snd[[All, 2]], 2, 0.002]); findPeaks[#, 1000] & /@ {sndC, sndCpiano, sndCEpiano} (*{{261.5}, {261.333, 523.667, 785.333}, {261.333, 329.333, 523., 659.}} *) So for "real" (my piano is electric) instrument we can easily see note C (261.6Hz) and E (329.6Hz) and their higher harmonics.