Lamp-Da 0.1
A compact lantern project
Loading...
Searching...
No Matches
fft.h
Go to the documentation of this file.
1
5#pragma once
6
7#include <array>
8#include <cmath>
9#include <cstdint>
10
11#define FFT_SQRT_APPROXIMATION
12#define FFT_SPEED_OVER_PRECISION
13#include "src/depends/arduinoFFT/src/arduinoFFT.h"
14
16
17namespace lampda {
18namespace utils {
19namespace fft {
20
21constexpr int SAMPLE_RATE = 16000;
22
28template<uint16_t samplesFFT, uint16_t resultSize, typename T = float> class FftAnalyzer
29{
30 static_assert(samplesFFT > 0 && (samplesFFT & (samplesFFT - 1)) == 0, "samplesFFT must be a power of two");
31 static constexpr uint16_t samplesFFTRes = samplesFFT >> 1;
33public:
34 std::array<T, samplesFFTRes> fftBin; //.< raw fft results
35 std::array<T, resultSize> fftLog;
38
39private:
41 std::array<T, resultSize + 1> minFrequenciesPerBin_log;
42
44 std::array<uint16_t, samplesFFTRes> _numSamplesPerBar_log;
46 const FFTWindow fftWindowing = FFTWindow::Rectangle;
47 T windowSum = 0.0;
48
50 T vReal[samplesFFT];
52 T vImag[samplesFFT];
54 ArduinoFFT<T> FFT = ArduinoFFT<T>(vReal, vImag, samplesFFT, SAMPLE_RATE, true);
55
56public:
57 void reset()
58 {
59 fftLog.fill(0);
60 fftBin.fill(0);
61 }
62
64 {
65 // fill the initial variables
66 // ]1; +oo[, closer to 1 get closer to a linear scale
67 static constexpr T logMultiplier = 1.2;
68
69 _numSamplesPerBar_log.fill(0);
70 minFrequenciesPerBin_log[0] = 0.0;
71 // calculate octave frequencies
72 for (uint16_t octave = 1; octave < resultSize; ++octave)
73 {
74 assert(octave < minFrequenciesPerBin_log.size());
75 // center frequency of this bin
76 minFrequenciesPerBin_log[octave] = (SAMPLE_RATE / 2.0) / pow(logMultiplier, resultSize - octave);
77 }
78 // set last max bin
79 minFrequenciesPerBin_log[resultSize] = (SAMPLE_RATE / 2.0);
80
81 // fill the linear to bin to log bin
82 _numSamplesPerBar_log[0] = 0;
83 for (uint16_t i = 1; i < samplesFFTRes; ++i)
84 {
85 const T freq = medium_bin_frequency(i);
86 uint16_t octave = 0;
87 for (; octave < resultSize; ++octave)
88 {
89 if (freq > get_log_bin_min_frequency(octave) && freq <= get_log_bin_max_frequency(octave))
90 break;
91 }
92 assert(octave >= 0 && octave < resultSize);
93 _numSamplesPerBar_log[i] = octave;
94 }
95
96 // compute window sum
97 // work on half the data for optimization
98 for (uint16_t i = 0; i < samplesFFTRes; ++i)
99 set_data(1.0, i);
100 FFT.windowing(fftWindowing, FFTDirection::Forward);
101 windowSum = 0.0;
102 for (uint16_t i = 0; i < samplesFFTRes; ++i)
103 windowSum += vReal[i] * 2;
104
105 // reset variables
106 reset();
107 }
108
110 int to_bin_index(const T frequency) const noexcept
111 {
112 return round(frequency / (static_cast<T>(SAMPLE_RATE) / samplesFFT));
113 }
115 T medium_bin_frequency(uint16_t index) const noexcept { return index * SAMPLE_RATE / static_cast<T>(samplesFFT); }
117 T min_bin_frequency(uint16_t index) const noexcept
118 {
119 if (index == 0)
120 return 0.0;
121 return max_bin_frequency(index - 1);
122 }
124 T max_bin_frequency(uint16_t index) const noexcept
125 {
126 return (index + 0.5) * SAMPLE_RATE / static_cast<T>(samplesFFT);
127 }
128
134 T get_log_bin_min_frequency(uint16_t index) const noexcept
135 {
136 if (index >= minFrequenciesPerBin_log.size())
137 index = minFrequenciesPerBin_log.size() - 1;
138 return minFrequenciesPerBin_log[index];
139 }
145 T get_log_bin_max_frequency(uint16_t index) const noexcept
146 {
147 // get next bin min, it will be this bin max
148 return get_log_bin_min_frequency(index + 1);
149 }
150
152 inline void set_data(const T data, uint16_t index)
153 {
154 if (index >= samplesFFT)
155 index = samplesFFT - 1;
156
157 vReal[index] = data;
158 vImag[index] = 0;
159 }
160
163 {
164 // recenter all data around zero
165 FFT.dcRemoval();
166
167 FFT.windowing(fftWindowing, FFTDirection::Forward);
168 FFT.compute(FFTDirection::Forward);
169 FFT.complexToMagnitude();
170
171 // store result
172 fftLog.fill(0);
173 maxMagnitude = 0.0;
174 maxFrequency = 0.0;
175 for (uint16_t i = 0; i < samplesFFTRes; ++i)
176 {
177 const T t = fabsf(vReal[i]) * 2.0 / windowSum;
178 fftBin[i] = t;
179
180 const uint16_t octave = _numSamplesPerBar_log[i];
181 assert(octave >= 0 && octave < fftLog.size());
182
183 // we prefer to keep the max every time, to not squash important frequencies in large bins
184 fftLog[octave] = max<T>(fftLog[octave], t);
185 if (t > maxMagnitude)
186 {
187 maxMagnitude = t;
189 }
190 }
191 } // run_fast_fourrier_transform()
192};
193
194} // namespace fft
195} // namespace utils
196} // namespace lampda
Definition: arduinoFFT.h:58
Wrapper class for the FFT implementation.
Definition: fft.h:29
T min_bin_frequency(uint16_t index) const noexcept
min frequency of a linear bin
Definition: fft.h:117
T medium_bin_frequency(uint16_t index) const noexcept
center frequency of a linear bin
Definition: fft.h:115
std::array< T, resultSize > fftLog
fft result as a log scale
Definition: fft.h:35
T max_bin_frequency(uint16_t index) const noexcept
max frequency of a linear bin
Definition: fft.h:124
T get_log_bin_max_frequency(uint16_t index) const noexcept
get the minimum frequency represented by the fft bin at index
Definition: fft.h:145
std::array< T, samplesFFTRes > fftBin
output variables
Definition: fft.h:34
T maxMagnitude
maximum detected frequency magnitude
Definition: fft.h:36
T get_log_bin_min_frequency(uint16_t index) const noexcept
get the minimum frequency represented by the fft bin at index
Definition: fft.h:134
void set_data(const T data, uint16_t index)
Set the microphone data at the target index.
Definition: fft.h:152
int to_bin_index(const T frequency) const noexcept
Map frequency to bin index.
Definition: fft.h:110
T maxFrequency
maximum detected frequency, in Hertz
Definition: fft.h:37
void run_fast_fourrier_transform()
Run FFT main code.
Definition: fft.h:162
constexpr int SAMPLE_RATE
Base sample rate in Hz - standard.
Definition: fft.h:21
Program scope.
Definition: control_fixed_modes.hpp:12
Define useful functions.