M11.2: audio playback + waveforms + audio effects

- Audio effects (Backwards, Bass & Treble, Delay, Flange & Chorus,
  High-Low Pass, Modulator, Parametric EQ, Reverb, Stereo Mixer, Tone)
  applied per layer in the comp mixdown, so export and preview share them.
  RBJ-cookbook biquads, Schroeder/Moorer reverb, modulated delay lines;
  stateless per block via pre-roll. Tone makes any layer audible.
- Real-time audio preview: ui-egui AudioFeed/AudioPlayback (feeder thread
  mixing ahead) behind an AudioDevice hook; the desktop app implements it
  with cpal (L6). Audio clock drives playback (consumed frames minus device
  latency, looped through the work area). Preview panel Include Audio /
  playback.muteAudio.
- Audio panel VU meters with peak hold and clip indicators; selected
  layer's Audio Levels.
- Timeline Audio > Waveform for footage with audio (L reveals Audio
  Levels, LL the Waveform), drawn from a cached per-item peak summary.
- ui.playback reports audio state and meter levels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas
2026-10-02 00:16:34 -04:00
co-authored by Claude Opus 5.5
parent 1c0da40173
commit ddb33cdfd0
19 changed files with 1483 additions and 77 deletions
+4 -1
View File
@@ -304,9 +304,12 @@ pub fn handle(app: &mut EffectcraftApp, ctx: &egui::Context, req: &ControlReques
match s("action").unwrap_or("toggle") {
"play" => app.play(now),
"stop" => app.stop(),
"status" => {}
_ => app.toggle_play(now),
}
ok(json!({"playing": app.playback.playing, "time": app.session.time().seconds()}))
ok(
json!({"playing": app.playback.playing, "time": app.session.time().seconds(), "audio": app.audio.is_some(), "levelsDb": app.meter.level_db, "peaksDb": app.meter.peak_db}),
)
}
"ui.screenshot" => {
let crop = s("panel").and_then(PanelKind::from_name).and_then(|pk| app.auto.find(&format!("panel.{}", pk.id())).map(|e| e.rect));