using System;
using System.Collections.Generic;
namespace AcDream.Core.Audio;
// ─────────────────────────────────────────────────────────────────────
// Scaffold for R5 — audio data model + engine interface.
// Full research: docs/research/deepdives/r05-audio-sound.md
// Runtime backend (Silk.NET.OpenAL) lives in AcDream.App.
// ─────────────────────────────────────────────────────────────────────
// SoundId moved to its own file on 2026-07-29, when the hand-curated
// 23-member subset was replaced by retail's full 205-entry SoundType
// catalog: src/AcDream.Core/Audio/SoundId.cs (same namespace).
// A local `SoundEntry` scaffold class lived here until 2026-08-08 (Campaign A
// slice A1). It had no implementers and no readers — the live path consumes
// DatReaderWriter's `SoundEntry` directly — and four of its eight fields were
// invented: `PitchMin`/`PitchMax` (retail never calls SetFrequency), `Loop`
// (retail never sets the DirectSound loop flag; "looping" ambients are
// re-fired one-shots), and `Is3D` (every retail gameplay buffer is created
// with m_3D = 0). Its `Priority` was also typed `int` "0..7" where the dat
// field is a float in [0,1]. The `ISoundCache` interface that returned it went
// the same way. Evidence:
// docs/research/2026-08-08-audio-retail-soundmanager-core.md,
// docs/research/2026-08-08-audio-retail-dat-layer.md §1.
///
/// Raw decoded PCM data from a Wave dat. Set by WaveDecoder at
/// load time. Retail supports both PCM and MP3 source; we decode MP3
/// to PCM once at load (same as retail does for long clips).
///
public sealed class WaveData
{
public int ChannelCount { get; init; }
public int SampleRate { get; init; }
public int BitsPerSample{ get; init; } // 8 or 16
public byte[] PcmBytes { get; init; } = Array.Empty();
public TimeSpan Duration { get; init; }
}
// An `AudioFalloff` helper lived here until 2026-08-08 (Campaign A slice A2).
// It had the right shape but a 1-metre reference distance where retail's is 5,
// a `PanFromRelative` that was linear in relative X over an invented 20-metre
// range where retail pans by the SINE of a compass bearing, and no audibility
// cutoff. Nothing ever called either method. Both are superseded by
// `RetailSoundMixer`, which carries the byte-decoded retail math.
///
/// Interface the platform audio engine (AcDream.App layer) implements.
/// Core defines the contract; App implements via Silk.NET.OpenAL.
///
public interface IAudioEngine : IDisposable
{
///
/// acdream's own master, on top of retail's two live knobs. Retail has
/// three preferences — effect, ambient, and an interface one it registers
/// and never reads — and NO master and no music knob. See AP-174.
///
float MasterVolume { get; set; }
/// Retail's effect_sound_volume.
float SfxVolume { get; set; }
/// Retail's ambient_sound_volume (applied twice — TS-65).
float AmbientVolume{ get; set; }
///
/// Update the listener pose. Retail's listener is SmartBox::viewer —
/// the COLLIDED third-person camera Position, refreshed once per rendered
/// frame (SmartBox::update_viewer @ 0x00453CE0), falling back
/// to the player's own position when the camera sweep fails. Only two
/// things are ever read out of it: the origin, for distance, and
/// Frame::get_heading, for pan. There is no up vector, no velocity,
/// and therefore no doppler and no elevation cue — which is why this takes
/// a single compass heading rather than a forward/up basis.
///
void SetListener(float posX, float posY, float posZ, float headingDegrees);
/// Play a 2D UI sound (no falloff).
void PlayUi(SoundId id);
/// Play a 3D sound at a world position.
void Play3D(SoundId id, float x, float y, float z);
// A `StartAmbient(id, x, y, z)` / `StopAmbient(handle)` pair lived here
// until 2026-08-08 (Campaign A slice A5). It modelled a LOOPING,
// handle-owned ambient voice, which retail does not have: retail never sets
// the DirectSound loop flag, and a "continuous" ambient is a one-shot
// re-fired every min_rate seconds off an absolute-deadline queue, with a
// fresh variant pick and crossfade volume each time. The implementation was
// a stub that minted a handle and played nothing, while StopAmbient looked
// up a source that was never created. `AmbientSoundController` +
// `AmbientSoundScheduler` carry the real model.
// A `PlayMusic(resourceName, loop)` / `StopMusic()` pair and a `MusicVolume`
// knob lived here until 2026-08-08 (Campaign A slice A6). They modelled a
// subsystem retail does not have. The EoR client links a complete winmm
// midiStream player and never feeds it: `midiPlay` @ 0x00553390 has zero
// callers (its only textual occurrences in the 65 MB decomp are its own
// definition and its own queue drainer), both MIDI callbacks are statically
// null, the string "music" appears zero times, `SoundType` has no music
// member, `SoundManager::InitPrefs` registers no music preference, and the
// retail install ships no .mid/.mp3/.wav at all. The string-keyed signature
// was itself the tell — every other entry point in this interface is
// DID-keyed. What players remember as music is the AdminEnvirons UI stinger
// family (see `EnvironSoundCueMap`) and the intro AVI's audio track.
// Evidence: docs/research/2026-08-08-audio-retail-music-absence.md.
}