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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
//! Read-only view of the importer-side, **pre-DAW** representation.
//!
//! Every format importer already parses its file into a faithful
//! mirror of the on-disk structures (`AmigaModule`, `XmModule`,
//! `S3mModule`, `ItModule`) before `to_module()` folds that into the
//! normalised [`Module`] and `build_timeline_layer` projects it onto
//! the DAW layer. Those per-format structs keep their fields private
//! — their layout tracks the file formats, not a public contract —
//! so until now the only way out of an importer was the normalised
//! `Module`.
//!
//! That is the wrong altitude for a whole class of consumers:
//! corpus analysis, censuses, near-duplicate detection, format
//! forensics. They want what the *file* said, not what the player
//! needs:
//!
//! * [`PatternSlot::effect_type`] / [`PatternSlot::effect_parameter`]
//! are the **raw bytes**, not a semantic [`TrackEffect`]. A
//! fingerprint built on them is stable across xmrs releases; one
//! built on the normalised effect enum moves whenever an effect's
//! translation is refined.
//! * The order list is a plain `&[u8]`, not dissolved into a
//! `TimelineMap`.
//! * A pattern is a `[row][channel]` grid, directly hashable, with
//! no clip/segment reconstruction.
//! * Nothing downstream of `load()` runs — no segment splitting, no
//! automation extraction, no DAW build.
//!
//! The trade-off is the flip side of the same coin: this layer is
//! **not comparable across formats**. A `effect_type` of `0x04` is
//! Vibrato in MOD and a volume slide in S3M. Cross-format work needs
//! either the normalised [`Module`] or the one thing that *is*
//! format-neutral here — the sample PCM, exposed via [`RawPcm`].
//!
//! Written against the trait rather than one importer's type: this module is
//! compiled as soon as *any* of MOD / XM / S3M / IT is enabled, so naming a
//! concrete one (`ItModule::load(&bytes)`, the obvious way to reach it) would
//! be an example that does not compile in half the builds that have the module.
//!
//! ```
//! use xmrs::tracker::import::raw::RawModule;
//!
//! /// Sum every raw effect byte in a file — a fingerprint that is stable
//! /// across xmrs releases, because it never touches the normalised effects.
//! fn effect_fingerprint<M: RawModule>(m: &M) -> u64 {
//! let mut sum = 0u64;
//! for pat in m.raw_patterns() {
//! for row in pat {
//! for slot in row {
//! sum += slot.effect_type as u64 + slot.effect_parameter as u64;
//! }
//! }
//! }
//! sum
//! }
//! ```
//!
//! [`Module`]: crate::core::module::Module
//! [`TrackEffect`]: crate::core::effect::TrackEffect
// The `pub mod raw;` declaration in the parent carries its own doc comment, and
// rustdoc resolves this whole merged block in the *parent's* scope — where
// neither `PatternSlot` (its module is `pub(crate)`) nor `RawPcm` is nameable.
// Spelling the targets out absolutely is what keeps the links live.
//! [`PatternSlot::effect_type`]: crate::tracker::import::raw::PatternSlot::effect_type
//! [`PatternSlot::effect_parameter`]: crate::tracker::import::raw::PatternSlot::effect_parameter
//! [`RawPcm`]: crate::tracker::import::raw::RawPcm
use Vec;
use Duration;
use crateSampleDataType;
/// The shared, importer-side pattern cell. Re-exported here because
/// the defining module stays `pub(crate)`: this is the one supported
/// path to it.
pub use cratePatternSlot;
/// Borrowed sample PCM, in whatever width and channel count the file
/// actually stored. The owning variant is
/// [`SampleDataType`]; this is its zero-copy counterpart, so walking
/// a corpus never clones a sample bank.
///
/// Unlike everything else in this module, PCM **is** comparable
/// across formats: the same 8-bit loop lifted from a MOD into an XM
/// is the same bytes in both.
// There is deliberately no `as_bytes()`: reinterpreting `&[i16]` as
// `&[u8]` needs `unsafe`, and this module is under
// `#![forbid(unsafe_code)]`. A consumer that wants to hash the
// payload should match on the variant and feed the typed slice to
// its hasher, or reinterpret it on its own side (`bytemuck::cast_
// slice`) where it — not xmrs — owns that decision. Note that any
// byte-level hash of a 16-bit or float variant is host-endian, and
// so not portable across architectures.
/// One sample slot as the file declared it.
///
/// Deliberately minimal. Volume, panning, finetune and relative
/// pitch are *not* here: each format encodes them on its own scale
/// (MOD's finetune nibble is a signed 4-bit index, XM's is `i8/127`,
/// IT's is a C5 frequency), so a single field would have to pick an
/// interpretation — which is exactly what this layer exists to
/// avoid. Read those from the normalised [`Sample`] instead.
///
/// [`Sample`]: crate::core::sample::Sample
/// One entry of IT's edit-history block.
///
/// Impulse Tracker 2.07+ and OpenMPT append one record per editing
/// session: when it started, and how long it lasted. Nothing else in
/// a tracker module is dated, which makes this the only intrinsic
/// evidence of *when* a file was worked on — and, between two files
/// sharing a history prefix, of which one came later.
///
/// Coverage is partial by nature: files written by older trackers
/// carry no block at all, and OpenMPT can be configured not to emit
/// one. An empty history means "not recorded", never "never edited".
/// What the file says about the tool that wrote it.
///
/// The four formats disagree on how to answer that, so every field
/// is optional and the caller reads whichever its format populates.
/// Read-only access to an importer's pre-DAW state.
///
/// Implemented by [`AmigaModule`], [`XmModule`], [`S3mModule`] and
/// [`ItModule`]. Not implemented by `SidModule` or `DwModule`: both
/// are synthesiser-native (register streams, not sample banks over a
/// pattern grid), and `DwModule` already exposes its fields
/// directly.
///
/// [`AmigaModule`]: crate::tracker::import::amiga::amiga_module::AmigaModule
/// [`XmModule`]: crate::tracker::import::xm::xmmodule::XmModule
/// [`S3mModule`]: crate::tracker::import::s3m::s3m_module::S3mModule
/// [`ItModule`]: crate::tracker::import::it::it_module::ItModule
/// Iterator returned by [`RawModule::raw_patterns`].
/// Iterator returned by [`RawModule::raw_samples`].