Skip to main content

nord_format/formats/npno/
encode.rs

1//! Writing a piano library: recordings coded into blocks, and a container laid out
2//! around them.
3//!
4//! [`build`] takes a **template** library and a set of [`Recording`]s — one per root
5//! note, [`Bank`] and velocity layer, frames at [`codec::RATE`] — and returns a
6//! [`Library`] the parent module's writer turns into a file. [`rebuild`] re-codes a
7//! library's own strokes from the frames they decode to: a file this crate did not
8//! write comes back block for block, and one it did write comes back byte for byte.
9//!
10//! # The coding laws
11//!
12//! A block's width fixes its frame count, `F(w) = ⌊8·(1022·C − 2)/(w·C)⌋`, so a wider
13//! block is a shorter one and the width and the segmentation are one choice. Per
14//! block: for each order up to [`codec::MAX_ORDER`], take the narrowest width whose
15//! order-`w` residuals all fit `w` signed bits and whose frames still fit what the
16//! stroke has left; then take the order that reaches the narrowest width, ties to the
17//! lowest order. Width 1 occurs and there is no floor above it.
18//!
19//! Every block but the first opens by restating the previous block's last
20//! [`codec::OVERLAP`] frames against the running history, so a block owns
21//! `F(w) − OVERLAP` frames and the stroke owns their sum. The last block is an
22//! ordinary full block whose own trailing overlap sits past the stroke's end, which
23//! is why coding a stroke again needs [`codec::Audio::tail`].
24//!
25//! A stroke therefore states whole blocks, and it states every frame it was given.
26//! Where the capped search lands on the frame count exactly, that count is what the
27//! stroke states. Where it cannot — the remainder shorter than any width's block —
28//! the blocks would have to stop short of the audio, so the stroke is laid out again
29//! with nothing capping any block and ends at the first block to reach the count, the
30//! source read as silent past its end. Such a stroke ends in silence, up to one block
31//! of it, and no frame is dropped for falling between block lengths.
32//!
33//! Both layouts code again unchanged. The cap is what the stroke has left to own, so
34//! a stroke whose blocks land on its frame count gives the same search the same room
35//! the second time and reaches the same widths. A stroke laid out with no cap states
36//! the sum of those blocks, and a capped search over that sum admits every one of
37//! them — each is no longer than what is left when it starts, and the cap only ever
38//! removes candidates — so it lays out the same blocks and this time lands exactly.
39//!
40//! What this codes plays. Confirmed on hardware. Libraries built here load and
41//! sound — mono and stereo, every key of a full-keyboard library including its lowest
42//! and highest root, each of three attack layers, the release stroke at note-off, a
43//! long stroke to its end, and the keys between roots transposed — and a vendor
44//! library coded again from its own audio plays indistinguishably from the original,
45//! in level and in spectrum.
46//!
47//! Given a block's width, order and attenuation, this reproduces its bytes, and the
48//! width and order it derives are the ones the file declares — except where a library's
49//! headers were decided on a signal the file does not store. The attenuation is the same
50//! kind of thing one step smaller: it is a statistic the vendor's encoder recorded
51//! rather than a function of the frames it went on to store, so a block coded again from
52//! its own audio can declare a neighbouring value. Nothing in [`codec`] reads it.
53//! Inferred from specimens; not confirmed on hardware.
54//!
55//! # What the audio does not say
56//!
57//! A stroke record carries fields no audio predicts: four length marks, fifteen
58//! one-pole decay coefficients, a velocity window, a per-stroke identifier, and two
59//! bytes the later streams use. Nor does the prefix's bank of per-note tables and
60//! playback parameters. [`Donor`] is where [`build`] gets them.
61//!
62//! [`Donor::Template`] copies them from a library — for each recording, the template
63//! stroke of the same bank and nearest root, with the marks rescaled to the new
64//! stroke's length. They go in as the template donated them: the instrument accepts
65//! them, and what it makes of them beyond accepting is not known. What comes with them
66//! is the vendor's tuning of an instrument these recordings are not.
67//!
68//! [`Donor::Rules`] states them instead, so a library can be written from recordings
69//! alone. Every one is then a neutral playback parameter: no decay applied over the
70//! recordings, each stroke trimmed by its own layer value, and the damper reaching the
71//! keys the kind of instrument dampens. Confirmed on hardware. A library written this
72//! way plays like the same audio built against a template, within about a decibel at
73//! every velocity and key, and sustains longer because nothing is applied over it.
74
75use super::codec::{self, MAX_ORDER, MAX_WIDTH, MIN_WIDTH, OVERLAP};
76use super::{
77    be32, block_bytes, midi_key, Bank, Library, Stroke, CNSP_MAGIC, DAMPER_TOP_AT, DECAYS,
78    DIRECTORY_AT, FINE_TUNE_AT, FORMAT, GAIN_AT, KEY_MAP_AT, KIND_AT, LADDER_UNITY, MARKS, NOTES,
79    RECORD, REC_BANK, REC_BLOCKS, REC_DECAY, REC_DECAYS, REC_FRAMES, REC_ID, REC_LAYER, REC_MARKS,
80    REC_MARK_BLOCK, REC_SEEDS, REC_START, REC_TRIM, REC_WINDOW, SEEDS, UNCOVERED, VERSION_AT,
81    VERSION_ECHO_AT,
82};
83use crate::cbin::Header;
84use crate::error::{Error, ParseError};
85use crate::formats::nsmp::kernel;
86use crate::formats::predictor;
87use std::borrow::Cow;
88use std::collections::{BTreeMap, BTreeSet};
89
90/// Full scale the header's attenuation statistic is measured against.
91const FULL_SCALE: f64 = 8192.0;
92
93/// Widest field a header can declare, as an index bound.
94const WIDTHS: usize = MAX_WIDTH as usize + 1;
95
96/// What a library states about itself besides its strokes. Everything else — the
97/// stream version, the per-note tables, the word at body `0x06` — comes from the
98/// [`Donor`] [`build`] is given.
99#[derive(Debug, Clone)]
100pub struct Options {
101    /// The half of the `Name#Variant` field before the separator.
102    pub name: String,
103    /// The half after it, where the vendor records the voicing and the library's size.
104    pub variant: String,
105}
106
107impl Options {
108    pub fn new(name: &str) -> Options {
109        Options {
110            name: name.to_owned(),
111            variant: String::new(),
112        }
113    }
114
115    pub fn variant(mut self, variant: &str) -> Options {
116        self.variant = variant.to_owned();
117        self
118    }
119}
120
121/// Where [`build`] takes the fields no audio predicts from.
122#[derive(Debug, Clone)]
123pub enum Donor<'a> {
124    /// A library to copy them from: its prefix whole, and per stroke the length marks
125    /// and decay ladder of its nearest stroke of the same bank.
126    Template(&'a Library<'a>),
127    /// The rules that state them instead, which is what a library built from nothing
128    /// but recordings carries.
129    Rules(Rules),
130}
131
132/// The kind of instrument a library states it holds, at body `0x18`.
133///
134/// The instrument files the library under it. Which code names which kind: Inferred
135/// from specimens; not confirmed on hardware. The byte changes nothing a library
136/// sounds like. Confirmed on hardware.
137#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
138pub enum Kind {
139    ElectricGrand,
140    /// The tine electric pianos.
141    ElectricPiano,
142    /// The reed electric pianos.
143    Wurlitzer,
144    Clavinet,
145    #[default]
146    Grand,
147    Upright,
148    Harpsichord,
149    DigitalPiano,
150    /// The hybrid and ballad electric pianos.
151    Hybrid,
152    Mallet,
153}
154
155impl Kind {
156    pub const ALL: [Kind; 10] = [
157        Kind::ElectricGrand,
158        Kind::ElectricPiano,
159        Kind::Wurlitzer,
160        Kind::Clavinet,
161        Kind::Grand,
162        Kind::Upright,
163        Kind::Harpsichord,
164        Kind::DigitalPiano,
165        Kind::Hybrid,
166        Kind::Mallet,
167    ];
168
169    pub fn from_code(code: u8) -> Option<Kind> {
170        match code {
171            1 => Some(Kind::ElectricGrand),
172            2 => Some(Kind::ElectricPiano),
173            3 => Some(Kind::Wurlitzer),
174            4 => Some(Kind::Clavinet),
175            5 => Some(Kind::Grand),
176            6 => Some(Kind::Upright),
177            7 => Some(Kind::Harpsichord),
178            14 => Some(Kind::DigitalPiano),
179            15 => Some(Kind::Hybrid),
180            16 => Some(Kind::Mallet),
181            _ => None,
182        }
183    }
184
185    pub fn code(self) -> u8 {
186        match self {
187            Kind::ElectricGrand => 1,
188            Kind::ElectricPiano => 2,
189            Kind::Wurlitzer => 3,
190            Kind::Clavinet => 4,
191            Kind::Grand => 5,
192            Kind::Upright => 6,
193            Kind::Harpsichord => 7,
194            Kind::DigitalPiano => 14,
195            Kind::Hybrid => 15,
196            Kind::Mallet => 16,
197        }
198    }
199
200    /// The [`Rules::damper_top`] this kind of instrument has: the acoustic pianos damp
201    /// to a key well below the top of the keyboard and let the rest ring, the reed
202    /// pianos to a higher one, and everything else damps every key.
203    pub fn damper_top(self) -> u8 {
204        match self {
205            Kind::Grand | Kind::Upright => 90,
206            Kind::Wurlitzer => 97,
207            _ => ALL_KEYS_DAMPED,
208        }
209    }
210}
211
212/// A [`Rules::damper_top`] above the highest key the instrument plays, so that every
213/// key is damped at note-off.
214pub const ALL_KEYS_DAMPED: u8 = 109;
215
216/// The [`Rules::gain`] a library states unless a caller says otherwise: +5.0 dB.
217pub const DEFAULT_GAIN: i8 = 50;
218
219/// What a library states about its playback where no template donates it.
220///
221/// These are the parameters a recording cannot carry, at their neutral settings: the
222/// library is heard at [`Rules::gain`], each stroke is trimmed by its own layer value,
223/// nothing is applied over the recordings' own decay, and the damper reaches
224/// [`Rules::damper_top`].
225#[derive(Debug, Clone, Copy, PartialEq, Eq)]
226pub struct Rules {
227    pub kind: Kind,
228    /// Body `0x40c`: a gain over the whole library in tenths of a decibel. Confirmed
229    /// on hardware.
230    pub gain: i8,
231    /// Body `0x40d`: the highest key the instrument damps at note-off. Keys above it
232    /// ring on, and [`ALL_KEYS_DAMPED`] leaves none of them. Confirmed on hardware.
233    pub damper_top: u8,
234}
235
236impl Rules {
237    /// The neutral rules for one kind of instrument.
238    pub fn new(kind: Kind) -> Rules {
239        Rules {
240            kind,
241            gain: DEFAULT_GAIN,
242            damper_top: kind.damper_top(),
243        }
244    }
245}
246
247impl Default for Rules {
248    fn default() -> Rules {
249        Rules::new(Kind::default())
250    }
251}
252
253/// One recording to code: what it is played for, and its frames.
254#[derive(Debug, Clone)]
255pub struct Recording {
256    /// The note it was recorded at.
257    pub root: u8,
258    pub bank: Bank,
259    /// The layer value the stroke record states, 0 being the loudest recording of the
260    /// root and bank. Selection reads this value, not a rank among the layers present
261    /// ([`Stroke::layer`]); [`layer_value`] spreads a root's layers across the
262    /// velocity range the way a vendor library does.
263    pub layer: u8,
264    /// One vector per channel at [`codec::RATE`], all the same length. Every
265    /// recording of one library states the same channel count, 1 or 2.
266    ///
267    /// The stroke holds whole blocks and holds all of these frames, so it states them
268    /// and whatever silence fills out the block they end in — the recording is read as
269    /// silent past its end rather than cut back to a block boundary.
270    pub channels: Vec<Vec<i16>>,
271}
272
273/// The softest layer value a root is given when [`layer_value`] spreads it: the top
274/// of the range vendor libraries use, and well inside [`HIGHEST_PLAYED_LAYER`].
275pub const SOFTEST_LAYER: u8 = 27;
276
277/// The largest layer value any velocity sounds: `(127 − 1)·31/127`, the selection
278/// bound at velocity 1, the softest note-on a key can send.
279///
280/// A key sounds the largest value its root holds that is at most
281/// `(127 − velocity)·31/127` ([`Stroke::layer`]), and that bound only falls as the
282/// velocity rises, so a stroke stating more than this is one no playing reaches.
283/// [`build`] refuses one rather than write a library with a silent stroke in it.
284pub const HIGHEST_PLAYED_LAYER: u8 = ((127 - 1) * 31 / 127) as u8;
285
286/// The value the `index`-th loudest of a root's `layers` takes when the caller states
287/// none: `round(index·27/(layers − 1))`, and 0 for a root holding one.
288///
289/// A key sounds the largest layer value its root holds that is at most
290/// `(127 − velocity)·31/127` ([`Stroke::layer`]), so the spread is what puts a layer
291/// change under each part of the velocity range; values packed at the loud end leave
292/// the softest layer playing almost everywhere. An `index` past the last is that one.
293pub fn layer_value(index: usize, layers: usize) -> u8 {
294    let last = layers.saturating_sub(1);
295    if last == 0 {
296        return 0;
297    }
298    let index = index.min(last);
299    let scale = usize::from(SOFTEST_LAYER);
300    ((index * scale * 2 + last) / (last * 2)) as u8
301}
302
303/// What a WAV's name says about the velocity layer its stroke sits at.
304#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
305pub enum LayerTag {
306    /// `l02`: the third-loudest layer of its root and bank, taking whatever value the
307    /// spread over that root's layers gives it.
308    Index(u8),
309    /// `v12`: the layer value itself, written to the record as it stands.
310    Value(u8),
311}
312
313/// Whether a stroke name may carry a stem of the caller's own in front of the stroke
314/// it states.
315#[derive(Clone, Copy, Debug, PartialEq, Eq)]
316pub enum Stem {
317    /// `060-b0-l00`, and nothing else.
318    None,
319    /// `Grand-060-b0-l00` as well: the trailing group is the whole claim.
320    Any,
321}
322
323/// The stroke a name states — `<root>-b<bank>-l<layer>`, or `<root>-b<bank>-v<value>`
324/// naming the layer value itself — read off a file name without its extension.
325///
326/// A root past [`NOTES`] is no note a library can hold.
327pub fn parse_stroke_name(name: &str, stem: Stem) -> Option<(u8, Bank, LayerTag)> {
328    let mut parts = name.rsplit('-');
329    let third = parts.next()?;
330    let layer = match (third.strip_prefix('l'), third.strip_prefix('v')) {
331        (Some(index), _) => LayerTag::Index(index.parse().ok()?),
332        (None, Some(value)) => LayerTag::Value(value.parse().ok()?),
333        (None, None) => return None,
334    };
335    let bank = Bank::from_code(parts.next()?.strip_prefix('b')?.parse().ok()?)?;
336    let root: u8 = parts.next()?.parse().ok()?;
337    if stem == Stem::None && parts.next().is_some() {
338        return None;
339    }
340    (usize::from(root) < NOTES).then_some((root, bank, layer))
341}
342
343/// Why one root and bank's names state no layer values.
344#[derive(Clone, Copy, Debug, PartialEq, Eq)]
345pub struct LayerClash {
346    pub root: u8,
347    pub bank: Bank,
348    pub how: Clash,
349}
350
351/// The two ways one root and bank's names fail to state a layer each.
352#[derive(Clone, Copy, Debug, PartialEq, Eq)]
353pub enum Clash {
354    /// Some layers named by index and some by value. The two forms mean different
355    /// things about how many layers a spread is over, so one root's bank names its
356    /// layers one way.
357    BothForms,
358    /// One layer named twice, which the spread would hand two different values.
359    Twice,
360}
361
362/// The layer value each stroke states, in the order they were given.
363///
364/// A [`LayerTag::Value`] is that value; a [`LayerTag::Index`] is spread across its root
365/// and bank's own layers, loudest first, by [`layer_value`].
366pub fn layer_values(strokes: &[(u8, Bank, LayerTag)]) -> Result<Vec<u8>, LayerClash> {
367    let mut groups: BTreeMap<(u8, Bank), Vec<usize>> = BTreeMap::new();
368    for (index, &(root, bank, _)) in strokes.iter().enumerate() {
369        groups.entry((root, bank)).or_default().push(index);
370    }
371
372    let mut values = vec![0u8; strokes.len()];
373    for ((root, bank), mut members) in groups {
374        let clash = |how| LayerClash { root, bank, how };
375        let stated = members
376            .iter()
377            .filter(|&&i| matches!(strokes[i].2, LayerTag::Value(_)))
378            .count();
379        if stated != 0 && stated != members.len() {
380            return Err(clash(Clash::BothForms));
381        }
382        members.sort_by_key(|&i| strokes[i].2);
383        if members
384            .windows(2)
385            .any(|pair| strokes[pair[0]].2 == strokes[pair[1]].2)
386        {
387            return Err(clash(Clash::Twice));
388        }
389        let layers = members.len();
390        for (rank, index) in members.into_iter().enumerate() {
391            values[index] = match strokes[index].2 {
392                LayerTag::Value(value) => value,
393                LayerTag::Index(_) => layer_value(rank, layers),
394            };
395        }
396    }
397    Ok(values)
398}
399
400/// A library rebuilt from its own audio, and how each stroke's blocks compare with
401/// the ones they were coded from.
402pub struct Rebuilt {
403    pub library: Library<'static>,
404    /// One entry per stroke, in directory order.
405    pub strokes: Vec<Recoded>,
406}
407
408/// How one stroke's blocks came back.
409#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
410pub struct Recoded {
411    /// Blocks the coder wrote.
412    pub blocks: usize,
413    /// Blocks byte-identical to the ones read.
414    pub identical: usize,
415    /// Blocks identical apart from the attenuation byte, which the decode never reads.
416    pub restated: usize,
417}
418
419impl Recoded {
420    /// Blocks that came back with different residuals, a different width or order, or
421    /// no counterpart at all.
422    pub fn recoded(&self) -> usize {
423        self.blocks - self.identical - self.restated
424    }
425}
426
427/// Build a library from recordings, taking from `donor` every field the audio does
428/// not decide.
429///
430/// The recordings may arrive in any order; the directory sorts them by root, then
431/// bank, then layer, which is the ascending root order the per-root counts index by.
432/// The key map routes every key up to one semitone above the highest root, and the
433/// per-key fine tune starts at zero rather than carrying a template's.
434pub fn build(
435    donor: &Donor<'_>,
436    options: &Options,
437    recordings: &[Recording],
438) -> Result<Library<'static>, Error> {
439    let channels = check_recordings(recordings)?;
440
441    let (header, mut prefix) = match donor {
442        Donor::Template(template) => (template.header.clone(), template.prefix.clone()),
443        Donor::Rules(rules) => (
444            Header::new(FORMAT, (0, 0), CONTENT_VERSION),
445            rules_prefix(rules),
446        ),
447    };
448    prefix[FINE_TUNE_AT..FINE_TUNE_AT + NOTES].fill(0);
449    let roots: BTreeSet<u8> = recordings.iter().map(|r| r.root).collect();
450    prefix[KEY_MAP_AT..KEY_MAP_AT + NOTES].copy_from_slice(&key_map(&roots));
451
452    let mut library = Library {
453        header,
454        prefix,
455        channels,
456        strokes: Vec::new(),
457    };
458    library.set_name_and_variant(&options.name, &options.variant)?;
459
460    let mut order: Vec<&Recording> = recordings.iter().collect();
461    order.sort_by_key(|r| (r.root, r.bank.code(), r.layer));
462
463    let mut donors = Vec::with_capacity(order.len());
464    for (index, recording) in order.iter().enumerate() {
465        donors.push(match donor {
466            Donor::Template(template) => *donor_record(template, recording)?,
467            Donor::Rules(_) => rules_record(recording, index),
468        });
469    }
470    // A donor serving several recordings would name them all the same, so the
471    // identifiers only carry over when they stay distinct.
472    let unique: BTreeSet<u32> = donors.iter().map(|d| be32(d, REC_ID)).collect();
473    let keep_ids = unique.len() == donors.len();
474
475    for (index, (recording, donor)) in order.iter().zip(&donors).enumerate() {
476        let seeds = seeds_for(&recording.channels);
477        let target = recording.channels[0].len();
478        let coded = code(&recording.channels, &seeds, target)?;
479        let id = if keep_ids {
480            be32(donor, REC_ID)
481        } else {
482            index as u32 + 1
483        };
484        library.strokes.push(Stroke {
485            root: recording.root,
486            record: record(
487                donor,
488                &coded,
489                recording.bank.code(),
490                recording.layer,
491                &seeds,
492                id,
493            )?,
494            audio: Cow::Owned(coded.audio),
495        });
496    }
497    Ok(library)
498}
499
500/// The content version the container states where no template donates one: a library's
501/// own version times a hundred, as the instrument reports it. The hardware evidence
502/// reaches no further than this: a library stating it loads and plays. Confirmed on
503/// hardware.
504const CONTENT_VERSION: u32 = 540;
505
506/// The stream version a rule-written prefix states, at [`VERSION_AT`],
507/// [`VERSION_REPEAT_AT`] and [`VERSION_ECHO_AT`].
508const RULES_VERSION: u16 = 0x450;
509
510/// u32 the vendor makes distinct per library. The instrument does not read it, so a
511/// rule-written prefix states one. Confirmed on hardware.
512const FILE_ID_AT: usize = 0x06;
513const FILE_ID: u32 = 1;
514
515/// The stream version again, ahead of the echo at [`VERSION_ECHO_AT`].
516const VERSION_REPEAT_AT: usize = 0x16;
517
518/// The three bytes after [`KIND_AT`]: a model id within the kind and the library's
519/// version digit — neither of which a rule-written prefix claims — then a format
520/// constant.
521const KIND_TRAILER: [u8; 3] = [0, 0, 2];
522
523/// The per-note tables, [`NOTES`] bytes each, at the value that states nothing about
524/// the note: no retune at [`FINE_TUNE_AT`], and for the rest the value a library that
525/// has been played holds, sweeping any of them having moved nothing measurable.
526/// Confirmed on hardware.
527const PER_NOTE_TABLES: [(usize, u8); 6] = [
528    (0x10c, 0),
529    (FINE_TUNE_AT, 0),
530    (0x20c, 57),
531    (0x28c, 0),
532    (0x30c, 0),
533    (0x38c, 0),
534];
535
536/// The playback parameters, zero but for the fields [`rules_prefix`] writes into them.
537const PARAMETERS: std::ops::Range<usize> = 0x40c..0x60f;
538/// The three bytes after the damper limit, whose meaning is open; every library holds
539/// these.
540const PARAMETER_TAIL_AT: usize = 0x40e;
541const PARAMETER_TAIL: [u8; 3] = [10, 108, 1];
542/// Nineteen bytes ahead of the damper cut whose meaning is open; every library holds
543/// these.
544const BEFORE_DAMPER_CUT_AT: usize = 0x489;
545const BEFORE_DAMPER_CUT: [u8; 19] = [128; 19];
546/// The damper cut per note, [`NOTES`] bytes of [`damper_cut`].
547const DAMPER_CUT_AT: usize = 0x49d;
548const _: () = assert!(DAMPER_CUT_AT + NOTES <= PARAMETERS.end);
549
550/// The prefix a library states where no template donates one: the stream's own
551/// constants, the parameters `rules` carries, and zero wherever the vendor writes
552/// something only a recording session knows.
553///
554/// The name, the key map and the counts are laid over this by [`build`] and the
555/// container's writer.
556fn rules_prefix(rules: &Rules) -> Vec<u8> {
557    let mut prefix = vec![0u8; DIRECTORY_AT];
558    prefix[..CNSP_MAGIC.len()].copy_from_slice(CNSP_MAGIC);
559    for at in [VERSION_AT, VERSION_REPEAT_AT, VERSION_ECHO_AT] {
560        prefix[at..at + 2].copy_from_slice(&RULES_VERSION.to_be_bytes());
561    }
562    prefix[FILE_ID_AT..FILE_ID_AT + 4].copy_from_slice(&FILE_ID.to_be_bytes());
563    prefix[KIND_AT] = rules.kind.code();
564    prefix[KIND_AT + 1..KIND_AT + 1 + KIND_TRAILER.len()].copy_from_slice(&KIND_TRAILER);
565    for (at, value) in PER_NOTE_TABLES {
566        prefix[at..at + NOTES].fill(value);
567    }
568    prefix[GAIN_AT] = rules.gain as u8;
569    prefix[DAMPER_TOP_AT] = rules.damper_top;
570    prefix[PARAMETER_TAIL_AT..PARAMETER_TAIL_AT + PARAMETER_TAIL.len()]
571        .copy_from_slice(&PARAMETER_TAIL);
572    prefix[BEFORE_DAMPER_CUT_AT..BEFORE_DAMPER_CUT_AT + BEFORE_DAMPER_CUT.len()]
573        .copy_from_slice(&BEFORE_DAMPER_CUT);
574    for note in 0..NOTES {
575        prefix[DAMPER_CUT_AT + note] = damper_cut(note);
576    }
577    prefix
578}
579
580/// The damper cut's entry for `note`, at [`DAMPER_CUT_AT`] `+ note`.
581///
582/// A plateau over the lowest notes, a straight fall to the highest key an instrument
583/// plays, and a fixed value past it. Confirmed on hardware. A curve of this shape takes
584/// a held key down within tens of milliseconds, where a flat table of any level takes
585/// about half a second. What axis the instrument reads the table on is open.
586fn damper_cut(note: usize) -> u8 {
587    /// The last note of the plateau, where the fall begins; it ends on `TOP`.
588    const FLAT_TO: usize = 24;
589    const TOP: usize = 108;
590    const PLATEAU: f64 = 79.0;
591    const FALL: f64 = 59.0;
592    /// What every note past [`TOP`] states, which no key reaches.
593    const PAST_TOP: u8 = 30;
594
595    if note < FLAT_TO {
596        PLATEAU as u8
597    } else if note <= TOP {
598        (PLATEAU - FALL * (note - FLAT_TO) as f64 / (TOP - FLAT_TO) as f64).round_ties_even() as u8
599    } else {
600        PAST_TOP
601    }
602}
603
604/// The record a stroke states where no template donates one: no length marks, a decay
605/// ladder that applies nothing, the trim its layer implies, and its own place in the
606/// directory as the identifier.
607///
608/// [`record`] lays the audio's own fields over this, and reads the absent marks as the
609/// zeros they are.
610fn rules_record(recording: &Recording, index: usize) -> [u8; RECORD] {
611    let mut out = [0u8; RECORD];
612    let (window, trim) = velocity_window(recording.bank, recording.layer);
613    out[REC_WINDOW..REC_WINDOW + 2].copy_from_slice(&window.to_be_bytes());
614    out[REC_TRIM..REC_TRIM + 2].copy_from_slice(&trim.to_be_bytes());
615    for entry in 0..DECAYS {
616        let at = REC_DECAYS + entry * 4;
617        out[at..at + 4].copy_from_slice(&LADDER_UNITY.to_be_bytes());
618    }
619    out[REC_ID..REC_ID + 4].copy_from_slice(&(index as u32 + 1).to_be_bytes());
620    out
621}
622
623/// The trim [`REC_TRIM`] states for a release stroke, in decibels.
624const RELEASE_TRIM: u16 = 12;
625
626/// The largest trim [`velocity_window`] states, the top of the range the layer values
627/// are selected over ([`Stroke::layer`]).
628const WIDEST_TRIM: u16 = 31;
629
630/// The pair at [`REC_WINDOW`] and [`REC_TRIM`] a stroke of `bank` and `layer` states.
631///
632/// An attack or resonance stroke is trimmed three decibels past its layer value, so
633/// that the softer layers of a root play softer than the loud ones by the amount their
634/// values already say they are; a release stroke takes a fixed trim instead, its layer
635/// value being no part of how it is selected.
636fn velocity_window(bank: Bank, layer: u8) -> (u16, u16) {
637    match bank {
638        Bank::Release => (0, RELEASE_TRIM),
639        Bank::Attack | Bank::Resonance => (
640            u16::from(layer),
641            u16::from(layer).saturating_add(3).min(WIDEST_TRIM),
642        ),
643    }
644}
645
646/// Code every stroke of `library` again from the frames it decodes to, each keeping
647/// its own record and its own place in the directory.
648///
649/// A stroke whose decode saturates is refused rather than coded: the frames it would
650/// be given are the clamped ones, so what came back would be a stroke holding audio
651/// the file does not, and a stream that saturates this predictor is one the codec
652/// does not describe.
653pub fn rebuild(library: &Library<'_>) -> Result<Rebuilt, Error> {
654    let block = library.block_bytes();
655    let mut strokes = Vec::new();
656    let mut report = Vec::new();
657    for stroke in library.strokes() {
658        let audio = codec::decode(stroke, library.channels())?;
659        if audio.clipped > 0 {
660            return Err(refuse(format!(
661                "{stroke:?}: {} sample(s) left int16 in the decode; coding a stroke this \
662                 codec does not describe would write the saturated frames as new audio",
663                audio.clipped
664            )));
665        }
666        let target = audio.frames();
667        let mut source = audio.lanes;
668        for (channel, tail) in source.iter_mut().zip(&audio.tail) {
669            channel.extend_from_slice(tail);
670        }
671        let seeds = stroke.seeds();
672        let coded = code(&source, &seeds, target)?;
673        report.push(compare(stroke.audio(), &coded.audio, block));
674        strokes.push(Stroke {
675            root: stroke.root,
676            record: record(
677                stroke.record(),
678                &coded,
679                stroke.bank_code(),
680                stroke.layer(),
681                &seeds,
682                stroke.id(),
683            )?,
684            audio: Cow::Owned(coded.audio),
685        });
686    }
687    Ok(Rebuilt {
688        library: Library {
689            header: library.header.clone(),
690            prefix: library.prefix.clone(),
691            channels: library.channels(),
692            strokes,
693        },
694        strokes: report,
695    })
696}
697
698/// What [`resample`] produced.
699pub struct Resampled {
700    /// One vector per channel at [`codec::RATE`].
701    pub channels: Vec<Vec<i16>>,
702    /// Samples a sum put outside `i16`, which saturate.
703    pub clipped: usize,
704}
705
706/// 16-bit PCM at `rate`, interleaved by channel, resampled onto the stroke lattice.
707///
708/// The tap bank is [`nsmp`](crate::formats::nsmp::kernel)'s and the lattice is
709/// `t(f) = rate·f / RATE`. Audio already at [`codec::RATE`] passes through untouched:
710/// the bank interpolates rather than reproduces, so running it at a ratio of one
711/// would filter the source for nothing.
712///
713/// The kernel's cutoff follows the rates: a source faster than [`codec::RATE`] is
714/// band-limited to the lattice's own Nyquist before it lands on it, and a slower one
715/// keeps its whole band.
716///
717/// The lattice count follows the rate the source declares, so a source that would
718/// stretch past the frame count a stroke states is refused rather than allocated.
719pub fn resample(samples: &[i16], channels: usize, rate: u32) -> Result<Resampled, Error> {
720    if channels == 0 || rate == 0 || !samples.len().is_multiple_of(channels) {
721        return Err(ParseError::OutOfBounds {
722            value: format!(
723                "{} sample(s) of {channels} channel(s) at {rate} Hz",
724                samples.len()
725            ),
726            bound: "whole frames of at least one channel at a positive rate".into(),
727        }
728        .into());
729    }
730    if rate == codec::RATE {
731        let mut lanes = vec![Vec::new(); channels];
732        for (i, &sample) in samples.iter().enumerate() {
733            lanes[i % channels].push(sample);
734        }
735        return Ok(Resampled {
736            channels: lanes,
737            clipped: 0,
738        });
739    }
740
741    let frames = samples.len() / channels;
742    let stretched = frames as u128 * u128::from(codec::RATE) / u128::from(rate);
743    let fields = usize::try_from(stretched)
744        .ok()
745        .filter(|&fields| u32::try_from(fields).is_ok())
746        .ok_or_else(|| ParseError::OutOfBounds {
747            value: format!("{frames} frame(s) at {rate} Hz, which is {stretched} on the lattice"),
748            bound: "the u32 frame count a stroke record holds".into(),
749        })?;
750    let kernel = kernel::Kernel::new(rate, codec::RATE);
751    let mut clipped = 0;
752    let mut lanes = Vec::with_capacity(channels);
753    for channel in 0..channels {
754        let lane: Vec<i16> = samples
755            .iter()
756            .skip(channel)
757            .step_by(channels)
758            .copied()
759            .collect();
760        let mut out = Vec::new();
761        out.try_reserve_exact(fields)
762            .map_err(|_| ParseError::OutOfBounds {
763                value: format!("{fields} frame(s)"),
764                bound: "an allocation that fits memory".into(),
765            })?;
766        out.extend((0..fields).map(|f| {
767            let value = kernel.field(&lane, f);
768            let narrow = value.clamp(i64::from(i16::MIN), i64::from(i16::MAX)) as i16;
769            clipped += usize::from(i64::from(narrow) != value);
770            narrow
771        }));
772        lanes.push(out);
773    }
774    Ok(Resampled {
775        channels: lanes,
776        clipped,
777    })
778}
779
780/// The channel count the recordings agree on, or the first thing about them a
781/// library cannot state.
782fn check_recordings(recordings: &[Recording]) -> Result<u16, Error> {
783    let Some(first) = recordings.first() else {
784        return Err(refuse(
785            "a library with no recordings at all has nothing to play",
786        ));
787    };
788    let channels = first.channels.len();
789    if !(1..=2).contains(&channels) {
790        return Err(ParseError::OutOfBounds {
791            value: format!("{channels} channels"),
792            bound: "1 or 2, which is what a library states".into(),
793        }
794        .into());
795    }
796
797    let mut seen = BTreeSet::new();
798    for recording in recordings {
799        let what = describe(recording);
800        midi_key("root", recording.root)?;
801        if recording.channels.len() != channels {
802            return Err(refuse(format!(
803                "{what} has {} channel(s) where another has {channels}; one library plays \
804                 one channel count",
805                recording.channels.len()
806            )));
807        }
808        let frames = recording.channels[0].len();
809        if recording.channels.iter().any(|c| c.len() != frames) {
810            return Err(refuse(format!(
811                "{what} has channels of unequal length; a frame is one sample of each"
812            )));
813        }
814        if frames == 0 {
815            return Err(refuse(format!("{what} has no frames")));
816        }
817        if recording.layer > HIGHEST_PLAYED_LAYER {
818            return Err(refuse(format!(
819                "{what} states a layer value no velocity selects; {HIGHEST_PLAYED_LAYER} is \
820                 the largest a key ever sounds"
821            )));
822        }
823        if !seen.insert((recording.root, recording.bank.code(), recording.layer)) {
824            return Err(refuse(format!(
825                "{what} is recorded twice; a root's layers are numbered within one bank"
826            )));
827        }
828    }
829    Ok(channels as u16)
830}
831
832fn describe(recording: &Recording) -> String {
833    format!(
834        "root {} {} layer {}",
835        recording.root, recording.bank, recording.layer
836    )
837}
838
839fn refuse(what: impl Into<String>) -> Error {
840    ParseError::AssertFail(what.into()).into()
841}
842
843/// The root each key plays: the lowest root the key sits no more than a semitone
844/// above. A key more than a semitone above the highest root is left uncovered.
845///
846/// Inferred from specimens; not confirmed on hardware.
847fn key_map(roots: &BTreeSet<u8>) -> [u8; NOTES] {
848    let mut map = [UNCOVERED; NOTES];
849    for (key, slot) in map.iter_mut().enumerate() {
850        if let Some(&root) = roots.range((key as u8).saturating_sub(1)..).next() {
851            *slot = root;
852        }
853    }
854    map
855}
856
857/// The template stroke a recording inherits the fields no audio predicts from: the
858/// same bank and nearest root, then the nearest layer.
859///
860/// A release stroke zeroes the marks and the decay coefficient at `+0x2e` it would
861/// inherit, so any stroke can donate to one; anything else needs a donor that declares
862/// marks of its own.
863fn donor_record<'a>(
864    template: &'a Library<'_>,
865    recording: &Recording,
866) -> Result<&'a [u8; RECORD], Error> {
867    let release = Bank::Release.code();
868    let wanted = recording.bank.code();
869    let same: Vec<&Stroke<'_>> = template
870        .strokes()
871        .iter()
872        .filter(|s| s.bank_code() == wanted)
873        .collect();
874    let pool: Vec<&Stroke<'_>> = match (same.is_empty(), recording.bank) {
875        (false, _) => same,
876        (true, Bank::Release) => template.strokes().iter().collect(),
877        (true, _) => template
878            .strokes()
879            .iter()
880            .filter(|s| s.bank_code() != release)
881            .collect(),
882    };
883    pool.iter()
884        .min_by_key(|s| {
885            (
886                s.root.abs_diff(recording.root),
887                s.layer().abs_diff(recording.layer),
888            )
889        })
890        .map(|s| s.record())
891        .ok_or_else(|| {
892            refuse(format!(
893                "the template records no stroke to take {}'s length marks and decay \
894                 coefficients from, and nothing in the audio predicts them",
895                describe(recording)
896            ))
897        })
898}
899
900/// One stroke's blocks, and what its record has to say about them.
901struct Coded {
902    audio: Vec<u8>,
903    /// Frames the blocks own between them.
904    owned: usize,
905    /// The frame each block starts at.
906    starts: Vec<usize>,
907}
908
909/// Where one block sits and what its header will say.
910#[derive(Debug, Clone, Copy)]
911struct Placed {
912    at: usize,
913    order: u8,
914    width: u8,
915}
916
917/// Code whole blocks over `target` frames of `source` — one vector per channel — and
918/// report the frames they own between them, which is never fewer than `target`.
919///
920/// Each block is capped at what `target` has left to own, and when the blocks land on
921/// `target` exactly that is the stroke. When instead they would stop short — the
922/// remainder shorter than [`shortest_block`], so that no width's block fits it — the
923/// stroke is laid out again with no cap on any block and ends at the first one to
924/// reach `target`, the source read as silent past its end. What it then owns past
925/// `target` is silence the stroke states rather than audio it drops.
926fn code(source: &[Vec<i16>], seeds: &[[i16; SEEDS]; 2], target: usize) -> Result<Coded, Error> {
927    let channels = source.len();
928    let block = block_bytes(channels as u16);
929    let counts = frame_counts(block, channels);
930    let widest = counts[usize::from(MIN_WIDTH)];
931    let total = target
932        .checked_add(widest)
933        .and_then(|total| total.checked_mul(channels).map(|_| total))
934        .ok_or_else(|| refuse("a stroke longer than this platform can address"))?;
935    let planes = planes(source, seeds, total)?;
936
937    let mut placed = lay_capped(&planes, channels, &counts, target);
938    if placed.is_empty() || owned_by(&placed, &counts) != target {
939        placed = lay_uncapped(&planes, channels, &counts, target);
940    }
941
942    let mut audio = Vec::new();
943    for block_at in &placed {
944        let frames = counts[usize::from(block_at.width)];
945        let span = block_at.at * channels..(block_at.at + frames) * channels;
946        let peak = planes[0][span.clone()]
947            .iter()
948            .map(|&v| i64::from(v).abs())
949            .max()
950            .unwrap_or(0);
951        pack(
952            &mut audio,
953            block_at.order,
954            block_at.width,
955            attenuation(peak),
956            &planes[usize::from(block_at.order)][span],
957            block,
958        );
959    }
960    Ok(Coded {
961        audio,
962        owned: owned_by(&placed, &counts),
963        starts: placed.iter().map(|b| b.at).collect(),
964    })
965}
966
967/// Frames a layout owns between its blocks.
968fn owned_by(placed: &[Placed], counts: &[usize; WIDTHS]) -> usize {
969    placed
970        .last()
971        .map_or(0, |b| b.at + counts[usize::from(b.width)] - OVERLAP)
972}
973
974/// Blocks over the frames from zero, each capped at what `target` has left to own.
975///
976/// They land on `target` exactly or stop short of it by less than
977/// [`shortest_block`], which is the length no width's block fits.
978fn lay_capped(
979    planes: &[Vec<i32>],
980    channels: usize,
981    counts: &[usize; WIDTHS],
982    target: usize,
983) -> Vec<Placed> {
984    let shortest = shortest_block(counts);
985    let mut out = Vec::new();
986    let mut at = 0usize;
987    while target - at >= shortest {
988        let block = place(planes, at, channels, counts, Some(target - at));
989        at += counts[usize::from(block.width)] - OVERLAP;
990        out.push(block);
991    }
992    out
993}
994
995/// Blocks over the frames from zero with nothing capping their length, up to and
996/// including the first one whose frames reach `target`.
997///
998/// A capped search over the frames these own admits every one of them — each is no
999/// longer than what is left when it starts — so it lays out the same blocks and lands
1000/// on that count exactly, which is what makes the stroke this writes one that codes
1001/// again unchanged.
1002fn lay_uncapped(
1003    planes: &[Vec<i32>],
1004    channels: usize,
1005    counts: &[usize; WIDTHS],
1006    target: usize,
1007) -> Vec<Placed> {
1008    let mut out = Vec::new();
1009    let mut at = 0usize;
1010    loop {
1011        let block = place(planes, at, channels, counts, None);
1012        at += counts[usize::from(block.width)] - OVERLAP;
1013        out.push(block);
1014        if at >= target {
1015            return out;
1016        }
1017    }
1018}
1019
1020/// The block starting at frame `at`, `room` being what the stroke has left to own.
1021fn place(
1022    planes: &[Vec<i32>],
1023    at: usize,
1024    channels: usize,
1025    counts: &[usize; WIDTHS],
1026    room: Option<usize>,
1027) -> Placed {
1028    let (order, width) = choose(planes, at, channels, counts, room)
1029        .expect("order zero states a sample outright, which always fits sixteen bits");
1030    Placed { at, order, width }
1031}
1032
1033/// Frames the shortest block a header can declare owns: the widest field, and so the
1034/// fewest frames. It is the grain the stroke's own frame count comes in.
1035fn shortest_block(counts: &[usize; WIDTHS]) -> usize {
1036    counts[usize::from(MAX_WIDTH)] - OVERLAP
1037}
1038
1039/// Frames a block of each candidate width carries, the overlap included.
1040fn frame_counts(block: usize, channels: usize) -> [usize; WIDTHS] {
1041    let mut out = [0; WIDTHS];
1042    for width in MIN_WIDTH..=MAX_WIDTH {
1043        out[usize::from(width)] = codec::block_frames(width, block, channels);
1044    }
1045    out
1046}
1047
1048/// `Δ^order` of the covered frames for every order a header can declare, each
1049/// interleaved by channel the way a block emits them and read as silent past the
1050/// source's end.
1051fn planes(
1052    source: &[Vec<i16>],
1053    seeds: &[[i16; SEEDS]; 2],
1054    total: usize,
1055) -> Result<Vec<Vec<i32>>, Error> {
1056    let channels = source.len();
1057    let sample = |channel: usize, n: isize| -> i64 {
1058        match usize::try_from(n) {
1059            Ok(n) => i64::from(source[channel].get(n).copied().unwrap_or(0)),
1060            Err(_) => i64::from(seeds[channel][(SEEDS as isize + n) as usize]),
1061        }
1062    };
1063    let mut out = Vec::with_capacity(MAX_ORDER + 1);
1064    for order in 0..=MAX_ORDER {
1065        let mut plane = residuals(total * channels)?;
1066        for n in 0..total {
1067            for channel in 0..channels {
1068                let mut acc = 0i64;
1069                for j in 0..=order {
1070                    let term =
1071                        predictor::binomial(order, j) * sample(channel, n as isize - j as isize);
1072                    acc += if j.is_multiple_of(2) { term } else { -term };
1073                }
1074                plane[n * channels + channel] = acc as i32;
1075            }
1076        }
1077        out.push(plane);
1078    }
1079    Ok(out)
1080}
1081
1082fn residuals(len: usize) -> Result<Vec<i32>, Error> {
1083    let mut out = Vec::new();
1084    out.try_reserve_exact(len)
1085        .map_err(|_| ParseError::OutOfBounds {
1086            value: format!("{len} residual(s)"),
1087            bound: "an allocation that fits memory".into(),
1088        })?;
1089    out.resize(len, 0);
1090    Ok(out)
1091}
1092
1093/// The `(order, width)` a block starting at frame `at` declares.
1094///
1095/// `owned_left` caps a block's owned frames at what the stroke has left to own. The
1096/// narrowest width is the longest block, so the cap rules out an opening range of
1097/// widths; at [`shortest_block`] it leaves only the widest, which order zero always
1098/// reaches, so a cap that large or larger always names a block. `None` lifts the cap,
1099/// which is what a last block reaching past the source is chosen without.
1100fn choose(
1101    planes: &[Vec<i32>],
1102    at: usize,
1103    channels: usize,
1104    counts: &[usize; WIDTHS],
1105    owned_left: Option<usize>,
1106) -> Option<(u8, u8)> {
1107    let mut best: Option<(u8, u8)> = None;
1108    for (order, plane) in planes.iter().enumerate() {
1109        let mut lo = 0i32;
1110        let mut hi = 0i32;
1111        let mut scanned = at;
1112        let mut narrowest = None;
1113        // A wider block is a shorter one, so walking widths down grows the window a
1114        // step at a time and the span the residuals need never narrows again.
1115        for width in (MIN_WIDTH..=MAX_WIDTH).rev() {
1116            let frames = counts[usize::from(width)];
1117            for &value in &plane[scanned * channels..(at + frames) * channels] {
1118                lo = lo.min(value);
1119                hi = hi.max(value);
1120            }
1121            scanned = at + frames;
1122            let bound = 1i32 << (width - 1);
1123            if lo < -bound || hi >= bound {
1124                break;
1125            }
1126            if owned_left.is_none_or(|left| frames - OVERLAP <= left) {
1127                narrowest = Some(width);
1128            }
1129        }
1130        if let Some(width) = narrowest {
1131            if best.is_none_or(|(_, reached)| width < reached) {
1132                best = Some((order as u8, width));
1133            }
1134        }
1135    }
1136    best
1137}
1138
1139/// Append one block: the header word, then the residuals as `width`-bit two's
1140/// complement fields low-bit-first, then zero to the block's length.
1141fn pack(out: &mut Vec<u8>, order: u8, width: u8, stat: u8, residuals: &[i32], block: usize) {
1142    let start = out.len();
1143    let header = (u16::from(stat) << 8) | (u16::from(order) << 5) | u16::from(width);
1144    out.extend_from_slice(&header.to_be_bytes());
1145    let mask = (1u64 << width) - 1;
1146    let mut reservoir = 0u64;
1147    let mut held = 0u32;
1148    for &value in residuals {
1149        reservoir |= (i64::from(value) as u64 & mask) << held;
1150        held += u32::from(width);
1151        while held >= 16 {
1152            out.extend_from_slice(&((reservoir & 0xffff) as u16).to_be_bytes());
1153            reservoir >>= 16;
1154            held -= 16;
1155        }
1156    }
1157    if held > 0 {
1158        out.extend_from_slice(&((reservoir & 0xffff) as u16).to_be_bytes());
1159    }
1160    out.resize(start + block, 0);
1161}
1162
1163/// The header's high byte: how far a block's loudest frame sits below [`FULL_SCALE`],
1164/// in dB, rounded to a whole one and clamped to `0..=100`.
1165///
1166/// A silent block declares 100 where one count declares 78. Inferred from specimens;
1167/// not confirmed on hardware.
1168fn attenuation(peak: i64) -> u8 {
1169    if peak == 0 {
1170        return 100;
1171    }
1172    let db = -20.0 * (peak as f64 / FULL_SCALE).log10();
1173    (db + 0.5).floor().clamp(0.0, 100.0) as u8
1174}
1175
1176/// The four seeds a new recording declares, oldest first: a zero, then the recording's
1177/// own first three frames.
1178///
1179/// Vendor strokes carry the four frames before the recording, the oldest of them zero.
1180/// A recording that starts in silence has no such frames to carry and this states zeros,
1181/// which is the same thing. Inferred from specimens; not confirmed on hardware.
1182fn seeds_for(source: &[Vec<i16>]) -> [[i16; SEEDS]; 2] {
1183    let mut out = [[0i16; SEEDS]; 2];
1184    for (channel, group) in source.iter().zip(out.iter_mut()) {
1185        for (i, slot) in group.iter_mut().skip(1).enumerate() {
1186            *slot = channel.get(i).copied().unwrap_or(0);
1187        }
1188    }
1189    out
1190}
1191
1192/// A donor record with everything the audio decides written over it.
1193///
1194/// The length marks scale with the stroke's length so that they stay inside it. A
1195/// release stroke declares no marks and zeroes the decay coefficient at `+0x2e`,
1196/// keeping the fourteen-entry ladder at `+0x36` exactly as the donor carries it, as a
1197/// stroke of any other bank does. The block index at `+0x2c` is derived: it is the
1198/// block holding the first mark.
1199fn record(
1200    donor: &[u8; RECORD],
1201    coded: &Coded,
1202    bank: u8,
1203    layer: u8,
1204    seeds: &[[i16; SEEDS]; 2],
1205    id: u32,
1206) -> Result<[u8; RECORD], Error> {
1207    let owned = u32::try_from(coded.owned).map_err(|_| ParseError::OutOfBounds {
1208        value: format!("{} frames", coded.owned),
1209        bound: "the u32 frame count a stroke record holds".into(),
1210    })?;
1211    let blocks = u16::try_from(coded.starts.len()).map_err(|_| ParseError::OutOfBounds {
1212        value: format!("{} blocks", coded.starts.len()),
1213        bound: "the u16 block count a stroke record holds".into(),
1214    })?;
1215
1216    let mut out = *donor;
1217    out[REC_START..REC_START + 4].fill(0);
1218    out[REC_BANK] = bank;
1219    out[REC_LAYER] = layer;
1220    out[REC_FRAMES..REC_FRAMES + 4].copy_from_slice(&owned.to_be_bytes());
1221    out[REC_BLOCKS..REC_BLOCKS + 2].copy_from_slice(&blocks.to_be_bytes());
1222    for (channel, group) in seeds.iter().enumerate() {
1223        for (i, seed) in group.iter().enumerate() {
1224            let at = REC_SEEDS + (channel * SEEDS + i) * 2;
1225            out[at..at + 2].copy_from_slice(&seed.to_be_bytes());
1226        }
1227    }
1228
1229    let silent = bank == Bank::Release.code();
1230    let donor_frames = be32(donor, REC_FRAMES);
1231    let mut first = 0u32;
1232    for mark in 0..MARKS {
1233        let at = REC_MARKS + mark * 4;
1234        let scaled = match (silent, donor_frames) {
1235            (true, _) | (_, 0) => 0,
1236            _ => rescale(be32(donor, at), owned, donor_frames),
1237        };
1238        out[at..at + 4].copy_from_slice(&scaled.to_be_bytes());
1239        if mark == 0 {
1240            first = scaled;
1241        }
1242    }
1243    let holding = coded
1244        .starts
1245        .iter()
1246        .rposition(|&start| start as u64 <= u64::from(first))
1247        .unwrap_or(0);
1248    out[REC_MARK_BLOCK..REC_MARK_BLOCK + 2].copy_from_slice(&(holding as u16).to_be_bytes());
1249    if silent {
1250        out[REC_DECAY..REC_DECAY + 4].fill(0);
1251    }
1252    out[REC_ID..REC_ID + 4].copy_from_slice(&id.to_be_bytes());
1253    Ok(out)
1254}
1255
1256/// A mark at the same place in a stroke of `owned` frames, and inside it.
1257fn rescale(mark: u32, owned: u32, donor_frames: u32) -> u32 {
1258    let moved = u64::from(mark) * u64::from(owned) / u64::from(donor_frames);
1259    moved.min(u64::from(owned.saturating_sub(1))) as u32
1260}
1261
1262/// How `coded` compares with the span it was coded from, block by block.
1263fn compare(before: &[u8], coded: &[u8], block: usize) -> Recoded {
1264    let mut out = Recoded {
1265        blocks: coded.len() / block,
1266        ..Recoded::default()
1267    };
1268    for (index, now) in coded.chunks_exact(block).enumerate() {
1269        let Some(was) = before.get(index * block..(index + 1) * block) else {
1270            continue;
1271        };
1272        if was == now {
1273            out.identical += 1;
1274        } else if was[1..] == now[1..] {
1275            out.restated += 1;
1276        }
1277    }
1278    out
1279}
1280
1281#[cfg(test)]
1282mod tests {
1283    use super::super::synthetic::{take, Build};
1284    use super::*;
1285    use crate::formats::npno::{be16, Piano, DECAYS};
1286
1287    /// A one-stroke library the encoder can donate from: a real prefix and one real
1288    /// record, holding marks and a full ladder of decay coefficients a new stroke
1289    /// inherits, over a block of silence a decode reads back.
1290    fn template(channels: u16) -> Piano {
1291        let mut build = Build::new();
1292        build.channels = channels;
1293        build.map = vec![(60, 60)];
1294        build.takes = vec![take(60, Bank::Attack, 0, 1)
1295            .marks(std::array::from_fn(|mark| (mark as u32 + 6) * 100))
1296            .decay(0x0000_2000)
1297            .ladder(std::array::from_fn(|entry| 0x0000_1000 + entry as u32))
1298            .id(77)
1299            .silent()];
1300        build.piano()
1301    }
1302
1303    /// A decaying tone, which is the shape the coder's width search is built for.
1304    fn tone(frames: usize, hertz: f64, channels: usize) -> Vec<Vec<i16>> {
1305        (0..channels)
1306            .map(|c| {
1307                (0..frames)
1308                    .map(|n| {
1309                        let t = n as f64 / f64::from(codec::RATE);
1310                        let envelope = (-3.0 * t).exp() * (1.0 - (-400.0 * t).exp());
1311                        let phase = std::f64::consts::TAU * hertz * (c as f64 * 0.01 + 1.0) * t;
1312                        (9000.0 * envelope * phase.sin()) as i16
1313                    })
1314                    .collect()
1315            })
1316            .collect()
1317    }
1318
1319    fn one(root: u8, bank: Bank, layer: u8, channels: Vec<Vec<i16>>) -> Recording {
1320        Recording {
1321            root,
1322            bank,
1323            layer,
1324            channels,
1325        }
1326    }
1327
1328    /// Build, write, read back, and decode: what the caller put in is what the
1329    /// instrument would be handed.
1330    fn round_trip(channels: u16, recordings: &[Recording]) -> Piano {
1331        let donor = template(channels);
1332        let built = build(
1333            &Donor::Template(&donor.library().unwrap()),
1334            &Options::new("Synth").variant("Test"),
1335            recordings,
1336        )
1337        .unwrap();
1338        let bytes = {
1339            let piano = built.to_piano().unwrap();
1340            let mut out = std::io::Cursor::new(Vec::new());
1341            piano.write_to(&mut out).unwrap();
1342            out.into_inner()
1343        };
1344        Piano::read_from(&mut std::io::Cursor::new(bytes)).unwrap()
1345    }
1346
1347    /// A stroke holds whole blocks and every frame of its recording: what it states
1348    /// past the recording is silence, and the recording itself comes back sample for
1349    /// sample.
1350    #[test]
1351    fn a_built_library_decodes_back_to_the_frames_it_was_given() {
1352        let source = tone(20_000, 220.0, 2);
1353        let piano = round_trip(2, &[one(60, Bank::Attack, 0, source.clone())]);
1354        let library = piano.library().unwrap();
1355        assert_eq!(library.name(), ("Synth".into(), "Test".into()));
1356        assert_eq!(library.channels(), 2);
1357
1358        let stroke = &library.strokes()[0];
1359        let audio = codec::decode(stroke, 2).unwrap();
1360        assert_eq!(audio.clipped, 0);
1361        let longest = codec::block_frames(MIN_WIDTH, library.block_bytes(), 2) - OVERLAP;
1362        let padding = audio
1363            .frames()
1364            .checked_sub(source[0].len())
1365            .unwrap_or_else(|| {
1366                panic!(
1367                    "the stroke states {} frames of a {} frame recording",
1368                    audio.frames(),
1369                    source[0].len()
1370                )
1371            });
1372        assert!(
1373            padding < longest,
1374            "the stroke states {padding} frames of silence, a whole block or more"
1375        );
1376        for (channel, given) in audio.lanes.iter().zip(&source) {
1377            assert_eq!(&channel[..given.len()], &given[..]);
1378            assert!(channel[given.len()..].iter().all(|&s| s == 0));
1379        }
1380    }
1381
1382    /// Nothing a recording holds is dropped for falling between block lengths: the
1383    /// coder states the silence that fills out the last block rather than fewer frames
1384    /// than it was given, including where the whole signal sits in the frames a
1385    /// truncating coder would leave off. The stroke it writes is one a rebuild leaves
1386    /// alone.
1387    #[test]
1388    fn a_recording_that_does_not_fill_its_last_block_keeps_every_frame() {
1389        let mut late = vec![vec![0i16; 892]; 2];
1390        for (channel, lane) in late.iter_mut().enumerate() {
1391            let signal = tone(64, 262.0, 2);
1392            lane[892 - 64..].copy_from_slice(&signal[channel]);
1393        }
1394        let cases: [(&str, Vec<Vec<i16>>); 3] = [
1395            ("a recording a block and a half long", tone(700, 262.0, 2)),
1396            (
1397                "a recording cut between block lengths",
1398                tone(9_133, 440.0, 2),
1399            ),
1400            ("a recording whose signal is all at the end", late),
1401        ];
1402
1403        for (what, source) in cases {
1404            let frames = source[0].len();
1405            let signal: i64 = source[0].iter().map(|&s| i64::from(s).abs()).sum();
1406            assert!(signal > 0, "{what}: the case states no signal");
1407            let piano = round_trip(2, &[one(60, Bank::Attack, 0, source.clone())]);
1408            let library = piano.library().unwrap();
1409            let audio = codec::decode(&library.strokes()[0], 2).unwrap();
1410            assert!(
1411                audio.frames() >= frames,
1412                "{what}: the stroke states {} of {frames} frames",
1413                audio.frames()
1414            );
1415            for (channel, given) in audio.lanes.iter().zip(&source) {
1416                assert_eq!(
1417                    &channel[..frames],
1418                    &given[..],
1419                    "{what}: frames came back changed"
1420                );
1421                assert!(
1422                    channel[frames..].iter().all(|&s| s == 0),
1423                    "{what}: the stroke states something other than silence past the recording"
1424                );
1425            }
1426
1427            let again = rebuild(&library).unwrap();
1428            for recoded in &again.strokes {
1429                assert_eq!(
1430                    (recoded.identical, recoded.recoded()),
1431                    (recoded.blocks, 0),
1432                    "{what}: the rebuild laid the stroke out differently"
1433                );
1434            }
1435            assert_eq!(
1436                again.library.to_body().unwrap(),
1437                piano.file.body.0,
1438                "{what}: the rebuild is a different file"
1439            );
1440        }
1441    }
1442
1443    /// The two claims above hold wherever a recording ends against the block grid, not
1444    /// only at the lengths a case picks: the stroke holds every frame, and coding it
1445    /// again reaches the same file.
1446    #[test]
1447    fn a_recording_of_any_length_codes_to_a_stroke_that_holds_it() {
1448        for frames in [
1449            1, 63, 64, 65, 445, 446, 447, 509, 891, 892, 893, 1_102, 2_658,
1450        ] {
1451            for channels in [1u16, 2] {
1452                let source = tone(frames, 262.0, usize::from(channels));
1453                let piano = round_trip(channels, &[one(60, Bank::Attack, 0, source.clone())]);
1454                let library = piano.library().unwrap();
1455                let audio = codec::decode(&library.strokes()[0], channels).unwrap();
1456                let what = format!("{frames} frame(s) over {channels} channel(s)");
1457                assert!(
1458                    audio.frames() >= frames,
1459                    "{what}: the stroke states {}",
1460                    audio.frames()
1461                );
1462                for (channel, given) in audio.lanes.iter().zip(&source) {
1463                    assert_eq!(&channel[..frames], &given[..], "{what}: frames changed");
1464                    assert!(
1465                        channel[frames..].iter().all(|&s| s == 0),
1466                        "{what}: not silent"
1467                    );
1468                }
1469                assert_eq!(
1470                    rebuild(&library).unwrap().library.to_body().unwrap(),
1471                    piano.file.body.0,
1472                    "{what}: the rebuild is a different file"
1473                );
1474            }
1475        }
1476    }
1477
1478    /// A stroke that saturates the decoder is refused rather than coded again: what a
1479    /// recode would write is the clamped reconstruction, which is audio the file it
1480    /// came from does not hold.
1481    #[test]
1482    fn a_stroke_whose_decode_saturates_is_not_coded_again() {
1483        let donor = template(1);
1484        let mut library = donor.library().unwrap();
1485        let block = library.block_bytes();
1486        let frames = codec::block_frames(MAX_WIDTH, block, 1);
1487        // Order one integrates its residuals, so a block of one large value runs the
1488        // reconstruction off the top of int16 within a few frames.
1489        let mut audio = Vec::new();
1490        pack(&mut audio, 1, MAX_WIDTH, 0, &vec![20_000i32; frames], block);
1491        library.strokes[0].audio = Cow::Owned(audio);
1492
1493        let decoded = codec::decode(&library.strokes()[0], 1).unwrap();
1494        assert!(decoded.clipped > 0, "the case does not saturate");
1495
1496        let error = match rebuild(&library) {
1497            Err(error) => error.to_string(),
1498            Ok(_) => panic!("expected a refusal"),
1499        };
1500        assert!(error.contains("left int16"), "{error}");
1501        assert!(
1502            error.contains("coding a stroke this codec does not describe"),
1503            "the refusal does not read as the sentence it states: {error}"
1504        );
1505    }
1506
1507    #[test]
1508    fn a_mono_library_codes_and_decodes_on_its_own_block_size() {
1509        let source = tone(9_000, 440.0, 1);
1510        let piano = round_trip(1, &[one(48, Bank::Attack, 0, source.clone())]);
1511        let library = piano.library().unwrap();
1512        assert_eq!(library.channels(), 1);
1513        let audio = codec::decode(&library.strokes()[0], 1).unwrap();
1514        assert_eq!(audio.lanes[0][..source[0].len()], source[0][..]);
1515        assert!(audio.lanes[0][source[0].len()..].iter().all(|&s| s == 0));
1516    }
1517
1518    #[test]
1519    fn the_directory_orders_strokes_by_root_then_bank_then_layer() {
1520        let short = tone(6_000, 300.0, 1);
1521        let piano = round_trip(
1522            1,
1523            &[
1524                one(72, Bank::Attack, 0, short.clone()),
1525                one(60, Bank::Release, 0, short.clone()),
1526                one(60, Bank::Attack, 4, short.clone()),
1527                one(60, Bank::Attack, 0, short.clone()),
1528            ],
1529        );
1530        let library = piano.library().unwrap();
1531        let seen: Vec<(u8, Option<Bank>, u8)> = library
1532            .strokes()
1533            .iter()
1534            .map(|s| (s.root, s.bank(), s.layer()))
1535            .collect();
1536        assert_eq!(
1537            seen,
1538            [
1539                (60, Some(Bank::Attack), 0),
1540                (60, Some(Bank::Attack), 4),
1541                (60, Some(Bank::Release), 0),
1542                (72, Some(Bank::Attack), 0),
1543            ]
1544        );
1545    }
1546
1547    /// The default spread is what decides which velocities reach which layer, so the
1548    /// values it produces are the contract, not an implementation detail.
1549    #[test]
1550    fn the_default_layer_values_spread_a_root_over_the_selection_range() {
1551        let spread = |layers| {
1552            (0..layers)
1553                .map(|i| layer_value(i, layers))
1554                .collect::<Vec<_>>()
1555        };
1556        assert_eq!(spread(1), vec![0], "a lone layer plays at every velocity");
1557        assert_eq!(spread(2), vec![0, 27]);
1558        assert_eq!(spread(3), vec![0, 14, 27]);
1559        assert_eq!(spread(9), vec![0, 3, 7, 10, 14, 17, 20, 24, 27]);
1560        assert_eq!(layer_value(9, 9), 27, "an index past the last is the last");
1561        assert_eq!(layer_value(0, 0), 0);
1562    }
1563
1564    /// A layer value is a field of the record, not a position in the directory: a
1565    /// library that states its own keeps them when its audio is coded again.
1566    #[test]
1567    fn a_rebuild_keeps_the_layer_value_every_stroke_states() {
1568        let short = tone(6_000, 300.0, 1);
1569        let piano = round_trip(
1570            1,
1571            &[
1572                one(60, Bank::Attack, 0, short.clone()),
1573                one(60, Bank::Attack, 6, short.clone()),
1574                one(60, Bank::Attack, 12, short),
1575            ],
1576        );
1577        let library = piano.library().unwrap();
1578        let again = rebuild(&library).unwrap();
1579        let values: Vec<u8> = again.library.strokes().iter().map(|s| s.layer()).collect();
1580        assert_eq!(values, [0, 6, 12]);
1581    }
1582
1583    /// Only the coefficient at `+0x2e` answers to the bank: a release stroke built
1584    /// from a donor that carries all fifteen zeroes that one and keeps the donor's
1585    /// fourteen-entry ladder, as a stroke of any other bank does.
1586    #[test]
1587    fn a_release_stroke_declares_no_marks_and_zeroes_one_decay_coefficient() {
1588        let short = tone(6_000, 300.0, 1);
1589        let piano = round_trip(
1590            1,
1591            &[
1592                one(60, Bank::Attack, 0, short.clone()),
1593                one(60, Bank::Release, 0, short.clone()),
1594            ],
1595        );
1596        let library = piano.library().unwrap();
1597        let donated: [u32; DECAYS] = std::array::from_fn(|c| 0x0000_1000u32 + c as u32);
1598        for stroke in library.strokes() {
1599            let record = stroke.record();
1600            let marks: Vec<u32> = (0..MARKS)
1601                .map(|m| be32(record, REC_MARKS + m * 4))
1602                .collect();
1603            assert_eq!(
1604                stroke.ladder(),
1605                donated,
1606                "a stroke of any bank inherits the donor's ladder unchanged"
1607            );
1608            if stroke.bank() == Some(Bank::Release) {
1609                assert_eq!(marks, [0; MARKS], "a release stroke declares no marks");
1610                assert_eq!(
1611                    stroke.decay(),
1612                    0,
1613                    "a release stroke zeroes the coefficient at +0x2e"
1614                );
1615            } else {
1616                assert!(marks.iter().all(|&m| m > 0 && m < stroke.frames()));
1617                assert_ne!(
1618                    stroke.decay(),
1619                    0,
1620                    "a stroke of another bank inherits the donor's coefficient at +0x2e"
1621                );
1622            }
1623        }
1624    }
1625
1626    /// The kind byte is what the instrument files a library under, so every kind must
1627    /// read back as itself and no other code may name one.
1628    #[test]
1629    fn every_kind_reads_back_from_the_code_it_writes() {
1630        for kind in Kind::ALL {
1631            assert_eq!(Kind::from_code(kind.code()), Some(kind));
1632        }
1633        let named: Vec<Kind> = (0..=u8::MAX).filter_map(Kind::from_code).collect();
1634        assert_eq!(named, Kind::ALL, "a code names a kind ALL does not list");
1635    }
1636
1637    /// Building without a template needs no library to donate anything, and what comes
1638    /// out reads back: the kind, the gain and the damper limit the rules state, a
1639    /// stroke trimmed by its own layer value with no decay applied over it, and
1640    /// identifiers counting the directory. A library that has been played holds these
1641    /// bytes; the corpus suite is where that comparison is made.
1642    #[test]
1643    fn a_library_built_from_rules_states_them_and_needs_no_template() {
1644        let short = tone(6_000, 300.0, 1);
1645        let rules = Rules {
1646            kind: Kind::Wurlitzer,
1647            gain: -20,
1648            damper_top: 97,
1649        };
1650        let built = build(
1651            &Donor::Rules(rules),
1652            &Options::new("Reeds"),
1653            &[
1654                one(60, Bank::Attack, 0, short.clone()),
1655                one(60, Bank::Attack, 17, short.clone()),
1656                one(60, Bank::Release, 0, short.clone()),
1657            ],
1658        )
1659        .unwrap();
1660
1661        assert_eq!(Kind::from_code(built.kind_code()), Some(Kind::Wurlitzer));
1662        assert_eq!(built.gain(), -20);
1663        assert_eq!(built.damper_top(), 97);
1664        assert_eq!(built.stream_version(), RULES_VERSION);
1665        assert_eq!(
1666            Rules::new(Kind::Wurlitzer).damper_top,
1667            rules.damper_top,
1668            "the kind names its own damper limit"
1669        );
1670
1671        let stated: Vec<(u8, u16, u16, u32)> = built
1672            .strokes()
1673            .iter()
1674            .map(|s| {
1675                let record = s.record();
1676                (
1677                    s.layer(),
1678                    be16(record, REC_WINDOW),
1679                    s.trim(),
1680                    be32(record, REC_ID),
1681                )
1682            })
1683            .collect();
1684        assert_eq!(stated, [(0, 0, 3, 1), (17, 17, 20, 2), (0, 0, 12, 3)]);
1685        for stroke in built.strokes() {
1686            let record = stroke.record();
1687            assert!((0..MARKS).all(|m| be32(record, REC_MARKS + m * 4) == 0));
1688            assert_eq!(
1689                (stroke.decay(), stroke.ladder()),
1690                (0, [LADDER_UNITY; DECAYS]),
1691                "a rule-written stroke of any bank applies no decay over the recording"
1692            );
1693        }
1694
1695        let again = rebuild(&built).unwrap();
1696        assert_eq!(
1697            again.library.to_body().unwrap(),
1698            built.to_body().unwrap(),
1699            "a rule-written library is not a fixed point of a recode"
1700        );
1701    }
1702
1703    /// The damper cut is flat over the lowest notes, falls straight to the highest key
1704    /// an instrument plays, and states one value past it.
1705    #[test]
1706    fn the_damper_cut_is_a_plateau_then_a_straight_fall_to_the_top_key() {
1707        let curve: Vec<u8> = (0..NOTES).map(damper_cut).collect();
1708        assert_eq!(curve[..25], [79; 25]);
1709        assert_eq!((curve[66], curve[108], curve[109]), (50, 20, 30));
1710        assert!(
1711            curve[24..=108].windows(2).all(|w| w[0] >= w[1]),
1712            "the fall never rises"
1713        );
1714        assert!(curve[109..].iter().all(|&v| v == 30));
1715    }
1716
1717    /// The bound is the selection rule at the softest note-on, so the value it names
1718    /// is the last one a key can reach and the spread stays inside it.
1719    #[test]
1720    fn the_highest_played_layer_is_the_rule_at_the_softest_velocity() {
1721        let selected = |velocity: u32| ((127 - velocity) * 31 / 127) as u8;
1722        assert_eq!(HIGHEST_PLAYED_LAYER, selected(1));
1723        assert!((1..=127).all(|v| selected(v) <= HIGHEST_PLAYED_LAYER));
1724        const { assert!(SOFTEST_LAYER <= HIGHEST_PLAYED_LAYER) };
1725
1726        let donor = template(1);
1727        let library = donor.library().unwrap();
1728        let short = tone(6_000, 300.0, 1);
1729        build(
1730            &Donor::Template(&library),
1731            &Options::new("Synth"),
1732            &[one(60, Bank::Attack, HIGHEST_PLAYED_LAYER, short)],
1733        )
1734        .expect("the bound itself is a value a key sounds");
1735    }
1736
1737    #[test]
1738    fn a_wav_name_states_its_root_bank_and_layer() {
1739        use LayerTag::{Index, Value};
1740        assert_eq!(
1741            parse_stroke_name("060-b0-l00", Stem::None),
1742            Some((60, Bank::Attack, Index(0)))
1743        );
1744        assert_eq!(
1745            parse_stroke_name("36-b2-l7", Stem::None),
1746            Some((36, Bank::Release, Index(7)))
1747        );
1748        assert_eq!(
1749            parse_stroke_name("101-b1-v12", Stem::None),
1750            Some((101, Bank::Resonance, Value(12)))
1751        );
1752        assert_eq!(
1753            parse_stroke_name("127-b0-l00", Stem::None),
1754            Some((127, Bank::Attack, Index(0)))
1755        );
1756        for bad in [
1757            "060-b3-l00",
1758            "300-b0-l00",
1759            "128-b0-l00",
1760            "060-0-l00",
1761            "060-b0-x2",
1762            "060-b0",
1763            "060-b0-l00-take2",
1764            "C4-b0-l00",
1765        ] {
1766            assert_eq!(parse_stroke_name(bad, Stem::None), None, "{bad}");
1767        }
1768    }
1769
1770    /// A name carrying something of its own in front of the stroke it states still
1771    /// states it, where the caller asks for that form: the trailing group is the whole
1772    /// claim.
1773    #[test]
1774    fn a_stem_before_the_stroke_is_taken_only_where_the_caller_takes_one() {
1775        assert_eq!(
1776            parse_stroke_name("Grand-060-b0-l00", Stem::Any),
1777            Some((60, Bank::Attack, LayerTag::Index(0)))
1778        );
1779        assert_eq!(parse_stroke_name("Grand-060-b0-l00", Stem::None), None);
1780        assert_eq!(
1781            parse_stroke_name("060-b0-l00", Stem::Any),
1782            Some((60, Bank::Attack, LayerTag::Index(0))),
1783            "a name with no stem states the same stroke either way"
1784        );
1785        assert_eq!(parse_stroke_name("Grand-060-b0-take2", Stem::Any), None);
1786    }
1787
1788    /// The velocity a layer answers to is its value, so the names decide which part of
1789    /// the range each recording plays over.
1790    #[test]
1791    fn indexed_layers_spread_over_their_own_root_and_bank() {
1792        let named = [
1793            (60, Bank::Attack, LayerTag::Index(2)),
1794            (60, Bank::Attack, LayerTag::Index(0)),
1795            (60, Bank::Attack, LayerTag::Index(1)),
1796            (60, Bank::Release, LayerTag::Index(0)),
1797            (72, Bank::Attack, LayerTag::Index(0)),
1798            (72, Bank::Attack, LayerTag::Index(1)),
1799        ];
1800        assert_eq!(
1801            layer_values(&named).unwrap(),
1802            [27, 0, 14, 0, 0, 27],
1803            "the order given is kept; the rank is the layer's own"
1804        );
1805    }
1806
1807    #[test]
1808    fn a_named_layer_value_is_written_as_it_stands() {
1809        let named = [
1810            (60, Bank::Attack, LayerTag::Value(0)),
1811            (60, Bank::Attack, LayerTag::Value(6)),
1812            (60, Bank::Attack, LayerTag::Value(12)),
1813        ];
1814        assert_eq!(layer_values(&named).unwrap(), [0, 6, 12]);
1815    }
1816
1817    /// A spread over indices and a stated value mean different things about how many
1818    /// layers a root has, and two names claiming one layer would be spread to two
1819    /// different values — neither of which is what either name said.
1820    #[test]
1821    fn one_roots_bank_names_its_layers_one_way_and_each_of_them_once() {
1822        let mixed = [
1823            (60, Bank::Attack, LayerTag::Index(0)),
1824            (60, Bank::Attack, LayerTag::Value(12)),
1825        ];
1826        assert_eq!(
1827            layer_values(&mixed),
1828            Err(LayerClash {
1829                root: 60,
1830                bank: Bank::Attack,
1831                how: Clash::BothForms,
1832            })
1833        );
1834        let twice = [
1835            (60, Bank::Attack, LayerTag::Index(0)),
1836            (60, Bank::Attack, LayerTag::Index(0)),
1837        ];
1838        assert_eq!(
1839            layer_values(&twice),
1840            Err(LayerClash {
1841                root: 60,
1842                bank: Bank::Attack,
1843                how: Clash::Twice,
1844            })
1845        );
1846        let apart = [
1847            (60, Bank::Attack, LayerTag::Index(0)),
1848            (60, Bank::Release, LayerTag::Value(12)),
1849        ];
1850        assert_eq!(
1851            layer_values(&apart).unwrap(),
1852            [0, 12],
1853            "a bank of its own names its layers its own way"
1854        );
1855    }
1856
1857    #[test]
1858    fn every_key_up_to_the_highest_roots_own_plays_the_root_above_it() {
1859        let roots: BTreeSet<u8> = [25, 30, 60].into_iter().collect();
1860        let map = key_map(&roots);
1861        assert_eq!(map[0], 25, "the lowest root takes everything under it");
1862        assert_eq!(map[26], 25, "a key one semitone above its root");
1863        assert_eq!(map[27], 30, "and the next one belongs to the root above");
1864        assert_eq!(map[31], 30, "a root reaches one semitone above itself");
1865        assert_eq!(map[32], 60, "and the key after that is the next root's");
1866        assert_eq!(map[61], 60, "the highest root reaches one semitone up");
1867        assert_eq!(map[62], UNCOVERED);
1868        assert_eq!(map[NOTES - 1], UNCOVERED);
1869    }
1870
1871    #[test]
1872    fn the_attenuation_states_decibels_below_full_scale() {
1873        assert_eq!(attenuation(8192), 0);
1874        assert_eq!(
1875            attenuation(9000),
1876            0,
1877            "louder than full scale clamps at zero"
1878        );
1879        assert_eq!(attenuation(819), 20);
1880        assert_eq!(attenuation(82), 40);
1881        assert_eq!(attenuation(1), 78);
1882        assert_eq!(attenuation(0), 100, "silence is not 78 dB down");
1883    }
1884
1885    #[test]
1886    fn a_recording_a_library_cannot_state_is_refused_by_name() {
1887        let donor = template(1);
1888        let library = donor.library().unwrap();
1889        let options = Options::new("Synth");
1890        let short = tone(6_000, 300.0, 1);
1891        let error = |recordings: &[Recording]| {
1892            build(&Donor::Template(&library), &options, recordings)
1893                .expect_err("expected a refusal")
1894                .to_string()
1895        };
1896
1897        assert!(error(&[]).contains("no recordings"));
1898        assert!(error(&[one(60, Bank::Attack, 0, vec![])]).contains("1 or 2"));
1899        assert!(error(&[one(60, Bank::Attack, 0, vec![vec![]])]).contains("no frames"));
1900        assert!(
1901            error(&[one(60, Bank::Attack, 0, vec![short[0].clone(), vec![0; 3]])])
1902                .contains("unequal length")
1903        );
1904        assert!(error(&[
1905            one(60, Bank::Attack, 0, short.clone()),
1906            one(60, Bank::Attack, 0, short.clone()),
1907        ])
1908        .contains("recorded twice"));
1909        let unplayable = error(&[one(
1910            60,
1911            Bank::Attack,
1912            HIGHEST_PLAYED_LAYER + 1,
1913            short.clone(),
1914        )]);
1915        assert!(unplayable.contains("no velocity selects"), "{unplayable}");
1916        assert!(
1917            unplayable.contains("30 is the largest a key ever sounds"),
1918            "{unplayable}"
1919        );
1920        assert!(error(&[
1921            one(60, Bank::Attack, 0, short.clone()),
1922            one(
1923                60,
1924                Bank::Attack,
1925                1,
1926                vec![short[0].clone(), short[0].clone()]
1927            ),
1928        ])
1929        .contains("one channel count"));
1930    }
1931
1932    /// A donor is read for its prefix and its stroke records, so a library stripped of
1933    /// its audio donates everything the whole one does.
1934    #[test]
1935    fn a_template_donates_the_same_library_with_its_audio_dropped() {
1936        let donor = template(1);
1937        let library = donor.library().unwrap();
1938        let skeleton = library.without_audio();
1939        assert!(skeleton.strokes().iter().all(|s| s.audio().is_empty()));
1940
1941        let options = Options::new("Synth").variant("Test");
1942        let recordings = [
1943            one(60, Bank::Attack, 0, tone(6_000, 300.0, 1)),
1944            one(72, Bank::Release, 4, tone(3_000, 500.0, 1)),
1945        ];
1946        let whole = build(&Donor::Template(&library), &options, &recordings).unwrap();
1947        let stripped = build(&Donor::Template(&skeleton), &options, &recordings).unwrap();
1948        assert_eq!(stripped.to_body().unwrap(), whole.to_body().unwrap());
1949    }
1950
1951    /// A built library states the name and the variant the caller gives, so the name is
1952    /// measured against that variant rather than against the one the template carries.
1953    #[test]
1954    fn a_name_that_fits_beside_the_variant_it_is_given_is_built() {
1955        let name = "Studio Nine";
1956        let donated = "Concert Grand Sml XL";
1957        let donor = template(1);
1958        let mut library = donor.library().unwrap();
1959        library.set_variant(donated).unwrap();
1960        assert!(
1961            library.clone().set_name(name).is_err(),
1962            "the name fits beside the template's variant, so the case states nothing"
1963        );
1964
1965        let built = build(
1966            &Donor::Template(&library),
1967            &Options::new(name),
1968            &[one(60, Bank::Attack, 0, tone(6_000, 300.0, 1))],
1969        )
1970        .expect("a name and an empty variant that fit the field they share");
1971        assert_eq!(built.name(), (name.to_string(), String::new()));
1972    }
1973
1974    #[test]
1975    fn a_template_with_only_release_strokes_cannot_donate_to_an_attack_stroke() {
1976        let donor = template(1);
1977        let mut library = donor.library().unwrap();
1978        library.strokes[0].record[REC_BANK] = Bank::Release.code();
1979        let short = tone(6_000, 300.0, 1);
1980        let error = build(
1981            &Donor::Template(&library),
1982            &Options::new("Synth"),
1983            &[one(60, Bank::Attack, 0, short)],
1984        )
1985        .expect_err("expected a refusal")
1986        .to_string();
1987        assert!(
1988            error.contains("length marks and decay coefficients"),
1989            "{error}"
1990        );
1991    }
1992
1993    /// A recording followed by digital silence, which is what a take trimmed to a
1994    /// fixed length holds and what a release stroke is mostly made of.
1995    fn trailing_silence(frames: usize, silence: usize, channels: usize) -> Vec<Vec<i16>> {
1996        let mut source = tone(frames, 262.0, channels);
1997        for channel in &mut source {
1998            channel.resize(frames + silence, 0);
1999        }
2000        source
2001    }
2002
2003    /// A library this module writes is one [`rebuild`] leaves alone: every block comes
2004    /// back byte for byte and so does the container around it. A stroke states the
2005    /// frames its blocks own and nothing past them, which is what leaves the width
2006    /// search the same room the second time.
2007    ///
2008    /// The lengths put the recording's end in each place it falls against the block
2009    /// grid: inside a single block, part-way down a decay, and after the recording has
2010    /// already reached digital silence.
2011    #[test]
2012    fn rebuilding_a_library_this_module_wrote_reproduces_every_block() {
2013        let cases: [(&str, u16, Vec<Recording>); 4] = [
2014            (
2015                "a source shorter than one block",
2016                2,
2017                vec![one(60, Bank::Attack, 0, tone(509, 262.0, 2))],
2018            ),
2019            (
2020                "two stereo strokes cut mid-decay",
2021                2,
2022                vec![
2023                    one(60, Bank::Attack, 0, tone(12_000, 262.0, 2)),
2024                    one(72, Bank::Attack, 0, tone(9_000, 523.0, 2)),
2025                ],
2026            ),
2027            (
2028                "a mono stroke cut mid-decay",
2029                1,
2030                vec![one(48, Bank::Attack, 0, tone(9_133, 440.0, 1))],
2031            ),
2032            (
2033                "a stroke that reaches silence before its source ends",
2034                2,
2035                vec![one(60, Bank::Attack, 0, trailing_silence(6_000, 5_000, 2))],
2036            ),
2037        ];
2038
2039        for (what, channels, recordings) in cases {
2040            let piano = round_trip(channels, &recordings);
2041            let library = piano.library().unwrap();
2042            let again = rebuild(&library).unwrap();
2043            assert_eq!(again.strokes.len(), recordings.len());
2044            for (index, recoded) in again.strokes.iter().enumerate() {
2045                assert_eq!(
2046                    (recoded.identical, recoded.recoded()),
2047                    (recoded.blocks, 0),
2048                    "{what}: stroke {index} came back with different blocks"
2049                );
2050            }
2051            assert_eq!(
2052                again.library.to_body().unwrap(),
2053                piano.file.body.0,
2054                "{what}: the library came back a different file"
2055            );
2056        }
2057    }
2058
2059    #[test]
2060    fn the_resampler_leaves_audio_already_on_the_lattice_alone() {
2061        let interleaved: Vec<i16> = (0..64).map(|n| (n * 100 - 3000) as i16).collect();
2062        let out = resample(&interleaved, 2, codec::RATE).unwrap();
2063        assert_eq!(out.clipped, 0);
2064        assert_eq!(out.channels[0][..3], [-3000, -2800, -2600]);
2065        assert_eq!(out.channels[1][..3], [-2900, -2700, -2500]);
2066
2067        assert!(resample(&[1, 2, 3], 2, codec::RATE).is_err());
2068        assert!(resample(&[1, 2], 1, 0).is_err());
2069    }
2070
2071    /// The lattice count is the source's length scaled by the rate the source itself
2072    /// declares. A rate far below the lattice's stretches a modest source past every
2073    /// frame count a stroke can state, which is a refusal rather than an allocation.
2074    #[test]
2075    fn a_rate_that_stretches_a_source_past_a_strokes_frame_count_is_refused() {
2076        let frames = u32::MAX as usize / codec::RATE as usize + 1;
2077        let error = match resample(&vec![0i16; frames], 1, 1) {
2078            Err(error) => error.to_string(),
2079            Ok(_) => panic!("expected a refusal"),
2080        };
2081        assert!(error.contains("at 1 Hz"), "{error}");
2082        assert!(error.contains("u32 frame count"), "{error}");
2083    }
2084
2085    /// A source faster than the lattice is band-limited to the lattice's own Nyquist
2086    /// on the way down: a tone above it comes through as near silence rather than
2087    /// folded back into the band as a tone the recording never held, and one well
2088    /// inside the band comes through at its level.
2089    #[test]
2090    fn resampling_a_faster_source_drops_what_the_lattice_cannot_hold() {
2091        let rate = 96_000;
2092        let tone_at = |hertz: f64| -> Vec<i16> {
2093            (0..rate as usize / 4)
2094                .map(|n| {
2095                    let t = n as f64 / f64::from(rate);
2096                    (8000.0 * (std::f64::consts::TAU * hertz * t).sin()) as i16
2097                })
2098                .collect()
2099        };
2100        // The kernel rings in and out at the ends, so the level is read off the middle.
2101        let peak = |lane: &[i16]| {
2102            lane[400..lane.len() - 400]
2103                .iter()
2104                .map(|&s| i32::from(s).abs())
2105                .max()
2106                .unwrap_or(0)
2107        };
2108
2109        let above = resample(&tone_at(24_000.0), 1, rate).unwrap();
2110        assert_eq!(above.clipped, 0);
2111        let level = peak(&above.channels[0]);
2112        assert!(level < 400, "a 24 kHz tone came through at {level} of 8000");
2113
2114        let inside = resample(&tone_at(1_000.0), 1, rate).unwrap();
2115        let level = peak(&inside.channels[0]);
2116        assert!(
2117            level > 7_900,
2118            "a 1 kHz tone came through at {level} of 8000"
2119        );
2120    }
2121
2122    #[test]
2123    fn resampling_a_slower_source_stretches_it_onto_the_lattice() {
2124        let rate = 22_050;
2125        let frames = 4_000;
2126        let source: Vec<i16> = (0..frames)
2127            .map(|n| {
2128                let t = n as f64 / f64::from(rate);
2129                (8000.0 * (std::f64::consts::TAU * 100.0 * t).sin()) as i16
2130            })
2131            .collect();
2132        let out = resample(&source, 1, rate).unwrap();
2133        assert_eq!(
2134            out.channels[0].len(),
2135            frames * codec::RATE as usize / rate as usize
2136        );
2137        // A 100 Hz sine keeps its zero crossings, so the resampled lattice holds the
2138        // same count of them over the same span of time.
2139        let crossings = |signal: &[i16]| {
2140            signal
2141                .windows(2)
2142                .filter(|w| (w[0] < 0) != (w[1] < 0))
2143                .count()
2144        };
2145        assert_eq!(
2146            crossings(&out.channels[0][100..]),
2147            crossings(&source[100..])
2148        );
2149    }
2150}