{"id":13430765,"url":"https://github.com/sevagh/pitch-detection","last_synced_at":"2025-05-15T13:07:13.076Z","repository":{"id":31133206,"uuid":"34692899","full_name":"sevagh/pitch-detection","owner":"sevagh","description":"autocorrelation-based O(NlogN) pitch detection","archived":false,"fork":false,"pushed_at":"2025-01-07T16:40:21.000Z","size":2747,"stargazers_count":598,"open_issues_count":2,"forks_count":70,"subscribers_count":27,"default_branch":"master","last_synced_at":"2025-05-10T16:13:16.304Z","etag":null,"topics":["autocorrelation","dsp","fft","mpm","pitch-detection","pitch-estimation","pitch-tracking","pyin","yin"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sevagh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-04-27T21:44:44.000Z","updated_at":"2025-05-09T19:34:04.000Z","dependencies_parsed_at":"2023-01-14T18:24:28.677Z","dependency_job_id":null,"html_url":"https://github.com/sevagh/pitch-detection","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sevagh%2Fpitch-detection","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sevagh%2Fpitch-detection/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sevagh%2Fpitch-detection/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sevagh%2Fpitch-detection/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sevagh","download_url":"https://codeload.github.com/sevagh/pitch-detection/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254346624,"owners_count":22055808,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["autocorrelation","dsp","fft","mpm","pitch-detection","pitch-estimation","pitch-tracking","pyin","yin"],"created_at":"2024-07-31T02:00:57.552Z","updated_at":"2025-05-15T13:07:08.063Z","avatar_url":"https://github.com/sevagh.png","language":"C++","funding_links":[],"categories":["C++"],"sub_categories":[],"readme":"# pitch-detection\n\nAutocorrelation-based C++ pitch detection algorithms with **O(nlogn) or lower** running time:\n\n* McLeod pitch method - [2005 paper](http://miracle.otago.ac.nz/tartini/papers/A_Smarter_Way_to_Find_Pitch.pdf) - [visualization](./misc/mcleod)\n* YIN(-FFT) - [2002 paper](http://audition.ens.fr/adc/pdf/2002_JASA_YIN.pdf) - [visualization](./misc/yin)\n* Probabilistic YIN - [2014 paper](https://www.eecs.qmul.ac.uk/~simond/pub/2014/MauchDixon-PYIN-ICASSP2014.pdf)\n* Probabilistic MPM - [my own invention](./misc/probabilistic-mcleod)\n\nThe size of the FFT used is the same as the size of the input waveform, such that the output is a single pitch for the entire waveform.\n\nLibrosa (among other libraries) uses the STFT to create _frames_ of the input waveform, and applies pitch tracking to each frame with a fixed FFT size (typically 2048 or some other power of two). If you want to track the temporal evolution of pitches in sub-sections of the waveform, you have to handle the waveform splitting yourself (look at [wav_analyzer](./wav_analyzer/wav_analyzer.cpp) for more details).\n\n## :postal_horn: Latest news :newspaper: \n\nDec 27, 2023 :santa: release:\n* Removed SWIPE' algorithm\n    * It is not based on autocorrelation, I skipped it in all of the tests, and my implementation was basically copy-pasted from [kylebgorman/swipe](https://github.com/kylebgorman/swipe): just use their code instead!\n* Fix autocorrelation (in YIN and MPM) for power-of-two sizes in FFTS (see [ffts issue #65](https://github.com/anthonix/ffts/issues/65)) by using r2c/c2r transforms (addresses [bug #72](https://github.com/sevagh/pitch-detection/issues/72) reported by jeychenne)\n* Fix PYIN bugs to pass all test cases (addresses jansommer's comments in [pull-request #84](https://github.com/sevagh/pitch-detection/pull/84#issuecomment-1843623594))\n* Added many more unit tests, all passing (228/228)\n\n## Other programming languages\n\n* Go: [Go implementation of YIN](./misc/yin) in this repo (for tutorial purposes)\n* Rust: [Rust implementation of MPM](./misc/mcleod) in this repo (for tutorial purposes)\n* Python: [transcribe](https://github.com/sevagh/transcribe) is a Python version of MPM for a proof-of-concept of primitive pitch transcription\n* Javascript (WebAssembly): [pitchlite](https://github.com/sevagh/pitchlite) has WASM modules of MPM/YIN running at realtime speeds in the browser, and also introduces sub-chunk detection to return the overall pitch of the chunk and the temporal sub-sequence of pitches within the chunk\n\n## Usage\n\nSuggested usage of this library can be seen in the utility [wav_analyzer](./wav_analyzer) which divides a wav file into chunks of 0.01s and checks the pitch of each chunk. Sample output of wav_analyzer:\n\n```\nstd::vector\u003cfloat\u003e chunk; // chunk of audio\n\nfloat pitch_mpm = pitch::mpm(chunk, sample_rate);\nfloat pitch_yin = pitch::yin(chunk, sample_rate);\n```\n\n## Tests\n\n### Unit tests\n\nThere are unit tests that use sinewaves (both generated with `std::sin` and with [librosa.tone](https://librosa.org/doc/main/generated/librosa.tone.html)), and instrument tests using txt files containing waveform samples from the [University of Iowa MIS](http://theremin.music.uiowa.edu/MIS.html) recordings:\n```\n$ ./build/pitch_tests\nRunning main() from ./googletest/src/gtest_main.cc\n[==========] Running 228 tests from 22 test suites.\n[----------] Global test environment set-up.\n[----------] 2 tests from MpmSinewaveTestManualAllocFloat\n[ RUN      ] MpmSinewaveTestManualAllocFloat.OneAllocMultipleFreqFromFile\n[       OK ] MpmSinewaveTestManualAllocFloat.OneAllocMultipleFreqFromFile (38 ms)\n...\n[----------] 5 tests from YinInstrumentTestFloat\n...\n[ RUN      ] YinInstrumentTestFloat.Acoustic_E2_44100\n[       OK ] YinInstrumentTestFloat.Acoustic_E2_44100 (1 ms)\n[ RUN      ] YinInstrumentTestFloat.Classical_FSharp4_48000\n[       OK ] YinInstrumentTestFloat.Classical_FSharp4_48000 (58 ms)\n[----------] 5 tests from YinInstrumentTestFloat (174 ms total)\n...\n[----------] 5 tests from MpmInstrumentTestFloat\n[ RUN      ] MpmInstrumentTestFloat.Violin_A4_44100\n[       OK ] MpmInstrumentTestFloat.Violin_A4_44100 (61 ms)\n[ RUN      ] MpmInstrumentTestFloat.Piano_B4_44100\n[       OK ] MpmInstrumentTestFloat.Piano_B4_44100 (24 ms)\n\n...\n[==========] 228 tests from 22 test suites ran. (2095 ms total)\n[  PASSED  ] 228 tests.\n```\n\n### Degraded audio tests\n\nAll testing files are [here](./misc/degraded_audio_tests) - the progressive degradations are described by the respective numbered JSON file, generated using [audio-degradation-toolbox](https://github.com/sevagh/audio-degradation-toolbox). The original clip is a Viola playing E3 from the [University of Iowa MIS](http://theremin.music.uiowa.edu/MIS.html). The results come from parsing the output of wav_analyzer to count how many 0.1s slices of the input clip were in the ballpark of the expected value of 164.81 - I considered anything 160-169 to be acceptable:\n\n| Degradation level | MPM # correct | YIN # correct |\n| ------------- | ------------- | ------------- |\n| 0 | 26 | 22 |\n| 1 | 23 | 21 |\n| 2 | 19 | 21 |\n| 3 | 18 | 19 |\n| 4 | 19 | 19 |\n| 5 | 18 | 19 |\n\n## Build and install\n\nYou need Linux, cmake, and gcc (I don't officially support other platforms). The library depends on [ffts](https://github.com/anthonix/ffts) and [mlpack](https://www.mlpack.org/). The tests depend on [libnyquist](https://github.com/ddiakopoulos/libnyquist), [googletest](https://github.com/google/googletest), and [google benchmark](https://github.com/google/benchmark). Dependency graph:\n![dep-graph](./misc/deps.png)\n\nBuild and install with cmake:\n```bash\ncmake -S . -B build -DCMAKE_BUILD_TYPE=Release\ncmake --build \"build\"\n\n# install to your system\ncd build \u0026\u0026 make install\n\n# run tests and benches \n./build/pitch_tests\n./build/pitch_bench\n\n# run wav_analyzer\n./build/wav_analyzer\n```\n\n### Docker\n\nTo simplify the setup, there's a [Dockerfile](./Dockerfile) that sets up a Ubuntu container with all the dependencies for compiling the library and running the included tests and benchmarks:\n```bash\n# build\n$ docker build --rm --pull -f \"Dockerfile\" -t pitchdetection:latest \".\"\n$ docker run --rm --init -it pitchdetection:latest\n```\n**n.b.** You can pull the [esimkowitz/pitchdetection](https://hub.docker.com/repository/docker/esimkowitz/pitchdetection) image from DockerHub, but I can't promise that it's up-to-date.\n\n## Detailed usage\n\nRead the [header](./include/pitch_detection.h) and the example [wav_analyzer program](./wav_analyzer).\n\nThe namespaces are `pitch` and `pitch_alloc`. The functions and classes are templated for `\u003cdouble\u003e` and `\u003cfloat\u003e` support.\n\nThe `pitch` namespace functions perform automatic buffer allocation, while `pitch_alloc::{Yin, Mpm}` give you a reusable object (useful for computing pitch for multiple uniformly-sized buffers):\n\n```c++\n#include \u003cpitch_detection.h\u003e\n\nstd::vector\u003cdouble\u003e audio_buffer(8192);\n\ndouble pitch_yin = pitch::yin\u003cdouble\u003e(audio_buffer, 48000);\ndouble pitch_mpm = pitch::mpm\u003cdouble\u003e(audio_buffer, 48000);\ndouble pitch_pyin = pitch::pyin\u003cdouble\u003e(audio_buffer, 48000);\ndouble pitch_pmpm = pitch::pmpm\u003cdouble\u003e(audio_buffer, 48000);\n\npitch_alloc::Mpm\u003cdouble\u003e ma(8192);\npitch_alloc::Yin\u003cdouble\u003e ya(8192);\n\nfor (int i = 0; i \u003c 10000; ++i) {\n        auto pitch_yin = ya.pitch(audio_buffer, 48000);\n        auto pitch_mpm = ma.pitch(audio_buffer, 48000);\n        auto pitch_pyin = ya.probabilistic_pitch(audio_buffer, 48000);\n        auto pitch_pmpm = ma.probabilistic_pitch(audio_buffer, 48000);\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsevagh%2Fpitch-detection","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsevagh%2Fpitch-detection","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsevagh%2Fpitch-detection/lists"}