Giter VIP home page Giter VIP logo

pitch-detection's Introduction

pitch-detection

Autocorrelation-based C++ pitch detection algorithms with O(nlogn) or lower running time:

The 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.

Librosa (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 for more details).

๐Ÿ“ฏ Latest news ๐Ÿ“ฐ

Dec 27, 2023 ๐ŸŽ… release:

  • Removed SWIPE' algorithm
    • It is not based on autocorrelation, I skipped it in all of the tests, and my implementation was basically copy-pasted from kylebgorman/swipe: just use their code instead!
  • Fix autocorrelation (in YIN and MPM) for power-of-two sizes in FFTS (see ffts issue #65) by using r2c/c2r transforms (addresses bug #72 reported by jeychenne)
  • Fix PYIN bugs to pass all test cases (addresses jansommer's comments in pull-request #84)
  • Added many more unit tests, all passing (228/228)

Other programming languages

  • Go: Go implementation of YIN in this repo (for tutorial purposes)
  • Rust: Rust implementation of MPM in this repo (for tutorial purposes)
  • Python: transcribe is a Python version of MPM for a proof-of-concept of primitive pitch transcription
  • Javascript (WebAssembly): 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

Usage

Suggested usage of this library can be seen in the utility wav_analyzer which divides a wav file into chunks of 0.01s and checks the pitch of each chunk. Sample output of wav_analyzer:

std::vector<float> chunk; // chunk of audio

float pitch_mpm = pitch::mpm(chunk, sample_rate);
float pitch_yin = pitch::yin(chunk, sample_rate);

Tests

Unit tests

There are unit tests that use sinewaves (both generated with std::sin and with librosa.tone), and instrument tests using txt files containing waveform samples from the University of Iowa MIS recordings:

$ ./build/pitch_tests
Running main() from ./googletest/src/gtest_main.cc
[==========] Running 228 tests from 22 test suites.
[----------] Global test environment set-up.
[----------] 2 tests from MpmSinewaveTestManualAllocFloat
[ RUN      ] MpmSinewaveTestManualAllocFloat.OneAllocMultipleFreqFromFile
[       OK ] MpmSinewaveTestManualAllocFloat.OneAllocMultipleFreqFromFile (38 ms)
...
[----------] 5 tests from YinInstrumentTestFloat
...
[ RUN      ] YinInstrumentTestFloat.Acoustic_E2_44100
[       OK ] YinInstrumentTestFloat.Acoustic_E2_44100 (1 ms)
[ RUN      ] YinInstrumentTestFloat.Classical_FSharp4_48000
[       OK ] YinInstrumentTestFloat.Classical_FSharp4_48000 (58 ms)
[----------] 5 tests from YinInstrumentTestFloat (174 ms total)
...
[----------] 5 tests from MpmInstrumentTestFloat
[ RUN      ] MpmInstrumentTestFloat.Violin_A4_44100
[       OK ] MpmInstrumentTestFloat.Violin_A4_44100 (61 ms)
[ RUN      ] MpmInstrumentTestFloat.Piano_B4_44100
[       OK ] MpmInstrumentTestFloat.Piano_B4_44100 (24 ms)

...
[==========] 228 tests from 22 test suites ran. (2095 ms total)
[  PASSED  ] 228 tests.

Degraded audio tests

All testing files are here - the progressive degradations are described by the respective numbered JSON file, generated using audio-degradation-toolbox. The original clip is a Viola playing E3 from the University of Iowa MIS. 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:

Degradation level MPM # correct YIN # correct
0 26 22
1 23 21
2 19 21
3 18 19
4 19 19
5 18 19

Build and install

You need Linux, cmake, and gcc (I don't officially support other platforms). The library depends on ffts and mlpack. The tests depend on libnyquist, googletest, and google benchmark. Dependency graph: dep-graph

Build and install with cmake:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build "build"

# install to your system
cd build && make install

# run tests and benches 
./build/pitch_tests
./build/pitch_bench

# run wav_analyzer
./build/wav_analyzer

Docker

To simplify the setup, there's a Dockerfile that sets up a Ubuntu container with all the dependencies for compiling the library and running the included tests and benchmarks:

# build
$ docker build --rm --pull -f "Dockerfile" -t pitchdetection:latest "."
$ docker run --rm --init -it pitchdetection:latest

n.b. You can pull the esimkowitz/pitchdetection image from DockerHub, but I can't promise that it's up-to-date.

Detailed usage

Read the header and the example wav_analyzer program.

The namespaces are pitch and pitch_alloc. The functions and classes are templated for <double> and <float> support.

The 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):

#include <pitch_detection.h>

std::vector<double> audio_buffer(8192);

double pitch_yin = pitch::yin<double>(audio_buffer, 48000);
double pitch_mpm = pitch::mpm<double>(audio_buffer, 48000);
double pitch_pyin = pitch::pyin<double>(audio_buffer, 48000);
double pitch_pmpm = pitch::pmpm<double>(audio_buffer, 48000);

pitch_alloc::Mpm<double> ma(8192);
pitch_alloc::Yin<double> ya(8192);

for (int i = 0; i < 10000; ++i) {
        auto pitch_yin = ya.pitch(audio_buffer, 48000);
        auto pitch_mpm = ma.pitch(audio_buffer, 48000);
        auto pitch_pyin = ya.probabilistic_pitch(audio_buffer, 48000);
        auto pitch_pmpm = ma.probabilistic_pitch(audio_buffer, 48000);
}

pitch-detection's People

Contributors

esimkowitz avatar forgemistress avatar fusionhuh avatar sevagh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pitch-detection's Issues

much faster mpm auto-correlation

Today I met with a friend, Ron Schafer, co-author of the textbook "Discrete-Time Signal Processing" to get a better understanding of how the mpm auto-correlation works. While reviewing the code he figured out that it is possible to completely eliminate one of the three FFTs in acorr_r() and still get identical results in a much shorter time! Here's the new code, which I have validated. I suggest you try it and if you agree, replace your existing acorr_r code with this improvement.
int N = signal.size();
int N2 = 2 * N;

auto fft_forward = ffts_init_1d(N2, FFTS_FORWARD);
auto fft_backward = ffts_init_1d(N2, FFTS_BACKWARD);

std::vector<std::complex<float>> signalb_ext(N2);

for (int i = 0; i < N; i++)
    signalb_ext[i] = {float(signal[i]), 0.0};

std::vector<std::complex<float>> outb(N2);
std::vector<std::complex<float>> out(N2);
std::vector<std::complex<float>> result(N2);

ffts_execute(fft_forward, signalb_ext.data(), outb.data());

std::complex<float> scale = {1.0f / (float)N2, 0.0};
for (int i = 0; i < N; ++i) 
     out[i] = outb[i] * std::conj(outb[i]) * scale;

ffts_execute(fft_backward, out.data(), result.data());

ffts_free(fft_forward);
ffts_free(fft_backward);

std::vector<double> normalized_result(N, 0.0);
for (int i = 0; i < N; ++i) 
     normalized_result[i] = std::real(result[i]) / std::real(result[0]);
return normalized_result;

fftw3.h: No such file or directory

Trying to compile the library, but getting

In file included from src/mpm.cpp:12:0:
src/./xcorr.hpp:8:19: fatal error: fftw3.h: No such file or directory

Convert this to a library

Better to have this as a library than an executable.

Make sinewave and mp3_file as sample programs in samples/ or examples/ directory.

xcorr: Calculate power-of-2 sized FFTs

First of all, thank you very much for your code, and for licensing it MIT :)
In the xcorr function of your libxcorr fork, you calculate three FFTs of size 2 * N - 1, which is always an odd number. Most FFT libraries, including fftw3, are optimized for FFT sizes that are powers of 2. When implementing MPM pitch estimation based on your code, but exchanging the FFT library for the non-GPL licensed KissFFT, this was very noticeable to me.

I changed the xcorr and normalized_square_difference functions to set N2 = 2 * N, and the first xcorr zero-padding memcpy call to

std::memcpy(signala_ext.data() + N, signala.data(), sizeof(std::complex<double>) * N);

, effectively duplicating the center sample. Since my N was a power of 2, this change resulted in a FFT calculation time of less than 1/10th the original time.

This change did not affect the results of the pitch estimation, when comparing the results of MPM pitch estimation over 4096 samples of data at a precision of 0.001.
I'm not sure if the change leads to mathematically equivalent results, but the resulting increase in performance is definitely worth it to me. Perhaps you have a better understanding of the algorithms and can prove whether it makes a difference.

add yin

I've always wanted to try YIN.

Could you give an example code to detect pitch?

I have compile your code on ubuntu:18.04, But I do not know how to use it.

I look up the file public header file, but I still confuse about the usage of it.

So could you give an example code to read and process the .wav/.mp3 file and the compile command of it?

Thank you very much.

Audit buffer/vector allocations and isolate them

Perhaps a user has a repetitive style of buffer, i.e. size 8092 - then, doing:

std::vector<std::vector> all_audio_chunks(N);

...

for (auto chunk : all_audio_chunks) {
    pitch::mpm(chunk, 48000);
}

This will allocate the NSDF vector N times.

Possible solution 1: classes

std::vector<std::vector> all_audio_chunks(N);
auto mpm = pitch::MpmDetector(8092, 48000);
...

for (auto chunk : all_audio_chunks) {
    mpm.analyze(chunk);
}

Possible solution 2: pass a reusable buffer (feels more C-ish than C++):

std::vector<std::vector> all_audio_chunks(N);
audo nsdf = std::vector(N);

...

for (auto chunk : all_audio_chunks) {
    pitch::mpm(chunk, 48000, nsdf);
}

xcorr.h missing

Hello,

I think you forgot to push the xcorr.h and relative files into the repo. On file autocorrelation.cpp line 8:

extern "C" {
#include <xcorr.h>
}

Making autocorrelation more efficient

Since the second half of a forward DFT's complex output vector contains the complex conjugates of the first half in reverse order, maybe the autocorrelation in MPM only needs to iterate over the first half of the two output vectors, thus reducing the number of complex multiplications?
I propose changing these lines in acorr_r():
for (int i = 0; i < N2; ++i)
out[i] = outa[i] * std::conj(outb[i]) * scale;

to this (though I haven't tested it):
for (int i = 0; i < N2/2; ++i) {
out[i] = outa[i] * std::conj(outb[i]) * scale;
out[N2-1 - i ] = std::conj(out[i]);
}

Are my assumptions correct? I don't claim to be an FFT expert. It's just a thought that occurred to me.

wrong results with different buffer sizes in the sinewave example

I tried to play with the sinewave example, changing the buffer size and sampling rate. Perhaps I'm missing something obvious, but it looks like depending on the buffer size, the results are wrong. Here is the test I ran, using the current master version of FFTS:

    std::cerr << "Expected pitch: 250 Hz" << std::endl;

    auto d = sinewave(2048, 250, 16000);
    auto p = pitch::yin<double>(d, 16000);
    std::cerr << "pitch with 2048 samples @ 16000 Hz: " << p << " Hz" << std::endl;

    d = sinewave(256, 250, 16000);
    p = pitch::yin<double>(d, 16000);
    std::cerr << "pitch with 256 samples @ 16000 Hz: " << p << " Hz" << std::endl;
    
    d = sinewave(4096, 250, 16000);
    p = pitch::yin<double>(d, 16000);
    std::cerr << "pitch with 4096 samples @ 16000 Hz: " << p << " Hz" << std::endl;

    d = sinewave(2048, 250, 22050);
    p = pitch::yin<double>(d, 22050);
    std::cerr << "pitch with 2048 samples @ 22050 Hz: " << p << " Hz" << std::endl;

    d = sinewave(4096, 250, 22050);
    p = pitch::yin<double>(d, 22050);
    std::cerr << "pitch with 4096 samples @ 22050 Hz: " << p << " Hz" << std::endl;

    d = sinewave(8092, 250, 22050);
    p = pitch::yin<double>(d, 22050);
    std::cerr << "pitch with 8092 samples @ 22050 Hz: " << p << " Hz" << std::endl;

    d = sinewave(8092, 250, 48000);
    p = pitch::yin<double>(d, 48000);
    std::cerr << "pitch with 8092 samples @ 4800 Hz: " << p << " Hz" << std::endl;

    d = sinewave(4046, 250, 22050);
    p = pitch::yin<double>(d, 22050);
    std::cerr << "pitch with 4046 samples @ 22050 Hz: " << p << " Hz" << std::endl;

    d = sinewave(4046, 250, 48000);
    p = pitch::yin<double>(d, 48000);
    std::cerr << "pitch with 4046 samples @ 4800 Hz: " << p << " Hz" << std::endl;

And here is the output on my machine (debian x64):

Expected pitch: 250 Hz
pitch with 2048 samples @ 16000 Hz: -1 Hz
pitch with 256 samples @ 16000 Hz: 1781.45 Hz
pitch with 4096 samples @ 16000 Hz: -1 Hz
pitch with 2048 samples @ 22050 Hz: 286.617 Hz
pitch with 4096 samples @ 22050 Hz: 1100.9 Hz
pitch with 8092 samples @ 22050 Hz: 250.226 Hz
pitch with 8092 samples @ 4800 Hz: 250.356 Hz
pitch with 4046 samples @ 22050 Hz: 250.337 Hz
pitch with 4046 samples @ 4800 Hz: 250.626 Hz

Can you confirm this problem, and do you have an explanation for it? Thank you!

Call to libxcorr is broken

I just changed libxcorr to stop exposing the xcorr function (now you must explicitly use xcorr_fftw or xcorr_timedomain).

This needs to be adjusted here.

Additional compiler flags maybe needed

Hi,

thank you for your efforts and sharing your code.

In order to compile the tests i had to add two additional compiler flags: -lstdc++ and -lm

CXX_FLAGS 	:= -ansi -pedantic -Werror -Wall -O3 -std=c++17 -fext-numeric-literals -flto -lffts -L$(CURDIR)/../lib -lpitch_detection -I$(CURDIR)/../include -Wl,-R$(CURDIR)/../lib -fopenmp -larmadillo -lmlpack -lstdc++ -lm

The missing -lstdc++ flag caused the following error:

/usr/bin/ld: /tmp/test.0Oc2eo.ltrans0.ltrans.o: undefined reference to symbol '_ZTVN10__cxxabiv121__vmi_class_type_infoE@@CXXABI_1.3'
//usr/lib/x86_64-linux-gnu/libstdc++.so.6: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
Makefile:8: recipe for target 'test' failed
make: *** [test] Error 1

The missing -lm flag caused the following error:

/usr/bin/ld: /tmp/test.Vf3wcq.ltrans0.ltrans.o: undefined reference to symbol 'roundf@@GLIBC_2.2.5'
//lib/x86_64-linux-gnu/libm.so.6: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
Makefile:8: recipe for target 'test' failed

My knowledge of C++ is very limited and i think there's a great probability that these issues are toolchain dependent. In any case this may be helpful to some other ppl trying to compile the tests.

Running the test gives me the following console output. Which leads to the question: are failing tests expected?

Running main() from gtest_main.cc
[==========] Running 76 tests from 13 test cases.
[----------] Global test environment set-up.
[----------] 1 test from MpmSinewaveTestManualAlloc
[ RUN      ] MpmSinewaveTestManualAlloc.OneAllocMultipleFreq
[       OK ] MpmSinewaveTestManualAlloc.OneAllocMultipleFreq (7 ms)
[----------] 1 test from MpmSinewaveTestManualAlloc (7 ms total)

[----------] 1 test from YinSinewaveTestManualAlloc
[ RUN      ] YinSinewaveTestManualAlloc.OneAllocMultipleFreq
[       OK ] YinSinewaveTestManualAlloc.OneAllocMultipleFreq (5 ms)
[----------] 1 test from YinSinewaveTestManualAlloc (5 ms total)

[----------] 5 tests from YinInstrumentTest
[ RUN      ] YinInstrumentTest.Violin_A4_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] YinInstrumentTest.Violin_A4_44100 (20 ms)
[ RUN      ] YinInstrumentTest.Piano_B4_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] YinInstrumentTest.Piano_B4_44100 (10 ms)
[ RUN      ] YinInstrumentTest.Piano_D4_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] YinInstrumentTest.Piano_D4_44100 (1 ms)
[ RUN      ] YinInstrumentTest.Acoustic_E2_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] YinInstrumentTest.Acoustic_E2_44100 (0 ms)
[ RUN      ] YinInstrumentTest.Classical_FSharp4_48000
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] YinInstrumentTest.Classical_FSharp4_48000 (0 ms)
[----------] 5 tests from YinInstrumentTest (31 ms total)

[----------] 5 tests from MpmInstrumentTest
[ RUN      ] MpmInstrumentTest.Violin_A4_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] MpmInstrumentTest.Violin_A4_44100 (1 ms)
[ RUN      ] MpmInstrumentTest.Piano_B4_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] MpmInstrumentTest.Piano_B4_44100 (0 ms)
[ RUN      ] MpmInstrumentTest.Piano_D4_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] MpmInstrumentTest.Piano_D4_44100 (0 ms)
[ RUN      ] MpmInstrumentTest.Acoustic_E2_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] MpmInstrumentTest.Acoustic_E2_44100 (0 ms)
[ RUN      ] MpmInstrumentTest.Classical_FSharp4_48000
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] MpmInstrumentTest.Classical_FSharp4_48000 (0 ms)
[----------] 5 tests from MpmInstrumentTest (1 ms total)

[----------] 5 tests from PYinInstrumentTest
[ RUN      ] PYinInstrumentTest.Violin_A4_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] PYinInstrumentTest.Violin_A4_44100 (0 ms)
[ RUN      ] PYinInstrumentTest.Piano_B4_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] PYinInstrumentTest.Piano_B4_44100 (0 ms)
[ RUN      ] PYinInstrumentTest.Piano_D4_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] PYinInstrumentTest.Piano_D4_44100 (0 ms)
[ RUN      ] PYinInstrumentTest.Acoustic_E2_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] PYinInstrumentTest.Acoustic_E2_44100 (0 ms)
[ RUN      ] PYinInstrumentTest.Classical_FSharp4_48000
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] PYinInstrumentTest.Classical_FSharp4_48000 (0 ms)
[----------] 5 tests from PYinInstrumentTest (0 ms total)

[----------] 5 tests from PMpmInstrumentTest
[ RUN      ] PMpmInstrumentTest.Violin_A4_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] PMpmInstrumentTest.Violin_A4_44100 (0 ms)
[ RUN      ] PMpmInstrumentTest.Piano_B4_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] PMpmInstrumentTest.Piano_B4_44100 (0 ms)
[ RUN      ] PMpmInstrumentTest.Piano_D4_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] PMpmInstrumentTest.Piano_D4_44100 (0 ms)
[ RUN      ] PMpmInstrumentTest.Acoustic_E2_44100
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] PMpmInstrumentTest.Acoustic_E2_44100 (0 ms)
[ RUN      ] PMpmInstrumentTest.Classical_FSharp4_48000
unknown file: Failure
C++ exception with description "std::bad_alloc" thrown in the test body.
[  FAILED  ] PMpmInstrumentTest.Classical_FSharp4_48000 (0 ms)
[----------] 5 tests from PMpmInstrumentTest (0 ms total)

[----------] 4 tests from MpmEdgeCase
[ RUN      ] MpmEdgeCase.EmptyData
[       OK ] MpmEdgeCase.EmptyData (0 ms)
[ RUN      ] MpmEdgeCase.SmallData
[       OK ] MpmEdgeCase.SmallData (2 ms)
[ RUN      ] MpmEdgeCase.InvalidAlloc
./test_edge_cases.cpp:18: Failure
Expected: pitch_alloc::Mpm<double> ma(-1) throws an exception of type std::bad_alloc.
  Actual: it throws a different type.
[  FAILED  ] MpmEdgeCase.InvalidAlloc (14 ms)
[ RUN      ] MpmEdgeCase.EmptyAlloc
[       OK ] MpmEdgeCase.EmptyAlloc (0 ms)
[----------] 4 tests from MpmEdgeCase (16 ms total)

[----------] 5 tests from YinEdgeCase
[ RUN      ] YinEdgeCase.EmptyData
[       OK ] YinEdgeCase.EmptyData (0 ms)
[ RUN      ] YinEdgeCase.TooSmallData
[       OK ] YinEdgeCase.TooSmallData (1 ms)
[ RUN      ] YinEdgeCase.SmallData
[       OK ] YinEdgeCase.SmallData (0 ms)
[ RUN      ] YinEdgeCase.InvalidAlloc
./test_edge_cases.cpp:45: Failure
Expected: pitch_alloc::Yin<double> ya(-1) throws an exception of type std::bad_alloc.
  Actual: it throws a different type.
[  FAILED  ] YinEdgeCase.InvalidAlloc (0 ms)
[ RUN      ] YinEdgeCase.EmptyAlloc
[       OK ] YinEdgeCase.EmptyAlloc (0 ms)
[----------] 5 tests from YinEdgeCase (1 ms total)

[----------] 7 tests from PYinSinewave/PYinSinewaveTest
[ RUN      ] PYinSinewave/PYinSinewaveTest.GetFreqManualAlloc/0
[       OK ] PYinSinewave/PYinSinewaveTest.GetFreqManualAlloc/0 (15 ms)
[ RUN      ] PYinSinewave/PYinSinewaveTest.GetFreqManualAlloc/1
[       OK ] PYinSinewave/PYinSinewaveTest.GetFreqManualAlloc/1 (15 ms)
[ RUN      ] PYinSinewave/PYinSinewaveTest.GetFreqManualAlloc/2
[       OK ] PYinSinewave/PYinSinewaveTest.GetFreqManualAlloc/2 (13 ms)
[ RUN      ] PYinSinewave/PYinSinewaveTest.GetFreqManualAlloc/3
[       OK ] PYinSinewave/PYinSinewaveTest.GetFreqManualAlloc/3 (17 ms)
[ RUN      ] PYinSinewave/PYinSinewaveTest.GetFreqManualAlloc/4
[       OK ] PYinSinewave/PYinSinewaveTest.GetFreqManualAlloc/4 (15 ms)
[ RUN      ] PYinSinewave/PYinSinewaveTest.GetFreqManualAlloc/5
[       OK ] PYinSinewave/PYinSinewaveTest.GetFreqManualAlloc/5 (19 ms)
[ RUN      ] PYinSinewave/PYinSinewaveTest.GetFreqManualAlloc/6
[       OK ] PYinSinewave/PYinSinewaveTest.GetFreqManualAlloc/6 (16 ms)
[----------] 7 tests from PYinSinewave/PYinSinewaveTest (111 ms total)

[----------] 6 tests from PMpmSinewave/PMpmSinewaveTest
[ RUN      ] PMpmSinewave/PMpmSinewaveTest.GetFreqManualAlloc/0
[       OK ] PMpmSinewave/PMpmSinewaveTest.GetFreqManualAlloc/0 (6 ms)
[ RUN      ] PMpmSinewave/PMpmSinewaveTest.GetFreqManualAlloc/1
[       OK ] PMpmSinewave/PMpmSinewaveTest.GetFreqManualAlloc/1 (5 ms)
[ RUN      ] PMpmSinewave/PMpmSinewaveTest.GetFreqManualAlloc/2
[       OK ] PMpmSinewave/PMpmSinewaveTest.GetFreqManualAlloc/2 (5 ms)
[ RUN      ] PMpmSinewave/PMpmSinewaveTest.GetFreqManualAlloc/3
[       OK ] PMpmSinewave/PMpmSinewaveTest.GetFreqManualAlloc/3 (5 ms)
[ RUN      ] PMpmSinewave/PMpmSinewaveTest.GetFreqManualAlloc/4
[       OK ] PMpmSinewave/PMpmSinewaveTest.GetFreqManualAlloc/4 (6 ms)
[ RUN      ] PMpmSinewave/PMpmSinewaveTest.GetFreqManualAlloc/5
[       OK ] PMpmSinewave/PMpmSinewaveTest.GetFreqManualAlloc/5 (9 ms)
[----------] 6 tests from PMpmSinewave/PMpmSinewaveTest (36 ms total)

[----------] 12 tests from MpmSinewave/MpmSinewaveTest
[ RUN      ] MpmSinewave/MpmSinewaveTest.GetFreq/0
[       OK ] MpmSinewave/MpmSinewaveTest.GetFreq/0 (4 ms)
[ RUN      ] MpmSinewave/MpmSinewaveTest.GetFreq/1
[       OK ] MpmSinewave/MpmSinewaveTest.GetFreq/1 (3 ms)
[ RUN      ] MpmSinewave/MpmSinewaveTest.GetFreq/2
[       OK ] MpmSinewave/MpmSinewaveTest.GetFreq/2 (3 ms)
[ RUN      ] MpmSinewave/MpmSinewaveTest.GetFreq/3
[       OK ] MpmSinewave/MpmSinewaveTest.GetFreq/3 (3 ms)
[ RUN      ] MpmSinewave/MpmSinewaveTest.GetFreq/4
[       OK ] MpmSinewave/MpmSinewaveTest.GetFreq/4 (4 ms)
[ RUN      ] MpmSinewave/MpmSinewaveTest.GetFreq/5
[       OK ] MpmSinewave/MpmSinewaveTest.GetFreq/5 (3 ms)
[ RUN      ] MpmSinewave/MpmSinewaveTest.GetFreqManualAlloc/0
[       OK ] MpmSinewave/MpmSinewaveTest.GetFreqManualAlloc/0 (4 ms)
[ RUN      ] MpmSinewave/MpmSinewaveTest.GetFreqManualAlloc/1
[       OK ] MpmSinewave/MpmSinewaveTest.GetFreqManualAlloc/1 (3 ms)
[ RUN      ] MpmSinewave/MpmSinewaveTest.GetFreqManualAlloc/2
[       OK ] MpmSinewave/MpmSinewaveTest.GetFreqManualAlloc/2 (4 ms)
[ RUN      ] MpmSinewave/MpmSinewaveTest.GetFreqManualAlloc/3
[       OK ] MpmSinewave/MpmSinewaveTest.GetFreqManualAlloc/3 (3 ms)
[ RUN      ] MpmSinewave/MpmSinewaveTest.GetFreqManualAlloc/4
[       OK ] MpmSinewave/MpmSinewaveTest.GetFreqManualAlloc/4 (4 ms)
[ RUN      ] MpmSinewave/MpmSinewaveTest.GetFreqManualAlloc/5
[       OK ] MpmSinewave/MpmSinewaveTest.GetFreqManualAlloc/5 (3 ms)
[----------] 12 tests from MpmSinewave/MpmSinewaveTest (42 ms total)

[----------] 6 tests from SwipeSinewave/SwipeSinewaveTest
[ RUN      ] SwipeSinewave/SwipeSinewaveTest.GetFreq/0
[       OK ] SwipeSinewave/SwipeSinewaveTest.GetFreq/0 (4 ms)
[ RUN      ] SwipeSinewave/SwipeSinewaveTest.GetFreq/1
[       OK ] SwipeSinewave/SwipeSinewaveTest.GetFreq/1 (4 ms)
[ RUN      ] SwipeSinewave/SwipeSinewaveTest.GetFreq/2
[       OK ] SwipeSinewave/SwipeSinewaveTest.GetFreq/2 (4 ms)
[ RUN      ] SwipeSinewave/SwipeSinewaveTest.GetFreq/3
[       OK ] SwipeSinewave/SwipeSinewaveTest.GetFreq/3 (3 ms)
[ RUN      ] SwipeSinewave/SwipeSinewaveTest.GetFreq/4
[       OK ] SwipeSinewave/SwipeSinewaveTest.GetFreq/4 (4 ms)
[ RUN      ] SwipeSinewave/SwipeSinewaveTest.GetFreq/5
[       OK ] SwipeSinewave/SwipeSinewaveTest.GetFreq/5 (5 ms)
[----------] 6 tests from SwipeSinewave/SwipeSinewaveTest (24 ms total)

[----------] 14 tests from YinSinewave/YinSinewaveTest
[ RUN      ] YinSinewave/YinSinewaveTest.GetFreq/0
[       OK ] YinSinewave/YinSinewaveTest.GetFreq/0 (4 ms)
[ RUN      ] YinSinewave/YinSinewaveTest.GetFreq/1
[       OK ] YinSinewave/YinSinewaveTest.GetFreq/1 (3 ms)
[ RUN      ] YinSinewave/YinSinewaveTest.GetFreq/2
[       OK ] YinSinewave/YinSinewaveTest.GetFreq/2 (4 ms)
[ RUN      ] YinSinewave/YinSinewaveTest.GetFreq/3
[       OK ] YinSinewave/YinSinewaveTest.GetFreq/3 (4 ms)
[ RUN      ] YinSinewave/YinSinewaveTest.GetFreq/4
[       OK ] YinSinewave/YinSinewaveTest.GetFreq/4 (4 ms)
[ RUN      ] YinSinewave/YinSinewaveTest.GetFreq/5
[       OK ] YinSinewave/YinSinewaveTest.GetFreq/5 (3 ms)
[ RUN      ] YinSinewave/YinSinewaveTest.GetFreq/6
[       OK ] YinSinewave/YinSinewaveTest.GetFreq/6 (3 ms)
[ RUN      ] YinSinewave/YinSinewaveTest.GetFreqManualAlloc/0
[       OK ] YinSinewave/YinSinewaveTest.GetFreqManualAlloc/0 (5 ms)
[ RUN      ] YinSinewave/YinSinewaveTest.GetFreqManualAlloc/1
[       OK ] YinSinewave/YinSinewaveTest.GetFreqManualAlloc/1 (4 ms)
[ RUN      ] YinSinewave/YinSinewaveTest.GetFreqManualAlloc/2
[       OK ] YinSinewave/YinSinewaveTest.GetFreqManualAlloc/2 (4 ms)
[ RUN      ] YinSinewave/YinSinewaveTest.GetFreqManualAlloc/3
[       OK ] YinSinewave/YinSinewaveTest.GetFreqManualAlloc/3 (4 ms)
[ RUN      ] YinSinewave/YinSinewaveTest.GetFreqManualAlloc/4
[       OK ] YinSinewave/YinSinewaveTest.GetFreqManualAlloc/4 (4 ms)
[ RUN      ] YinSinewave/YinSinewaveTest.GetFreqManualAlloc/5
[       OK ] YinSinewave/YinSinewaveTest.GetFreqManualAlloc/5 (3 ms)
[ RUN      ] YinSinewave/YinSinewaveTest.GetFreqManualAlloc/6
[       OK ] YinSinewave/YinSinewaveTest.GetFreqManualAlloc/6 (4 ms)
[----------] 14 tests from YinSinewave/YinSinewaveTest (54 ms total)

[----------] Global test environment tear-down
[==========] 76 tests from 13 test cases ran. (329 ms total)
[  PASSED  ] 54 tests.
[  FAILED  ] 22 tests, listed below:
[  FAILED  ] YinInstrumentTest.Violin_A4_44100
[  FAILED  ] YinInstrumentTest.Piano_B4_44100
[  FAILED  ] YinInstrumentTest.Piano_D4_44100
[  FAILED  ] YinInstrumentTest.Acoustic_E2_44100
[  FAILED  ] YinInstrumentTest.Classical_FSharp4_48000
[  FAILED  ] MpmInstrumentTest.Violin_A4_44100
[  FAILED  ] MpmInstrumentTest.Piano_B4_44100
[  FAILED  ] MpmInstrumentTest.Piano_D4_44100
[  FAILED  ] MpmInstrumentTest.Acoustic_E2_44100
[  FAILED  ] MpmInstrumentTest.Classical_FSharp4_48000
[  FAILED  ] PYinInstrumentTest.Violin_A4_44100
[  FAILED  ] PYinInstrumentTest.Piano_B4_44100
[  FAILED  ] PYinInstrumentTest.Piano_D4_44100
[  FAILED  ] PYinInstrumentTest.Acoustic_E2_44100
[  FAILED  ] PYinInstrumentTest.Classical_FSharp4_48000
[  FAILED  ] PMpmInstrumentTest.Violin_A4_44100
[  FAILED  ] PMpmInstrumentTest.Piano_B4_44100
[  FAILED  ] PMpmInstrumentTest.Piano_D4_44100
[  FAILED  ] PMpmInstrumentTest.Acoustic_E2_44100
[  FAILED  ] PMpmInstrumentTest.Classical_FSharp4_48000
[  FAILED  ] MpmEdgeCase.InvalidAlloc
[  FAILED  ] YinEdgeCase.InvalidAlloc

22 FAILED TESTS
  YOU HAVE 5 DISABLED TESTS

Embedded acorr screws up autocorrelation

When using libxcorr, old days:

sevagh:pitch-detection $ ./bin/sinewave --freq 1337 --algo autocorrelation --size 8092
Freq: 1337      pitch: 1339.12

With embedded acorr:

sevagh:pitch-detection $ ./bin/sinewave --freq 1337 --algo autocorrelation --size 8092
Freq: 1337      pitch: 1492.58

MpmSinewaveTest fails for 77.0

[----------] 7 tests from MpmSinewave/MpmSinewaveTest
[ RUN      ] MpmSinewave/MpmSinewaveTest.GetFreq/0
test/sinewave_test.cpp:18: Failure
The difference between freq and pitch is 78, which exceeds 0.01 * freq, where
freq evaluates to 77,
pitch evaluates to -1, and
0.01 * freq evaluates to 0.77000000000000002.

Clean up the normalized FFT vs time-domain FFT

It's a bit messy -

  • The time-domain autocorrelation does the opinionated improvements of MPM
  • The FFT autocorrelation doesn't have the same but it has some form of normalization via completely pointless division by result[size/2]

Come up with:

enum NormalizationStrategy

Make sure they align for time domain and FFT.

Looks like MPM is now using frequency domain acf?

First of all - thanks for your implementation! I borrowed some of your code a while ago here: https://github.com/adamski/PitchDetector

MPM is definitely the best algorithm I've found for musical sounds, after a lot of tests.
My only issue has been speed on slow devices e.g. older phones, it is much slower than FFT based detection methods. I've just come back to check this repository and it looks like you're now using FFT for the autocorrelation rather than the time-based method. Is this correct? If so, could I suggest to update the Readme?

Re-add ffmpeg code in examples

Reintroduce the ffmpeg code I used to have, to create an example file.cpp, or ffmpeg.cpp which takes a file as input, i.e. mp3, wav, whatever, gets the raw audio, and invokes pitch detection on it.

YIN FFT

YIN paper mentions how to use autocorrelation to approximate the distance function.

C API

C, the universal ABI - this would unlock the whole world of C FFI, including Rust, Python, Ruby, Lua, etc.

the error of the project

hi
This project has too much error and it cann't run directly. It takes me a week in order to solve this error.

Flesh out main.cpp "testbench"

Right now it just hardcodes a run through some sine waves.

Ideas:

  • Add a guitar clip, EADGBE
  • Take the algo as an argument, e.g. "./pitch_detection mpm" will only run MPM
  • Take a list of pitches in a txt file to generate sine waves

Use of "sign" in FFTS used by MPM

According to comments in ffts.h, the second "sign" argument to ffts_init_1d() should be -1 for forward (real to complex) and +1 for backward (complex to real), however the value in mpm.c is equal to 0:
auto fft_forward = ffts_init_1d(N2, false);
auto fft_backward = ffts_init_1d(N2, false);
How can this work correctly with a value of false=0? Is it documented somewhere how this is supposed to work?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.