Add librosa beat/mood analysis and an Ambience background driven by it
The background worker now runs a real librosa analyzer (tempo, beat grid, per-second energy/valence curves) instead of only the null baseline, kept behind build_analyzer() so a plain checkout without the analysis extra still runs fine. The web player reads that per-track analysis and drives a new animated "Ambience" background (bubbles, colour, current) that reacts to the beat and the mood curve as the track plays, plus a debug overlay for tuning it. Also adds a one-off script to backfill podcast cover art from iTunes.
This commit is contained in:
68
web/src/lib/ambienceTunables.ts
Normal file
68
web/src/lib/ambienceTunables.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
/** The 5 parameters `?debugDynamicUI=1` can adjust live, session-only (plain React
|
||||
* state in App.tsx, no persistence). Every other constant this refactor introduces
|
||||
* lives in `lib/ambience.ts` / `components/Ambience.tsx` as a plain named constant -
|
||||
* that pair of files plus this one's `DEFAULT_TUNABLES` is the complete answer to
|
||||
* "what do I edit to make a debug-slider tweak permanent."
|
||||
*/
|
||||
|
||||
export interface AmbienceTunables {
|
||||
/** Multiplies energy's swing on chroma/lightness. 1.0 = default. */
|
||||
energyColorGain: number;
|
||||
/** Multiplies how much a beat crossing boosts current magnitude, on top of the
|
||||
* always-present tempo x drive-derived base. 0 = no effect. */
|
||||
beatCurrentFactor: number;
|
||||
/** Multiplies the sampled `drive` curve before it modulates current magnitude (see
|
||||
* `currentMagnitudeAt`). 1.0 = default; 0 = current ignores drive entirely and sits
|
||||
* at `DRIVE_MAGNITUDE_FLOOR` x base magnitude regardless of the music. */
|
||||
driveCurrentGain: number;
|
||||
/** How much bigger a bubble gets by the time it's risen a full screen height, e.g.
|
||||
* 0.4 = 40% larger at the top. */
|
||||
bubbleGrowthRate: number;
|
||||
/** Seconds: exponential smoothing time constant for how quickly the curve-sampled
|
||||
* energy/valence/drive feeding colour and current track the underlying (already
|
||||
* linearly-interpolated) curve - a frontend preview knob, distinct from the
|
||||
* backend's fixed analysis-time sampling rate (`_ANALYSIS_HOP_SECONDS` in
|
||||
* `librosa_analyzer.py`, which needs a re-analysis to change). 0 = no extra
|
||||
* smoothing. */
|
||||
curveSmoothingTau: number;
|
||||
}
|
||||
|
||||
export const DEFAULT_TUNABLES: AmbienceTunables = {
|
||||
energyColorGain: 1.0,
|
||||
beatCurrentFactor: 0.6,
|
||||
driveCurrentGain: 1.0,
|
||||
bubbleGrowthRate: 0.4,
|
||||
curveSmoothingTau: 0.4,
|
||||
};
|
||||
|
||||
/** Drives the whole visual pipeline from these values instead of real playback data -
|
||||
* a "playground mode" for developing a feel for the effect without needing music
|
||||
* playing. Only takes effect while nothing is actually playing (`!state.playing`) -
|
||||
* real playback always wins the instant it starts, so there's never a fight between
|
||||
* the two. Session-only, same as `AmbienceTunables`; distinct from it conceptually:
|
||||
* tunables are *parameters* (how strongly the system reacts), this is *input*
|
||||
* (pretending to be the music). */
|
||||
export interface ManualControl {
|
||||
enabled: boolean;
|
||||
energy: number; // 0..1
|
||||
valence: number; // 0..1
|
||||
tempo: number; // bpm - also stands in for the current's base magnitude and rise
|
||||
/** 0..1 - stands in for the sampled `drive` curve, since there's no real track to
|
||||
* derive rhythmic intensity from. 1.0 default = full current magnitude, matching
|
||||
* the flat-curve fallback's own default (see `effectiveCurve`). */
|
||||
drive: number;
|
||||
/** Strength of each synthetic beat pulse, auto-generated at the `tempo` above while
|
||||
* manual mode is active - a continuous fake metronome rather than a one-shot
|
||||
* trigger button, so the beat-driven effects (spawn burst + current kick) can be
|
||||
* watched continuously rather than poked one click at a time. */
|
||||
beatStrength: number; // 0..1
|
||||
}
|
||||
|
||||
export const DEFAULT_MANUAL_CONTROL: ManualControl = {
|
||||
enabled: false,
|
||||
energy: 0.5,
|
||||
valence: 0.5,
|
||||
tempo: 120,
|
||||
drive: 1.0,
|
||||
beatStrength: 0.8,
|
||||
};
|
||||
Reference in New Issue
Block a user