Migrate audio payloads onto asset VFS

This commit is contained in:
gamer147
2026-07-11 11:01:11 -04:00
parent e0ca7936e2
commit 233f791c10
9 changed files with 132 additions and 64 deletions

View File

@@ -11,7 +11,7 @@ public sealed class GodotAdvHost : IHost
private readonly string _scene; // e.g. "SC0000" — for section_base
private readonly object _imageLock = new();
private readonly Dictionary<int, RgbaImage?> _images = new(); // raw catalog id -> decoded pixels
private readonly string?[] _sfxPaths = new string?[10]; // SC0000 native channel subset
private readonly string?[] _sfxNames = new string?[10]; // SC0000 native channel subset
// slot -> dims. Slot 0 is the primary/screen surface (800x600), normally created at engine boot which
// the single-scene harness skips; seed it so the first CG's anchor math stays correct (not 0x0).
private readonly Dictionary<int, (int W, int H)> _slotDims = new() { { 0, (800, 600) } };
@@ -280,46 +280,57 @@ public sealed class GodotAdvHost : IHost
// BGM: addressed by direct name (BGM{id:D3}.OGG), NOT the manifest. Voice: via the per-scene manifest.
public void PlayBgm(long id)
{
var path = _res.BgmPathById(id);
_timeline?.Event("bgm", new() { ["id"] = id, ["file"] = path != null ? System.IO.Path.GetFileName(path) : null });
if (path != null) _main.CallDeferred("PlayBgm", path);
var asset = _res.ResolveBgm(id);
var audio = asset != null ? LoadAudio(asset) : null;
_timeline?.Event("bgm", new() { ["id"] = id, ["file"] = audio?.Name });
if (audio != null) _main.CallDeferred("PlayBgm", audio.Bytes, audio.Name);
}
public void PlayVoice(long id)
{
var asset = _res.Resolve(_scene, id);
var path = asset != null ? ResourceMap.AudioPath(asset) : null;
if (path != null) _main.CallDeferred("PlayVoice", path);
var audio = asset != null ? LoadAudio(asset) : null;
if (audio != null) _main.CallDeferred("PlayVoice", audio.Bytes, audio.Name);
}
public void LoadSoundEffect(long resourceId, int channel)
{
if ((uint)channel >= (uint)_sfxPaths.Length) return;
if ((uint)channel >= (uint)_sfxNames.Length) return;
var asset = _res.Resolve(_scene, resourceId);
var path = asset != null ? ResourceMap.AudioPath(asset) : null;
_sfxPaths[channel] = path;
var audio = asset != null ? LoadAudio(asset) : null;
_sfxNames[channel] = audio?.Name;
_timeline?.Event("sfx-load", new() { ["resource"] = resourceId, ["channel"] = channel,
["file"] = path != null ? System.IO.Path.GetFileName(path) : null });
if (path != null) _main.CallDeferred("LoadSoundEffect", path, channel);
["file"] = audio?.Name });
if (audio != null) _main.CallDeferred("LoadSoundEffect", audio.Bytes, audio.Name, channel);
}
public void StartSoundEffect(int channel)
{
if ((uint)channel >= (uint)_sfxPaths.Length || _sfxPaths[channel] == null) return;
if ((uint)channel >= (uint)_sfxNames.Length || _sfxNames[channel] == null) return;
_timeline?.Event("sfx-start", new() { ["channel"] = channel,
["file"] = System.IO.Path.GetFileName(_sfxPaths[channel]) });
["file"] = _sfxNames[channel] });
_main.CallDeferred("StartSoundEffect", channel);
}
public void ReleaseSoundEffect(int channel)
{
if ((uint)channel >= (uint)_sfxPaths.Length) return;
if ((uint)channel >= (uint)_sfxNames.Length) return;
_timeline?.Event("sfx-release", new() { ["channel"] = channel,
["file"] = _sfxPaths[channel] != null ? System.IO.Path.GetFileName(_sfxPaths[channel]) : null });
_sfxPaths[channel] = null;
["file"] = _sfxNames[channel] });
_sfxNames[channel] = null;
_main.CallDeferred("ReleaseSoundEffect", channel);
}
private AudioPayload? LoadAudio(AssetEntry asset)
{
try { return _res.ReadAudio(asset); }
catch (System.Exception e)
{
Godot.GD.Print($"audio read failed {asset.Name}: {e.Message}");
return null;
}
}
public void FadeBgm(int targetPercent, long durationMs)
{
long ms = System.Math.Clamp(durationMs, 0, 60_000);

View File

@@ -514,31 +514,31 @@ public partial class Main : Godot.Control
img.SetData(w, h, false, img.GetFormat(), px);
}
// Load an OGG off disk and play it. BGM loops; voice plays once, cutting off any prior line.
public void PlayBgm(string oggPath)
// Decode VFS-owned bytes in Godot. BGM loops; voice plays once, cutting off any prior line.
public void PlayBgm(byte[] oggBytes, string assetName)
{
var stream = AudioStreamOggVorbis.LoadFromBuffer(System.IO.File.ReadAllBytes(oggPath));
if (stream == null) { GD.Print($"OGG load failed {oggPath}"); return; }
var stream = AudioStreamOggVorbis.LoadFromBuffer(oggBytes);
if (stream == null) { GD.Print($"OGG load failed {assetName}"); return; }
stream.Loop = true;
_bgm.VolumeDb = 0;
_bgm.Stream = stream;
_bgm.Play();
}
public void PlayVoice(string oggPath)
public void PlayVoice(byte[] oggBytes, string assetName)
{
var stream = AudioStreamOggVorbis.LoadFromBuffer(System.IO.File.ReadAllBytes(oggPath));
if (stream == null) { GD.Print($"OGG load failed {oggPath}"); return; }
var stream = AudioStreamOggVorbis.LoadFromBuffer(oggBytes);
if (stream == null) { GD.Print($"OGG load failed {assetName}"); return; }
stream.Loop = false;
_voice.Stream = stream;
_voice.Play();
}
public void LoadSoundEffect(string wavPath, int channel)
public void LoadSoundEffect(byte[] wavBytes, string assetName, int channel)
{
if ((uint)channel >= (uint)_sfx.Length) return;
var stream = AudioStreamWav.LoadFromBuffer(System.IO.File.ReadAllBytes(wavPath));
if (stream == null) { GD.Print($"WAV load failed {wavPath}"); return; }
var stream = AudioStreamWav.LoadFromBuffer(wavBytes);
if (stream == null) { GD.Print($"WAV load failed {assetName}"); return; }
stream.LoopMode = AudioStreamWav.LoopModeEnum.Disabled;
_sfx[channel].Stop();
_sfx[channel].VolumeDb = 0;