Skip to main content

nord_format/formats/nsmp/
codec.rs

1//! The stroke codec: one zone's encoded audio back into samples.
2//!
3//! A stroke payload is a fixed header followed by a stream of words. The words carry
4//! *fields* — the source resampled onto a uniform lattice of [`PITCH_DEN`] fields per
5//! [`PITCH_NUM`] input samples — quantised by truncation and one arithmetic shift the
6//! header records. Decoding is a walk, that shift, and one integration: a record may
7//! store the Nth backward difference of its fields rather than the fields themselves.
8//!
9//! Three generations share this one codec, and every entry point takes the [`Layout`]
10//! saying which. Mostly what differs is *units* — word width, cell size, header size —
11//! and the lattice, the kernel, the quantiser and the grammar's bit layout do not move
12//! at all. The one behavioural difference is how a **stereo** stroke carries its two
13//! channels: v2 and v3 alternate fields, v4 alternates words.
14//!
15//! The lattice is absolute, so field 0 is the start of the source and the stream's
16//! own length gives the duration. The resampling kernel's DC gain is unity to within
17//! the source's own quantisation, which is why a field is already a sample in the
18//! source's 16-bit units and dequantising is a shift and nothing more.
19//!
20//! ⚠️ **The slack in front of a stream can hold stale words that look like records**,
21//! so where the chain begins comes from the header's [`Directory`] rather than from
22//! the first non-zero word. That is why a walk needs the stroke's offset in the body.
23//!
24//! A record may store the Nth backward difference of its fields rather than the
25//! fields themselves, so [`decode`] runs a predictor: `V(f) = e(f) − Σ(−1)^j
26//! C(N,j)·V(f−j)`, over a running history carried across every record boundary and
27//! through every skip. Nothing needs seeding — a stroke opens with a 1:1 ramp-in that
28//! settles on the content's own field value, and the history takes it from there.
29//! **A stereo stroke keeps one history per channel**; they are two signals sharing a
30//! header, and predicting one against the other's samples diverges.
31//!
32//! ⚠️ **A record's fields are left-anchored**: they start at the first bit after the
33//! header word, and the alignment tail is at the *end* of the segment. Reading from
34//! the far end instead is invisible on content records, whose field counts leave no
35//! tail, and displaces every 1:1 record — the warmup and the resyncs — by a whole
36//! number of field slots, or rotates the values inside their width when the tail is
37//! not a multiple of it.
38//!
39//! For [`Layout::V2`], a decode of both our own encodes and vendor content matches
40//! the Electro 5's own playback of the same instrument, a transposed note plays as
41//! exact `2^(n/12)` resampling, and a stereo stroke's two streams reach the outputs
42//! in the order the de-interleave produces. Confirmed on hardware. The V3 and V4
43//! constants: Inferred from specimens; not confirmed on hardware. The Electro 5
44//! plays only v2.
45
46use crate::formats::predictor;
47use std::fmt;
48
49/// Stream units for one sample generation.
50/// V4 alone alternates stereo channel words instead of fields.
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum Layout {
53    /// `.nsmp`: 3-byte words, 24-field cells, a 51-byte stroke header.
54    V2,
55    /// `.nsmp3`: 4-byte words, 32-field cells, a 68-byte stroke header.
56    V3,
57    /// `.nsmp4`: [`Layout::V3`]'s units, and a word stream per channel on stereo.
58    V4,
59}
60
61/// The content version at which the wide chain passes the generations this codec
62/// describes. A version at or above it has unknown stream units, so it is refused
63/// rather than decoded as [`Layout::V4`].
64pub const V5_FROM_VERSION: u32 = 500;
65
66impl Layout {
67    /// The layout implied by a `format × 100 + revision` content version, or `None`
68    /// for a version at or above [`V5_FROM_VERSION`].
69    pub fn from_version(version: u32) -> Option<Layout> {
70        match version {
71            v if v >= V5_FROM_VERSION => None,
72            v if v >= super::V4_FROM_VERSION => Some(Layout::V4),
73            v if v >= super::V3_FROM_VERSION => Some(Layout::V3),
74            _ => Some(Layout::V2),
75        }
76    }
77
78    /// The file extension a generation's instruments carry, without the dot.
79    pub const fn extension(self) -> &'static str {
80        match self {
81            Layout::V2 => "nsmp",
82            Layout::V3 => "nsmp3",
83            Layout::V4 => "nsmp4",
84        }
85    }
86
87    /// Whether stereo channels occupy alternating, independently padded word streams.
88    /// True only for V4; only 1:1 records gain padding because content tiles whole words.
89    pub const fn splits_wide_openings(self) -> bool {
90        matches!(self, Layout::V4)
91    }
92
93    /// Bytes per stream word. A header occupies one word's low 24 bits.
94    pub const fn word(self) -> usize {
95        match self {
96            Layout::V2 => 3,
97            Layout::V3 | Layout::V4 => 4,
98        }
99    }
100
101    /// Bytes in the fixed stroke header before the word stream.
102    pub const fn header_len(self) -> usize {
103        match self {
104            Layout::V2 => 51,
105            Layout::V3 | Layout::V4 => 68,
106        }
107    }
108
109    /// Fields per mono cell. Content counts are multiples; stereo terminators double it.
110    pub const fn cell(self) -> usize {
111        match self {
112            Layout::V2 => 24,
113            Layout::V3 | Layout::V4 => 32,
114        }
115    }
116
117    /// Fields one 1:1 record covers at most, per channel — RMAX. A run is split into
118    /// whole records of at least [`Layout::cell`] and at most this, which is what
119    /// makes the reachable run lengths come in windows with gaps between them.
120    pub const fn rmax(self) -> usize {
121        match self {
122            Layout::V2 => 32,
123            Layout::V3 | Layout::V4 => 48,
124        }
125    }
126
127    /// Whether statistic B carries the extreme content field's sign. V2 stores its
128    /// magnitude, so `ffffff` is a peak of 16,777,215 there and −1 in the wide chain.
129    pub const fn signed_peak(self) -> bool {
130        !matches!(self, Layout::V2)
131    }
132
133    const fn word_bits(self) -> usize {
134        self.word() * 8
135    }
136}
137
138/// Statistic A's mantissa: a 24-bit big-endian value in front of its exponent byte.
139pub(super) const MANTISSA_AT: usize = 9;
140
141/// Statistic A's exponent byte; [`shift`] recovers the quantiser scale from it.
142pub(super) const STAT_A_EXP_AT: usize = 12;
143
144/// Statistic B: the content peak as a 24-bit big-endian value.
145pub(super) const PEAK_AT: usize = 13;
146
147/// Where the wide stroke header's two float32s sit: the zone's playing gain in
148/// decibels, then the stroke's loop decay amount. Both big-endian.
149pub(super) const TAIL_FLOATS_AT: [usize; 2] = [57, 62];
150
151/// What statistic A's exponent is offset by. `A = gain · 2^(41+s) / PEAK` with a 20-bit
152/// mantissa at unity gain, so the exponent lands `22 − bits(PEAK) + s` above zero; the
153/// zone's gain scales the mantissa alone.
154const EXPONENT_BIAS: i32 = 22;
155
156/// Shifts beyond this are not a scale, they are a misread header.
157pub(crate) const SHIFT_LIMIT: i32 = 32;
158
159/// Word directory: `u16` big-endian at this offset, on a 9-byte stride.
160pub(super) const SEEK_AT: usize = 20;
161pub(super) const SEEK_STRIDE: usize = 9;
162
163/// Period of a 16-bit directory pointer, in words.
164/// Openings use the first alias; terminators use the last in-range alias.
165pub const WRAP: usize = 1 << 16;
166
167/// Input samples per [`PITCH_DEN`] fields: field `f` samples the source at exactly
168/// `PITCH_NUM·f / PITCH_DEN`. The ratio is exact; `349/277` is its penultimate
169/// continued-fraction convergent and drifts one field per 17,501.
170pub const PITCH_NUM: u32 = 22_050;
171/// Fields per [`PITCH_NUM`] input samples.
172pub const PITCH_DEN: u32 = 17_501;
173
174/// Rate the editor resamples every import to before encoding. Neither the source
175/// rate nor its bit depth survives anywhere in the file.
176pub const SOURCE_RATE: u32 = 44_100;
177
178/// Field rate in Hz. Exactly 35,002: `44100 × 17501/22050` is a whole number.
179pub const FIELD_RATE: u32 = SOURCE_RATE * PITCH_DEN / PITCH_NUM;
180const _: () = assert!((SOURCE_RATE * PITCH_DEN).is_multiple_of(PITCH_NUM));
181
182/// Mask for the 14-bit count in `[flag][width−1][reserved][mark][order][count]`.
183const COUNT_MASK: u32 = 0x3fff;
184
185/// Field values the predictor keeps. The order field is three bits wide, but only
186/// 0 to 4 occur and a fourth-order difference reaches no further back than this.
187const MAX_ORDER: usize = predictor::MAX_ORDER;
188
189/// Why a stroke stream could not be walked or decoded.
190#[derive(Debug, Clone, Copy, PartialEq, Eq)]
191pub enum Unsupported {
192    /// Shorter than the fixed header, so there is no stream to walk.
193    Short,
194    /// The directory's opening pointer names no word in the stroke. Where the chain
195    /// begins comes from the directory alone, because the slack in front of a stream
196    /// can hold stale words that look like records.
197    Directory {
198        /// The opening pointer, as the header states it.
199        pointer: u16,
200    },
201    /// A word violates the record header or content-count grammar.
202    Malformed {
203        /// Word index within the stream, counting from the end of the stroke header
204        /// ([`Layout::header_len`]).
205        word: usize,
206    },
207    /// A record whose fields run past the end of the stroke — some earlier record
208    /// was read at the wrong size.
209    Desync {
210        /// Word index within the stream, counting from the end of the stroke header
211        /// ([`Layout::header_len`]).
212        word: usize,
213    },
214    /// Bytes remain after the last complete stream word.
215    PartialWord {
216        /// Trailing bytes, always fewer than [`Layout::word`].
217        bytes: usize,
218    },
219    /// The header requests a shift that cannot describe encoded audio.
220    Shift {
221        /// Signed shift recovered from the header.
222        bits: i32,
223    },
224    /// The chain reached the end of the stroke without a terminator.
225    NoTerminator,
226}
227
228impl Unsupported {
229    /// A stable label, for tallying coverage across a whole library.
230    pub fn reason(self) -> &'static str {
231        match self {
232            Unsupported::Short => "short-stroke",
233            Unsupported::Directory { .. } => "bad-directory",
234            Unsupported::Malformed { .. } => "malformed-record",
235            Unsupported::Desync { .. } => "desync",
236            Unsupported::PartialWord { .. } => "partial-word",
237            Unsupported::Shift { .. } => "invalid-shift",
238            Unsupported::NoTerminator => "no-terminator",
239        }
240    }
241}
242
243impl fmt::Display for Unsupported {
244    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245        match self {
246            Unsupported::Short => write!(f, "the stroke is shorter than its own header"),
247            Unsupported::Directory { pointer } => write!(
248                f,
249                "the directory's opening pointer {pointer} names no word in the stroke"
250            ),
251            Unsupported::Malformed { word } => {
252                write!(f, "word {word} is not a record header")
253            }
254            Unsupported::Desync { word } => write!(
255                f,
256                "the record at word {word} runs past the end of the stroke"
257            ),
258            Unsupported::PartialWord { bytes } => {
259                write!(f, "the stream ends with {bytes} byte(s) of a partial word")
260            }
261            Unsupported::Shift { bits } => {
262                write!(f, "the header's {bits}-bit quantiser shift is invalid")
263            }
264            Unsupported::NoTerminator => {
265                write!(f, "the chain ran off the end with no terminator")
266            }
267        }
268    }
269}
270
271impl std::error::Error for Unsupported {}
272
273/// One record, placed on the field lattice.
274#[derive(Debug, Clone, PartialEq, Eq)]
275pub struct Record {
276    /// Word index within the stream, counting from the end of the stroke header
277    /// ([`Layout::header_len`]).
278    pub at: usize,
279    /// Lattice index of this record's first field.
280    pub first_field: usize,
281    /// `false` for lattice content; `true` for the 1:1 regime — the warmup and the
282    /// resync. Both sit on the same lattice.
283    pub one_to_one: bool,
284    /// Bits per field, 1 to 16.
285    pub width: u8,
286    /// Difference order, 0..=4. Content stores the Nth backward difference.
287    pub order: u8,
288    /// Set on the record a loop starts at; the directory's third pointer names it.
289    pub mark: bool,
290    /// Channel-major signed values at order zero; signed differences otherwise.
291    pub values: Vec<i32>,
292}
293
294/// A walked record chain.
295#[derive(Debug, Clone, PartialEq, Eq)]
296pub struct Stream {
297    pub records: Vec<Record>,
298    /// Fields the chain covers, which is the decoded length.
299    pub fields: usize,
300    /// Word index the chain started at.
301    pub first_record: usize,
302    /// Word index where the chain stops.
303    pub terminator: usize,
304    /// Terminator cell count, or `None` when the directory ends the chain directly.
305    pub cell: Option<usize>,
306    /// Channels the stroke carries: 2 when the terminator doubles the layout's cell.
307    pub channels: usize,
308}
309
310/// Decoded audio for one zone.
311#[derive(Debug, Clone, PartialEq, Eq)]
312pub struct Audio {
313    /// One sample per field, at [`FIELD_RATE`], **interleaved by channel** — so on a
314    /// stereo zone this is `L R L R` and holds two samples per frame.
315    pub samples: Vec<i16>,
316    /// 1 or 2.
317    pub channels: u16,
318    /// Fields clamped after dequantisation, commonly from kernel ringing.
319    pub clipped: usize,
320    /// Fields reconstructed from differences rather than stated outright.
321    pub differenced: usize,
322}
323
324impl Audio {
325    /// Frames — samples per channel, which is what the duration is measured in.
326    pub fn frames(&self) -> usize {
327        self.samples.len() / usize::from(self.channels).max(1)
328    }
329
330    /// Duration in seconds.
331    pub fn seconds(&self) -> f64 {
332        self.frames() as f64 / f64::from(FIELD_RATE)
333    }
334}
335
336/// Header content peak. Wide layouts store it signed; V2 stores its magnitude.
337pub fn peak(stroke: &[u8], layout: Layout) -> Option<i32> {
338    let b = stroke.get(PEAK_AT..PEAK_AT + 3)?;
339    let v = u32::from_be_bytes([0, b[0], b[1], b[2]]);
340    Some(match layout.signed_peak() && v >= 1 << 23 {
341        true => v as i32 - (1 << 24),
342        false => v as i32,
343    })
344}
345
346/// Signed quantiser shift recovered from statistic A's exponent and [`peak`].
347/// Dequantising applies the shift alone: statistic A's mantissa carries the zone's
348/// gain, which the instrument applies at playback rather than the decoder.
349pub fn shift(stroke: &[u8], layout: Layout) -> Option<i32> {
350    let peak = peak(stroke, layout)?.unsigned_abs().max(1);
351    let exponent = i32::from(*stroke.get(STAT_A_EXP_AT)?);
352    let bits = peak.ilog2() as i32 + 1;
353    let exact_power = i32::from(peak.is_power_of_two());
354    Some(exponent + bits - EXPONENT_BIAS - exact_power)
355}
356
357fn tail_float(stroke: &[u8], layout: Layout, at: usize) -> Option<f32> {
358    if layout == Layout::V2 {
359        return None;
360    }
361    let b = stroke.get(at..at + 4)?;
362    Some(f32::from_be_bytes([b[0], b[1], b[2], b[3]]))
363}
364
365/// The zone's playing gain in decibels, `None` on the narrow header, which has no
366/// such field — v2 keeps the same gain as a linear u24 in the zone record instead.
367///
368/// ⚠️ **Silence is `-inf` here**, which the linear field cannot express, and the
369/// value is neither clamped nor gridded: the editor writes `20·log10(g)` straight
370/// through, past +24 dB and below -40 dB alike.
371///
372/// Inferred from specimens; not confirmed on hardware.
373pub fn zone_gain_db(stroke: &[u8], layout: Layout) -> Option<f32> {
374    tail_float(stroke, layout, TAIL_FLOATS_AT[0])
375}
376
377/// The stroke's loop decay amount, verbatim in the project's own units, `None` on the
378/// narrow header, which drops the field.
379///
380/// ⚠️ It is stored whether or not the decay is switched on: nothing in the file says
381/// which, so a reader cannot tell an active decay from a default that was never used.
382///
383/// Inferred from specimens; not confirmed on hardware.
384pub fn loop_decay(stroke: &[u8], layout: Layout) -> Option<f32> {
385    tail_float(stroke, layout, TAIL_FLOATS_AT[1])
386}
387
388/// Four word pointers into a stroke chain, relative to the sample body.
389#[derive(Debug, Clone, Copy, PartialEq, Eq)]
390pub struct Directory {
391    /// Where the chain starts.
392    pub first_record: u16,
393    /// The stroke's resync record.
394    pub resync: u16,
395    /// Marked record, or the terminator when no record is marked.
396    pub mark: u16,
397    /// Where the chain ends.
398    pub terminator: u16,
399}
400
401impl Directory {
402    /// Reads the directory out of a stroke header.
403    pub fn read(stroke: &[u8]) -> Option<Directory> {
404        let at = |i: usize| -> Option<u16> {
405            let o = SEEK_AT + SEEK_STRIDE * i;
406            let b = stroke.get(o..o + 2)?;
407            Some(u16::from_be_bytes([b[0], b[1]]))
408        };
409        Some(Directory {
410            first_record: at(0)?,
411            resync: at(1)?,
412            mark: at(2)?,
413            terminator: at(3)?,
414        })
415    }
416
417    /// Resolve a pointer to its first alias within the stream; callers must range-check.
418    pub fn resolve(pointer: u16, stroke_at: usize, layout: Layout) -> usize {
419        let base = (stroke_at + layout.header_len()) / layout.word() % WRAP;
420        (usize::from(pointer) + WRAP - base) % WRAP
421    }
422
423    /// Resolve a pointer to its last alias within a stream of `words`.
424    pub fn resolve_end(pointer: u16, stroke_at: usize, layout: Layout, words: usize) -> usize {
425        let mut at = Directory::resolve(pointer, stroke_at, layout);
426        while at + WRAP < words {
427            at += WRAP;
428        }
429        at
430    }
431}
432
433/// Return the mono or stereo cell count when `raw` is a valid terminator.
434fn terminator_cell(raw: u32, cell: usize) -> Option<usize> {
435    let v = raw & 0x00ff_ffff;
436    let one_to_one = v >> 23 != 0;
437    let width = ((v >> 19) & 0xf) + 1;
438    let mark = (v >> 18) & 1 != 0;
439    let reserved = (v >> 17) & 1 != 0;
440    let order = (v >> 14) & 0x7;
441    let count = (v & COUNT_MASK) as usize;
442    let ok = one_to_one
443        && width == 1
444        && raw >> 24 == 0
445        && !mark
446        && !reserved
447        && order == 0
448        && (count == cell || count == 2 * cell);
449    ok.then_some(count)
450}
451
452/// Walk signed record fields from the directory opening to the terminator.
453/// `stroke_at` is the stroke payload's offset from the sample body.
454pub fn walk(stroke: &[u8], stroke_at: usize, layout: Layout) -> Result<Stream, Unsupported> {
455    let word_len = layout.word();
456    let word_bits = layout.word_bits();
457    let cell = layout.cell();
458    let stream = stroke
459        .get(layout.header_len()..)
460        .ok_or(Unsupported::Short)?;
461    let trailing = stream.len() % word_len;
462    if trailing != 0 {
463        return Err(Unsupported::PartialWord { bytes: trailing });
464    }
465    let words = stream.len() / word_len;
466    let word = |i: usize| -> u32 {
467        stream[i * word_len..][..word_len]
468            .iter()
469            .fold(0u32, |v, &b| (v << 8) | u32::from(b))
470    };
471    let directory = Directory::read(stroke).ok_or(Unsupported::Short)?;
472
473    let first_record = Directory::resolve(directory.first_record, stroke_at, layout);
474    if first_record >= words {
475        return Err(Unsupported::Directory {
476            pointer: directory.first_record,
477        });
478    }
479    let last = Some(Directory::resolve_end(
480        directory.terminator,
481        stroke_at,
482        layout,
483        words,
484    ))
485    .filter(|&at| at < words);
486
487    // Stereo affects V4 record sizing, so read it from the directory's terminator
488    // before walking the first record.
489    let stereo = last.is_some_and(|at| terminator_cell(word(at), cell) == Some(2 * cell));
490    let wide_openings = stereo && layout.splits_wide_openings();
491
492    let mut records = Vec::new();
493    // Reused across records: one channel's words, gathered out of the stream.
494    let mut gathered: Vec<u8> = Vec::new();
495    let mut fields = 0usize;
496    let mut i = first_record;
497    while i < words {
498        let raw = word(i);
499        // A wide word's top byte is not part of the record header, and no header has
500        // ever set it; a narrow word has no top byte to set.
501        let over = raw >> 24;
502        let v = raw & 0x00ff_ffff;
503        let one_to_one = v >> 23 != 0;
504        let width = (((v >> 19) & 0xf) + 1) as u8;
505        let mark = (v >> 18) & 1 != 0;
506        let order = ((v >> 14) & 0x7) as u8;
507        let count = (v & COUNT_MASK) as usize;
508
509        let terminal_cell = terminator_cell(raw, cell);
510        let ends_here = terminal_cell.is_some();
511        if Some(i) == last || ends_here {
512            if terminal_cell == Some(2 * cell) && !stereo {
513                return Err(Unsupported::Malformed { word: i });
514            }
515            return Ok(Stream {
516                records,
517                fields,
518                first_record,
519                terminator: i,
520                cell: ends_here.then_some(count),
521                channels: if stereo { 2 } else { 1 },
522            });
523        }
524        if over != 0
525            || (v >> 17) & 1 != 0
526            || usize::from(order) > MAX_ORDER
527            || count == 0
528            || (stereo && !count.is_multiple_of(2))
529            || (!one_to_one && !count.is_multiple_of(cell))
530        {
531            return Err(Unsupported::Malformed { word: i });
532        }
533
534        let span = if wide_openings && one_to_one {
535            // Each channel half is word-padded independently.
536            1 + 2 * (count / 2 * usize::from(width)).div_ceil(word_bits)
537        } else {
538            (word_bits + count * usize::from(width)).div_ceil(word_bits)
539        };
540        if i + span > last.unwrap_or(words) {
541            return Err(Unsupported::Desync { word: i });
542        }
543        let base = (i + 1) * word_bits;
544        let values = if !stereo {
545            (0..count)
546                .map(|k| read_field(stream, base + k * usize::from(width), width))
547                .collect()
548        } else if wide_openings {
549            // Gather each channel's alternating words into a contiguous bitstream.
550            let per = count / 2;
551            let channel_words = (per * usize::from(width)).div_ceil(word_bits);
552            let mut values = Vec::with_capacity(count);
553            for channel in 0..2 {
554                gathered.clear();
555                for k in 0..channel_words {
556                    let at = (i + 1 + 2 * k + channel) * word_len;
557                    gathered.extend_from_slice(&stream[at..at + word_len]);
558                }
559                values
560                    .extend((0..per).map(|k| read_field(&gathered, k * usize::from(width), width)));
561            }
562            values
563        } else {
564            // V2 and V3 alternate channel fields in one bitstream.
565            let field = |k: usize| read_field(stream, base + k * usize::from(width), width);
566            (0..count)
567                .map(|k| field(2 * (k % (count / 2)) + k / (count / 2)))
568                .collect()
569        };
570
571        records.push(Record {
572            at: i,
573            first_field: fields,
574            one_to_one,
575            width,
576            order,
577            mark,
578            values,
579        });
580        fields += count;
581        i += span;
582        while i < words && word(i) == 0 {
583            i += 1;
584        }
585    }
586    Err(Unsupported::NoTerminator)
587}
588
589/// Decode a stroke at body offset `stroke_at` into [`FIELD_RATE`] audio.
590pub fn decode(stroke: &[u8], stroke_at: usize, layout: Layout) -> Result<Audio, Unsupported> {
591    let stream = walk(stroke, stroke_at, layout)?;
592    let channels = stream.channels;
593    let shift = shift(stroke, layout).ok_or(Unsupported::Short)?;
594    if !(-SHIFT_LIMIT..=SHIFT_LIMIT).contains(&shift) {
595        return Err(Unsupported::Shift { bits: shift });
596    }
597    let mut samples = vec![0i16; stream.fields];
598    let mut clipped = 0;
599    let mut differenced = 0;
600    // Predictor history spans records and skips; stereo channels need independent state.
601    let mut history = [[0i64; MAX_ORDER]; 2];
602    for record in &stream.records {
603        // Only content records difference; the 1:1 regime always states values.
604        let order = if record.one_to_one {
605            0
606        } else {
607            usize::from(record.order)
608        };
609        if order > 0 {
610            differenced += record.values.len();
611        }
612        // Values arrive channel-major, so the halves index their own channel and the
613        // output interleaves them back together.
614        let per = record.values.len() / channels;
615        for (k, &residual) in record.values.iter().enumerate() {
616            let (channel, k) = if channels == 2 {
617                (k / per, k % per)
618            } else {
619                (0, k)
620            };
621            let value = predictor::predict(&mut history[channel], order, i64::from(residual));
622
623            let at = record.first_field + k * channels + channel;
624            let Some(slot) = samples.get_mut(at) else {
625                continue;
626            };
627            let wide = if shift >= 0 {
628                value.saturating_mul(1i64 << shift)
629            } else {
630                value >> -shift
631            };
632            *slot = wide.clamp(i64::from(i16::MIN), i64::from(i16::MAX)) as i16;
633            if i64::from(*slot) != wide {
634                clipped += 1;
635            }
636        }
637    }
638    Ok(Audio {
639        samples,
640        channels: channels as u16,
641        clipped,
642        differenced,
643    })
644}
645
646/// One field, `width` bits big-endian from `bit`, sign-extended.
647fn read_field(stream: &[u8], bit: usize, width: u8) -> i32 {
648    let mut v: u32 = 0;
649    for i in bit..bit + usize::from(width) {
650        v = (v << 1) | u32::from((stream[i / 8] >> (7 - i % 8)) & 1);
651    }
652    if v & (1 << (width - 1)) != 0 {
653        v as i32 - (1i32 << width)
654    } else {
655        v as i32
656    }
657}
658
659#[cfg(test)]
660mod tests {
661    use super::*;
662
663    const BOTH: [Layout; 2] = [Layout::V2, Layout::V3];
664
665    /// The terminator: flag 1, width 1, count = the layout's cell size.
666    fn terminator(layout: Layout) -> Vec<u8> {
667        let head = (1u32 << 23) | layout.cell() as u32;
668        head.to_be_bytes()[4 - layout.word()..].to_vec()
669    }
670
671    /// Packs a record with fields immediately after its header.
672    fn block(layout: Layout, one_to_one: bool, width: u8, order: u8, values: &[i32]) -> Vec<u8> {
673        packed(layout, one_to_one, width, order, false, values)
674    }
675
676    fn packed(
677        layout: Layout,
678        one_to_one: bool,
679        width: u8,
680        order: u8,
681        mark: bool,
682        values: &[i32],
683    ) -> Vec<u8> {
684        let bits = layout.word_bits();
685        let count = values.len();
686        let head = (u32::from(one_to_one) << 23)
687            | (u32::from(width - 1) << 19)
688            | (u32::from(mark) << 18)
689            | (u32::from(order) << 14)
690            | count as u32;
691        let span = (bits + count * usize::from(width)).div_ceil(bits);
692        let mut out = vec![0u8; span * layout.word()];
693        out[..layout.word()].copy_from_slice(&head.to_be_bytes()[4 - layout.word()..]);
694        write_values(&mut out, bits, width, values);
695        out
696    }
697
698    fn split_block(layout: Layout, width: u8, values: &[i32]) -> Vec<u8> {
699        let bits = layout.word_bits();
700        let half = values.len() / 2;
701        let half_words = (half * usize::from(width)).div_ceil(bits);
702        let mut out = vec![0u8; (1 + 2 * half_words) * layout.word()];
703        let head = (1u32 << 23) | (u32::from(width - 1) << 19) | values.len() as u32;
704        out[..layout.word()].copy_from_slice(&head.to_be_bytes());
705        for channel in 0..2 {
706            let mut packed = vec![0u8; half_words * layout.word()];
707            write_values(
708                &mut packed,
709                0,
710                width,
711                &values[channel * half..(channel + 1) * half],
712            );
713            for word in 0..half_words {
714                let from = word * layout.word();
715                let to = (1 + 2 * word + channel) * layout.word();
716                out[to..to + layout.word()].copy_from_slice(&packed[from..from + layout.word()]);
717            }
718        }
719        out
720    }
721
722    fn write_values(out: &mut [u8], mut at: usize, width: u8, values: &[i32]) {
723        for &v in values {
724            let raw = (v as u32) & ((1u32 << width) - 1);
725            for b in (0..width).rev() {
726                if raw >> b & 1 != 0 {
727                    out[at / 8] |= 1 << (7 - at % 8);
728                }
729                at += 1;
730            }
731        }
732    }
733
734    /// Build a stroke at body offset zero whose directory points at `chain`.
735    fn stroke(layout: Layout, peak: i32, exponent: u8, lead: usize, chain: &[Vec<u8>]) -> Vec<u8> {
736        let word = layout.word();
737        let mut s = vec![0u8; layout.header_len()];
738        s[STAT_A_EXP_AT] = exponent;
739        s[PEAK_AT..PEAK_AT + 3].copy_from_slice(&(peak as u32 & 0xff_ffff).to_be_bytes()[1..]);
740        let body: Vec<u8> = chain.concat();
741        let base = layout.header_len() / word;
742        let at = (base + lead) as u16;
743        let end = (base + lead + body.len() / word) as u16;
744        for (i, p) in [at, at, end, end].iter().enumerate() {
745            let o = SEEK_AT + SEEK_STRIDE * i;
746            s[o..o + 2].copy_from_slice(&p.to_be_bytes());
747        }
748        s.extend(std::iter::repeat_n(0u8, lead * word));
749        s.extend_from_slice(&body);
750        s.extend_from_slice(&terminator(layout));
751        s
752    }
753
754    /// A stereo stroke carrying one content record of `l` and `r`, laid out the way
755    /// `layout` lays a stereo stroke out: alternating fields on v2 and v3, a word
756    /// stream each on v4.
757    fn stereo_stroke(layout: Layout, width: u8, l: &[i32], r: &[i32]) -> Vec<u8> {
758        assert_eq!(l.len(), r.len());
759        let word = layout.word();
760        let bits = layout.word_bits();
761        let count = l.len() * 2;
762        let head = (u32::from(width - 1) << 19) | count as u32;
763
764        let mut body = head.to_be_bytes()[4 - word..].to_vec();
765        if layout.splits_wide_openings() {
766            // Pack each channel on its own, then take one word from each in turn.
767            let per = |v: &[i32]| {
768                let mut out = vec![0u8; (v.len() * usize::from(width)).div_ceil(bits) * word];
769                let mut at = 0;
770                for &x in v {
771                    for b in (0..width).rev() {
772                        if (x as u32) >> b & 1 != 0 {
773                            out[at / 8] |= 1 << (7 - at % 8);
774                        }
775                        at += 1;
776                    }
777                }
778                out
779            };
780            let (a, b) = (per(l), per(r));
781            for k in 0..a.len() / word {
782                body.extend_from_slice(&a[k * word..][..word]);
783                body.extend_from_slice(&b[k * word..][..word]);
784            }
785        } else {
786            let woven: Vec<i32> = l.iter().zip(r).flat_map(|(&a, &b)| [a, b]).collect();
787            body = block(layout, false, width, 0, &woven);
788        }
789
790        let mut s = vec![0u8; layout.header_len()];
791        s[STAT_A_EXP_AT] = exponent_for(1, 0);
792        s[PEAK_AT..PEAK_AT + 3].copy_from_slice(&1u32.to_be_bytes()[1..]);
793        let base = (layout.header_len() / word) as u16;
794        let end = base + (body.len() / word) as u16;
795        for (i, p) in [base, base, end, end].iter().enumerate() {
796            let o = SEEK_AT + SEEK_STRIDE * i;
797            s[o..o + 2].copy_from_slice(&p.to_be_bytes());
798        }
799        s.extend_from_slice(&body);
800        let term = (1u32 << 23) | (2 * layout.cell()) as u32;
801        s.extend_from_slice(&term.to_be_bytes()[4 - word..]);
802        s
803    }
804
805    #[test]
806    fn a_stereo_stroke_decodes_to_two_channels() {
807        for layout in [Layout::V2, Layout::V3, Layout::V4] {
808            let per = layout.cell(); // one stereo cell is `cell` fields per channel
809            let l: Vec<i32> = (0..per as i32).map(|k| 100 + k).collect();
810            let r: Vec<i32> = (0..per as i32).map(|k| -100 - k).collect();
811            let s = stereo_stroke(layout, 11, &l, &r);
812
813            let stream = walk(&s, 0, layout).expect("the stereo stroke walks");
814            assert_eq!(stream.channels, 2, "{layout:?}");
815            assert_eq!(stream.cell, Some(2 * layout.cell()), "{layout:?}");
816
817            let audio = decode(&s, 0, layout).expect("the stereo stroke decodes");
818            assert_eq!(audio.channels, 2, "{layout:?}");
819            assert_eq!(audio.frames(), per, "{layout:?}");
820            let got_l: Vec<i32> = audio
821                .samples
822                .iter()
823                .step_by(2)
824                .map(|&v| i32::from(v))
825                .collect();
826            let got_r: Vec<i32> = audio.samples[1..]
827                .iter()
828                .step_by(2)
829                .map(|&v| i32::from(v))
830                .collect();
831            assert_eq!(got_l, l, "{layout:?}: left channel");
832            assert_eq!(got_r, r, "{layout:?}: right channel");
833        }
834    }
835
836    #[test]
837    fn a_stereo_terminator_needs_a_valid_directory_pointer() {
838        let layout = Layout::V2;
839        let fields = vec![0; layout.cell()];
840        let mut s = stereo_stroke(layout, 1, &fields, &fields);
841        let at = SEEK_AT + 3 * SEEK_STRIDE;
842        s[at..at + 2].copy_from_slice(&u16::MAX.to_be_bytes());
843        let terminator = (s.len() - layout.header_len()) / layout.word() - 1;
844
845        assert_eq!(
846            walk(&s, 0, layout),
847            Err(Unsupported::Malformed { word: terminator })
848        );
849    }
850
851    #[test]
852    fn an_opening_pointer_outside_the_stream_is_refused_rather_than_searched_for() {
853        for layout in BOTH {
854            let values = run(layout, 6);
855            let mut s = stroke(layout, 1, 22, 2, &[block(layout, false, 4, 0, &values)]);
856            s[SEEK_AT..SEEK_AT + 2].copy_from_slice(&u16::MAX.to_be_bytes());
857            assert_eq!(
858                walk(&s, 0, layout),
859                Err(Unsupported::Directory { pointer: u16::MAX }),
860                "{layout:?}"
861            );
862        }
863    }
864
865    #[test]
866    fn a_stereo_stroke_predicts_each_channel_against_its_own_history() {
867        // Opposing first-order ramps expose shared predictor history as runaway output.
868        for layout in [Layout::V2, Layout::V3, Layout::V4] {
869            let per = layout.cell();
870            let l: Vec<i32> = (0..per as i32).map(|k| 10 * k).collect();
871            let r: Vec<i32> = (0..per as i32).map(|k| -7 * k).collect();
872            let diff = |v: &[i32]| -> Vec<i32> {
873                v.iter()
874                    .enumerate()
875                    .map(|(i, &x)| if i == 0 { x } else { x - v[i - 1] })
876                    .collect()
877            };
878            let s = stereo_stroke_ordered(layout, 11, &diff(&l), &diff(&r));
879            let audio = decode(&s, 0, layout).expect("decodes");
880            let got_l: Vec<i32> = audio
881                .samples
882                .iter()
883                .step_by(2)
884                .map(|&v| i32::from(v))
885                .collect();
886            let got_r: Vec<i32> = audio.samples[1..]
887                .iter()
888                .step_by(2)
889                .map(|&v| i32::from(v))
890                .collect();
891            assert_eq!(got_l, l, "{layout:?}: left ramp");
892            assert_eq!(got_r, r, "{layout:?}: right ramp");
893        }
894    }
895
896    /// [`stereo_stroke`] with the record's order set to 1.
897    fn stereo_stroke_ordered(layout: Layout, width: u8, l: &[i32], r: &[i32]) -> Vec<u8> {
898        let mut s = stereo_stroke(layout, width, l, r);
899        let at = layout.header_len();
900        let word = layout.word();
901        let mut head = 0u32;
902        for &b in &s[at..at + word] {
903            head = (head << 8) | u32::from(b);
904        }
905        head |= 1 << 14;
906        s[at..at + word].copy_from_slice(&head.to_be_bytes()[4 - word..]);
907        s
908    }
909
910    /// One cell of fields, which is the smallest a content record may be.
911    fn run(layout: Layout, value: i32) -> Vec<i32> {
912        let mut v = vec![0; layout.cell()];
913        v[0] = value;
914        v
915    }
916
917    /// The exponent byte that spells a given shift for a given peak, which is what
918    /// the encoder writes: `A8 = 22 + s − bits(PEAK) + (PEAK a power of two)`.
919    fn exponent_for(peak: i32, shift: i32) -> u8 {
920        let peak = peak.unsigned_abs().max(1);
921        let bits = peak.ilog2() as i32 + 1;
922        (EXPONENT_BIAS + shift - bits + i32::from(peak.is_power_of_two())) as u8
923    }
924
925    #[test]
926    fn the_shift_comes_off_the_exponent_byte_and_is_signed() {
927        for layout in BOTH {
928            // A negative shift only arises on quiet content, which is the only case
929            // whose exponent byte stays in range.
930            for (peak, want) in [(8191i32, 2i32), (1, 0), (4096, 7), (255, -8), (12345, 3)] {
931                let s = stroke(layout, peak, exponent_for(peak, want), 0, &[]);
932                assert_eq!(shift(&s, layout), Some(want), "{layout:?} peak {peak}");
933            }
934            // A peak of zero reads as one rather than dividing by nothing.
935            let s = stroke(layout, 0, exponent_for(1, 5), 0, &[]);
936            assert_eq!(shift(&s, layout), Some(5), "{layout:?}");
937        }
938    }
939
940    #[test]
941    fn statistic_b_is_signed_in_the_wide_layout() {
942        let s = stroke(Layout::V3, -8191, exponent_for(8191, 2), 0, &[]);
943        assert_eq!(peak(&s, Layout::V3), Some(-8191));
944        assert_eq!(shift(&s, Layout::V3), Some(2));
945        let silent = stroke(Layout::V3, -1, exponent_for(1, 0), 0, &[]);
946        assert_eq!(peak(&silent, Layout::V3), Some(-1));
947        assert_eq!(shift(&silent, Layout::V3), Some(0));
948        // The same bytes are a large positive peak in the narrow layout, which does
949        // not sign the field.
950        assert_eq!(peak(&silent, Layout::V2), Some(0xff_ffff));
951    }
952
953    /// The wide header's two floats are the zone gain in decibels and the loop decay
954    /// amount; the narrow header has neither.
955    #[test]
956    fn the_wide_header_carries_a_zone_gain_and_a_loop_decay() {
957        let mut s = stroke(Layout::V3, 1, 22, 0, &[]);
958        s[TAIL_FLOATS_AT[0]..][..4].copy_from_slice(&(-6.0206f32).to_be_bytes());
959        s[TAIL_FLOATS_AT[1]..][..4].copy_from_slice(&20.0f32.to_be_bytes());
960        assert_eq!(zone_gain_db(&s, Layout::V3), Some(-6.0206));
961        assert_eq!(loop_decay(&s, Layout::V3), Some(20.0));
962        assert_eq!(zone_gain_db(&s, Layout::V2), None);
963        assert_eq!(loop_decay(&s, Layout::V2), None);
964    }
965
966    #[test]
967    fn fields_are_left_anchored_and_sign_extended() {
968        for layout in BOTH {
969            let mut values = run(layout, 0);
970            values[..4].copy_from_slice(&[1, -1, 4095, -4096]);
971            let s = stroke(layout, 1, 22, 0, &[block(layout, false, 13, 0, &values)]);
972            let walked = walk(&s, 0, layout).unwrap();
973            assert_eq!(walked.records.len(), 1, "{layout:?}");
974            assert_eq!(walked.records[0].width, 13);
975            assert_eq!(walked.records[0].values[..4], [1, -1, 4095, -4096]);
976            assert_eq!(walked.fields, layout.cell());
977            assert_eq!(walked.cell, Some(layout.cell()));
978        }
979    }
980
981    /// The order bits are not layout: a record with an order set covers exactly the
982    /// fields it counts, at the base the records before it left off.
983    #[test]
984    fn an_order_moves_neither_the_length_nor_the_field_base() {
985        for layout in BOTH {
986            let values = run(layout, 3);
987            let plain = stroke(layout, 1, 22, 0, &[block(layout, false, 4, 0, &values)]);
988            let ordered = stroke(layout, 1, 22, 0, &[block(layout, false, 4, 2, &values)]);
989            let a = walk(&plain, 0, layout).unwrap();
990            let b = walk(&ordered, 0, layout).unwrap();
991            assert_eq!(a.fields, b.fields);
992            assert_eq!(a.records[0].first_field, b.records[0].first_field);
993            assert_eq!(b.records[0].order, 2);
994            assert_eq!(a.terminator, b.terminator);
995        }
996    }
997
998    #[test]
999    fn a_marked_record_walks_and_says_it_is_marked() {
1000        for layout in BOTH {
1001            let values = run(layout, 1);
1002            let s = stroke(
1003                layout,
1004                1,
1005                22,
1006                0,
1007                &[packed(layout, true, 4, 0, true, &values)],
1008            );
1009            let walked = walk(&s, 0, layout).unwrap();
1010            assert_eq!(walked.records.len(), 1, "{layout:?}");
1011            assert!(walked.records[0].mark, "{layout:?}");
1012            assert_eq!(walked.records[0].values, values);
1013            // The bit below it has never been seen set, and stays a refusal.
1014            let mut s = stroke(layout, 1, 22, 0, &[block(layout, true, 4, 0, &values)]);
1015            let head = layout.header_len();
1016            s[head + layout.word() - 3] |= 0x02;
1017            assert_eq!(
1018                walk(&s, 0, layout),
1019                Err(Unsupported::Malformed { word: 0 }),
1020                "{layout:?}"
1021            );
1022        }
1023    }
1024
1025    #[test]
1026    fn a_differenced_run_integrates_from_the_running_history() {
1027        for layout in BOTH {
1028            // A plain record settling on 100, then a first-order run of zeros, which
1029            // is how sustained material is coded: nothing changes, so nothing is sent.
1030            let settle = vec![100i32; layout.cell()];
1031            let hold = vec![0i32; 2 * layout.cell()];
1032            let s = stroke(
1033                layout,
1034                1,
1035                22,
1036                0,
1037                &[
1038                    block(layout, true, 13, 0, &settle),
1039                    block(layout, false, 13, 1, &hold),
1040                ],
1041            );
1042            let audio = decode(&s, 0, layout).unwrap();
1043            assert_eq!(audio.differenced, 2 * layout.cell(), "{layout:?}");
1044            // The level carries: every field of the differenced run holds the value
1045            // the 1:1 record settled on.
1046            assert!(
1047                audio.samples[layout.cell()..].iter().all(|&v| v == 100),
1048                "{layout:?}"
1049            );
1050        }
1051    }
1052
1053    /// A second-order run integrates twice, so a zero residual continues the slope
1054    /// the history already holds.
1055    #[test]
1056    fn a_second_order_run_carries_slope_as_well_as_level() {
1057        for layout in BOTH {
1058            let ramp: Vec<i32> = (0..layout.cell()).map(|k| 10 * k as i32).collect();
1059            let coast = vec![0i32; layout.cell()];
1060            let s = stroke(
1061                layout,
1062                1,
1063                22,
1064                0,
1065                &[
1066                    block(layout, true, 13, 0, &ramp),
1067                    block(layout, false, 13, 2, &coast),
1068                ],
1069            );
1070            let audio = decode(&s, 0, layout).unwrap();
1071            let last = layout.cell() - 1;
1072            assert_eq!(audio.samples[last], 10 * last as i16, "{layout:?}");
1073            assert_eq!(
1074                audio.samples[last + 1],
1075                10 * (last + 1) as i16,
1076                "{layout:?}"
1077            );
1078            assert_eq!(
1079                audio.samples[last + 2],
1080                10 * (last + 2) as i16,
1081                "{layout:?}"
1082            );
1083        }
1084    }
1085
1086    /// The 1:1 records are the ones an anchoring mistake moves: their field counts
1087    /// leave an alignment tail, and reading it as a lead-in displaces every value.
1088    #[test]
1089    fn a_one_to_one_record_with_an_alignment_tail_reads_from_the_front() {
1090        for layout in BOTH {
1091            // A count that is not a whole number of words at this width, so the
1092            // segment carries a tail.
1093            let values: Vec<i32> = (0..layout.cell() as i32 + 6).map(|k| k * 7 - 40).collect();
1094            let spent = values.len() * 13;
1095            assert_ne!(spent % layout.word_bits(), 0, "{layout:?}: no tail to test");
1096            let s = stroke(layout, 1, 22, 0, &[block(layout, true, 13, 0, &values)]);
1097            let walked = walk(&s, 0, layout).unwrap();
1098            assert_eq!(walked.records[0].values, values, "{layout:?}");
1099        }
1100    }
1101
1102    #[test]
1103    fn v4_stereo_openings_skip_each_channels_padding() {
1104        let values: Vec<i32> = (0..66).map(|k| k % 31 - 15).collect();
1105        let mut s = stroke(Layout::V4, 1, 22, 0, &[split_block(Layout::V4, 5, &values)]);
1106        let term = s.len() - Layout::V4.word();
1107        s[term..].copy_from_slice(&((1u32 << 23) | 64).to_be_bytes());
1108
1109        let walked = walk(&s, 0, Layout::V4).unwrap();
1110        assert_eq!(walked.records[0].values, values);
1111        assert_eq!(walked.cell, Some(64));
1112        let audio = decode(&s, 0, Layout::V4).unwrap();
1113        let interleaved = values[..33]
1114            .iter()
1115            .zip(&values[33..])
1116            .flat_map(|(&left, &right)| [left as i16, right as i16])
1117            .collect::<Vec<_>>();
1118        assert_eq!(audio.samples, interleaved);
1119    }
1120
1121    /// A v4 stereo stroke of one split 1:1 record, whose header word states `count`
1122    /// fields at `width` over a body packed for 66 of them.
1123    fn v4_split_stroke(width: u8, count: usize) -> Vec<u8> {
1124        let layout = Layout::V4;
1125        let values: Vec<i32> = (0..66).map(|k| k % 31 - 15).collect();
1126        let mut s = stroke(layout, 1, 22, 0, &[split_block(layout, width, &values)]);
1127        let term = s.len() - layout.word();
1128        s[term..].copy_from_slice(&((1u32 << 23) | (2 * layout.cell()) as u32).to_be_bytes());
1129        let head = layout.header_len();
1130        let raw = (1u32 << 23) | (u32::from(width - 1) << 19) | count as u32;
1131        s[head..head + layout.word()].copy_from_slice(&raw.to_be_bytes());
1132        s
1133    }
1134
1135    #[test]
1136    fn a_v4_stereo_opening_whose_channels_outrun_the_terminator_is_a_desync() {
1137        assert!(walk(&v4_split_stroke(5, 66), 0, Layout::V4).is_ok());
1138        assert_eq!(
1139            walk(&v4_split_stroke(5, 80), 0, Layout::V4),
1140            Err(Unsupported::Desync { word: 0 })
1141        );
1142    }
1143
1144    #[test]
1145    fn a_v4_stereo_record_needs_whole_channel_pairs() {
1146        assert_eq!(
1147            walk(&v4_split_stroke(5, 33), 0, Layout::V4),
1148            Err(Unsupported::Malformed { word: 0 })
1149        );
1150    }
1151
1152    #[test]
1153    fn a_stereo_record_needs_whole_channel_pairs() {
1154        let layout = Layout::V3;
1155        let mut s = stroke(layout, 1, 22, 0, &[block(layout, true, 5, 0, &[1])]);
1156        let term = s.len() - layout.word();
1157        s[term..].copy_from_slice(&((1u32 << 23) | 64).to_be_bytes());
1158        assert_eq!(walk(&s, 0, layout), Err(Unsupported::Malformed { word: 0 }));
1159    }
1160
1161    #[test]
1162    fn dequantising_shifts_by_the_headers_own_scale() {
1163        for layout in BOTH {
1164            let mut values = run(layout, 0);
1165            values[..2].copy_from_slice(&[100, -100]);
1166            let s = stroke(
1167                layout,
1168                8191,
1169                exponent_for(8191, 1),
1170                0,
1171                &[block(layout, false, 13, 0, &values)],
1172            );
1173            assert_eq!(decode(&s, 0, layout).unwrap().samples[..2], [200, -200]);
1174        }
1175    }
1176
1177    /// Content below the source's 16-bit LSB is shifted left by the encoder, so
1178    /// dequantising it shifts back the other way.
1179    #[test]
1180    fn a_negative_shift_scales_back_down() {
1181        for layout in BOTH {
1182            let mut values = run(layout, 0);
1183            values[..2].copy_from_slice(&[2048, -2048]);
1184            let s = stroke(
1185                layout,
1186                8191,
1187                exponent_for(8191, -4),
1188                0,
1189                &[block(layout, false, 13, 0, &values)],
1190            );
1191            assert_eq!(decode(&s, 0, layout).unwrap().samples[..2], [128, -128]);
1192        }
1193    }
1194
1195    #[test]
1196    fn a_transient_past_full_scale_clamps_and_says_so() {
1197        for layout in BOTH {
1198            let mut values = run(layout, 0);
1199            values[0] = 4095;
1200            let s = stroke(
1201                layout,
1202                8191,
1203                exponent_for(8191, 4),
1204                0,
1205                &[block(layout, false, 13, 0, &values)],
1206            );
1207            let audio = decode(&s, 0, layout).unwrap();
1208            assert_eq!(audio.samples[0], i16::MAX);
1209            assert_eq!(audio.clipped, 1);
1210        }
1211    }
1212
1213    /// Dense content merges whole runs into one record, and those counts need the
1214    /// full width of the field — an eight-bit read frames them short and derails.
1215    #[test]
1216    fn a_merged_run_carries_a_count_past_a_byte() {
1217        for layout in BOTH {
1218            let n = 43 * layout.cell();
1219            let values: Vec<i32> = (0..n).map(|k| k as i32 % 7 - 3).collect();
1220            let s = stroke(layout, 1, 22, 0, &[block(layout, false, 4, 0, &values)]);
1221            let walked = walk(&s, 0, layout).unwrap();
1222            assert_eq!(walked.records.len(), 1, "{layout:?}");
1223            assert_eq!(walked.records[0].values, values);
1224        }
1225    }
1226
1227    /// The slack in front of a stream can hold stale words, so the directory —
1228    /// not the first non-zero word — is what says where the chain begins.
1229    #[test]
1230    fn the_walk_starts_where_the_directory_says_not_at_the_first_data() {
1231        for layout in BOTH {
1232            let values = run(layout, 6);
1233            let mut s = stroke(layout, 1, 22, 2, &[block(layout, false, 4, 0, &values)]);
1234            let head = layout.header_len();
1235            s[head..head + 2 * layout.word()].fill(0x5a);
1236            let walked = walk(&s, 0, layout).unwrap();
1237            assert_eq!(walked.first_record, 2, "{layout:?}");
1238            assert_eq!(walked.records.len(), 1);
1239            assert_eq!(walked.records[0].values[0], 6);
1240        }
1241    }
1242
1243    #[test]
1244    fn every_refusal_names_itself() {
1245        for layout in BOTH {
1246            assert_eq!(walk(&[0u8; 8], 0, layout), Err(Unsupported::Short));
1247
1248            // A content run that is not a whole number of cells.
1249            let s = stroke(layout, 1, 22, 0, &[block(layout, false, 4, 0, &[1, 2, 3])]);
1250            assert_eq!(
1251                walk(&s, 0, layout),
1252                Err(Unsupported::Malformed { word: 0 }),
1253                "{layout:?}"
1254            );
1255
1256            // A record whose fields run past the end of the stroke.
1257            let mut s = stroke(
1258                layout,
1259                1,
1260                22,
1261                0,
1262                &[block(layout, false, 13, 0, &run(layout, 1))],
1263            );
1264            s[layout.header_len() + layout.word() - 2] = 0xff;
1265            assert!(
1266                matches!(walk(&s, 0, layout), Err(Unsupported::Desync { .. })),
1267                "{layout:?}"
1268            );
1269
1270            // A chain with nothing to end it.
1271            let mut s = stroke(
1272                layout,
1273                1,
1274                22,
1275                0,
1276                &[block(layout, false, 4, 0, &run(layout, 1))],
1277            );
1278            s.truncate(s.len() - layout.word());
1279            assert_eq!(walk(&s, 0, layout), Err(Unsupported::NoTerminator));
1280        }
1281    }
1282
1283    #[test]
1284    fn malformed_codec_boundaries_are_refused() {
1285        for layout in BOTH {
1286            let values = run(layout, 1);
1287            let order_five = stroke(layout, 1, 22, 0, &[block(layout, false, 4, 5, &values)]);
1288            assert_eq!(
1289                walk(&order_five, 0, layout),
1290                Err(Unsupported::Malformed { word: 0 })
1291            );
1292
1293            let mut partial = stroke(layout, 1, 22, 0, &[]);
1294            partial.push(0);
1295            assert_eq!(
1296                walk(&partial, 0, layout),
1297                Err(Unsupported::PartialWord { bytes: 1 })
1298            );
1299
1300            let shifted = stroke(layout, 1, exponent_for(1, SHIFT_LIMIT + 1), 0, &[]);
1301            assert_eq!(
1302                decode(&shifted, 0, layout),
1303                Err(Unsupported::Shift {
1304                    bits: SHIFT_LIMIT + 1
1305                })
1306            );
1307        }
1308    }
1309
1310    /// A wide word's top byte is not part of the record header, and a word carrying
1311    /// one is not a record.
1312    #[test]
1313    fn a_wide_word_with_a_top_byte_is_not_a_record() {
1314        let mut s = stroke(
1315            Layout::V3,
1316            1,
1317            22,
1318            0,
1319            &[block(Layout::V3, false, 4, 0, &run(Layout::V3, 1))],
1320        );
1321        s[Layout::V3.header_len()] = 0x01;
1322        assert_eq!(
1323            walk(&s, 0, Layout::V3),
1324            Err(Unsupported::Malformed { word: 0 })
1325        );
1326    }
1327
1328    #[test]
1329    fn the_directory_resolves_against_the_strokes_own_offset() {
1330        let mut s = vec![0u8; Layout::V2.header_len()];
1331        for (i, p) in [444u16, 483, 762, 762].iter().enumerate() {
1332            let at = SEEK_AT + SEEK_STRIDE * i;
1333            s[at..at + 2].copy_from_slice(&p.to_be_bytes());
1334        }
1335        let dir = Directory::read(&s).unwrap();
1336        assert_eq!(dir.first_record, 444);
1337        assert_eq!(dir.resync, 483);
1338        assert_eq!(dir.mark, 762);
1339        assert_eq!(dir.terminator, 762);
1340        // A single-zone instrument puts its one stroke 981 bytes into the body, so
1341        // its stream starts at word 344 and the pointers count on from there.
1342        assert_eq!(Directory::resolve(dir.first_record, 981, Layout::V2), 100);
1343        assert_eq!(Directory::resolve(dir.terminator, 981, Layout::V2), 418);
1344        // A pointer below the base belongs to an earlier stroke, and lands past the
1345        // wrap rather than before zero — which is why a caller range-checks.
1346        assert_eq!(Directory::resolve(1, 981, Layout::V2), WRAP - 343);
1347        // A stroke far enough into a big instrument has a base past the wrap, and
1348        // its pointers count on from there modulo it.
1349        let far = 3 * (344 + 3 * WRAP) - Layout::V2.header_len();
1350        assert_eq!(Directory::resolve(dir.first_record, far, Layout::V2), 100);
1351        // The unit is the layout's word, so the same pointer at the same byte offset
1352        // names a different word in the wide chain.
1353        assert_eq!(Directory::resolve(444, 4 * 100 - 68, Layout::V3), 344);
1354    }
1355
1356    #[test]
1357    fn the_field_rate_is_the_lattice_rate() {
1358        assert_eq!(FIELD_RATE, 35_002);
1359    }
1360
1361    #[test]
1362    fn the_layout_follows_the_content_version() {
1363        assert_eq!(Layout::from_version(8), Some(Layout::V2));
1364        assert_eq!(Layout::from_version(200), Some(Layout::V2));
1365        assert_eq!(Layout::from_version(300), Some(Layout::V3));
1366        assert_eq!(Layout::from_version(310), Some(Layout::V3));
1367        assert_eq!(Layout::from_version(400), Some(Layout::V4));
1368        assert_eq!(Layout::from_version(420), Some(Layout::V4));
1369    }
1370
1371    /// A generation past the last one modelled has unknown stream units, so it is
1372    /// refused rather than decoded as the newest one known.
1373    #[test]
1374    fn a_content_version_past_the_last_modelled_generation_is_refused() {
1375        assert_eq!(
1376            Layout::from_version(V5_FROM_VERSION - 1),
1377            Some(Layout::V4),
1378            "the ceiling is exclusive"
1379        );
1380        assert_eq!(Layout::from_version(V5_FROM_VERSION), None);
1381        assert_eq!(Layout::from_version(u32::MAX), None);
1382    }
1383
1384    #[test]
1385    fn only_v4_splits_a_stereo_stroke_s_openings() {
1386        assert!(Layout::V4.splits_wide_openings());
1387        assert!(!Layout::V3.splits_wide_openings());
1388        assert!(!Layout::V2.splits_wide_openings());
1389    }
1390
1391    #[test]
1392    fn a_terminator_is_a_width_one_word_stating_the_cell_size() {
1393        // Mono and stereo terminators, in both word sizes.
1394        assert_eq!(terminator_cell(0x0080_0018, 24), Some(24));
1395        assert_eq!(terminator_cell(0x0080_0030, 24), Some(48));
1396        assert_eq!(terminator_cell(0x0080_0020, 32), Some(32));
1397        assert_eq!(terminator_cell(0x0080_0040, 32), Some(64));
1398        // The shape without the count is payload, not the end of the stream.
1399        assert_eq!(terminator_cell(0x0080_0000, 32), None);
1400        assert_eq!(terminator_cell(0x0080_0018, 32), None);
1401        // Mark, reserved, order, and a wide word's top byte must stay clear.
1402        assert_eq!(terminator_cell(0x00c4_0020, 32), None);
1403        assert_eq!(terminator_cell(0x0082_0020, 32), None);
1404        assert_eq!(terminator_cell(0x0080_4020, 32), None);
1405        assert_eq!(terminator_cell(0x6580_0020, 32), None);
1406    }
1407
1408    #[test]
1409    fn the_terminator_pointer_rises_past_the_period_and_the_opening_does_not() {
1410        let first = Directory::resolve(5_999, 0, Layout::V2);
1411        // A stroke inside one period has one alias, so both readings agree.
1412        assert_eq!(
1413            Directory::resolve_end(5_999, 0, Layout::V2, first + 1),
1414            first
1415        );
1416        // Past it the terminator takes the last alias that still lands inside the
1417        // stream, while resolve — what the opening pointer uses — stays at the first.
1418        for periods in 1..4 {
1419            let words = first + periods * WRAP + 1;
1420            assert_eq!(
1421                Directory::resolve_end(5_999, 0, Layout::V2, words),
1422                first + periods * WRAP
1423            );
1424        }
1425    }
1426}