mirror of
https://codeberg.org/armin/beamgrid.git
synced 2026-09-01 20:30:47 +02:00
- BOOST: global display level multiplier (0.75 - 2.5, 1.0 default) - 3D: pseudo-3D bevel/shading on bars, LEDs, peaks and curve (0 = flat) - G-ANG: 3D gradient angle (-180 - 180, 0 = top-lit) - Apply CURVING rounded corners to bars and peaks in BAR mode
88 lines
3.5 KiB
C++
88 lines
3.5 KiB
C++
#pragma once
|
|
|
|
#include "JuceHeader.h"
|
|
|
|
// Beamgrid analyser: log-spaced spectrum bands with peak-hold markers.
|
|
// Each band tracks a current level and a peak that holds for a definable
|
|
// "grace" time before falling off at a definable rate.
|
|
class Analyser
|
|
{
|
|
public:
|
|
static constexpr int maxBands = 256;
|
|
enum { fftOrder = 11, fftSize = 1 << fftOrder, fftBins = fftSize / 2 };
|
|
|
|
Analyser();
|
|
|
|
void prepare (double sampleRate, int blockSize);
|
|
void push (const float* channelData, int numSamples);
|
|
void update (double dt);
|
|
|
|
int getNumBands() const noexcept { return numBands; }
|
|
float getLevel (int band) const noexcept { return band < numBands ? juce::jmin (1.0f, levels[band]) : 0.0f; }
|
|
float getPeak (int band) const noexcept { return band < numBands ? juce::jmin (1.0f, peaks[band]) : 0.0f; }
|
|
|
|
// Helpers for drawing axis legends.
|
|
double getMinFreq() const noexcept { return minFreq; }
|
|
double getMaxFreq() const noexcept { return maxFreq; }
|
|
float freqToFraction (double freq) const noexcept;
|
|
float dbToFraction (double db) const noexcept;
|
|
|
|
void setGraceSeconds (double seconds) noexcept { grace = seconds; }
|
|
void setFalloffRate (double rate) noexcept { falloffRate = rate; }
|
|
void setBarReleaseTau (double seconds) noexcept { barReleaseTau = seconds; }
|
|
void setNumBands (int n) noexcept;
|
|
void setTilt (double amount) noexcept { tilt = amount; }
|
|
double getTilt() const noexcept { return tilt; }
|
|
void setBoost (double amount) noexcept { boost = amount; }
|
|
double getBoost() const noexcept { return boost; }
|
|
double getGraceSeconds() const noexcept { return grace; }
|
|
double getFalloffRate() const noexcept { return falloffRate; }
|
|
|
|
private:
|
|
void pushSample (float sample);
|
|
void computeFFT();
|
|
void computeBandEdges();
|
|
|
|
juce::dsp::FFT fft { fftOrder };
|
|
juce::dsp::WindowingFunction<float> window { fftSize, juce::dsp::WindowingFunction<float>::hann, true };
|
|
|
|
// Sliding time-domain window so the FFT is recomputed every `hopSize`
|
|
// samples (overlapped) instead of only once per full window. This keeps
|
|
// the visualization temporally smooth.
|
|
static constexpr int hopSize = 256;
|
|
|
|
std::array<float, fftSize> ring {};
|
|
std::array<float, 2 * fftSize> fftData {};
|
|
int writePos = 0;
|
|
int samplesSinceFFT = 0;
|
|
int totalSamples = 0;
|
|
|
|
int numBands = 64;
|
|
std::array<float, maxBands> levels {};
|
|
std::array<float, maxBands> targets {};
|
|
std::array<float, maxBands> peaks {};
|
|
std::array<float, maxBands> peakTimers {};
|
|
|
|
// First/last FFT bin index covered by each band (log spaced).
|
|
std::array<int, maxBands + 1> bandBinEdges {};
|
|
|
|
double minFreq = 20.0;
|
|
double maxFreq = 22050.0;
|
|
|
|
double sampleRate = 44100.0;
|
|
double grace = 0.8; // seconds the peak is held before falling
|
|
double falloffRate = 0.8; // normalized units per second while falling
|
|
double barReleaseTau = 0.2; // seconds for the live bar to fall (release)
|
|
double minDb = -48.0; // dBFS-equivalent floor (bands below this -> 0)
|
|
double maxDb = 0.0; // dBFS-equivalent ceiling (bands at/above -> 1)
|
|
|
|
// Spectral tilt: +1 fully boosts the highs and cuts the lows, -1 does the
|
|
// opposite, 0 is flat. The gain is applied symmetrically about the band at
|
|
// the centre of the log-frequency range.
|
|
static constexpr double maxTiltDb = 18.0;
|
|
double tilt = 0.0;
|
|
|
|
// Display boost: a global multiplier on the level the analyser shows
|
|
// (0.75 = quieter, 1.5 = louder, 1.0 = unity).
|
|
double boost = 1.0;
|
|
};
|