From d845d87e2c7fa7afb2ac0ecb239ae1cc0b341937 Mon Sep 17 00:00:00 2001 From: Robin Haberkorn Date: Tue, 19 Dec 2023 22:19:50 +0300 Subject: added example for pitch tracking via FFT --- COPYING | 3 +++ examples/fft.ipynb | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++-- examples/fft.lua | 17 ++++++++++++++ examples/opera.flac | Bin 0 -> 895467 bytes 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 examples/opera.flac diff --git a/COPYING b/COPYING index 1fb6c6b..a119d7c 100644 --- a/COPYING +++ b/COPYING @@ -1,6 +1,9 @@ examples/haiku.flac is licensed under CC BY-NC 4.0 Deed and based on https://freesound.org/people/klankbeeld/sounds/237856/ +examples/opera.flac is licensed under CC BY-NC 4.0 Deed and based on +https://freesound.org/people/Ramston/sounds/262274/ + All the other source files are licensed under the GPLv3: GNU GENERAL PUBLIC LICENSE diff --git a/examples/fft.ipynb b/examples/fft.ipynb index d38cda3..0ac3e96 100644 --- a/examples/fft.ipynb +++ b/examples/fft.ipynb @@ -2144,10 +2144,68 @@ "end):IFFT(1024):play()" ] }, + { + "cell_type": "markdown", + "id": "178a3734-815b-44fa-bd1f-ecc4b45d567b", + "metadata": {}, + "source": [ + "Pitch Tracking. This however is [not so easy to get right](http://blog.bjornroche.com/2012/07/frequency-detection-using-fft-aka-pitch.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "eb6f6047-c6f0-4155-9029-2f643cdc9c04", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING: Buffer underrun detected\n" + ] + } + ], + "source": [ + "opera = SndfileStream(\"examples/opera.flac\")\n", + "opera:play()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a11139f5-bb3e-4dc1-aa04-eede12abe0d3", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING: Buffer underrun detected\n", + "WARNING: Buffer underrun detected\n" + ] + } + ], + "source": [ + "opera:LPF(10000):FFT(1024, Hanning):map(function(spectrum)\n", + "\tlocal size = (#spectrum-1)*2\n", + "\tlocal peak_i, peak_val\n", + "\tfor i = 1, #spectrum do\n", + "\t\t-- We don't have to take the square root to find the peak\n", + "\t\tlocal val = spectrum[i].re^2 + spectrum[i].im^2\n", + "\t\tif not peak_val or val > peak_val then\n", + "\t\t\tpeak_i, peak_val = i, val\n", + "\t\tend\n", + "\tend\n", + "\t-- Return peak as frequency\n", + "\treturn tostream((peak_i-1)*samplerate/size):sub(1, size)\n", + "end):ravel():div(10000):crush(7):mul(10000):SqrOsc():crush():play()" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "fc38550e-835c-47a8-a792-7164109dafee", + "id": "7f43540f-2b92-4e7f-ac8d-160223518a08", "metadata": {}, "outputs": [], "source": [] @@ -2163,7 +2221,7 @@ "file_extension": ".lua", "mimetype": "text/x-lua", "name": "lua", - "version": "5.1" + "version": "n/a" } }, "nbformat": 4, diff --git a/examples/fft.lua b/examples/fft.lua index 5930f46..1f8687d 100644 --- a/examples/fft.lua +++ b/examples/fft.lua @@ -36,3 +36,20 @@ noisy:FFT(1024, Hamming):map(function(spectrum) end return spectrum end):IFFT(1024):play() + +-- Pitch Tracking +-- See also: http://blog.bjornroche.com/2012/07/frequency-detection-using-fft-aka-pitch.html +opera = SndfileStream("examples/opera.flac") +opera:LPF(10000):FFT(1024, Hanning):map(function(spectrum) + local size = (#spectrum-1)*2 + local peak_i, peak_val + for i = 1, #spectrum do + -- We don't have to take the square root to find the peak + local val = spectrum[i].re^2 + spectrum[i].im^2 + if not peak_val or val > peak_val then + peak_i, peak_val = i, val + end + end + -- Return peak as frequency + return tostream((peak_i-1)*samplerate/size):sub(1, size) +end):ravel():div(10000):crush(7):mul(10000):SqrOsc():crush():play() diff --git a/examples/opera.flac b/examples/opera.flac new file mode 100644 index 0000000..e4bce4c Binary files /dev/null and b/examples/opera.flac differ -- cgit v1.2.3