53 "C",
"C#",
"D",
"D#",
"E",
"F",
"F#",
"G",
"G#",
"A",
"A#",
"B"
66 if (bufferSize < 8)
return;
69 fl::snprintf(buffer, bufferSize,
"%s %s", root, quality);
88 for (
int i = 0; i < 12; i++) {
100 float majorSum = 0.0f, majorSqSum = 0.0f;
101 for (
int i = 0; i < 12; i++) {
109 float minorSum = 0.0f, minorSqSum = 0.0f;
110 for (
int i = 0; i < 12; i++) {
122 u32 timestamp = context->getTimestamp();
125 float chroma[12] = {0};
133 float avgChroma[12] = {0};
149 bool acceptChange =
false;
200 for (
int i = 0; i < 12; i++) {
211 for (
int i = 0; i < 12; i++) {
218 const int numBins =
static_cast<int>(linearBins.
size());
219 const float fmin =
fft.linearFmin();
220 const float fmax =
fft.linearFmax();
221 const float linearBinWidth = (numBins > 0) ? (fmax - fmin) /
static_cast<float>(numBins) : 1.0f;
224 for (
int bin = 0; bin < numBins; bin++) {
225 float magnitude = linearBins[bin];
226 if (magnitude < 1e-6f)
continue;
229 float freq = fmin + (
static_cast<float>(bin) + 0.5f) * linearBinWidth;
232 if (freq < 60.0f)
continue;
236 float midiNote = 69.0f + 12.0f * (
fl::logf(freq / 440.0f) /
fl::logf(2.0f));
239 int pitchClass =
static_cast<int>(midiNote + 0.5f) % 12;
240 if (pitchClass < 0) pitchClass += 12;
243 chroma[pitchClass] += magnitude;
250 for (
int i = 0; i < 12; i++) {
251 if (chroma[i] > maxVal) {
257 if (maxVal > 1e-6f) {
258 for (
int i = 0; i < 12; i++) {
266 for (
int i = 0; i < 12; i++) {
283 for (
int i = 0; i < 12; i++) {
290 for (
int i = 0; i < 12; i++) {
304 float bestCorrelation = -1.0f;
306 bool bestIsMinor =
false;
309 for (
int root = 0; root < 12; root++) {
312 if (majorCorr > bestCorrelation) {
313 bestCorrelation = majorCorr;
320 if (minorCorr > bestCorrelation) {
321 bestCorrelation = minorCorr;
329 float confidence = (bestCorrelation + 1.0f) / 2.0f;
332 return Key(bestRoot, bestIsMinor, confidence, timestamp);
340 float profileMean, profileStdDev;
348 if (profileStdDev < 1e-6f) profileStdDev = 1.0f;
351 float chromaSum = 0.0f;
352 float chromaSqSum = 0.0f;
353 for (
int i = 0; i < 12; i++) {
354 chromaSum += chroma[i];
355 chromaSqSum += chroma[i] * chroma[i];
357 float chromaMean = chromaSum / 12.0f;
358 float chromaStdDev =
sqrtf((chromaSqSum / 12.0f) - (chromaMean * chromaMean));
359 if (chromaStdDev < 1e-6f) chromaStdDev = 1.0f;
362 float correlation = 0.0f;
363 for (
int i = 0; i < 12; i++) {
364 int profileIdx = (i - rootNote + 12) % 12;
365 float chromaNorm = (chroma[i] - chromaMean) / chromaStdDev;
366 float profileNorm = (profile[profileIdx] - profileMean) / profileStdDev;
367 correlation += chromaNorm * profileNorm;
369 correlation /= 12.0f;
KeyDetector() FL_NOEXCEPT
shared_ptr< const fft::Bins > mRetainedFFT
void normalizeChroma(float *chroma)
float correlateWithProfile(const float *chroma, const float *profile, int rootNote)
static const float MAJOR_PROFILE[12]
~KeyDetector() FL_NOEXCEPT override
vector< float > mChromaHistory[12]
function_list< void()> onKeyEnd
void extractChroma(const fft::Bins &fft, float *chroma)
float mMinorProfileStdDev
function_list< void(const Key &key)> onKeyChange
float mMajorProfileStdDev
static const float MINOR_PROFILE[12]
void updateChromaHistory(const float *chroma)
void fireCallbacks() override
function_list< void(const Key &key)> onKey
float mConfidenceThreshold
void update(shared_ptr< Context > context) override
Key detectKey(const float *chroma, u32 timestamp)
void initializeProfileStats()
void getAveragedChroma(float *chroma)
constexpr fl::size size() const FL_NOEXCEPT
Centralized logging categories for FastLED hardware interfaces and subsystems.
static const char * NOTE_NAMES[12]
FL_DISABLE_WARNING_PUSH U constexpr common_type_t< T, U > min(T a, U b) FL_NOEXCEPT
float sqrtf(float value) FL_NOEXCEPT
constexpr common_type_t< T, U > max(T a, U b) FL_NOEXCEPT
int snprintf(char *buffer, fl::size size, const char *format, const Args &... args) FL_NOEXCEPT
Snprintf-like formatting function that writes to a buffer.
float logf(float value) FL_NOEXCEPT
Base definition for an LED controller.
const char * getRootName() const
const char * getQuality() const
void getKeyName(char *buffer, size_t bufferSize) const