feat(a2b): audio — wire play-bgm/play-voice, fix BGM addressing
Wire audio end-to-end (OGG plays natively in Godot; no Frida, no decode):
- IHost.PlayBgm/PlayVoice + VM dispatch for play-bgm(0xbf)/play-voice(0xc4).
Non-Godot hosts no-op them so --selftest + engine 8/8 stay byte-identical.
- GodotAdvHost resolves id->OGG; Main plays via two AudioStreamPlayer nodes
(BGM looping; voice interrupt-on-new).
Key finding (by-ear, systematic-debugging): the two audio ops use DIFFERENT
addressing — the earlier "unified manifest" assumption was wrong for BGM.
- play-voice -> per-scene manifest files[base+id] (offset 0, same as textures).
Confirmed by ear; upgraded med->HIGH.
- play-bgm -> DIRECT LITERAL NAME BGM{id:03d}.OGG, NOT the manifest.
Real game: play-bgm 5->BGM005, 8->BGM008 (manifest gave +1). Proven by
play-bgm 0x23->BGM035 (real standalone track; BGM set skips 030-034) that the
manifest mis-resolved to a graphics entry. Fix is BGM-only:
ResourceMap.BgmPathById; voices/textures unchanged.
Also: Lily's silence root-caused as correct form-gating (G[0xa57/0xa58/0xa59]),
left unseeded by choice (no dummy state). Added diagnostic
`Age.Cli audio <SCENE.BIN> [0xADDR=VAL ...]`. Corrected opcodes.toml (play-bgm
direct-name, play-voice HIGH) + docs/memory (dropped the bogus unified-manifest
/ Frida-BGM006 claims).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,8 @@ public partial class Main : Godot.Control
|
||||
private readonly Dictionary<int, TextureRect> _slots = new();
|
||||
private Label _text = null!;
|
||||
private Label _status = null!;
|
||||
private AudioStreamPlayer _bgm = null!; // looping background music
|
||||
private AudioStreamPlayer _voice = null!; // interrupt-on-new voice
|
||||
private VirtualMachine _vm = null!;
|
||||
private GodotAdvHost _host = null!;
|
||||
private volatile bool _done;
|
||||
@@ -50,6 +52,11 @@ public partial class Main : Godot.Control
|
||||
catch { /* fall back to the default font */ }
|
||||
}
|
||||
|
||||
_bgm = new AudioStreamPlayer();
|
||||
_voice = new AudioStreamPlayer();
|
||||
AddChild(_bgm);
|
||||
AddChild(_voice);
|
||||
|
||||
_selftest = System.Array.IndexOf(OS.GetCmdlineUserArgs(), "--selftest") >= 0;
|
||||
|
||||
var table = OpcodeTableJson.Load(Paths.OpcodesJson);
|
||||
@@ -109,6 +116,25 @@ public partial class Main : Godot.Control
|
||||
tr.Visible = true;
|
||||
}
|
||||
|
||||
// Load an OGG off disk and play it. BGM loops; voice plays once, cutting off any prior line.
|
||||
public void PlayBgm(string oggPath)
|
||||
{
|
||||
var stream = AudioStreamOggVorbis.LoadFromBuffer(System.IO.File.ReadAllBytes(oggPath));
|
||||
if (stream == null) { GD.Print($"OGG load failed {oggPath}"); return; }
|
||||
stream.Loop = true;
|
||||
_bgm.Stream = stream;
|
||||
_bgm.Play();
|
||||
}
|
||||
|
||||
public void PlayVoice(string oggPath)
|
||||
{
|
||||
var stream = AudioStreamOggVorbis.LoadFromBuffer(System.IO.File.ReadAllBytes(oggPath));
|
||||
if (stream == null) { GD.Print($"OGG load failed {oggPath}"); return; }
|
||||
stream.Loop = false;
|
||||
_voice.Stream = stream;
|
||||
_voice.Play();
|
||||
}
|
||||
|
||||
public void AppendLine(string text) => _text.Text += text + "\n";
|
||||
public void PageBreak() => _status.Text = "▼ click / Enter";
|
||||
public void ClearPage() { _text.Text = ""; _status.Text = ""; }
|
||||
|
||||
Reference in New Issue
Block a user