2#include <ossia/audio/fft.hpp>
8#include <Ui/Monitor/MidiText.hpp>
37 std::vector<double> window;
38 double coherent_gain{1.};
45 void prepare(
int n,
double sample_rate, Window w)
49 if(n == size && w == kind && !window.empty())
55 power.assign(n / 2 + 1, 0.);
59 constexpr double pi = std::numbers::pi;
60 for(
int i = 0; i < n; i++)
62 const double t = double(i) / n;
66 window[i] = 0.5 - 0.5 * std::cos(2 * pi * t);
68 case Window::BlackmanHarris:
69 window[i] = 0.35875 - 0.48829 * std::cos(2 * pi * t)
70 + 0.14128 * std::cos(4 * pi * t) - 0.01168 * std::cos(6 * pi * t);
73 window[i] = 0.21557895 - 0.41663158 * std::cos(2 * pi * t)
74 + 0.277263158 * std::cos(4 * pi * t)
75 - 0.083578947 * std::cos(6 * pi * t)
76 + 0.006947368 * std::cos(8 * pi * t);
81 for(
double v : window)
94 auto* in = fft.input();
95 for(
int i = 0; i < size; i++)
96 in[i] = x[i] * window[i];
97 const auto* out = fft.execute();
99 const double k = 4. / (coherent_gain * coherent_gain);
100 const int bins = size / 2 + 1;
101 for(
int b = 0; b < bins; b++)
102 power[b] = (out[b][0] * out[b][0] + out[b][1] * out[b][1]) * k;
105 double binFrequency(
double bin)
const noexcept {
return bin * rate / size; }
106 double frequencyBin(
double f)
const noexcept {
return f * size / rate; }
110 double enbw() const noexcept {
return size * sum_sq / (coherent_gain * coherent_gain); }
115 double bandPower(std::span<const double> p,
double f0,
double f1)
const noexcept
119 const int last = int(p.size()) - 1;
120 const int b0 = std::clamp(
int(std::ceil(frequencyBin(f0))), 0, last);
121 const int b1 = std::clamp(
int(std::floor(frequencyBin(f1))), 0, last);
126 const int b = std::clamp(
int(std::lround(frequencyBin((f0 + f1) / 2))), 0, last);
127 return p[b] * (frequencyBin(f1) - frequencyBin(f0)) /
enbw();
130 for(
int b = b0; b <= b1; b++)
142 static constexpr int count = 31;
143 static double centre(
int i)
noexcept {
return 1000. * std::pow(10., (i - 17) / 10.); }
144 static double lower(
int i)
noexcept {
return centre(i) * std::pow(10., -1. / 20.); }
145 static double upper(
int i)
noexcept {
return centre(i) * std::pow(10., 1. / 20.); }
147 static constexpr std::array<const char*, count>
labels{
148 "20",
"25",
"31.5",
"40",
"50",
"63",
"80",
"100",
"125",
"160",
"200",
149 "250",
"315",
"400",
"500",
"630",
"800",
"1k",
"1.25k",
"1.6k",
"2k",
"2.5k",
150 "3.15k",
"4k",
"5k",
"6.3k",
"8k",
"10k",
"12.5k",
"16k",
"20k"};
158 double operator()(std::span<const float> frame,
double rate,
double fmax = 2000.)
160 if(rate <= 0 || frame.size() < 64)
162 if(!m_yin || m_rate != rate)
164 m_yin.emplace(
int(rate));
165 m_yin->setMaxFrequency(
float(fmax));
168 m_frame.assign(frame.begin(), frame.end());
169 const float f = m_yin->pitchYin(m_frame);
170 return f > 0.f ? double(f) : 0.;
174 std::optional<Yin<float>> m_yin;
175 std::vector<float> m_frame;
180inline QString
noteName(
double f,
double* cents =
nullptr)
184 const double midi = 69. + 12. * std::log2(f / 440.);
185 const int n = int(std::lround(midi));
187 *cents = (midi - n) * 100.;
Definition SpectrumAnalysis.hpp:156
Definition BufferDecode.hpp:16
QString noteName(double f, double *cents=nullptr)
"A4", "C#3"
Definition SpectrumAnalysis.hpp:180
QString midiNoteName(int pitch)
The name of a MIDI note, middle C (60) being C4; "?" out of 0 to 127.
Definition MidiText.hpp:15
Definition SpectrumAnalysis.hpp:33
void analyse(const T *x) noexcept
Analyses size samples; x must hold at least that many.
Definition SpectrumAnalysis.hpp:92
double bandPower(std::span< const double > p, double f0, double f1) const noexcept
Definition SpectrumAnalysis.hpp:115
double enbw() const noexcept
Definition SpectrumAnalysis.hpp:110
std::vector< double > power
Definition SpectrumAnalysis.hpp:43
Definition SpectrumAnalysis.hpp:141
static constexpr std::array< const char *, count > labels
IEC 61260 nominal centre frequencies.
Definition SpectrumAnalysis.hpp:147