Add real-time note detection and NOTEDROP visualization

Implemented musical note detection in AudioAnalyzer, exposing dominant note and strength in AudioFeatures. Updated HUD to display detected note. Added "NOTEDROP" fractal preset that animates detected notes as colored glyphs. Extended bitmap font to support sharps. Increased fractal preset count and adjusted HUD layout.
This commit is contained in:
Michal Vrba
2026-02-13 20:43:29 +01:00
parent 8bf3978354
commit 6a1bdfb935
5 changed files with 221 additions and 5 deletions
+46
View File
@@ -168,6 +168,8 @@ public sealed class AudioAnalyzer : IDisposable
float bassEnergy = 0f;
float midEnergy = 0f;
float highEnergy = 0f;
float dominantMagnitude = 0f;
float dominantFrequency = 0f;
for (int bin = 1; bin < half; bin++)
{
@@ -195,6 +197,12 @@ public sealed class AudioAnalyzer : IDisposable
{
highEnergy += magnitude;
}
if (frequency >= 80f && frequency <= 2000f && magnitude > dominantMagnitude)
{
dominantMagnitude = magnitude;
dominantFrequency = frequency;
}
}
float[] spectrum = new float[_spectrumBins];
@@ -248,6 +256,9 @@ public sealed class AudioAnalyzer : IDisposable
float bpmInstant = CalculateBpm();
_smoothedBpm = Smooth(_smoothedBpm, bpmInstant, 0.18f, 0.04f);
int noteIndex = GetNoteIndexFromFrequency(dominantFrequency);
float dominantStrength = Clamp01(dominantMagnitude * 0.02f);
AudioFeatures features = new()
{
Level = _smoothedLevel,
@@ -256,6 +267,9 @@ public sealed class AudioAnalyzer : IDisposable
High = _smoothedHigh,
Beat = _smoothedBeat,
Bpm = _smoothedBpm,
DominantFrequencyHz = dominantFrequency,
DetectedNoteIndex = noteIndex,
DetectedNoteStrength = dominantStrength,
Spectrum = spectrum,
TimeSeconds = now
};
@@ -319,6 +333,38 @@ public sealed class AudioAnalyzer : IDisposable
return Math.Clamp(bpm, 40f, 220f);
}
private static int GetNoteIndexFromFrequency(float frequencyHz)
{
if (frequencyHz <= 0f)
{
return -1;
}
float midi = 69f + (12f * MathF.Log2(frequencyHz / 440f));
int note = ((int)MathF.Round(midi)) % 12;
if (note < 0)
{
note += 12;
}
return note;
}
private static float Clamp01(float value)
{
if (value < 0f)
{
return 0f;
}
if (value > 1f)
{
return 1f;
}
return value;
}
private void ThrowIfDisposed()
{
if (_disposed)