1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! Amiga ProTracker / SoundTracker module ("MOD") support, plus a
//! structural parser for Scream Tracker v1 (`.stm`) files.
//!
//! MOD files are self-contained song data: a 20-byte title, 31 sample
//! descriptors, a pattern order list, a 4-character signature that
//! identifies the channel count, 64×N-channel patterns, then raw signed
//! 8-bit sample bodies.
//!
//! This crate registers:
//!
//! - A **container** (`mod`) that slurps the entire file and emits it as
//! a single packet. The "packets" abstraction isn't natural for MOD —
//! playback is driven by song position + effect state, not per-packet
//! decode — so the container just delivers the bytes to the codec.
//! - A **mixed-stereo decoder** under codec id [`CODEC_ID_STR`] = `"mod"`.
//! Emits one interleaved S16 stereo `AudioFrame` every ~1024 samples;
//! the drop-in option for plug-and-play playback. Stereo pan defaults
//! to a 70 % L/R separation (matching xmp/openmpt/libxmp's default)
//! rather than the strict Amiga hard pan — see
//! [`player::PlayerState::set_pan_separation`] for the override and
//! the rationale (`Protracker-effects-MODFIL12.txt` §11 itself
//! recommends *against* full hard pan "Especially when using
//! headphones").
//! - A **per-channel decoder** under codec id [`CODEC_ID_PLANAR_STR`] =
//! `"mod_planar"`. Emits planar S16P `AudioFrame`s with one plane per
//! MOD tracker channel (4 / 6 / 8 / … / 32), post-volume but
//! pre-pan/pre-mix. Consumers that need independent channel streams
//! (DAWs, visualisers, per-instrument remastering) select this codec
//! id instead of `"mod"`.
//! - A **container** (`stm`) that recognises Scream Tracker v1 modules
//! (pre-S3M, 4-channel-fixed), parses the header + instrument table +
//! pattern data + sample bodies, and exposes them for downstream
//! consumers. A minimum STM playback engine is wired via the shared
//! [`mixer::MixerVoice`] core and [`mixer::StmC3Pitch`] pitch model;
//! drive it via [`stm_player::StmPlayerState::render`]. The associated
//! codec id [`CODEC_ID_STM_STR`] = `"stm"` is still a parsing-only
//! decoder registration (it validates the packet then returns an
//! explicit `unsupported`), because effect support and global mixer
//! parameters are not yet feature-complete; callers wiring STM into
//! the broader oxideav pipeline should hook up
//! `StmPlayerState::render` directly for now.
//! - A **container** (`xm`) that recognises FastTracker 2 Extended
//! Module files by the 17-byte `"Extended Module: "` ASCII banner at
//! offset 0. Parses the 336-byte file header (banner, module/tracker
//! names, version, header size, song length, restart position,
//! channel / pattern / instrument counts, frequency-table flag,
//! default tempo / BPM, 256-entry order table), the variable-length
//! bit-packed patterns (note / instrument / volume-column / effect /
//! effect-param, each optional per mask byte), and the instrument
//! table (per-note sample mapping, volume + panning envelopes, vibrato
//! state, fadeout, multiple samples per instrument with delta-encoded
//! 8- or 16-bit PCM bodies). A minimum XM playback engine is wired via
//! the shared [`mixer::MixerVoice`] core and [`mixer::XmPitch`] pitch
//! model (both Amiga and Linear frequency tables supported); drive it
//! via [`xm_player::XmPlayerState::render`]. Volume + panning envelopes
//! (tick-based linear interpolation with sustain-point hold and
//! loop-start/loop-end looping), fadeout (on key-off / note 97), and
//! key-off events are supported. Vibrato, tone portamento, and the
//! bulk of the Bxy/Dxy/Exy/Fxy/Kxy/Lxy effect space remain
//! unimplemented. The codec id [`CODEC_ID_XM_STR`] = `"xm"` remains
//! a parsing-only decoder pending effect-set completeness — use
//! [`xm::parse_header`] / [`xm::parse_patterns`] / [`xm::parse_instruments`]
//! / [`xm::extract_sample_bodies`] for structural access, or drive
//! `XmPlayerState` directly for PCM output.
//!
//! The tracker convention of exposing per-channel streams alongside a
//! mixed stereo mix is shared across tracker formats — see
//! `MEMORY.md → MOD multichannel` for the broader sketch.
//!
//! Decode only — there is no MOD or STM encoder, by design.
use CodecRegistry;
use ContainerRegistry;
use RuntimeContext;
/// Codec id for the mixed-stereo MOD decoder.
pub const CODEC_ID_STR: &str = "mod";
/// Codec id for the planar per-channel MOD decoder.
pub const CODEC_ID_PLANAR_STR: &str = "mod_planar";
/// Codec id for the STM (Scream Tracker v1) parsing-only decoder.
pub const CODEC_ID_STM_STR: &str = "stm";
/// Codec id for the XM (FastTracker 2 Extended Module) parsing-only decoder.
pub const CODEC_ID_XM_STR: &str = "xm";
/// Unified entry point: install every codec and container provided by
/// `oxideav-mod` into a [`RuntimeContext`].
///
/// Also wired into [`oxideav_meta::register_all`] via the
/// [`oxideav_core::register!`] macro below. The
/// short-name `amiga_mod` matches the umbrella's existing trace name
/// from the #502 sweep.
register!;