chore(audio): Campaign A slice A6 — delete what retail does not have

Retail EoR has no music system: the linked winmm MIDI player has zero
callers, 'music' appears zero times in the 65 MB decomp, SoundType has no
music member, InitPrefs registers no music key, and the install ships no
music files. So PlayMusic/StopMusic/MusicVolume and the AudioSettings
Music knob are deleted rather than left as an API modelling dead code —
the string-keyed signature was the tell, since every other entry point is
DID-keyed. Old settings.json files carrying a 'music' key still load; the
reader ignores unknown keys and the next save drops it.

The Ambient slider is now surfaced, because slice A5 gave it something to
drive, and its default returns to retail's 1.0 from an invented 0.8 —
InitPrefs defaults every sound preference to unity. The panel rule is
unchanged: no slider that does nothing.

r05-audio-sound.md gets a SUPERSEDED banner naming its five wrong
sections (falloff, pan, voice pool, selection, music, ambient) so a
future reader reaches the lane notes instead of the Ghidra-era reads that
this campaign spent its first two slices undoing.

TS-9 re-scoped from 'any MP3 cue' to the measured blast radius: exactly 1
MP3 among 786 shipped waves, a ~2 s mono clip. Its original framing
assumed a music system that does not exist. The ADPCM count remains
unmeasured and is named as the open question.

Deferred deliberately: #321's sound-cache decode-dedup race. It is a
pre-existing concurrency flake rather than audio-parity behaviour, and
shipping a speculative fix to a race I have not reproduced is exactly the
shortcut this project's no-workarounds rule exists to prevent.

Campaign A is code-complete; the plan carries the closeout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-08 22:58:49 +02:00
parent 7c4dd1ade7
commit dd2cb92b99
13 changed files with 152 additions and 65 deletions

View file

@ -13,13 +13,13 @@ public sealed class AudioSettingsTests
[Fact]
public void Default_values_match_engine_constructor_defaults()
{
// OpenAlAudioEngine ctor: Master=1.0, Music=0.7, Sfx=1.0,
// Ambient=0.8 — see src/AcDream.App/Audio/OpenAlAudioEngine.cs.
// Retail's SoundManager::InitPrefs @ 0x005503F0 defaults every sound
// preference to 1.0; acdream's extra master matches. The Music knob is
// gone (retail has no music system) and Ambient's invented 0.8 with it.
var d = AudioSettings.Default;
Assert.Equal(1.0f, d.Master);
Assert.Equal(0.7f, d.Music);
Assert.Equal(1.0f, d.Sfx);
Assert.Equal(0.8f, d.Ambient);
Assert.Equal(1.0f, d.Ambient);
}
[Fact]
@ -35,8 +35,8 @@ public sealed class AudioSettingsTests
[Fact]
public void With_expression_clones_one_field()
{
var d = AudioSettings.Default with { Music = 0.25f };
Assert.Equal(0.25f, d.Music);
var d = AudioSettings.Default with { Ambient = 0.25f };
Assert.Equal(0.25f, d.Ambient);
// Other fields untouched.
Assert.Equal(AudioSettings.Default.Master, d.Master);
Assert.Equal(AudioSettings.Default.Sfx, d.Sfx);

View file

@ -349,11 +349,11 @@ public sealed class SettingsPanelTests
[Fact]
public void Audio_tab_when_active_renders_implemented_volume_sliders()
{
// L.0 ships Master + SFX only — Music + Ambient sliders are
// hidden until R5 MIDI / ambient-loop engines exist. The
// AudioSettings record still carries those fields so the
// JSON round-trips, but the panel doesn't surface a slider
// that wouldn't actually do anything.
// The rule is "no slider that does nothing", and Campaign A changed
// what qualifies. Ambient is now surfaced because slice A5 gave it a
// region ambient system to drive. Music is gone entirely — retail has
// no music system, so slice A6 deleted the field rather than leaving a
// knob that could never do anything.
var (panel, _, _, _) = Build();
var r = new FakePanelRenderer { ActiveTabLabel = "Audio" };
@ -363,8 +363,8 @@ public sealed class SettingsPanelTests
.Select(c => (string)c.Args[0]!).ToList();
Assert.Contains("Master", sliders);
Assert.Contains("SFX", sliders);
Assert.Contains("Ambient", sliders);
Assert.DoesNotContain("Music", sliders);
Assert.DoesNotContain("Ambient", sliders);
}
[Fact]

View file

@ -156,7 +156,7 @@ public sealed class SettingsStoreTests : System.IDisposable
public void SaveAudio_then_LoadAudio_round_trips_all_fields()
{
var store = new SettingsStore(_tempPath);
var original = new AudioSettings(Master: 0.3f, Music: 0.45f, Sfx: 0.9f, Ambient: 0.6f);
var original = new AudioSettings(Master: 0.3f, Sfx: 0.9f, Ambient: 0.6f);
store.SaveAudio(original);
var loaded = store.LoadAudio();
@ -178,7 +178,6 @@ public sealed class SettingsStoreTests : System.IDisposable
var loaded = store.LoadAudio();
Assert.Equal(0.25f, loaded.Master);
Assert.Equal(AudioSettings.Default.Music, loaded.Music);
Assert.Equal(AudioSettings.Default.Sfx, loaded.Sfx);
Assert.Equal(AudioSettings.Default.Ambient, loaded.Ambient);
}
@ -200,10 +199,10 @@ public sealed class SettingsStoreTests : System.IDisposable
{
// Reverse order — audio must survive a subsequent display save.
var store = new SettingsStore(_tempPath);
store.SaveAudio(AudioSettings.Default with { Music = 0.1f });
store.SaveAudio(AudioSettings.Default with { Ambient = 0.1f });
store.SaveDisplay(DisplaySettings.Default with { ShowFps = true });
Assert.Equal(0.1f, store.LoadAudio().Music);
Assert.Equal(0.1f, store.LoadAudio().Ambient);
Assert.True(store.LoadDisplay().ShowFps);
}

View file

@ -360,7 +360,7 @@ public sealed class SettingsVMTests
[Fact]
public void AudioDraft_initial_value_matches_persisted()
{
var custom = AudioSettings.Default with { Master = 0.3f, Music = 0.1f };
var custom = AudioSettings.Default with { Master = 0.3f, Ambient = 0.1f };
var (vm, _, _, _, _, _, _, _, _, _) = Build(persistedAudio: custom);
Assert.Equal(custom, vm.AudioDraft);
Assert.False(vm.HasUnsavedChanges);
@ -391,9 +391,9 @@ public sealed class SettingsVMTests
[Fact]
public void Cancel_reverts_audio_draft_to_persisted()
{
var custom = AudioSettings.Default with { Music = 0.2f };
var custom = AudioSettings.Default with { Ambient = 0.2f };
var (vm, _, _, _, _, _, _, _, _, _) = Build(persistedAudio: custom);
vm.SetAudio(vm.AudioDraft with { Music = 0.9f, Master = 0.3f });
vm.SetAudio(vm.AudioDraft with { Ambient = 0.9f, Master = 0.3f });
Assert.True(vm.HasUnsavedChanges);
vm.Cancel();