Own XM/MOD replayer, audio mixer with crossfades, synthesised chip sound effects, music director

- src/xm.rs: FastTracker 2 XM and ProTracker MOD replayer (verified against libopenmpt)
- src/audio.rs: music players with crossfade + effect voices, rendered on demand
- src/sfx.rs: 29 synthesised chip sound effects
- www/dc_plugin.js: AudioContext started on first input, streams samples from WASM
- music follows the game: title, rotating build tracks, battle, gate assault, jingles
- macroquad audio removed; all tests run natively with cargo test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-03 06:10:28 +01:00
1 parent 2dba65be8b
commit 925be17e82
13 files changed
+1711 -205

No files matched your search

+2 -3
View File
@@ -7,7 +7,6 @@ use crate::i18n::{languages, tr, trf};
use crate::map::*;
use crate::render3d::{minimap_color, Cam};
use crate::settings::{key_name, Action, Settings};
use crate::sound::SoundBank;
use macroquad::prelude::*;
// UI font scale (setting: 2 = normal, 3 = large); character cell size follows it
@@ -237,7 +236,7 @@ impl Ui {
UiAction::None
}
pub fn draw(&mut self, g: &Game, gfx: &Gfx, cam: &Cam, snd: &SoundBank, settings: &Settings, info: &[(String, Color)], now: f64) {
pub fn draw(&mut self, g: &Game, gfx: &Gfx, cam: &Cam, settings: &Settings, info: &[(String, Color)], now: f64) {
self.rects.clear();
let keys: Vec<String> = (0..TOOLS.len()).map(|i| key_name(settings.key(Action::Tool(i)))).collect();
let sw = screen_width();
@@ -280,7 +279,7 @@ impl Ui {
let wave_col = if g.wave_timer < 30.0 { RED } else { TXT };
seg(&trf("hud.wave", &[(g.wave + 1).to_string()]), wave_col, &mmss(g.wave_timer), wave_col, &mut x);
seg(&tr("hud.army"), TXT, &format!("{}", g.army_value() as i32), TXT, &mut x);
let vol = if snd.muted { tr("hud.off") } else { format!("{}%", (snd.volume * 100.0).round() as i32) };
let vol = if settings.muted { tr("hud.off") } else { format!("{}%", (settings.volume * 100.0).round() as i32) };
// volume only if it still fits into the bar
if x + gfx.measure(&format!("♪ {}", vol), S()) < sw - CW() * 2.0 {
seg("♪", DIM, &vol, DIM, &mut x);