// Copyright 2017 Rob Riggs // All rights reserved. #pragma once #include "AudioLevel.hpp" #include #include #include #include namespace mobilinkd { namespace tnc { extern const float WINDOW[]; template class GoertzelFilter { float filterFreq_; int bin_; float coeff_; float d1 { 0.0f }; float d2 { 0.0f }; uint32_t count { 0 }; const float* window_; public: GoertzelFilter(float filter_freq, const float* window = WINDOW) : filterFreq_(filter_freq), bin_( 0.5f + ((filter_freq * SAMPLES) / SAMPLE_RATE)), coeff_( 2.0f * cos((2.0f * PI * bin_) / float(SAMPLES))), window_(window) { } void operator()(float* samples, uint32_t n) { for (size_t i = 0; i != n; ++i) { float w = window_ ? window_[count] : 1.0; float y = w * samples[i] + coeff_ * d1 - d2; d2 = d1; d1 = y; ++count; } } void operator()(uint16_t* samples, uint32_t n) { for (uint32_t i = 0; i != n; ++i) { float w = window_ ? window_[count] : 1.0; float sample = (float(samples[i]) - audio::virtual_ground) * audio::i_vgnd; float y = w * sample + coeff_ * d1 - d2; d2 = d1; d1 = y; ++count; } } operator float() const { return d2 * d2 + d1 * d1 - coeff_ * d1 * d2; } void reset() { d1 = 0.0f; d2 = 0.0f; count = 0; } }; #if 0 template class GoertzelFactory { float window_[SAMPLES]; static void make_window(float* window) { for (size_t i = 0; i != SAMPLES; ++i) { window[i] = 0.54f - 0.46f * cos(2.0f * M_PI * (float(i) / float(SAMPLE_RATE))); } } public: GoertzelFactory() : window_() { make_window(window_); } GoertzelFilter make(float freq) { return GoertzelFilter(freq, window_); } }; #endif // Complex Goertzel // Based on https://dsp.stackexchange.com/a/23946/36581 template struct Goertzel { typedef Goertzel type; typedef std::complex complex_type; typedef Float float_type; float_type sin_; float_type cos_; float_type coeff_; float_type q0 { 0.0 }, q1 { 0.0 }, q2 { 0.0 }; explicit Goertzel(float_type omega) : sin_(sin(omega)), cos_(cos(omega)), coeff_(2.0 * cos_) { } static type from_frequency(float_type frequency, float_type sample_rate) { return Goertzel(2.0 * PI * frequency / sample_rate); } template complex_type operator()(const Container& data) { for (auto& v : data) { q0 = coeff_ * q1 - q2 + v; q2 = q1; q1 = q0; } auto real = (q1 - q2 * cos_); auto imag = (q2 * sin_); q0 = q1 = q2 = 0.0; return complex_type(real, imag); } }; typedef Goertzel FloatGoertzel; } } // mobilinkd::tnc