From 308a53c389cdc2631860f434989cd57efbf91145 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Sun, 17 Feb 2019 14:53:33 +0100 Subject: Implemented Tuner --- Makefile | 7 +++++-- TODO | 1 + fft.cpp | 27 +++------------------------ fft.h | 4 ---- testsuite.cpp | 3 ++- tuner.cpp | 40 +++++++++++++++++++++++++++++++++++++--- tunerdemo.cpp | 5 ++++- util.cpp | 25 +++++++++++++++++++++++++ util.h | 15 +++++++++++++++ 9 files changed, 92 insertions(+), 35 deletions(-) create mode 100644 util.cpp create mode 100644 util.h diff --git a/Makefile b/Makefile index c78f4d7..c6cd4e3 100644 --- a/Makefile +++ b/Makefile @@ -12,10 +12,10 @@ PREFIX=/usr/local/bin all: tunerdemo testsuite -tunerdemo: fft.o autocorrelation.o tuner.o tunerdemo.o +tunerdemo: util.o fft.o autocorrelation.o tuner.o tunerdemo.o $(CXX) $(CXXFLAGS) -o $@ $^ -testsuite: fft.o autocorrelation.o tuner.o testsuite.o +testsuite: util.o fft.o autocorrelation.o tuner.o testsuite.o $(CXX) $(CXXFLAGS) -o $@ $^ fft.o: fft.cpp fft.h @@ -27,6 +27,9 @@ autocorrelation.o: autocorrelation.cpp autocorrelation.h tuner.o: tuner.cpp tuner.h $(CXX) $(CXXFLAGS) -c -o $@ $< +util.o: util.cpp util.h + $(CXX) $(CXXFLAGS) -c -o $@ $< + testsuite.o: testsuite.cpp fft.h autocorrelation.h tuner.h $(CXX) $(CXXFLAGS) -c -o $@ $< diff --git a/TODO b/TODO index e31bca0..dff5398 100644 --- a/TODO +++ b/TODO @@ -2,6 +2,7 @@ TODO ==== google test +test: compare w/ naive implementation autocorrelation, ifft debian tuner diff --git a/fft.cpp b/fft.cpp index a8009ad..12aeece 100644 --- a/fft.cpp +++ b/fft.cpp @@ -2,6 +2,8 @@ #include "fft.h" +#include "util.h" + #include #include #include @@ -15,15 +17,6 @@ namespace { // Helper functions - bool is_power_of_two(unsigned int n) { - return n != 0 && (n & (n - 1)) == 0; - } -} - -std::vector RIT::magnitudes(std::vector>& v) { - std::vector result(v.size()); - std::transform(std::begin(v), std::end(v), std::begin(result), std::abs); - return result; } RIT::FFT::FFT(int size, bool halfOnly): mSize(size), order(size), expLUT(size/2), mFlagHalfOnly(halfOnly) { @@ -33,7 +26,7 @@ RIT::FFT::FFT(int size, bool halfOnly): mSize(size), order(size), expLUT(size/2) // reorder LUT for (int i = 0; i < size; ++i) { - order[i] = bitreverse(i); + order[i] = RIT::bitreverse(i, size); } // exp LUT @@ -63,20 +56,6 @@ RIT::FFT& RIT::FFT::SetHalfOnly(bool enable) { return *this; } -int RIT::FFT::bitreverse(int i) const { - int size{mSize}; - int result{0}; - - while (size > 1) { - result <<= 1; - result |= i & 1; - i >>= 1; - size >>= 1; - } - - return result; -} - void RIT::FFT::reorder(const std::vector>& src, std::vector>& dst) const { int size = src.size(); diff --git a/fft.h b/fft.h index 980b4b3..4bc493d 100644 --- a/fft.h +++ b/fft.h @@ -24,8 +24,6 @@ public: FFT& SetHalfOnly(bool enable); private: - int bitreverse(int i) const; - void reorder(const std::vector>& src, std::vector>& dst) const; void fft_recursive(std::vector>::iterator X, int N) const; void fft_half(std::vector>::iterator X, int N) const; @@ -44,6 +42,4 @@ private: std::unique_ptr mImpl; }; // class IFFT -std::vector magnitudes(std::vector>& v); - } // namespace RIT diff --git a/testsuite.cpp b/testsuite.cpp index 2b8042b..f925b7e 100644 --- a/testsuite.cpp +++ b/testsuite.cpp @@ -1,8 +1,9 @@ // FFT Test -#include "fft.h" #include "autocorrelation.h" +#include "fft.h" #include "tuner.h" +#include "util.h" #include #include diff --git a/tuner.cpp b/tuner.cpp index d1833a3..3013390 100644 --- a/tuner.cpp +++ b/tuner.cpp @@ -2,12 +2,19 @@ #include "autocorrelation.h" #include "fft.h" +#include "util.h" + +#include +#include + +using namespace RIT; struct RIT::Tuner::Impl { public: - Impl(int size, int f_sample): mAC(size), m_f_sample(f_sample) {} + Impl(int size, int f_sample): mAC(size), m_f_sample(f_sample), mSize(size) {} RIT::AutoCorrelation mAC; int m_f_sample; + int mSize; }; RIT::Tuner::Tuner(int size, int f_sample): mImpl(std::make_unique(size, f_sample)) @@ -16,9 +23,36 @@ RIT::Tuner::Tuner(int size, int f_sample): mImpl(std::make_unique noteNames{ "A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#" }; + const double base = std::pow(2.0, 1./12); + + RIT::Pitch getPitch(double f) + { + double noteIndexD = std::log(f / 110.0) / std::log(base) + 0.5; + + double noteIndexI{}; + double deviation = std::modf(noteIndexD, ¬eIndexI) - 0.5; + int noteIndex = int(noteIndexI); + + noteIndex %= 12; + + return RIT::Pitch {f, deviation, noteNames[noteIndex]}; + } +} + RIT::Pitch RIT::Tuner::operator() (const std::vector> &v) { - std::vector> autoCorrelation = mImpl->mAC(v); + std::vector autoCorrelation = magnitudes(mImpl->mAC(v)); + + auto maxElement = std::max_element(std::begin(autoCorrelation) + 1, std::begin(autoCorrelation) + mImpl->mSize / 2); + + int index = maxElement - std::begin(autoCorrelation); + + if (autoCorrelation[index] > autoCorrelation[index - 1] && autoCorrelation[index] > autoCorrelation[index + 1]) { + double f = double(mImpl->m_f_sample) / index; + return getPitch(f); + } - return Pitch(); + return Pitch{}; } diff --git a/tunerdemo.cpp b/tunerdemo.cpp index 716bd8e..e70afb2 100644 --- a/tunerdemo.cpp +++ b/tunerdemo.cpp @@ -23,7 +23,10 @@ int main(int argc, char* argv[]) { auto start = std::chrono::high_resolution_clock::now(); RIT::Pitch pitch = tuner(dataIn); auto end = std::chrono::high_resolution_clock::now(); - std::cout << "Detected Note: " << pitch.name + std::string name = pitch.name; + if (name == "") + name = ""; + std::cout << "Detected Note: " << name << " Deviation: " << pitch.deviation << " Frequency: " << pitch.f << ", took " << std::chrono::nanoseconds(end - start).count() * 0.000001 << "ms" diff --git a/util.cpp b/util.cpp new file mode 100644 index 0000000..2a81823 --- /dev/null +++ b/util.cpp @@ -0,0 +1,25 @@ +#include "util.h" + +bool RIT::is_power_of_two(unsigned int n) { + return n != 0 && (n & (n - 1)) == 0; +} + +std::vector RIT::magnitudes(const std::vector>& v) { + std::vector result(v.size()); + std::transform(std::begin(v), std::end(v), std::begin(result), std::abs); + return result; +} + +/// size: fft size in sample points, power of 2 +int RIT::bitreverse(int i, int size) { + int result{0}; + + while (size > 1) { + result <<= 1; + result |= i & 1; + i >>= 1; + size >>= 1; + } + + return result; +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..1d64a80 --- /dev/null +++ b/util.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace RIT { + +int bitreverse(int i, int size); + +bool is_power_of_two(unsigned int n); + +std::vector magnitudes(const std::vector>& v); + +} // namespace RIT + -- cgit v1.2.3