diff options
author | Roland Stigge <stigge@antcom.de> | 2019-02-17 14:53:33 +0100 |
---|---|---|
committer | Roland Stigge <stigge@antcom.de> | 2019-02-17 14:53:33 +0100 |
commit | 308a53c389cdc2631860f434989cd57efbf91145 (patch) | |
tree | 2eadba9413a881045caedd589cc9aec7fda40134 | |
parent | fef594c82518a8fe4c96794852c1fc849c0ed3b3 (diff) |
Implemented Tuner
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | TODO | 1 | ||||
-rw-r--r-- | fft.cpp | 27 | ||||
-rw-r--r-- | fft.h | 4 | ||||
-rw-r--r-- | testsuite.cpp | 3 | ||||
-rw-r--r-- | tuner.cpp | 40 | ||||
-rw-r--r-- | tunerdemo.cpp | 5 | ||||
-rw-r--r-- | util.cpp | 25 | ||||
-rw-r--r-- | util.h | 15 |
9 files changed, 92 insertions, 35 deletions
@@ -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 $@ $< @@ -2,6 +2,7 @@ TODO ==== google test +test: compare w/ naive implementation autocorrelation, ifft debian tuner @@ -2,6 +2,8 @@ #include "fft.h" +#include "util.h" + #include <algorithm> #include <chrono> #include <cmath> @@ -15,15 +17,6 @@ namespace { // Helper functions - bool is_power_of_two(unsigned int n) { - return n != 0 && (n & (n - 1)) == 0; - } -} - -std::vector<double> RIT::magnitudes(std::vector<std::complex<double>>& v) { - std::vector<double> result(v.size()); - std::transform(std::begin(v), std::end(v), std::begin(result), std::abs<double>); - 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<std::complex<double>>& src, std::vector<std::complex<double>>& dst) const { int size = src.size(); @@ -24,8 +24,6 @@ public: FFT& SetHalfOnly(bool enable); private: - int bitreverse(int i) const; - void reorder(const std::vector<std::complex<double>>& src, std::vector<std::complex<double>>& dst) const; void fft_recursive(std::vector<std::complex<double>>::iterator X, int N) const; void fft_half(std::vector<std::complex<double>>::iterator X, int N) const; @@ -44,6 +42,4 @@ private: std::unique_ptr<Impl> mImpl; }; // class IFFT -std::vector<double> magnitudes(std::vector<std::complex<double>>& 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 <complex> #include <chrono> @@ -2,12 +2,19 @@ #include "autocorrelation.h" #include "fft.h" +#include "util.h" + +#include <algorithm> +#include <cmath> + +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<RIT::Tuner::Impl>(size, f_sample)) @@ -16,9 +23,36 @@ RIT::Tuner::Tuner(int size, int f_sample): mImpl(std::make_unique<RIT::Tuner::Im RIT::Tuner::~Tuner(){} +namespace { + const std::vector<std::string> 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<std::complex<double>> &v) { - std::vector<std::complex<double>> autoCorrelation = mImpl->mAC(v); + std::vector<double> 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 = "<none>"; + 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<double> RIT::magnitudes(const std::vector<std::complex<double>>& v) { + std::vector<double> result(v.size()); + std::transform(std::begin(v), std::end(v), std::begin(result), std::abs<double>); + 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; +} @@ -0,0 +1,15 @@ +#pragma once + +#include <complex> +#include <vector> + +namespace RIT { + +int bitreverse(int i, int size); + +bool is_power_of_two(unsigned int n); + +std::vector<double> magnitudes(const std::vector<std::complex<double>>& v); + +} // namespace RIT + |