use super::codec::{self, Layout, PITCH_DEN, PITCH_NUM, WRAP};
use super::kernel;
use super::section::{self, Section, Section4};
use super::stroke::packet_len;
use super::{Sample, SampleV3};
use crate::cbin::{Cbin, Generation, Header};
use crate::error::{Error, ParseError};
use crate::formats::nsmpproj;
const fn version(layout: Layout) -> u32 {
match layout {
Layout::V2 => 200,
Layout::V3 => 300,
Layout::V4 => 400,
}
}
const AUX: u32 = 0x000f_0000;
const MAX_COUNT: usize = (1 << 14) - 1;
const PEAK_WIDTH: u8 = 14;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Units {
layout: Layout,
channels: usize,
}
impl Units {
const fn word(self) -> usize {
self.layout.word()
}
const fn word_bits(self) -> usize {
self.layout.word() * 8
}
const fn cell(self) -> usize {
self.layout.cell() * self.channels
}
const fn chunk(self) -> usize {
self.layout.rmax() * self.channels
}
const fn splits(self) -> bool {
self.channels == 2 && self.layout.splits_wide_openings()
}
const fn half(self, count: usize, width: u8) -> usize {
(count / 2 * width as usize).div_ceil(self.word_bits())
}
const fn span(self, count: usize, width: u8) -> usize {
if self.splits() {
1 + 2 * self.half(count, width)
} else {
(self.word_bits() + count * width as usize).div_ceil(self.word_bits())
}
}
const fn packet_words(self) -> usize {
packet_len(self.layout) / self.word()
}
const fn min_lead(self) -> usize {
match self.layout {
Layout::V2 => 0,
Layout::V3 | Layout::V4 => 7,
}
}
const fn max_fields(self) -> usize {
MAX_STREAM_WORDS * self.word_bits() / MIN_WIDTH as usize
}
}
const fn dead_last_record(layout: Layout) -> Option<&'static [usize]> {
match layout {
Layout::V2 => Some(&[24, 29, 32]),
Layout::V3 => Some(&[32, 41, 43, 45, 47, 48]),
Layout::V4 => None,
}
}
fn spends_extra_bit(values: &[i64], plan: &Plan) -> bool {
if plan.channels != 1 {
return false;
}
let Some(dead) = dead_last_record(plan.layout) else {
return false;
};
let over = 1i64 << (PEAK_WIDTH - 2);
let shift = peak_shift(values, PEAK_WIDTH);
[
Some((0, plan.warmup)),
Some((plan.resync_at, plan.resync)),
plan.looped.map(|points| (points.at, points.warmup)),
]
.into_iter()
.flatten()
.any(|(base, run)| {
let Some(&last) = chunks(run, plan.chunk()).last() else {
return false;
};
!dead.contains(&(last / plan.channels))
&& values[base + run - last..base + run].iter().any(|&v| {
let v = v >> shift;
v < -over || v >= over
})
})
}
fn peak_shift(values: &[i64], width: u8) -> i32 {
let low = values.iter().copied().min().unwrap_or(0);
let high = values.iter().copied().max().unwrap_or(0);
let mut shift = 0i32;
while width_of(low >> shift, high >> shift) > width {
shift += 1;
}
shift
}
const MAX_STORED_WIDTH: u8 = 16;
const MIN_WIDTH: u8 = 2;
const MAX_CHANNELS: usize = 2;
const MAX_ZONES: usize = u8::MAX as usize;
const MAX_STROKE_ID: u32 = u8::MAX as u32;
const RING_OUT: usize = 127;
const RAMP_IN: usize = 35;
pub const MIN_FRAMES: usize = 92;
const LOOP_LEAD: usize = 5;
const fn min_resync_gap(layout: Layout) -> usize {
match layout {
Layout::V2 => 72,
Layout::V3 | Layout::V4 => 64,
}
}
const MAX_STREAM_WORDS: usize = WRAP;
const DIFFERENCE: [&[i32]; 5] = [
&[1],
&[1, -1],
&[1, -2, 1],
&[1, -3, 3, -1],
&[1, -4, 6, -4, 1],
];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Predictor {
Plain,
#[default]
Minimising,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Loop {
pub start: usize,
pub end: usize,
pub crossfade: f64,
}
impl Loop {
pub fn new(start: usize, end: usize) -> Loop {
Loop {
start,
end,
crossfade: 0.0,
}
}
pub fn crossfade(mut self, frames: f64) -> Loop {
self.crossfade = frames;
self
}
}
#[derive(Debug, Clone)]
pub struct Options {
name: String,
root_key: u8,
top_note: Option<u8>,
predictor: Predictor,
loops: Option<Loop>,
channels: u16,
secondary_start: Option<f64>,
shift: Option<u8>,
layout: Layout,
}
impl Options {
pub fn new(name: impl Into<String>) -> Options {
Options {
name: name.into(),
root_key: 60,
top_note: None,
predictor: Predictor::default(),
loops: None,
channels: 1,
secondary_start: None,
shift: None,
layout: Layout::V2,
}
}
pub fn layout(mut self, layout: Layout) -> Options {
self.layout = layout;
self
}
pub fn secondary_start(mut self, frames: f64) -> Options {
self.secondary_start = Some(frames);
self
}
pub fn channels(mut self, channels: u16) -> Options {
self.channels = channels;
self
}
pub fn shift(mut self, bits: u8) -> Options {
self.shift = Some(bits);
self
}
pub fn loops(mut self, points: Loop) -> Options {
self.loops = Some(points);
self
}
pub fn root_key(mut self, note: u8) -> Options {
self.root_key = note;
self
}
pub fn top_note(mut self, note: u8) -> Options {
self.top_note = Some(note);
self
}
pub fn predictor(mut self, predictor: Predictor) -> Options {
self.predictor = predictor;
self
}
fn resolved_top_note(&self) -> u8 {
self.top_note
.unwrap_or_else(|| self.root_key.saturating_add(24).min(127))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Looped {
pub at: usize,
pub lead: usize,
pub crossfade: usize,
pub warmup: usize,
pub cells: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Plan {
pub layout: Layout,
pub channels: usize,
pub fields: usize,
pub resync_at: usize,
pub warmup: usize,
pub resync: usize,
pub cells_before: usize,
pub cells_after: usize,
pub looped: Option<Looped>,
}
impl Plan {
const fn units(&self) -> Units {
Units {
layout: self.layout,
channels: self.channels,
}
}
pub const fn cell(&self) -> usize {
self.units().cell()
}
const fn chunk(&self) -> usize {
self.units().chunk()
}
}
fn fields_of(frames: usize) -> Option<usize> {
let frames = u64::try_from(frames).ok()?;
frames
.checked_mul(u64::from(PITCH_DEN))
.and_then(|n| round_ratio(n, u64::from(PITCH_NUM)))
}
fn fields_at(frames: f64) -> Option<usize> {
let fields = frames * f64::from(PITCH_DEN) / f64::from(PITCH_NUM);
(fields.is_finite() && (0.0..=f64::from(u32::MAX)).contains(&fields))
.then_some(fields.round() as usize)
}
impl Plan {
pub fn new(
layout: Layout,
frames: usize,
channels: usize,
secondary_start: f64,
) -> Result<Plan, Error> {
Plan::modelled(frames, channels)?;
let fields = fields_of(frames)
.and_then(|f| f.checked_add(RING_OUT))
.and_then(|f| f.checked_mul(channels))
.ok_or_else(|| size_error(frames))?;
let resync_at = Plan::resync_at(secondary_start, channels)?;
Plan::lay_out(layout, frames, channels, fields, None, resync_at)
}
pub fn looped(
layout: Layout,
frames: usize,
channels: usize,
points: Loop,
secondary_start: f64,
) -> Result<Plan, Error> {
Plan::modelled(points.end, channels)?;
if points.start >= points.end || points.end > frames {
return Err(ParseError::OutOfBounds {
value: format!("a loop over frames {}..{}", points.start, points.end),
bound: format!("a non-empty region of the {frames} frames given"),
}
.into());
}
let lattice = |n: usize| fields_of(n).and_then(|f| f.checked_mul(channels));
let lattice_at = |n: f64| fields_at(n).and_then(|f| f.checked_mul(channels));
let start = lattice(points.start).ok_or_else(|| size_error(points.start))?;
let span = points.end - points.start;
let length = lattice(span).ok_or_else(|| size_error(points.end))?;
let end = start
.checked_add(length)
.ok_or_else(|| size_error(points.end))?;
let units = Units { layout, channels };
let (cell, chunk) = (units.cell(), units.chunk());
let resync_at = Plan::resync_at(secondary_start, channels)?;
if resync_at > start {
return Err(ParseError::OutOfBounds {
value: format!("a secondary start at field {resync_at}"),
bound: format!(
"field {start}, where the loop starts, or earlier — the marked \
record clears the resync point, so the loop cannot open ahead of it"
),
}
.into());
}
let at = start
.checked_add(LOOP_LEAD * channels)
.zip(resync_at.checked_add(min_resync_gap(layout) * channels))
.map(|(ideal, floor)| ideal.max(floor))
.ok_or_else(|| size_error(points.start))?;
let lead = at - start;
let fields = end
.checked_add(lead)
.ok_or_else(|| size_error(points.end))?;
let warmup = band(length, cell, chunk);
if length < warmup.saturating_add(cell) {
return Err(ParseError::OutOfBounds {
value: format!("a {length}-field loop"),
bound: format!(
"a loop long enough for the {warmup}-field 1:1 run it opens with and \
one {cell}-field cell after it"
),
}
.into());
}
if !(0.0..=points.start as f64).contains(&points.crossfade) {
return Err(ParseError::OutOfBounds {
value: format!("a {} frame crossfade", points.crossfade),
bound: format!(
"the {} frames before the loop starts — the fade compares \
each frame with the material one loop length behind it",
points.start,
),
}
.into());
}
let crossfade = if points.crossfade <= span as f64 {
let opens =
lattice_at(span as f64 - points.crossfade).ok_or_else(|| size_error(span))?;
length.checked_sub(opens).ok_or_else(|| size_error(span))?
} else {
let before = lattice_at(points.crossfade - span as f64)
.ok_or_else(|| size_error(points.start))?;
length
.checked_add(before)
.ok_or_else(|| size_error(points.end))?
};
if crossfade > start {
return Err(ParseError::OutOfBounds {
value: format!("a {} frame crossfade", points.crossfade),
bound: format!(
"the {} frames before the loop starts — the field lattice \
leaves no earlier material to compare",
points.start,
),
}
.into());
}
Plan::lay_out(
layout,
frames,
channels,
fields,
Some(Looped {
at,
lead,
crossfade,
warmup,
cells: (length - warmup) / cell,
}),
resync_at,
)
}
fn resync_at(secondary_start: f64, channels: usize) -> Result<usize, Error> {
fields_at(secondary_start)
.and_then(|f| f.checked_mul(channels))
.ok_or_else(|| {
ParseError::OutOfBounds {
value: format!("a secondary start at frame {secondary_start}"),
bound: "a position on the field lattice".into(),
}
.into()
})
}
fn modelled(frames: usize, channels: usize) -> Result<(), Error> {
if !(1..=MAX_CHANNELS).contains(&channels) {
return Err(ParseError::OutOfBounds {
value: format!("{channels} channels"),
bound: format!(
"1 or {MAX_CHANNELS} — the terminator states one cell size, and all \
it can say is whether the cell is doubled"
),
}
.into());
}
if frames >= MIN_FRAMES {
return Ok(());
}
Err(ParseError::OutOfBounds {
value: format!("{frames} frames"),
bound: format!(
"the modelled range: at least {MIN_FRAMES} frames, below which the \
stream opens a way this crate has not modelled"
),
}
.into())
}
fn lay_out(
layout: Layout,
frames: usize,
channels: usize,
fields: usize,
looped: Option<Looped>,
resync_at: usize,
) -> Result<Plan, Error> {
let units = Units { layout, channels };
if fields > units.max_fields() {
return Err(size_error(frames).into());
}
let (cell, chunk) = (units.cell(), units.chunk());
let band = |r: usize| band(r, cell, chunk);
let head = looped.map_or(fields, |l| l.at);
let warmup = band(resync_at);
let fits = resync_at >= warmup
&& head
.checked_sub(warmup)
.and_then(|rest| resync_at.checked_add(band(rest)))
.is_some_and(|end| head >= end);
if !fits {
return Err(ParseError::OutOfBounds {
value: format!("a secondary start at field {resync_at}"),
bound: format!(
"the {head} fields ahead of the {}, less the 1:1 run at each end",
if looped.is_some() {
"loop"
} else {
"terminator"
}
),
}
.into());
}
let resync = band(head - warmup);
Ok(Plan {
layout,
channels,
fields,
resync_at,
warmup,
resync,
cells_before: (resync_at - warmup) / cell,
cells_after: (head - resync_at - resync) / cell,
looped,
})
}
}
pub fn default_secondary_start(frames: usize, loops: Option<Loop>) -> f64 {
let stop = frames as f64;
nsmpproj::repaired_secondary_start(
nsmpproj::default_secondary_start(stop),
stop,
loops.map(|l| nsmpproj::repaired_loop_start(l.start as f64)),
)
}
fn round_ratio(num: u64, den: u64) -> Option<usize> {
num.checked_add(den / 2)
.and_then(|n| usize::try_from(n / den).ok())
}
fn frames_of(source: &[i16], channels: usize) -> Result<usize, Error> {
if channels == 0 || !source.len().is_multiple_of(channels) {
return Err(ParseError::AssertFail(format!(
"{} sample(s) is not a whole number of {channels}-channel frames",
source.len()
))
.into());
}
Ok(source.len() / channels)
}
fn size_error(frames: usize) -> ParseError {
ParseError::OutOfBounds {
value: format!("{frames} frames"),
bound: format!("audio whose encoded stream fits {MAX_STREAM_WORDS} words"),
}
}
fn band(r: usize, cell: usize, rmax: usize) -> usize {
let residue = if r.is_multiple_of(cell) {
cell
} else {
r % cell
};
let mut length = if residue == cell {
cell
} else {
residue + cell
};
while length <= 64 * cell {
if (1..=8).any(|j| j * cell <= length && length <= j * rmax) {
return length;
}
length += cell;
}
length
}
fn chunks(mut n: usize, chunk: usize) -> Vec<usize> {
let mut out = Vec::new();
while n > chunk {
out.push(chunk);
n -= chunk;
}
out.push(n);
out
}
#[derive(Debug, Clone)]
struct Quantised {
values: Vec<i32>,
shift: i32,
peak: i32,
}
const MAX_PEAK: i64 = (1 << 23) - 1;
fn ramp_in(fields: &mut [i64]) {
let cube = |n: usize| (n * n * n) as i64;
for (f, value) in fields.iter_mut().enumerate().take(RAMP_IN) {
*value = *value * cube(f) / cube(RAMP_IN);
}
}
fn bake_loop(raw: &mut [i64], at: usize, lead: usize, crossfade: usize) {
let fields = raw.len();
let end = fields - lead;
let length = fields - at;
let span = crossfade as i64;
for k in 0..crossfade {
let f = end - crossfade + k;
let (near, far) = (raw[f], raw[f - length]);
let step = (far - near) * k as i64;
raw[f] = near + (2 * step + span * step.signum()) / (2 * span);
}
for k in 0..lead {
raw[end + k] = raw[at - lead + k];
}
}
fn quantise(source: &[i16], plan: &Plan, forced: Option<u8>) -> Quantised {
let channels = plan.channels;
let per = plan.fields / channels;
let mut raw = vec![0i64; plan.fields];
let mut sums = vec![0f64; plan.fields];
let mut lane: Vec<i16> = Vec::with_capacity(source.len().div_ceil(channels));
for channel in 0..channels {
lane.clear();
lane.extend(source.iter().skip(channel).step_by(channels).copied());
let accumulated: Vec<f64> = (0..per).map(|f| kernel::accumulate(&lane, f)).collect();
let mut fields: Vec<i64> = accumulated.iter().map(|sum| sum.trunc() as i64).collect();
ramp_in(&mut fields);
match &plan.looped {
Some(points) => bake_loop(
&mut fields,
points.at / channels,
points.lead / channels,
points.crossfade / channels,
),
None => fields[per - RING_OUT..].fill(0),
}
for (f, (value, sum)) in fields.into_iter().zip(accumulated).enumerate() {
let at = f * channels + channel;
raw[at] = value;
sums[at] = if value == sum.trunc() as i64 {
sum
} else {
value as f64
};
}
}
let mut shift = peak_shift(&raw, PEAK_WIDTH);
if spends_extra_bit(&raw, plan) {
shift += 1;
}
if let Some(bits) = forced {
shift = i32::from(bits);
}
let opening = plan.looped.map(|l| l.at..l.at + l.warmup);
let content = |f: usize| {
((f >= plan.warmup && f < plan.resync_at) || f >= plan.resync_at + plan.resync)
&& !opening.as_ref().is_some_and(|run| run.contains(&f))
};
let extreme = (0..plan.fields)
.filter(|&f| content(f))
.fold(None, |best: Option<usize>, f| match best {
Some(b) if sums[f].abs() <= sums[b].abs() => Some(b),
_ => Some(f),
});
let signed = extreme
.map_or(0, |f| raw[f] >> 2)
.clamp(-MAX_PEAK - 1, MAX_PEAK) as i32;
let peak = match plan.layout.signed_peak() {
true => signed,
false => signed.abs(),
};
Quantised {
values: raw.iter().map(|&v| (v >> shift) as i32).collect(),
shift,
peak,
}
}
fn width_of(low: i64, high: i64) -> u8 {
let mut w = MIN_WIDTH;
while i128::from(low) < -(1i128 << (w - 1)) || i128::from(high) > (1i128 << (w - 1)) - 1 {
w += 1;
}
w
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Spec {
one_to_one: bool,
width: u8,
order: u8,
mark: bool,
first: usize,
count: usize,
}
impl Spec {
fn span(&self, units: Units) -> usize {
units.span(self.count, self.width)
}
}
fn residual(values: &[i32], at: usize, order: u8, stride: usize) -> i64 {
DIFFERENCE[usize::from(order)]
.iter()
.enumerate()
.map(|(j, &c)| match at.checked_sub(j * stride) {
Some(k) => i64::from(c) * i64::from(values[k]),
None => 0,
})
.sum()
}
fn width_at(values: &[i32], first: usize, order: u8, cell: usize, stride: usize) -> u8 {
let mut low = 0i64;
let mut high = 0i64;
for at in first..first + cell {
let e = residual(values, at, order, stride);
low = low.min(e);
high = high.max(e);
}
width_of(low, high)
}
fn widths_at(
values: &[i32],
first: usize,
predictor: Predictor,
cell: usize,
stride: usize,
) -> Vec<u8> {
let orders = match predictor {
Predictor::Plain => 1,
Predictor::Minimising => DIFFERENCE.len(),
};
(0..orders as u8)
.map(|order| width_at(values, first, order, cell, stride))
.collect()
}
fn choose_order(widths: &[u8], extending: Option<(u8, u8)>) -> (u8, u8) {
let narrowest = *widths.iter().min().unwrap_or(&MIN_WIDTH);
let reaches = |order: u8| widths.get(usize::from(order)) == Some(&narrowest);
let order = extending
.filter(|&(order, width)| width == narrowest && reaches(order))
.map_or_else(
|| (0..widths.len() as u8).find(|&o| reaches(o)).unwrap_or(0),
|(order, _)| order,
);
(order, narrowest)
}
fn records(values: &[i32], plan: &Plan, predictor: Predictor) -> Result<(Vec<Spec>, usize), Error> {
let mut out = Vec::new();
let mut at = 0usize;
let (cell, chunk, stride) = (plan.cell(), plan.chunk(), plan.channels);
let one_to_one = |out: &mut Vec<Spec>, at: &mut usize, fields: usize| {
for count in chunks(fields, chunk) {
let mut low = 0i64;
let mut high = 0i64;
for &v in &values[*at..*at + count] {
low = low.min(i64::from(v));
high = high.max(i64::from(v));
}
out.push(Spec {
one_to_one: true,
width: width_of(low, high),
order: 0,
mark: false,
first: *at,
count,
});
*at += count;
}
};
let content = |out: &mut Vec<Spec>, at: &mut usize, cells: usize| {
let mut run: Option<Spec> = None;
for index in 0..cells {
let first = *at + index * cell;
let widths = widths_at(values, first, predictor, cell, stride);
let (order, width) = choose_order(&widths, run.map(|r| (r.order, r.width)));
match run {
Some(ref mut record)
if (record.order, record.width) == (order, width)
&& record.count + cell <= MAX_COUNT =>
{
record.count += cell;
}
_ => {
out.extend(run.take());
run = Some(Spec {
one_to_one: false,
width,
order,
mark: false,
first,
count: cell,
});
}
}
}
out.extend(run);
*at += cells * cell;
};
one_to_one(&mut out, &mut at, plan.warmup);
content(&mut out, &mut at, plan.cells_before);
let resync_record = out.len();
one_to_one(&mut out, &mut at, plan.resync);
content(&mut out, &mut at, plan.cells_after);
if let Some(points) = &plan.looped {
let opening = out.len();
one_to_one(&mut out, &mut at, points.warmup);
out[opening].mark = true;
content(&mut out, &mut at, points.cells);
pad_to_packet(&mut out, opening, plan.units())?;
}
if at != plan.fields {
return Err(ParseError::AssertFail(format!(
"the record plan covered {at} of {} fields",
plan.fields
))
.into());
}
Ok((out, resync_record))
}
fn pad_to_packet(specs: &mut Vec<Spec>, opening: usize, units: Units) -> Result<(), Error> {
let cell = units.cell();
let packet = units.packet_words();
let words = |specs: &[Spec]| specs.iter().map(|s| s.span(units)).sum::<usize>();
let mut pad = (packet - words(&specs[opening..]) % packet) % packet;
let splittable = |spec: &Spec| !spec.one_to_one && spec.count > cell;
while pad > 0 && specs[opening..].iter().any(splittable) {
let mut at = opening;
while pad > 0 && at < specs.len() {
let spec = specs[at];
if splittable(&spec) {
let head = spec.count / cell / 2 * cell;
specs[at].count = head;
specs.insert(
at + 1,
Spec {
first: spec.first + head,
count: spec.count - head,
..spec
},
);
pad -= 1;
}
at += 1;
}
}
let cap = widen_cap(units.layout);
for spec in specs[opening..].iter_mut() {
if pad == 0 {
break;
}
if spec.one_to_one {
continue;
}
let count = spec.count;
let step = |width: u8| units.span(count, width + 1) - units.span(count, width);
while spec.width < cap && step(spec.width) <= pad {
pad -= step(spec.width);
spec.width += 1;
}
}
if pad > 0 {
return Err(ParseError::OutOfBounds {
value: format!("a loop of {} record(s)", specs.len() - opening),
bound: format!(
"a loop with {pad} more word(s) of room in it — the encoded loop has to \
be whole packets long, and no record of this one may be widened past \
{cap}"
),
}
.into());
}
Ok(())
}
const fn widen_cap(layout: Layout) -> u8 {
match layout {
Layout::V2 | Layout::V3 => 13,
Layout::V4 => 14,
}
}
struct Stream {
words: Vec<u8>,
first_record: usize,
resync: usize,
mark: Option<usize>,
terminator: usize,
}
fn pack(
specs: &[Spec],
values: &[i32],
resync_record: usize,
preamble: usize,
plan: &Plan,
) -> Result<Stream, Error> {
let units = plan.units();
let (word, header) = (units.word(), plan.layout.header_len());
let chain: usize = specs.iter().map(|s| s.span(units)).sum::<usize>() + 1;
let need = (chain + units.min_lead())
.checked_mul(word)
.and_then(|bytes| bytes.checked_add(header))
.ok_or_else(|| ParseError::OutOfBounds {
value: format!("a chain of {chain} words"),
bound: "a stroke payload of addressable length".into(),
})?;
let mut payload = preamble;
while payload < need {
payload += packet_len(plan.layout);
}
if !(payload - header).is_multiple_of(word) {
return Err(ParseError::AssertFail(format!(
"a {preamble}-byte preamble puts the word stream off a word boundary; the \
sections in front of the stroke are not whole words"
))
.into());
}
let total = (payload - header) / word;
if total > MAX_STREAM_WORDS {
return Err(ParseError::OutOfBounds {
value: format!("a stream of {total} words"),
bound: format!(
"{MAX_STREAM_WORDS} words, the reach of the stroke header's 16-bit word \
directory"
),
}
.into());
}
let mut words = vec![0u8; total * word];
let lead = total - chain;
let mut at = lead;
let mut resync = lead;
let mut mark = None;
for (index, spec) in specs.iter().enumerate() {
if index == resync_record {
resync = at;
}
if spec.mark {
mark = Some(at);
}
write_record(&mut words, at, spec, values, units);
at += spec.span(units);
}
if at.checked_add(1) != Some(total) {
return Err(ParseError::AssertFail(format!(
"the record chain ended at word {at} of {total}"
))
.into());
}
let terminator = (1u32 << 23) | plan.cell() as u32;
words[at * word..(at + 1) * word].copy_from_slice(&terminator.to_be_bytes()[4 - word..]);
Ok(Stream {
words,
first_record: lead,
resync,
mark,
terminator: at,
})
}
fn write_record(words: &mut [u8], at: usize, spec: &Spec, values: &[i32], units: Units) {
let (word, bits) = (units.word(), units.word_bits());
let head = (u32::from(spec.one_to_one) << 23)
| (u32::from(spec.width - 1) << 19)
| (u32::from(spec.mark) << 18)
| (u32::from(spec.order) << 14)
| spec.count as u32;
words[at * word..(at + 1) * word].copy_from_slice(&head.to_be_bytes()[4 - word..]);
let stored = |k: usize| -> u64 {
let value = residual(values, spec.first + k, spec.order, units.channels);
(value as u64) & ((1u64 << spec.width) - 1)
};
let put = |words: &mut [u8], mut bit: usize, raw: u64| {
for b in (0..spec.width).rev() {
if raw >> b & 1 != 0 {
words[bit / 8] |= 1 << (7 - bit % 8);
}
bit += 1;
}
};
if !units.splits() {
for k in 0..spec.count {
put(
words,
(at + 1) * bits + k * usize::from(spec.width),
stored(k),
);
}
return;
}
let per = spec.count / 2;
let half = units.half(spec.count, spec.width);
let mut packed = vec![0u8; half * word];
for channel in 0..2 {
packed.fill(0);
for k in 0..per {
put(
&mut packed,
k * usize::from(spec.width),
stored(2 * k + channel),
);
}
for w in 0..half {
let to = (at + 1 + 2 * w + channel) * word;
words[to..to + word].copy_from_slice(&packed[w * word..(w + 1) * word]);
}
}
}
fn statistic_a(peak: u32, shift: i32, gain: u64) -> (u32, u8) {
let peak = u64::from(peak.max(1));
let bits = 64 - peak.leading_zeros() as i32;
let exact_power = i32::from(peak.is_power_of_two());
let reciprocal = (1u64 << (21 + bits + (1 - exact_power))) / peak;
let mantissa = (reciprocal * gain) >> (super::zone::GAIN_BITS + 3);
(
(mantissa % (1 << 24)) as u32,
(22 + shift - bits + exact_power) as u8,
)
}
fn stroke_header(
layout: Layout,
zone: &NewZone<'_>,
encoded: &Encoded,
body_at: usize,
file_peak: u32,
) -> Vec<u8> {
let (q, stream) = (&encoded.q, &encoded.stream);
let mut head = vec![0u8; layout.header_len()];
head[0..4].copy_from_slice(&zone.global_id.to_be_bytes());
head[super::stroke::ROOT_KEY] = zone.root_key;
head[6..8].copy_from_slice(&[0x88, 0xba]);
head[8] = zone.channels as u8;
let (mantissa, exponent) =
statistic_a(file_peak, q.shift, gain_units(gain_decibels(zone.gain)));
head[codec::MANTISSA_AT..codec::MANTISSA_AT + 3].copy_from_slice(&mantissa.to_be_bytes()[1..]);
head[codec::STAT_A_EXP_AT] = exponent;
head[codec::PEAK_AT..codec::PEAK_AT + 3].copy_from_slice(&(q.peak as u32).to_be_bytes()[1..]);
let base = (body_at + layout.header_len()) / layout.word() % WRAP;
let pointer = |word: usize| ((base + word) % WRAP) as u16;
let directory = [
pointer(stream.first_record),
pointer(stream.resync),
pointer(stream.mark.unwrap_or(stream.terminator)),
pointer(stream.terminator),
];
for (i, p) in directory.iter().enumerate() {
let at = codec::SEEK_AT + codec::SEEK_STRIDE * i;
head[at..at + 2].copy_from_slice(&p.to_be_bytes());
if i < 3 {
head[at + 2] = 0x80;
}
}
let tails = [gain_decibels(zone.gain), zone.loop_decay];
for (at, value) in codec::TAIL_FLOATS_AT.iter().zip(tails) {
if let Some(slot) = head.get_mut(*at..at + 4) {
slot.copy_from_slice(&value.to_be_bytes());
}
}
head
}
pub const DEFAULT_LOOP_DECAY: f32 = 20.0;
struct Encoded {
q: Quantised,
stream: Stream,
}
fn encode_stroke(
layout: Layout,
zone: &NewZone<'_>,
preamble: usize,
predictor: Predictor,
) -> Result<Encoded, Error> {
let channels = usize::from(zone.channels);
let frames = frames_of(zone.source, channels)?;
let plan = match zone.loops {
Some(points) => Plan::looped(layout, frames, channels, points, zone.secondary_start)?,
None => Plan::new(layout, frames, channels, zone.secondary_start)?,
};
if let Some(bits) = zone.shift {
if i32::from(bits) > codec::SHIFT_LIMIT {
return Err(ParseError::OutOfBounds {
value: format!("a quantiser shift of {bits} bits"),
bound: format!("0 through {} bits", codec::SHIFT_LIMIT),
}
.into());
}
}
let q = quantise(zone.source, &plan, zone.shift);
let low = q.values.iter().copied().min().unwrap_or(0);
let high = q.values.iter().copied().max().unwrap_or(0);
if width_of(i64::from(low), i64::from(high)) > MAX_STORED_WIDTH {
return Err(ParseError::OutOfBounds {
value: format!(
"a quantiser shift of {} bits for fields spanning {low}..={high}",
q.shift
),
bound: format!("values that fit the stream's {MAX_STORED_WIDTH}-bit fields"),
}
.into());
}
let (specs, resync_record) = records(&q.values, &plan, predictor)?;
let stream = pack(&specs, &q.values, resync_record, preamble, &plan)?;
Ok(Encoded { q, stream })
}
fn encode_strokes(
layout: Layout,
zones: &[NewZone<'_>],
predictor: Predictor,
cat_len: usize,
map_len: usize,
) -> Result<(Vec<Encoded>, u32), Error> {
let encoded = zones
.iter()
.enumerate()
.map(|(index, zone)| {
let chain = super::Chain::written_for(layout);
let preamble = super::stroke::header_len(layout, chain, index, cat_len, map_len);
encode_stroke(layout, zone, preamble, predictor)
})
.collect::<Result<Vec<_>, Error>>()?;
let peak = encoded
.iter()
.map(|e| e.q.peak.unsigned_abs())
.max()
.unwrap_or(1);
Ok((encoded, peak))
}
fn stroke_payload(
layout: Layout,
zone: &NewZone<'_>,
encoded: &Encoded,
body_at: usize,
file_peak: u32,
) -> Result<Vec<u8>, Error> {
midi_note("root key", zone.root_key)?;
body_at
.checked_add(layout.header_len())
.ok_or_else(|| ParseError::OutOfBounds {
value: format!("body offset {body_at}"),
bound: "an addressable stroke header".into(),
})?;
let mut payload = stroke_header(layout, zone, encoded, body_at, file_peak);
payload.extend_from_slice(&encoded.stream.words);
Ok(payload)
}
const HDR_VERSION: u8 = 9;
const CAT_VERSION: u8 = 5;
const STK_VERSION: u8 = 9;
const STY_VERSION: u8 = 5;
const CONTAINER_VERSION: u8 = 11;
const CATEGORY: u8 = 0x0f;
fn hdr(name: &str) -> Result<Section, Error> {
let mut payload = vec![0u8; 111];
payload[0..6].copy_from_slice(&[0x00, 0x01, 0xb4, 0x00, 0x06, 0x50]);
super::StringField::NAME.write(&mut payload, name)?;
Ok(Section {
tag: *section::HDR,
version: HDR_VERSION,
payload,
})
}
fn cat() -> Section {
let mut payload = vec![CATEGORY, 0x00, 0x00, 0x00, 0x01];
for label in [&b"Production"[..], &b"Origin"[..]] {
payload.push(label.len() as u8);
payload.extend_from_slice(label);
}
while !payload.len().is_multiple_of(3) {
payload.push(0);
}
Section {
tag: *section::CAT,
version: CAT_VERSION,
payload,
}
}
fn map(map_gain: u32, zones: &[ZoneRecord]) -> Result<Section, Error> {
let mut payload = vec![0u8; super::zone::RECORDS_AT + super::zone::RECORD_LEN * zones.len()];
let mut keys = super::keymap::KeyTable::NEUTRAL;
keys.instrument = super::keymap::Level::new(map_gain, 0)?;
payload[..super::zone::COUNT_AT].copy_from_slice(&keys.prefix());
payload[super::zone::COUNT_AT] = zones.len() as u8;
for (index, record) in zones.iter().enumerate() {
let at = super::zone::RECORDS_AT + super::zone::RECORD_LEN * index;
payload[at + 2] = record.id;
payload[at + 3..at + 6].copy_from_slice(&record.gain.to_be_bytes()[1..]);
payload[at + 9] = record.top_note;
payload[at + 10..at + 12].copy_from_slice(&super::zone::REL_STRENGTH_DEFAULT.to_be_bytes());
}
Ok(Section {
tag: *section::MAP,
version: super::keymap::VERSION,
payload,
})
}
fn sty(preset: Preset) -> Result<Section, Error> {
if preset.velocity_to_amplitude >= super::sty::VELOCITY_LEVELS
|| preset.velocity_to_timbre >= super::sty::VELOCITY_LEVELS
{
return Err(ParseError::OutOfBounds {
value: format!(
"velocity levels {} and {}",
preset.velocity_to_amplitude, preset.velocity_to_timbre
),
bound: format!("levels below {}", super::sty::VELOCITY_LEVELS),
}
.into());
}
let mut payload = vec![0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00];
payload[3] = u8::from(preset.dynamics_enabled);
payload[4] = preset.velocity_to_amplitude;
payload[5] = preset.velocity_to_timbre;
Ok(Section {
tag: *section::STY,
version: STY_VERSION,
payload,
})
}
struct WideSchema {
container: u32,
container_payload: [u8; 4],
hdr: u32,
map: u32,
key_stride: usize,
map_gap: &'static [u8],
map_tail: &'static [u8],
sty: u32,
sty_payload: &'static [u8],
sty_dynamics: &'static [(usize, u8)],
}
fn level(gain: u32) -> [u8; super::keymap::RECORD_LEN] {
let mut out = [0u8; super::keymap::RECORD_LEN];
out[..3].copy_from_slice(&gain.to_be_bytes()[1..]);
out
}
const STY_V3_PAYLOAD: [u8; super::sty::V3_LEN] = [
0x00, 0x00, 0x7f, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
];
const STY_V4_PAYLOAD: [u8; super::sty::V4_LEN_LONG] = [
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e,
0x1e, 0x1e, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
const STY_V3_DYNAMICS: [(usize, u8); 4] = [(4, 43), (12, 74), (14, 1), (16, 74)];
const STY_V4_DYNAMICS: [(usize, u8); 5] = [(3, 1), (4, 1), (85, 74), (86, 82), (87, 90)];
fn wide_schema(layout: Layout) -> Option<WideSchema> {
match layout {
Layout::V2 => None,
Layout::V3 => Some(WideSchema {
container: 30,
container_payload: [0x00, 0x02, 0x00, 0x0c],
hdr: 10,
map: 14,
key_stride: super::keymap::RECORD_LEN,
map_gap: &[],
map_tail: &[0x00],
sty: super::sty::VERSION_V3,
sty_payload: &STY_V3_PAYLOAD,
sty_dynamics: &STY_V3_DYNAMICS,
}),
Layout::V4 => Some(WideSchema {
container: 40,
container_payload: [0x00, 0x02, 0x00, 0x05],
hdr: 11,
map: 21,
key_stride: super::keymap::RECORD_LEN + 4,
map_gap: &[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x02, 0x02, 0x02, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00,
],
map_tail: &[0x00, 0x00, 0x00, 0x01, 0x00, 0x00],
sty: super::sty::VERSION_V4,
sty_payload: &STY_V4_PAYLOAD,
sty_dynamics: &STY_V4_DYNAMICS,
}),
}
}
fn hdr4(schema: &WideSchema, name: &str) -> Result<Section4, Error> {
let mut payload = vec![0u8; 112];
payload[4..6].copy_from_slice(&[0x06, 0x50]);
super::StringField::NAME_V3.write(&mut payload, name)?;
Ok(Section4 {
tag: *section::HDR4,
version: schema.hdr,
payload,
})
}
fn cat4() -> Section4 {
let mut payload = vec![0u8; 8];
payload[0] = CATEGORY;
Section4 {
tag: *section::CAT4,
version: 7,
payload,
}
}
fn map4(schema: &WideSchema, map_gain: u32, zones: &[WideZoneRecord]) -> Section4 {
let mut payload = Vec::with_capacity(
super::keymap::RECORD_LEN
+ super::keymap::KEYS * schema.key_stride
+ schema.map_gap.len()
+ 1
+ super::zone::WIDE_RECORD_LEN * zones.len()
+ schema.map_tail.len(),
);
payload.extend_from_slice(&level(map_gain));
for key in 0..super::keymap::KEYS as u8 {
payload.extend_from_slice(&level(super::zone::GAIN_UNITY));
payload.extend(std::iter::repeat_n(
key,
schema.key_stride - super::keymap::RECORD_LEN,
));
}
payload.extend_from_slice(schema.map_gap);
payload.push(zones.len() as u8);
for record in zones {
payload.extend_from_slice(&record.bytes());
}
payload.extend_from_slice(schema.map_tail);
Section4 {
tag: *section::MAP4,
version: schema.map,
payload,
}
}
fn sty4(schema: &WideSchema, preset: Preset) -> Section4 {
let mut payload = schema.sty_payload.to_vec();
if preset.dynamics_enabled {
for &(at, value) in schema.sty_dynamics {
payload[at] = value;
}
}
Section4 {
tag: *section::STY4,
version: schema.sty,
payload,
}
}
fn meta4(chain_len: usize) -> Section4 {
let mut payload = vec![0u8; super::meta::LEN];
payload[0..2].copy_from_slice(&2u16.to_be_bytes());
payload[2..6].copy_from_slice(&(chain_len as u32).to_be_bytes());
Section4 {
tag: *section::META4,
version: super::meta::VERSION,
payload,
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct NewZone<'a> {
pub source: &'a [i16],
pub channels: u16,
pub root_key: u8,
pub top_note: u8,
pub global_id: u32,
pub loops: Option<Loop>,
pub secondary_start: f64,
pub shift: Option<u8>,
pub loop_decay: f32,
pub gain: f64,
}
pub fn instrument(source: &[i16], options: &Options) -> Result<crate::Sample, Error> {
midi_note("root key", options.root_key)?;
let frames = frames_of(source, usize::from(options.channels))?;
let secondary_start = options
.secondary_start
.unwrap_or_else(|| default_secondary_start(frames, options.loops));
multi_zone(
Instrument {
name: &options.name,
map_gain: 1.0,
predictor: options.predictor,
layout: options.layout,
preset: Preset::default(),
},
&[NewZone {
source,
channels: options.channels,
root_key: options.root_key,
top_note: options.resolved_top_note(),
global_id: 1,
loops: options.loops,
secondary_start,
shift: options.shift,
gain: 1.0,
loop_decay: DEFAULT_LOOP_DECAY,
}],
)
}
#[derive(Debug, Clone, Copy)]
pub struct Instrument<'a> {
pub name: &'a str,
pub map_gain: f64,
pub predictor: Predictor,
pub layout: Layout,
pub preset: Preset,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Preset {
pub dynamics_enabled: bool,
pub velocity_to_amplitude: u8,
pub velocity_to_timbre: u8,
}
impl Default for Preset {
fn default() -> Preset {
Preset {
dynamics_enabled: false,
velocity_to_amplitude: 1,
velocity_to_timbre: 1,
}
}
}
pub fn multi_zone(
instrument: Instrument<'_>,
zones: &[NewZone<'_>],
) -> Result<crate::Sample, Error> {
match wide_schema(instrument.layout) {
Some(schema) => wide_chain(instrument, zones, &schema).map(crate::Sample::V3),
None => narrow_chain(instrument, zones).map(crate::Sample::V2),
}
}
fn container(layout: Layout) -> Header {
Header {
generation: Generation::V1,
tag: *b"nsmp",
location: 0xFFFF_FFFF,
aux: AUX,
version: version(layout),
}
}
fn narrow_chain(instrument: Instrument<'_>, zones: &[NewZone<'_>]) -> Result<Cbin<Sample>, Error> {
let table = zone_table(zones)?;
let hdr = hdr(instrument.name)?;
let cat = cat();
let map = map(map_gain_units(instrument.map_gain), &table)?;
let cat_len = cat.payload.len();
let map_len = map.payload.len();
let mut sections = vec![
Section {
tag: *section::CONTAINER,
version: CONTAINER_VERSION,
payload: Vec::new(),
},
hdr,
cat,
map,
];
let (encoded, file_peak) =
encode_strokes(Layout::V2, zones, instrument.predictor, cat_len, map_len)?;
let mut body_at: usize = sections.iter().map(Section::encoded_len).sum();
for (zone, stroke) in zones.iter().zip(&encoded) {
let payload = stroke_payload(
Layout::V2,
zone,
stroke,
body_at + section::HEADER_LEN,
file_peak,
)?;
body_at += section::HEADER_LEN + payload.len();
sections.push(Section {
tag: *section::STK,
version: STK_VERSION,
payload,
});
}
sections.push(sty(instrument.preset)?);
Ok(Cbin {
header: container(Layout::V2),
body: Sample { sections },
})
}
const STK4_VERSION: u32 = 11;
fn wide_chain(
instrument: Instrument<'_>,
zones: &[NewZone<'_>],
schema: &WideSchema,
) -> Result<Cbin<SampleV3>, Error> {
let layout = instrument.layout;
let table = wide_zone_table(zones)?;
let hdr = hdr4(schema, instrument.name)?;
let cat = cat4();
let map = map4(schema, map_gain_units(instrument.map_gain), &table);
let cat_len = cat.payload.len();
let map_len = map.payload.len();
let mut sections = vec![
Section4 {
tag: *section::CONTAINER4,
version: schema.container,
payload: schema.container_payload.to_vec(),
},
hdr,
cat,
map,
];
let (encoded, file_peak) =
encode_strokes(layout, zones, instrument.predictor, cat_len, map_len)?;
let mut body_at: usize = sections.iter().map(Section4::encoded_len).sum();
for (zone, stroke) in zones.iter().zip(&encoded) {
let payload = stroke_payload(
layout,
zone,
stroke,
body_at + section::HEADER4_LEN,
file_peak,
)?;
body_at += section::HEADER4_LEN + payload.len();
sections.push(Section4 {
tag: *section::STK4,
version: STK4_VERSION,
payload,
});
}
sections.push(sty4(schema, instrument.preset));
let chain_len: usize = sections.iter().map(Section4::encoded_len).sum();
sections.push(meta4(chain_len));
Ok(Cbin {
header: container(layout),
body: SampleV3 { sections },
})
}
struct WideZoneRecord {
root_key: u8,
top_note: u8,
low_note: u8,
global_id: u32,
}
impl WideZoneRecord {
fn bytes(&self) -> [u8; super::zone::WIDE_RECORD_LEN] {
let mut r = [0u8; super::zone::WIDE_RECORD_LEN];
r[0] = self.root_key;
r[1] = self.top_note;
r[2] = self.low_note;
r[7] = 1;
r[8..12].copy_from_slice(&self.global_id.to_be_bytes());
r[12..14].copy_from_slice(&super::zone::REL_STRENGTH_DEFAULT.to_be_bytes());
let full = super::zone::VelocityWindow::FULL;
r[14] = full.low;
r[15] = full.high;
r
}
}
fn wide_zone_table(zones: &[NewZone<'_>]) -> Result<Vec<WideZoneRecord>, Error> {
let table = zone_table(zones)?;
Ok(table
.iter()
.enumerate()
.map(|(index, record)| WideZoneRecord {
root_key: zones[index].root_key,
top_note: record.top_note,
low_note: match table.get(index + 1) {
Some(below) => below.top_note.saturating_add(1),
None => super::zone::KEY_FLOOR,
},
global_id: zones[index].global_id,
})
.collect())
}
struct ZoneRecord {
id: u8,
top_note: u8,
gain: u32,
}
fn zone_table(zones: &[NewZone<'_>]) -> Result<Vec<ZoneRecord>, Error> {
if zones.is_empty() || zones.len() > MAX_ZONES {
return Err(ParseError::OutOfBounds {
value: format!("{} zones", zones.len()),
bound: format!("1 through {MAX_ZONES}, the map section's own count byte"),
}
.into());
}
let mut table = Vec::with_capacity(zones.len());
for (index, zone) in zones.iter().enumerate() {
midi_note("root key", zone.root_key)?;
midi_note("top note", zone.top_note)?;
if !(1..=MAX_STROKE_ID).contains(&zone.global_id) {
return Err(ParseError::OutOfBounds {
value: format!("stroke id {}", zone.global_id),
bound: format!("1 through {MAX_STROKE_ID}, what a zone record can name"),
}
.into());
}
if !zone.gain.is_finite() || zone.gain > MAX_ZONE_GAIN {
return Err(ParseError::OutOfBounds {
value: format!("zone {index} gain {}", zone.gain),
bound: format!("a finite gain up to {MAX_ZONE_GAIN}"),
}
.into());
}
let id = zone.global_id as u8;
if table.iter().any(|seen: &ZoneRecord| seen.id == id) {
return Err(ParseError::AssertFail(format!(
"two zones claim stroke id {id}, and a zone record names its stroke by id"
))
.into());
}
if index > 0 && zone.top_note >= zones[index - 1].top_note {
return Err(ParseError::AssertFail(format!(
"zone {index} reaches up to note {} but the zone before it stops at {}; \
zones are stored highest first and may not overlap",
zone.top_note,
zones[index - 1].top_note
))
.into());
}
table.push(ZoneRecord {
id,
top_note: zone.top_note,
gain: zone_record_gain(zone.gain),
});
}
Ok(table)
}
pub const MAX_MAP_GAIN_DB: f64 = 9.0;
pub const MAX_ZONE_GAIN: f64 = 1000.0;
fn gain_decibels(gain: f64) -> f32 {
let decibels = 20.0 * gain.log10();
match decibels.is_nan() {
true => f32::from_bits(0x7fc0_0000),
false => decibels as f32,
}
}
fn gain_units(decibels: f32) -> u64 {
let units = 10f64.powf(f64::from(decibels) / 20.0) * f64::from(super::zone::GAIN_UNITY);
units.round() as u64
}
fn map_gain_units(gain: f64) -> u32 {
let ceiling = MAX_MAP_GAIN_DB as f32;
let decibels = gain_decibels(gain);
let clamped = if decibels < ceiling {
decibels
} else {
ceiling
};
gain_units(clamped) as u32
}
fn zone_record_gain(gain: f64) -> u32 {
let units = (gain * f64::from(super::zone::GAIN_UNITY)).round() as u64;
(units % (1 << 24)) as u32
}
fn midi_note(name: &str, note: u8) -> Result<(), Error> {
if note <= 127 {
return Ok(());
}
Err(ParseError::OutOfBounds {
value: format!("{name} {note}"),
bound: "a MIDI note from 0 through 127".into(),
}
.into())
}
#[cfg(test)]
mod tests {
use super::super::codec;
use super::super::zone::GAIN_UNITY;
use super::*;
const CELL: usize = Layout::V2.cell();
const CHUNK: usize = Layout::V2.rmax();
const HEADER_LEN: usize = Layout::V2.header_len();
const PACKET_LEN: usize = packet_len(Layout::V2);
const VERSION: u32 = version(Layout::V2);
const MONO: Units = Units {
layout: Layout::V2,
channels: 1,
};
const PACKET_WORDS: usize = MONO.packet_words();
fn narrow(sample: crate::Sample) -> Cbin<Sample> {
match sample {
crate::Sample::V2(file) => file,
crate::Sample::V3(_) => panic!("the narrow chain was asked for"),
}
}
fn built(
zones: &[NewZone<'_>],
name: &str,
predictor: Predictor,
) -> Result<Cbin<Sample>, Error> {
multi_zone(made(name, predictor, Layout::V2), zones).map(narrow)
}
fn made(name: &str, predictor: Predictor, layout: Layout) -> Instrument<'_> {
Instrument {
name,
map_gain: 1.0,
predictor,
layout,
preset: Preset::default(),
}
}
fn plan(frames: usize, channels: usize) -> Result<Plan, Error> {
Plan::new(
Layout::V2,
frames,
channels,
default_secondary_start(frames, None),
)
}
fn looped(frames: usize, channels: usize, points: Loop) -> Result<Plan, Error> {
Plan::looped(
Layout::V2,
frames,
channels,
points,
default_secondary_start(frames, Some(points)),
)
}
fn sine(hz: f64, amplitude: f64, frames: usize) -> Vec<i16> {
(0..frames)
.map(|k| {
let t = k as f64 / f64::from(codec::SOURCE_RATE);
(amplitude * (2.0 * std::f64::consts::PI * hz * t).sin()).round() as i16
})
.collect()
}
fn encoded(source: &[i16], predictor: Predictor) -> Cbin<Sample> {
narrow(instrument(source, &Options::new("Test").predictor(predictor)).unwrap())
}
#[test]
fn the_band_is_the_shortest_run_a_whole_number_of_records_can_cover() {
for channels in [1usize, 2] {
let (cell, rmax) = (CELL * channels, CHUNK * channels);
for r in 0..2000usize {
let b = band(r, cell, rmax);
assert_eq!(b % cell, r % cell, "{channels}ch r {r}");
assert!(b >= cell, "band({r}) = {b}");
let records = (1..=8).find(|j| j * cell <= b && b <= j * rmax);
assert!(records.is_some(), "{channels}ch band({r}) = {b}");
for shorter in (cell..b).filter(|s| s % cell == b % cell) {
assert!(
!(1..=8).any(|j| j * cell <= shorter && shorter <= j * rmax),
"{channels}ch band({r}) = {b}, but {shorter} is reachable"
);
}
}
assert_eq!(band(0, cell, rmax), cell);
assert_eq!(band(cell, cell, rmax), cell);
}
}
#[test]
fn every_one_to_one_chunk_is_a_legal_count() {
for channels in [1usize, 2] {
let (cell, rmax) = (CELL * channels, CHUNK * channels);
for r in 0..2000usize {
let run = band(r, cell, rmax);
let split = chunks(run, rmax);
assert_eq!(split.iter().sum::<usize>(), run, "band({r})");
for c in split {
assert!((cell..=rmax).contains(&c), "band({r}) chunk {c}");
}
}
}
}
#[test]
fn the_plan_covers_every_field_exactly_once() {
for frames in [4096, 8192, 10_000, 44_100, 100_000, 441_000] {
let p = plan(frames, 1).unwrap();
assert_eq!(
p.warmup + CELL * p.cells_before + p.resync + CELL * p.cells_after,
p.fields,
"{frames} frames"
);
assert_eq!(p.warmup + CELL * p.cells_before, p.resync_at);
}
}
#[test]
fn the_resync_lands_where_the_projects_secondary_start_says() {
let mono = Plan::new(Layout::V2, 44_099, 1, 5_512.5 - 1.0).unwrap();
assert_eq!(
(mono.fields, mono.warmup, mono.resync_at, mono.resync),
(35_128, 30, 4_374, 58)
);
let both = Plan::new(Layout::V2, 30_869, 2, 3_858.75 - 1.0).unwrap();
assert_eq!(
(both.fields, both.warmup, both.resync_at, both.resync),
(49_256, 124, 6_124, 124)
);
assert_eq!(
Plan::new(Layout::V2, 88_200, 1, 11_025.0)
.unwrap()
.resync_at,
8_751
);
}
#[test]
fn a_secondary_start_the_stream_cannot_resync_at_is_refused() {
for at in [0.0, 20.0, 50_000.0, -1.0, f64::NAN, f64::INFINITY] {
assert!(
Plan::new(Layout::V2, 44_100, 1, at).is_err(),
"secondary start {at}"
);
}
let looped = |at| Plan::looped(Layout::V2, 44_100, 1, Loop::new(8_192, 40_000), at);
assert!(looped(8_193.0).is_err(), "past the loop start");
assert!(looped(8_192.0).is_ok(), "at the loop start, mark pushed");
assert!(looped(4_096.0).is_ok());
}
#[test]
fn a_loop_mark_clears_the_resync_point_by_the_generations_floor() {
for (start, secondary, channels, marks) in [
(92, 92.0, 1, [145, 137, 137]),
(200, 150.0, 1, [191, 183, 183]),
(600, 500.0, 1, [481, 481, 481]),
(92, 92.0, 2, [290, 274, 274]),
] {
let points = Loop::new(start, start + 16_384);
for (layout, mark) in [Layout::V2, Layout::V3, Layout::V4].into_iter().zip(marks) {
let plan = Plan::looped(layout, 88_200, channels, points, secondary).unwrap();
let looped = plan.looped.unwrap();
assert_eq!(
looped.at, mark,
"{layout:?} {channels}ch: a loop at frame {start} resyncing at {secondary}"
);
assert_eq!(looped.lead, mark - fields_of(start).unwrap() * channels);
assert_eq!(plan.fields, mark + fields_of(16_384).unwrap() * channels);
}
}
}
#[test]
fn audio_without_a_project_resyncs_where_a_fresh_project_would() {
assert_eq!(default_secondary_start(44_100, None), 5_512.5);
assert_eq!(
default_secondary_start(44_100, Some(Loop::new(1_000, 40_000))),
500.0
);
assert_eq!(
default_secondary_start(44_100, Some(Loop::new(0, 40_000))),
nsmpproj::MIN_SECONDARY_START
);
let stated = instrument(
&vec![0i16; 44_100],
&Options::new("Stated").secondary_start(5_521.281862),
)
.unwrap();
let fresh = instrument(&vec![0i16; 44_100], &Options::new("Stated")).unwrap();
assert_ne!(stated.stroke_streams()[0].1, fresh.stroke_streams()[0].1);
}
#[test]
fn the_stream_opens_on_a_cubic_ramp() {
let mut fields = vec![-4_000i64; 40];
ramp_in(&mut fields);
assert_eq!(fields[0], 0);
assert_eq!(fields[7], -4_000 * 343 / 42_875);
assert_eq!(fields[34], -4_000 * 39_304 / 42_875);
assert!(fields[..RAMP_IN].windows(2).all(|w| w[0] >= w[1]));
assert!(fields[RAMP_IN..].iter().all(|&v| v == -4_000));
}
#[test]
fn a_width_tie_goes_to_the_lowest_order_unless_a_record_already_holds_it() {
let widths = [13, 10, 7, 4, 4];
assert_eq!(choose_order(&widths, None), (3, 4));
assert_eq!(choose_order(&widths, Some((4, 4))), (4, 4));
assert_eq!(choose_order(&widths, Some((4, 3))), (3, 4), "width changed");
assert_eq!(choose_order(&widths, Some((2, 4))), (3, 4));
assert_eq!(choose_order(&[9], Some((3, 9))), (0, 9));
let values: Vec<i32> = (0..48).map(|k| k * (k - 1) * (k - 2) / 6).collect();
assert_eq!(
widths_at(&values, 8, Predictor::Minimising, CELL, 1)[3..],
[MIN_WIDTH, MIN_WIDTH]
);
assert_eq!(widths_at(&values, 8, Predictor::Plain, CELL, 1).len(), 1);
}
#[test]
fn short_input_is_refused_rather_than_guessed_at() {
assert!(plan(MIN_FRAMES - 1, 1).is_err());
assert!(plan(MIN_FRAMES, 1).is_ok());
assert!(plan(usize::MAX, 1).is_err());
assert!(instrument(&[0i16; MIN_FRAMES - 1], &Options::new("Test")).is_err());
assert!(instrument(&[0i16; MIN_FRAMES], &Options::new("Test")).is_ok());
}
#[test]
fn forced_shifts_that_cannot_be_encoded_are_refused() {
let mut step = vec![i16::MIN; MIN_FRAMES];
step[MIN_FRAMES / 2..].fill(i16::MAX);
assert!(instrument(&step, &Options::new("Test").shift(0)).is_err());
assert!(instrument(
&[0i16; MIN_FRAMES],
&Options::new("Test").shift(codec::SHIFT_LIMIT as u8 + 1)
)
.is_err());
}
#[test]
fn midi_notes_outside_the_wire_range_are_refused() {
let source = vec![0i16; MIN_FRAMES];
assert!(instrument(&source, &Options::new("Test").root_key(128)).is_err());
assert!(instrument(&source, &Options::new("Test").top_note(255)).is_err());
let bad_root = NewZone {
root_key: 128,
..zone(&source, 60, 127, 1)
};
let encoded = encode_stroke(Layout::V2, &bad_root, 165, Predictor::Plain).unwrap();
assert!(stroke_payload(Layout::V2, &bad_root, &encoded, 0, 1).is_err());
}
#[test]
fn the_allocation_is_whole_packets_with_the_chain_at_the_end() {
let file = encoded(&sine(440.0, 8000.0, 44_100), Predictor::Plain);
let map_len = section::find(&file.body.sections, section::MAP)
.unwrap()
.payload
.len();
let cat_len = section::find(&file.body.sections, section::CAT)
.unwrap()
.payload
.len();
let stroke = section::find(&file.body.sections, section::STK).unwrap();
let head = super::super::stroke::header_len(
Layout::V2,
super::super::Chain::Library2,
0,
cat_len,
map_len,
);
assert_eq!((stroke.payload.len() - head) % PACKET_LEN, 0);
assert_eq!(&stroke.payload[stroke.payload.len() - 3..], &[0x80, 0, 24]);
}
#[test]
fn every_predictor_round_trips_through_the_decoder_exactly() {
let mut differenced = 0usize;
for predictor in [Predictor::Plain, Predictor::Minimising] {
for source in [
sine(440.0, 12_000.0, 44_100),
sine(30.0, 32_000.0, 20_000),
vec![0i16; 8192],
vec![9000i16; 8192],
] {
let file = encoded(&source, predictor);
let (at, stroke) = file.stroke_streams()[0];
let plan = plan(source.len(), 1).unwrap();
let q = quantise(&source, &plan, None);
let audio = codec::decode(stroke, at, codec::Layout::V2).unwrap();
assert_eq!(audio.samples.len(), plan.fields);
if predictor == Predictor::Plain {
assert_eq!(audio.differenced, 0);
} else {
differenced += audio.differenced;
}
let gain = 1i32 << q.shift;
for (f, (&want, &got)) in q.values.iter().zip(&audio.samples).enumerate() {
assert_eq!(i32::from(got), want * gain, "{predictor:?} field {f}");
}
}
}
assert!(differenced > 0, "minimising never chose a predictor");
}
#[test]
fn a_sine_comes_back_a_sine() {
let source = sine(440.0, 20_000.0, 44_100);
let file = encoded(&source, Predictor::Plain);
let (at, stroke) = file.stroke_streams()[0];
let audio = codec::decode(stroke, at, codec::Layout::V2).unwrap();
let window = &audio.samples[10_000..20_000];
let peak = window.iter().map(|&v| i32::from(v).abs()).max().unwrap();
assert!((19_000..=21_000).contains(&peak), "peak {peak}");
let zero_crossings = window.windows(2).filter(|w| w[0] < 0 && w[1] >= 0).count();
assert!((124..=127).contains(&zero_crossings), "{zero_crossings}");
}
#[test]
fn a_records_fields_start_right_after_its_header() {
let spec = Spec {
one_to_one: true,
width: 13,
order: 0,
mark: false,
first: 0,
count: 30,
};
let tail = spec.span(MONO) * 24 - 24 - spec.count * usize::from(spec.width);
assert_eq!(tail, 18, "this spec is chosen to leave a tail");
let values: Vec<i32> = (0..30).map(|k| k * 7 - 40).collect();
let mut words = vec![0u8; spec.span(MONO) * 3];
write_record(&mut words, 0, &spec, &values, MONO);
let total = spec.span(MONO) * 24;
for bit in total - tail..total {
assert_eq!(
words[bit / 8] >> (7 - bit % 8) & 1,
0,
"bit {bit} is in the alignment tail and should be clear"
);
}
let mut stroke = vec![0u8; HEADER_LEN];
stroke.extend_from_slice(&words);
stroke.extend_from_slice(&[0x80, 0x00, 0x18]);
let end = (HEADER_LEN / 3 + spec.span(MONO)) as u16;
for (i, p) in [HEADER_LEN as u16 / 3, 0, end, end].iter().enumerate() {
let at = codec::SEEK_AT + codec::SEEK_STRIDE * i;
stroke[at..at + 2].copy_from_slice(&p.to_be_bytes());
}
let walked = codec::walk(&stroke, 0, codec::Layout::V2).unwrap();
assert_eq!(walked.records[0].values, values);
}
#[test]
fn the_instrument_reads_back_as_one() {
let file = instrument(
&sine(220.0, 15_000.0, 30_000),
&Options::new("Encoded").root_key(48).top_note(72),
)
.unwrap();
let bytes = file.to_bytes().unwrap();
let read = super::super::from_bytes(&bytes).unwrap();
assert_eq!(read.name().unwrap(), "Encoded");
assert_eq!(read.header.version, VERSION);
let zones = read.zones().unwrap();
assert_eq!(zones.len(), 1);
assert_eq!(zones[0].top_note, 72);
assert_eq!(read.strokes().unwrap()[0].root_key, 48);
assert_eq!(read.to_bytes().unwrap(), bytes);
}
#[test]
fn the_directory_names_the_records_the_walk_finds() {
let file = encoded(&sine(300.0, 9000.0, 50_000), Predictor::Plain);
let (at, stroke) = file.stroke_streams()[0];
let stream = codec::walk(stroke, at, codec::Layout::V2).unwrap();
let directory = codec::Directory::read(stroke).unwrap();
assert_eq!(
codec::Directory::resolve(directory.first_record, at, codec::Layout::V2),
stream.first_record
);
assert_eq!(
codec::Directory::resolve(directory.terminator, at, codec::Layout::V2),
stream.terminator
);
let resync = codec::Directory::resolve(directory.resync, at, codec::Layout::V2);
let record = stream.records.iter().find(|r| r.at == resync).unwrap();
assert!(record.one_to_one);
assert_eq!(record.first_field, plan(50_000, 1).unwrap().resync_at);
}
#[test]
fn the_header_states_the_shift_it_quantised_at() {
for amplitude in [40.0, 900.0, 8000.0, 32_000.0] {
let source = sine(440.0, amplitude, 20_000);
let plan = plan(source.len(), 1).unwrap();
let file = encoded(&source, Predictor::Plain);
let (_, stroke) = file.stroke_streams()[0];
let q = quantise(&source, &plan, None);
assert_eq!(
codec::shift(stroke, codec::Layout::V2),
Some(q.shift),
"amplitude {amplitude}"
);
assert_eq!(codec::peak(stroke, codec::Layout::V2), Some(q.peak));
assert!(q.shift >= 0);
}
}
#[test]
fn the_shift_tracks_how_loud_the_content_is() {
let quiet = plan(20_000, 1)
.map(|p| quantise(&sine(440.0, 500.0, 20_000), &p, None).shift)
.unwrap();
let loud = plan(20_000, 1)
.map(|p| quantise(&sine(440.0, 32_000.0, 20_000), &p, None).shift)
.unwrap();
assert_eq!(quiet, 0);
assert!(loud > quiet, "loud {loud} vs quiet {quiet}");
}
#[test]
fn a_stereo_stroke_stops_shifting_where_its_peak_fits() {
let frames = 30_000;
let left = sine(220.0, 12_000.0, frames);
let right = sine(330.0, 12_000.0, frames);
let both: Vec<i16> = left
.iter()
.zip(&right)
.flat_map(|(&l, &r)| [l, r])
.collect();
let mono = quantise(&left, &plan(frames, 1).unwrap(), None);
let stereo = quantise(&both, &plan(frames, 2).unwrap(), None);
assert_eq!(stereo.shift, 1);
let widest = stereo
.values
.iter()
.map(|v| width_of(i64::from(*v), i64::from(*v)))
.max()
.unwrap();
assert_eq!(widest, PEAK_WIDTH);
assert!((stereo.shift..=stereo.shift + 1).contains(&mono.shift));
}
fn probe(layout: Layout, resync: usize, field: usize) -> (Plan, Vec<i64>) {
let frames = 100_000;
let secondary = resync as f64 * f64::from(PITCH_NUM) / f64::from(PITCH_DEN);
let plan = Plan::new(layout, frames, 1, secondary).unwrap();
assert_eq!(plan.resync_at, resync);
let mut values = vec![0i64; plan.fields];
values[field] = 1 << (PEAK_WIDTH - 2);
(plan, values)
}
#[test]
fn only_a_run_s_last_record_buys_the_extra_bit() {
let (plan, values) = probe(Layout::V2, 5464, 5464 + 76);
assert!(spends_extra_bit(&values, &plan));
for offset in [12, 61, 89, 95] {
let (plan, values) = probe(Layout::V2, 5464, 5464 + offset);
assert!(!spends_extra_bit(&values, &plan), "run offset {offset}");
}
}
fn opening_run(layout: Layout, last: usize, value: i64) -> (Plan, Vec<i64>) {
let chunk = layout.rmax();
let warmup = if last == chunk { chunk } else { chunk + last };
let plan = Plan {
layout,
channels: 1,
fields: warmup,
resync_at: warmup,
warmup,
resync: 0,
cells_before: 0,
cells_after: 0,
looped: None,
};
let mut values = vec![0; warmup];
values[warmup - 1] = value;
(plan, values)
}
#[test]
fn each_last_record_width_obeys_the_measured_rule() {
for (last, buys) in [
(24, false),
(25, true),
(26, true),
(27, true),
(28, true),
(29, false),
(30, true),
(31, true),
(32, false),
] {
let (plan, values) = opening_run(Layout::V2, last, 1 << (PEAK_WIDTH - 2));
assert_eq!(spends_extra_bit(&values, &plan), buys, "width {last}");
}
}
#[test]
fn the_extra_bit_uses_signed_thirteen_bit_bounds() {
for (value, buys) in [(-4097, true), (-4096, false), (4095, false), (4096, true)] {
let (plan, values) = opening_run(Layout::V2, 25, value);
assert_eq!(spends_extra_bit(&values, &plan), buys, "value {value}");
}
}
const V3_LIVE: [usize; 11] = [33, 34, 35, 36, 37, 38, 39, 40, 42, 44, 46];
#[test]
fn six_of_the_seventeen_v3_last_record_widths_never_buy_it() {
for last in 32..=48 {
let (plan, values) = opening_run(Layout::V3, last, 1 << (PEAK_WIDTH - 2));
assert_eq!(
spends_extra_bit(&values, &plan),
V3_LIVE.contains(&last),
"width {last}"
);
}
}
#[test]
fn a_v4_mono_stroke_never_buys_the_extra_bit() {
for last in 32..=48 {
let (plan, values) = opening_run(Layout::V4, last, 1 << (PEAK_WIDTH - 2));
assert!(!spends_extra_bit(&values, &plan), "width {last}");
}
}
fn loop_run(layout: Layout, last: usize) -> (Plan, Vec<i64>) {
let at = layout.rmax();
let fields = at + last;
let plan = Plan {
layout,
channels: 1,
fields,
resync_at: at,
warmup: at,
resync: 0,
cells_before: 0,
cells_after: 0,
looped: Some(Looped {
at,
lead: 0,
crossfade: 0,
warmup: last,
cells: 0,
}),
};
let mut values = vec![0; fields];
values[fields - 1] = 1 << (PEAK_WIDTH - 2);
(plan, values)
}
#[test]
fn the_run_a_loop_mark_opens_buys_the_extra_bit() {
for (layout, live, dead) in [(Layout::V2, 25, 29), (Layout::V3, 33, 41)] {
let (plan, values) = loop_run(layout, live);
assert!(spends_extra_bit(&values, &plan), "{layout:?} live");
let unmarked = Plan {
looped: None,
..plan
};
assert!(!spends_extra_bit(&values, &unmarked), "{layout:?} unlooped");
let (plan, values) = loop_run(layout, dead);
assert!(!spends_extra_bit(&values, &plan), "{layout:?} dead");
}
}
#[test]
fn the_extra_bit_narrows_the_stroke_the_header_declares() {
let loud = header_shift(&sine(440.0, 12_000.0, 44_100), 1);
let quiet = header_shift(&sine(440.0, 3_000.0, 44_100), 1);
assert_eq!(loud - quiet, 2);
}
fn header_shift(source: &[i16], channels: u16) -> i32 {
let options = Options::new("Shift")
.channels(channels)
.predictor(Predictor::Minimising);
let file = instrument(source, &options).unwrap();
let (_, stroke) = file.stroke_streams()[0];
codec::shift(stroke, codec::Layout::V2).unwrap()
}
#[test]
fn statistic_b_takes_the_sign_of_the_extreme_field() {
let frames = 20_000;
let mut up = vec![0i16; frames];
up[10_000] = 13;
let down: Vec<i16> = up.iter().map(|v| -v).collect();
let positive = quantise(&up, &plan(frames, 1).unwrap(), None).peak;
let negative = quantise(&down, &plan(frames, 1).unwrap(), None).peak;
assert_eq!(positive, 2);
assert_eq!(negative, 3);
let opposed: Vec<i16> = up.iter().zip(&down).flat_map(|(&l, &r)| [l, r]).collect();
let stereo = quantise(&opposed, &plan(frames, 2).unwrap(), None).peak;
assert_eq!(stereo, positive);
}
#[test]
fn no_field_overflows_the_width_its_record_declares() {
for predictor in [Predictor::Plain, Predictor::Minimising] {
let source = sine(440.0, 32_000.0, 30_000);
let plan = plan(source.len(), 1).unwrap();
let q = quantise(&source, &plan, None);
let (specs, _) = records(&q.values, &plan, predictor).unwrap();
for spec in specs {
let limit = 1i64 << (spec.width - 1);
for k in 0..spec.count {
let v = residual(&q.values, spec.first + k, spec.order, 1);
assert!((-limit..limit).contains(&v), "{spec:?} field {k} = {v}");
}
assert!(spec.width <= PEAK_WIDTH || spec.order > 0);
}
}
}
#[test]
fn records_tile_the_lattice_the_way_the_laws_say() {
let source = sine(440.0, 20_000.0, 60_000);
let plan = plan(source.len(), 1).unwrap();
let q = quantise(&source, &plan, None);
let (specs, _) = records(&q.values, &plan, Predictor::Plain).unwrap();
let mut at = 0;
for spec in &specs {
assert_eq!(spec.first, at);
if !spec.one_to_one {
assert_eq!(spec.count % CELL, 0);
assert!(spec.count <= MAX_COUNT);
}
at += spec.count;
}
assert_eq!(at, plan.fields);
let one_to_one: usize = specs.iter().filter(|s| s.one_to_one).map(|s| s.count).sum();
assert_eq!(one_to_one, plan.warmup + plan.resync);
}
#[test]
fn the_minimising_predictor_narrows_smooth_material() {
let source = sine(60.0, 30_000.0, 60_000);
let plan = plan(source.len(), 1).unwrap();
let q = quantise(&source, &plan, None);
let (plain, _) = records(&q.values, &plan, Predictor::Plain).unwrap();
let (minimised, _) = records(&q.values, &plan, Predictor::Minimising).unwrap();
let bits = |specs: &[Spec]| -> usize { specs.iter().map(|s| s.span(MONO)).sum() };
assert!(
bits(&minimised) < bits(&plain),
"{} words vs {}",
bits(&minimised),
bits(&plain)
);
assert!(minimised.iter().any(|s| s.order > 0));
assert!(minimised.iter().all(|s| !s.one_to_one || s.order == 0));
}
#[test]
fn a_residual_integrates_back_to_the_field_it_came_from() {
let values: Vec<i32> = (0..200).map(|k| (k * k / 7) % 501 - 250).collect();
for order in 1..DIFFERENCE.len() as u8 {
for at in usize::from(order)..values.len() {
let mut v = residual(&values, at, order, 1);
for (j, &c) in DIFFERENCE[usize::from(order)].iter().enumerate().skip(1) {
v -= i64::from(c) * i64::from(values[at - j]);
}
assert_eq!(v, i64::from(values[at]), "order {order} at {at}");
}
}
}
#[test]
fn statistic_a_round_trips_the_shift() {
for peak in [0u32, 1, 2, 255, 4095, 4096, 8191, 8192] {
for shift in 0..6 {
let (mantissa, exponent) = statistic_a(peak, shift, u64::from(GAIN_UNITY));
let mut stroke = vec![0u8; HEADER_LEN];
stroke[codec::STAT_A_EXP_AT] = exponent;
stroke[codec::PEAK_AT..codec::PEAK_AT + 3]
.copy_from_slice(&peak.to_be_bytes()[1..]);
assert_eq!(
codec::shift(&stroke, codec::Layout::V2),
Some(shift),
"peak {peak}"
);
assert!((1 << 19..1 << 20).contains(&mantissa) || peak == 0);
}
}
}
#[test]
fn the_stroke_header_holds_the_fixed_bytes_where_the_format_puts_them() {
let file = instrument(
&sine(440.0, 9000.0, 20_000),
&Options::new("Test").root_key(64),
)
.unwrap();
let (_, head) = file.stroke_streams()[0];
assert_eq!(head[0..5], [0, 0, 0, 1, 0]);
assert_eq!(head[5], 64);
assert_eq!(head[6..9], [0x88, 0xba, 0x01]);
let stereo = instrument(
&vec![0i16; 2 * MIN_FRAMES],
&Options::new("Test").channels(2),
)
.unwrap();
assert_eq!(stereo.stroke_streams()[0].1[6..9], [0x88, 0xba, 0x02]);
assert_eq!(head[16..20], [0, 0, 0, 0]);
assert_eq!([head[22], head[31], head[40]], [0x80, 0x80, 0x80]);
assert_eq!(head[49..51], [0, 0]);
for gap in [23..29, 32..38, 41..47] {
assert!(head[gap.clone()].iter().all(|&b| b == 0), "{gap:?}");
}
}
fn zone(source: &[i16], root_key: u8, top_note: u8, global_id: u32) -> NewZone<'_> {
NewZone {
source,
channels: 1,
root_key,
top_note,
global_id,
loops: None,
secondary_start: default_secondary_start(source.len(), None),
shift: None,
gain: 1.0,
loop_decay: DEFAULT_LOOP_DECAY,
}
}
#[test]
fn statistic_a_scales_a_24_bit_reciprocal_by_the_gain() {
assert_eq!(statistic_a(4096, 2, u64::from(GAIN_UNITY)), (524_288, 12));
assert_eq!(
statistic_a(4096, 2, u64::from(GAIN_UNITY / 2)),
(262_144, 12)
);
assert_eq!(
statistic_a(4096, 2, 2 * u64::from(GAIN_UNITY)),
(1_048_576, 12)
);
assert_eq!(statistic_a(1225, 0, 1_436_549), (1_200_837, 11));
assert_eq!(statistic_a(4195, 2, 8_378_122), (8_180_401, 11));
assert_eq!(statistic_a(1225, 0, 5_557_453), (4_645_576, 11));
}
#[test]
fn statistic_a_reciprocates_the_loudest_zone_in_the_file() {
let loud = sine(440.0, 12_000.0, 20_000);
let quiet = sine(440.0, 3_000.0, 20_000);
let file = built(
&[zone(&loud, 72, 127, 1), zone(&quiet, 48, 71, 2)],
"Two",
Predictor::Plain,
)
.unwrap();
let field = |s: &[u8], at: usize| u32::from_be_bytes([0, s[at], s[at + 1], s[at + 2]]);
let streams = file.stroke_streams();
let (mantissa, peak) = (|s| field(s, 9), |s| field(s, 13));
let (first, second) = (streams[0].1, streams[1].1);
assert!(peak(first) > peak(second));
assert_eq!(mantissa(first), mantissa(second));
assert_eq!(
mantissa(second),
statistic_a(peak(first), 0, u64::from(GAIN_UNITY)).0,
"the quiet zone reciprocates the loud zone's peak"
);
assert_ne!(
mantissa(second),
statistic_a(peak(second), 0, u64::from(GAIN_UNITY)).0
);
}
#[test]
fn a_zone_gain_scales_statistic_a_and_touches_nothing_else() {
let source = sine(440.0, 12_000.0, 20_000);
let unity = built(&[zone(&source, 60, 127, 1)], "Gain", Predictor::Plain).unwrap();
let half = NewZone {
gain: 0.5,
..zone(&source, 60, 127, 1)
};
let halved = built(&[half], "Gain", Predictor::Plain).unwrap();
let (_, a) = unity.stroke_streams()[0];
let (_, b) = halved.stroke_streams()[0];
assert_eq!(a[..9], b[..9]);
assert_eq!(a[12..], b[12..]);
let mantissa = |s: &[u8]| u32::from_be_bytes([0, s[9], s[10], s[11]]);
assert_eq!(mantissa(b), mantissa(a) / 2);
assert_eq!(unity.zones().unwrap()[0].gain, GAIN_UNITY);
assert_eq!(halved.zones().unwrap()[0].gain, GAIN_UNITY / 2);
let over = NewZone {
gain: MAX_ZONE_GAIN * 2.0,
..zone(&source, 60, 127, 1)
};
assert!(built(&[over], "Gain", Predictor::Plain).is_err());
}
#[test]
fn every_zone_reads_back_paired_to_its_own_stroke() {
let high = sine(880.0, 12_000.0, 12_000);
let mid = sine(440.0, 12_000.0, 9_000);
let low = sine(220.0, 12_000.0, 15_000);
let file = built(
&[
zone(&high, 72, 96, 7),
zone(&mid, 60, 65, 3),
zone(&low, 48, 53, 9),
],
"Three",
Predictor::Plain,
)
.unwrap();
let read = super::super::from_bytes(&file.to_bytes().unwrap()).unwrap();
assert_eq!(read.name().unwrap(), "Three");
let zones = read.zones().unwrap();
assert_eq!(
zones.iter().map(|z| z.top_note).collect::<Vec<_>>(),
[96, 65, 53]
);
assert_eq!(
zones.iter().map(|z| z.stroke_id).collect::<Vec<_>>(),
[7, 3, 9]
);
assert_eq!(
read.strokes()
.unwrap()
.iter()
.map(|s| s.root_key)
.collect::<Vec<_>>(),
[72, 60, 48]
);
for (index, source) in [&high, &mid, &low].iter().enumerate() {
let (at, stream) = read.zone_stream(index).unwrap();
let audio = codec::decode(stream, at, codec::Layout::V2).unwrap();
let plan = plan(source.len(), 1).unwrap();
let q = quantise(source, &plan, None);
let gain = 1i32 << q.shift;
assert_eq!(audio.samples.len(), plan.fields, "zone {index}");
for (f, (&want, &got)) in q.values.iter().zip(&audio.samples).enumerate() {
assert_eq!(i32::from(got), want * gain, "zone {index} field {f}");
}
}
}
#[test]
fn a_zone_decodes_the_same_alone_as_in_a_crowd() {
let source = sine(330.0, 18_000.0, 20_000);
let alone = narrow(instrument(&source, &Options::new("One").root_key(60)).unwrap());
let crowd = built(
&[
zone(&sine(880.0, 9000.0, 8000), 72, 96, 3),
zone(&source, 60, 65, 2),
zone(&sine(110.0, 9000.0, 8000), 48, 53, 1),
],
"Three",
Predictor::default(),
)
.unwrap();
let one = alone.zone_stream(0).unwrap();
let many = crowd.zone_stream(1).unwrap();
assert_ne!(one.1, many.1, "the streams differ; only the audio must not");
assert_eq!(
codec::decode(one.1, one.0, codec::Layout::V2).unwrap(),
codec::decode(many.1, many.0, codec::Layout::V2).unwrap()
);
}
#[test]
fn every_stroke_is_its_own_header_length_plus_whole_packets() {
let source = sine(440.0, 12_000.0, 12_000);
for count in 1..=6usize {
let zones: Vec<NewZone> = (0..count)
.map(|i| zone(&source, 60, 120 - 10 * i as u8, i as u32 + 1))
.collect();
let file = built(&zones, "Ladder", Predictor::Plain).unwrap();
let cat_len = section::find(&file.body.sections, section::CAT)
.unwrap()
.payload
.len();
let map_len = section::find(&file.body.sections, section::MAP)
.unwrap()
.payload
.len();
for (index, section) in file
.body
.sections
.iter()
.filter(|s| s.is(section::STK))
.enumerate()
{
let head = super::super::stroke::header_len(
Layout::V2,
super::super::Chain::Library2,
index,
cat_len,
map_len,
);
assert_eq!(
(section.payload.len() - head) % PACKET_LEN,
0,
"{count} zones, stroke {index}: {} bytes over a {head}-byte header",
section.payload.len()
);
}
}
}
#[test]
fn a_zone_list_the_format_cannot_store_is_refused() {
let source = vec![0i16; MIN_FRAMES];
let one = |root, top, id| built(&[zone(&source, root, top, id)], "x", Predictor::Plain);
assert!(built(&[], "x", Predictor::Plain).is_err());
assert!(one(60, 84, 0).is_err(), "id zero names no stroke");
assert!(one(60, 84, 256).is_err(), "id past the record's one byte");
assert!(one(60, 128, 1).is_err());
assert!(one(128, 84, 1).is_err());
assert!(one(60, 84, 1).is_ok());
let pair = |tops: [u8; 2], ids: [u32; 2]| {
built(
&[
zone(&source, 60, tops[0], ids[0]),
zone(&source, 48, tops[1], ids[1]),
],
"x",
Predictor::Plain,
)
};
assert!(pair([84, 53], [1, 1]).is_err(), "duplicate stroke id");
assert!(pair([53, 84], [2, 1]).is_err(), "zones out of order");
assert!(pair([84, 84], [2, 1]).is_err(), "zones overlap");
assert!(pair([84, 53], [2, 1]).is_ok());
}
#[test]
fn a_looped_plan_covers_every_field_exactly_once() {
for (frames, start, end) in [
(88_200, 16_384, 32_768),
(88_200, 4_096, 20_480),
(88_200, 92, 16_476),
(88_200, 43_981, 60_365),
(44_100, 20_000, 44_100),
] {
let plan = looped(frames, 1, Loop::new(start, end)).unwrap();
let points = plan.looped.unwrap();
assert_eq!(
plan.warmup + CELL * plan.cells_before + plan.resync + CELL * plan.cells_after,
points.at,
"{start}..{end}: the pre-roll does not reach the loop"
);
assert_eq!(
points.at + points.warmup + CELL * points.cells,
plan.fields,
"{start}..{end}: the loop does not reach the terminator"
);
assert_eq!(points.at - fields_of(start).unwrap(), points.lead);
}
}
#[test]
fn a_loop_comes_back_the_length_it_asked_for() {
let source = sine(220.0, 18_000.0, 88_200);
for (start, end) in [
(16_384, 32_768),
(43_981, 60_365),
(4_096, 20_480),
(65_536, 81_920),
] {
let file = instrument(
&source,
&Options::new("Looped").loops(Loop::new(start, end)),
)
.unwrap_or_else(|e| panic!("loop {start}..{end}: {e}"));
let (at, stroke) = file.stroke_streams()[0];
let walk = codec::walk(stroke, at, codec::Layout::V2).unwrap();
let mark = walk.records.iter().find(|r| r.mark).unwrap();
let frames = (walk.fields - mark.first_field) as f64 * f64::from(codec::SOURCE_RATE)
/ f64::from(codec::FIELD_RATE);
assert!(
(frames - (end - start) as f64).abs() < 1.0,
"loop {start}..{end} came back {frames} frames long"
);
}
}
#[test]
fn the_loop_starts_a_packet_and_the_directory_says_so() {
let source = sine(330.0, 14_000.0, 60_000);
for (start, end) in [(8_192, 24_576), (20_000, 40_000), (4_096, 59_000)] {
for predictor in [Predictor::Plain, Predictor::Minimising] {
let file = instrument(
&source,
&Options::new("Looped")
.predictor(predictor)
.loops(Loop::new(start, end)),
)
.unwrap();
let (at, stroke) = file.stroke_streams()[0];
let walk = codec::walk(stroke, at, codec::Layout::V2).unwrap();
let directory = codec::Directory::read(stroke).unwrap();
let marked: Vec<_> = walk.records.iter().filter(|r| r.mark).collect();
assert_eq!(marked.len(), 1, "{start}..{end} {predictor:?}");
assert_eq!(
codec::Directory::resolve(directory.mark, at, codec::Layout::V2),
marked[0].at
);
assert_ne!(directory.mark, directory.terminator);
assert_eq!(
(walk.terminator - marked[0].at) % PACKET_WORDS,
0,
"{start}..{end} {predictor:?}: {} words",
walk.terminator - marked[0].at
);
}
}
}
#[test]
fn an_unlooped_stroke_marks_nothing() {
let file = encoded(&sine(440.0, 9_000.0, 44_100), Predictor::Plain);
let (at, stroke) = file.stroke_streams()[0];
let directory = codec::Directory::read(stroke).unwrap();
assert_eq!(directory.mark, directory.terminator);
assert!(codec::walk(stroke, at, codec::Layout::V2)
.unwrap()
.records
.iter()
.all(|r| !r.mark));
}
#[test]
fn the_tail_repeats_the_loops_opening() {
let source = sine(200.0, 20_000.0, 88_200);
let plan = looped(source.len(), 1, Loop::new(16_384, 32_768)).unwrap();
let points = plan.looped.unwrap();
let values = quantise(&source, &plan, None).values;
assert_eq!(
values[plan.fields - points.lead..],
values[points.at - points.lead..points.at]
);
}
const MEASURED_FADES: &[(usize, f64, usize)] = &[
(8_192, 81.92, 65),
(8_192, 163.84, 130),
(8_192, 409.6, 325),
(8_192, 819.2, 650),
(8_192, 1_638.4, 1_300),
(8_192, 2_048.0, 1_626),
(8_192, 3_276.8, 2_601),
(8_192, 4_096.0, 3_251),
(8_192, 6_144.0, 4_877),
(8_192, 8_192.0, 6_502),
(2_048, 512.0, 406),
(4_096, 1_024.0, 813),
(16_384, 4_096.0, 3_251),
(32_768, 8_192.0, 6_502),
(7_000, 700.0, 556),
(10_000, 1_000.0, 794),
(4_096, 409.6, 325),
(1_024, 409.6, 325),
(16_384, 256.0, 203),
(16_384, 1_024.0, 813),
(16_384, 8_192.0, 6_502),
];
#[test]
fn the_fade_opens_where_the_editors_own_renders_open_it() {
for &(length, crossfade, want) in MEASURED_FADES {
let points = Loop::new(16_384, 16_384 + length).crossfade(crossfade);
let plan = looped(88_200, 1, points).unwrap();
assert_eq!(
plan.looped.unwrap().crossfade,
want,
"a {crossfade} frame fade in a {length} frame loop"
);
}
}
#[test]
fn the_crossfade_ramps_linearly_into_the_material_before_the_loop() {
let source = sine(150.0, 22_000.0, 88_200);
let points = Loop::new(16_384, 32_768);
let plan = looped(source.len(), 1, points).unwrap();
let faded = looped(source.len(), 1, points.crossfade(4_096.0)).unwrap();
let (plain, mixed) = (
quantise(&source, &plan, None).values,
quantise(&source, &faded, None).values,
);
assert_eq!(plain.len(), mixed.len());
let loop_at = faded.looped.unwrap();
let end = faded.fields - loop_at.lead;
let length = faded.fields - loop_at.at;
let span = loop_at.crossfade;
assert!(span > 3_000, "the fade is {span} fields");
assert_eq!(plain[..end - span], mixed[..end - span]);
for k in 0..span {
let f = end - span + k;
let (near, far) = (f64::from(plain[f]), f64::from(plain[f - length]));
let u = k as f64 / span as f64;
let want = near + (far - near) * u;
assert!(
(f64::from(mixed[f]) - want).abs() <= 1.0,
"field {f}: {} against {want}",
mixed[f]
);
}
}
#[test]
fn a_crossfade_may_begin_before_the_loop_start() {
let source = sine(150.0, 22_000.0, 60_000);
let points = Loop::new(16_384, 24_576).crossfade(16_384.0);
let plan = looped(source.len(), 1, points).unwrap();
let looped = plan.looped.unwrap();
assert!(looped.crossfade > fields_of(points.end - points.start).unwrap());
assert!(looped.crossfade <= fields_of(points.start).unwrap());
let file = instrument(&source, &Options::new("Long fade").loops(points)).unwrap();
let (at, stroke) = file.stroke_streams()[0];
assert!(codec::decode(stroke, at, codec::Layout::V2).is_ok());
}
#[test]
fn a_loop_the_format_cannot_state_is_refused() {
let frames = 44_100;
let stated = |points| looped(frames, 1, points);
assert!(stated(Loop::new(8_192, 40_000)).is_ok());
assert!(stated(Loop::new(8_192, 8_192)).is_err(), "empty loop");
assert!(stated(Loop::new(40_000, 8_192)).is_err(), "loop runs back");
assert!(stated(Loop::new(8_192, 44_101)).is_err(), "past the audio");
assert!(
stated(Loop::new(8_192, 8_250)).is_err(),
"shorter than a run"
);
assert!(
stated(Loop::new(1_024, 40_000).crossfade(4_096.0)).is_err(),
"nothing in front of the loop to fade from"
);
assert!(
stated(Loop::new(8_192, 40_000).crossfade(40_000.0)).is_err(),
"not enough material before the fade"
);
assert!(looped(MIN_FRAMES - 1, 1, Loop::new(10, 60)).is_err());
}
#[test]
fn a_looped_stroke_round_trips_through_the_decoder_exactly() {
let source = sine(180.0, 16_000.0, 60_000);
for predictor in [Predictor::Plain, Predictor::Minimising] {
for points in [
Loop::new(8_192, 40_960),
Loop::new(8_192, 40_960).crossfade(4_096.0),
] {
let file = instrument(
&source,
&Options::new("Looped").predictor(predictor).loops(points),
)
.unwrap();
let (at, stroke) = file.stroke_streams()[0];
let plan = looped(source.len(), 1, points).unwrap();
let q = quantise(&source, &plan, None);
let audio = codec::decode(stroke, at, codec::Layout::V2).unwrap();
assert_eq!(audio.samples.len(), plan.fields);
let gain = 1i32 << q.shift;
for (f, (&want, &got)) in q.values.iter().zip(&audio.samples).enumerate() {
assert_eq!(i32::from(got), want * gain, "{predictor:?} field {f}");
}
}
}
}
#[test]
fn the_widen_fallback_walks_past_the_regions_alignment_records() {
for (layout, widths) in [
(Layout::V3, [1, 1, 13, 9, 1, 1]),
(Layout::V4, [1, 1, 14, 8, 1, 1]),
] {
let units = Units {
layout,
channels: 1,
};
let record = Spec {
one_to_one: false,
width: 1,
order: 0,
mark: false,
first: 0,
count: units.cell(),
};
let opening = Spec {
one_to_one: true,
..record
};
let mut specs = vec![
Spec {
mark: true,
..opening
},
opening,
record,
record,
record,
record,
];
pad_to_packet(&mut specs, 0, units).unwrap();
assert_eq!(
specs.iter().map(|s| s.width).collect::<Vec<_>>(),
widths,
"{layout:?}"
);
let words: usize = specs.iter().map(|s| s.span(units)).sum();
assert_eq!(words % units.packet_words(), 0, "{layout:?}");
}
}
#[test]
fn the_widen_cap_is_the_generations_constant() {
for (layout, alignment, content, spent) in [
(Layout::V2, 2usize, 9usize, [13u8, 13]),
(Layout::V3, 1, 2, [13, 13]),
(Layout::V4, 1, 2, [14, 12]),
] {
let units = Units {
layout,
channels: 1,
};
let record = Spec {
one_to_one: false,
width: 12,
order: 0,
mark: false,
first: 0,
count: units.cell(),
};
let mut specs: Vec<Spec> = (0..alignment)
.map(|i| Spec {
one_to_one: true,
width: 3,
mark: i == 0,
..record
})
.chain(std::iter::repeat_n(record, content))
.collect();
pad_to_packet(&mut specs, 0, units).unwrap();
let mut want = vec![3u8; alignment];
want.extend(spent);
want.resize(alignment + content, record.width);
assert_eq!(
specs.iter().map(|s| s.width).collect::<Vec<_>>(),
want,
"{layout:?}"
);
let words: usize = specs.iter().map(|s| s.span(units)).sum();
assert_eq!(words % units.packet_words(), 0, "{layout:?}");
}
}
#[test]
fn a_loop_that_needs_width_past_the_measured_cap_is_refused() {
let units = Units {
layout: Layout::V3,
channels: 1,
};
let record = Spec {
one_to_one: false,
width: widen_cap(Layout::V3),
order: 0,
mark: false,
first: 0,
count: units.cell(),
};
let mut specs = vec![
Spec {
one_to_one: true,
width: 1,
mark: true,
..record
},
record,
record,
];
let before = specs.clone();
assert!(pad_to_packet(&mut specs, 0, units).is_err());
assert_eq!(specs, before);
}
#[test]
fn a_loop_lands_on_a_packet_boundary_or_is_refused() {
let mut source = Vec::with_capacity(60_000);
let mut state = 12_345u64;
for k in 0..60_000u64 {
state = state
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1);
let noise = ((state >> 40) as i32 - 8_192) / 4;
let tone = (20_000.0 * (k as f64 * 0.031).sin()) as i32;
source.push((tone + noise).clamp(-32_768, 32_767) as i16);
}
let mut placed = 0usize;
let mut refused = 0usize;
for start in (4_096..48_000).step_by(7_919) {
for length in [900, 1_500, 4_096, 11_000] {
for predictor in [Predictor::Plain, Predictor::Minimising] {
let points =
Loop::new(start, start + length).crossfade((length / 4).min(start) as f64);
let options = Options::new("Sweep").predictor(predictor).loops(points);
let Ok(file) = instrument(&source, &options) else {
refused += 1;
continue;
};
let (at, stroke) = file.stroke_streams()[0];
let walk = codec::walk(stroke, at, codec::Layout::V2).unwrap();
let mark = walk.records.iter().find(|r| r.mark).unwrap();
assert_eq!(
(walk.terminator - mark.at) % PACKET_WORDS,
0,
"loop {start}..{} under {predictor:?} covers {} words",
start + length,
walk.terminator - mark.at
);
placed += 1;
}
}
}
assert!(placed > 0, "no loop was placed");
assert!(refused > 0, "no loop was refused");
}
fn stereo(hz: f64, ratio: f64, amplitude: f64, frames: usize) -> Vec<i16> {
let left = sine(hz, amplitude, frames);
let right = sine(hz * ratio, amplitude * 0.6, frames);
left.iter()
.zip(&right)
.flat_map(|(&l, &r)| [l, r])
.collect()
}
#[test]
fn a_stereo_plan_is_the_mono_plan_doubled() {
for frames in [4096, 4409, 8192, 10_000, 44_100, 100_000, 441_000] {
let mono = plan(frames, 1).unwrap();
let both = plan(frames, 2).unwrap();
assert_eq!(both.fields, 2 * mono.fields, "{frames} frames: T");
assert_eq!(both.resync_at, 2 * mono.resync_at, "{frames} frames: R1");
assert_eq!(both.warmup, 2 * mono.warmup, "{frames} frames: W");
assert_eq!(both.resync, 2 * mono.resync, "{frames} frames: R");
assert_eq!(both.cells_before, mono.cells_before, "{frames} frames");
assert_eq!(both.cells_after, mono.cells_after, "{frames} frames");
assert_eq!(
both.warmup
+ both.cell() * both.cells_before
+ both.resync
+ both.cell() * both.cells_after,
both.fields,
"{frames} frames: the plan does not tile the lattice"
);
}
}
#[test]
fn a_stereo_stroke_round_trips_through_the_decoder_exactly() {
for predictor in [Predictor::Plain, Predictor::Minimising] {
let source = stereo(220.0, 1.5, 14_000.0, 30_000);
let file = instrument(
&source,
&Options::new("Stereo").channels(2).predictor(predictor),
)
.unwrap();
let (at, stroke) = file.stroke_streams()[0];
let stream = codec::walk(stroke, at, codec::Layout::V2).unwrap();
assert_eq!(stream.channels, 2, "{predictor:?}");
assert_eq!(stream.cell, Some(2 * CELL), "{predictor:?}");
assert_eq!(&stroke[stroke.len() - 3..], &[0x80, 0, 48]);
let plan = plan(30_000, 2).unwrap();
let q = quantise(&source, &plan, None);
let audio = codec::decode(stroke, at, codec::Layout::V2).unwrap();
assert_eq!(audio.channels, 2);
assert_eq!(audio.samples.len(), plan.fields);
let gain = 1i32 << q.shift;
for (f, (&want, &got)) in q.values.iter().zip(&audio.samples).enumerate() {
assert_eq!(i32::from(got), want * gain, "{predictor:?} field {f}");
}
}
}
#[test]
fn each_channel_predicts_against_its_own_history() {
let frames = 20_000;
let source: Vec<i16> = (0..frames)
.flat_map(|k| {
let up = (k as i32 % 2048) - 1024;
[up as i16, -(up as i16)]
})
.collect();
let file = instrument(
&source,
&Options::new("Ramps")
.channels(2)
.predictor(Predictor::Minimising),
)
.unwrap();
let (at, stroke) = file.stroke_streams()[0];
let audio = codec::decode(stroke, at, codec::Layout::V2).unwrap();
assert!(audio.differenced > 0, "nothing chose a predictor");
let plan = plan(frames, 2).unwrap();
let q = quantise(&source, &plan, None);
let gain = 1i32 << q.shift;
for (f, (&want, &got)) in q.values.iter().zip(&audio.samples).enumerate() {
assert_eq!(i32::from(got), want * gain, "field {f}");
}
}
#[test]
fn the_channels_are_resampled_apart() {
let frames = 12_000;
let source: Vec<i16> = sine(300.0, 20_000.0, frames)
.into_iter()
.flat_map(|l| [l, 0])
.collect();
let file = instrument(&source, &Options::new("Panned").channels(2)).unwrap();
let (at, stroke) = file.stroke_streams()[0];
let audio = codec::decode(stroke, at, codec::Layout::V2).unwrap();
assert!(audio.samples.iter().step_by(2).any(|&v| v.abs() > 10_000));
assert!(audio.samples[1..].iter().step_by(2).all(|&v| v == 0));
}
#[test]
fn a_stereo_stroke_loops_the_way_a_mono_one_does() {
let source = stereo(180.0, 1.25, 16_000.0, 60_000);
let points = Loop::new(8_192, 40_960).crossfade(2_048.0);
let file = instrument(&source, &Options::new("Looped").channels(2).loops(points)).unwrap();
let (at, stroke) = file.stroke_streams()[0];
let walk = codec::walk(stroke, at, codec::Layout::V2).unwrap();
assert_eq!(walk.channels, 2);
let mark = walk.records.iter().find(|r| r.mark).unwrap();
assert_eq!((walk.terminator - mark.at) % PACKET_WORDS, 0);
let frames = (walk.fields - mark.first_field) as f64 / 2.0 * f64::from(codec::SOURCE_RATE)
/ f64::from(codec::FIELD_RATE);
assert!(
(frames - 32_768.0).abs() < 1.0,
"loop came back {frames} frames"
);
let plan = looped(60_000, 2, points).unwrap();
let q = quantise(&source, &plan, None);
let audio = codec::decode(stroke, at, codec::Layout::V2).unwrap();
let gain = 1i32 << q.shift;
for (f, (&want, &got)) in q.values.iter().zip(&audio.samples).enumerate() {
assert_eq!(i32::from(got), want * gain, "field {f}");
}
}
#[test]
fn a_channel_count_the_terminator_cannot_state_is_refused() {
let source = vec![0i16; 3 * MIN_FRAMES];
assert!(plan(MIN_FRAMES, 0).is_err());
assert!(plan(MIN_FRAMES, 3).is_err());
assert!(instrument(&source, &Options::new("x").channels(3)).is_err());
assert!(instrument(
&vec![0i16; 2 * MIN_FRAMES + 1],
&Options::new("x").channels(2)
)
.is_err());
assert!(instrument(&vec![0i16; 2 * MIN_FRAMES], &Options::new("x").channels(2)).is_ok());
let short = vec![0i16; MIN_FRAMES];
assert!(instrument(&short, &Options::new("x")).is_ok());
assert!(instrument(&short, &Options::new("x").channels(2)).is_err());
}
#[test]
fn every_generation_round_trips_through_the_decoder_exactly() {
for layout in [Layout::V2, Layout::V3, Layout::V4] {
for channels in [1u16, 2] {
let frames = 30_000;
let source: Vec<i16> = match channels {
1 => sine(220.0, 14_000.0, frames),
_ => stereo(220.0, 1.5, 14_000.0, frames),
};
let file = instrument(
&source,
&Options::new("Round trip")
.layout(layout)
.channels(channels)
.predictor(Predictor::Minimising),
)
.unwrap();
let (at, stroke) = file.stroke_streams()[0];
let plan = Plan::new(
layout,
frames,
usize::from(channels),
default_secondary_start(frames, None),
)
.unwrap();
let q = quantise(&source, &plan, None);
let audio = codec::decode(stroke, at, layout)
.unwrap_or_else(|e| panic!("{layout:?} {channels}ch: {e}"));
assert_eq!(audio.channels, channels, "{layout:?} {channels}ch");
assert_eq!(audio.samples.len(), plan.fields, "{layout:?} {channels}ch");
let gain = 1i32 << q.shift;
for (f, (&want, &got)) in q.values.iter().zip(&audio.samples).enumerate() {
assert_eq!(
i32::from(got),
want * gain,
"{layout:?} {channels}ch field {f}"
);
}
}
}
}
#[test]
fn a_wide_instrument_reads_back_as_one() {
for (layout, version) in [(Layout::V3, 300u32), (Layout::V4, 400)] {
let file = instrument(
&sine(220.0, 15_000.0, 30_000),
&Options::new("Encoded")
.layout(layout)
.root_key(48)
.top_note(72),
)
.unwrap();
let bytes = file.to_bytes().unwrap();
let read = crate::from_stream(&mut std::io::Cursor::new(&bytes)).unwrap();
let crate::Entity::Sample(read) = read else {
panic!("{layout:?} did not read back as a sample");
};
assert_eq!(read.name().unwrap(), "Encoded", "{layout:?}");
assert_eq!(read.layout().unwrap(), layout, "{layout:?}");
assert_eq!(read.to_bytes().unwrap(), bytes, "{layout:?}");
let crate::Sample::V3(read) = &read else {
panic!("{layout:?} did not read back on the wide chain");
};
assert_eq!(read.header.version, version);
let zones = read.zones().unwrap();
assert_eq!(zones.len(), 1);
assert_eq!(zones[0].root_key, 48);
assert_eq!(zones[0].top_note, 72);
assert_eq!(zones[0].low_note, Some(super::super::zone::KEY_FLOOR));
assert_eq!(
read.meta().unwrap().chain_len as usize,
read.chain_len_before_meta()
);
}
}
#[test]
fn a_wide_zone_states_its_own_bottom() {
let high = sine(880.0, 12_000.0, 12_000);
let low = sine(220.0, 12_000.0, 15_000);
let floor = super::super::zone::KEY_FLOOR;
let stored = [(96, 66), (65, floor)];
for layout in [Layout::V3, Layout::V4] {
let file = multi_zone(
made("Two", Predictor::Plain, layout),
&[zone(&high, 72, 96, 2), zone(&low, 48, 65, 1)],
)
.unwrap();
let zones = file.zones().unwrap();
assert_eq!(
zones
.iter()
.map(|z| (z.top_note, z.low_note.unwrap()))
.collect::<Vec<_>>(),
stored,
"{layout:?}"
);
}
}
#[test]
fn a_zone_gain_in_decibels_is_the_word_the_editor_writes() {
for (gain, word) in [
(-1.0, 0x7fc0_0000u32),
(0.0, 0xff80_0000),
(0.01, 0xc220_0000),
(0.1, 0xc1a0_0000),
(0.5, 0xc0c0_a8c1),
(1.0, 0x0000_0000),
(1.1, 0x3f53_ee38),
(1.5, 0x4061_6595),
(2.0, 0x40c0_a8c1),
(4.0, 0x4140_a8c1),
(8.0, 0x4190_7e91),
(16.0, 0x41c0_a8c1),
(20.5, 0x41d1_e170),
(63.75, 0x4210_5bc1),
(333.33, 0x4249_d478),
(1000.0, 0x4270_0000),
] {
assert_eq!(gain_decibels(gain).to_bits(), word, "a gain of {gain}");
}
}
#[test]
fn the_gain_statistic_a_uses_is_the_decibels_round_trip() {
for (gain, delta) in [
(0.01, 0i64),
(1.1, 0),
(15.99, 0),
(16.0, -1),
(20.5, -1),
(24.0, 1),
(33.0, 2),
(48.0, -1),
(63.75, -3),
(100.0, 0),
(333.33, 39),
(1000.0, 0),
] {
let plain = (gain * f64::from(GAIN_UNITY)).round() as i64;
let round_trip = gain_units(gain_decibels(gain)) as i64;
assert_eq!(round_trip - plain, delta, "a gain of {gain}");
}
}
#[test]
fn a_map_gain_is_the_word_the_editor_writes_and_clamps_at_the_ceiling() {
for (gain, units) in [
(-1.0, 0x2d_18_19_u32),
(0.0, 0x00_00_00),
(0.0001, 0x00_00_69),
(0.01, 0x00_28_f6),
(0.5, 0x08_00_00),
(1.0, 0x10_00_00),
(1.1, 0x11_99_9a),
(2.0, 0x20_00_00),
(2.8125, 0x2d_00_00),
(2.828125, 0x2d_18_19),
(4.0, 0x2d_18_19),
(16.0, 0x2d_18_19),
] {
assert_eq!(map_gain_units(gain), units, "a map gain of {gain}");
}
}
#[test]
fn a_zone_gain_past_sixteen_wraps_in_both_stores() {
for (gain, record) in [
(-1.0, 0x00_00_00_u32),
(0.0, 0x00_00_00),
(15.99, 0xff_d7_0a),
(16.0, 0x00_00_00),
(33.0, 0x10_00_00),
(333.33, 0xd5_47_ae),
(1000.0, 0x80_00_00),
] {
assert_eq!(zone_record_gain(gain), record, "a gain of {gain}");
}
for (gain, mantissa) in [
(-1.0, 0x00_00_00_u32),
(0.0, 0x00_00_00),
(15.99, 0x7f_eb_85),
(16.0, 0x7f_ff_ff),
(33.0, 0x08_00_01),
(333.33, 0x6a_a3_ea),
(1000.0, 0x40_00_00),
] {
let (got, _) = statistic_a(4096, 0, gain_units(gain_decibels(gain)));
assert_eq!(got, mantissa, "a gain of {gain}");
}
}
#[test]
fn a_map_gain_moves_the_map_section_alone() {
let source = sine(440.0, 12_000.0, 20_000);
for layout in [Layout::V2, Layout::V3, Layout::V4] {
let unity = made("Map", Predictor::Plain, layout);
let quiet = Instrument {
map_gain: 0.5,
..unity
};
let one = [zone(&source, 60, 127, 1)];
let before = multi_zone(unity, &one).unwrap().to_bytes().unwrap();
let after = multi_zone(quiet, &one).unwrap().to_bytes().unwrap();
assert_eq!(before.len(), after.len(), "{layout:?}");
let moved: Vec<_> = (0..before.len())
.filter(|&i| before[i] != after[i])
.collect();
assert!(moved.len() <= 1 + 4, "{layout:?}: {moved:?}");
}
}
#[test]
fn a_project_preset_reaches_each_generation_in_its_own_schema() {
let source = sine(440.0, 12_000.0, 20_000);
let preset = Preset {
dynamics_enabled: true,
velocity_to_amplitude: 2,
velocity_to_timbre: 0,
};
for layout in [Layout::V2, Layout::V3, Layout::V4] {
let instrument = Instrument {
preset,
..made("Preset", Predictor::Plain, layout)
};
let sample = multi_zone(instrument, &[zone(&source, 60, 127, 1)]).unwrap();
match sample {
crate::Sample::V2(file) => {
let sty = section::find(&file.body.sections, section::STY).unwrap();
assert_eq!(sty.payload, [0, 1, 0, 1, 2, 0, 0, 0, 0]);
}
crate::Sample::V3(file) => {
let sty = section::find4(&file.body.sections, section::STY4).unwrap();
match layout {
Layout::V3 => {
assert_eq!((sty.payload[4], sty.payload[12]), (43, 74));
assert_eq!((sty.payload[14], sty.payload[16]), (1, 74));
}
Layout::V4 => {
assert_eq!((sty.payload[3], sty.payload[4]), (1, 1));
assert_eq!(sty.payload[85..88], [74, 82, 90]);
}
Layout::V2 => unreachable!(),
}
}
}
}
}
#[test]
fn a_wide_zone_gain_lands_in_the_stroke_header() {
let source = sine(440.0, 12_000.0, 20_000);
for layout in [Layout::V3, Layout::V4] {
let one = zone(&source, 60, 127, 1);
let made = made("Gain", Predictor::Plain, layout);
let unity = multi_zone(made, &[one]).unwrap();
let halved = multi_zone(made, &[NewZone { gain: 0.5, ..one }]).unwrap();
let (_, a) = unity.stroke_streams()[0];
let (_, b) = halved.stroke_streams()[0];
let mantissa = |s: &[u8]| u32::from_be_bytes([0, s[9], s[10], s[11]]);
assert_eq!(mantissa(b), mantissa(a) / 2, "{layout:?}");
let gain_at = codec::TAIL_FLOATS_AT[0];
assert_eq!(a[..9], b[..9], "{layout:?}");
assert_eq!(a[12..gain_at], b[12..gain_at], "{layout:?}");
assert_eq!(a[gain_at + 4..], b[gain_at + 4..], "{layout:?}");
assert_eq!(
codec::zone_gain_db(b, layout),
Some(gain_decibels(0.5)),
"{layout:?}"
);
let (before, after) = (unity.to_bytes().unwrap(), halved.to_bytes().unwrap());
let differing = before.iter().zip(&after).filter(|(x, y)| x != y).count();
assert_eq!(before.len(), after.len(), "{layout:?}");
assert!(differing <= 3 + 4 + 4, "{layout:?}: {differing} bytes");
}
}
#[test]
fn a_loop_decay_lands_in_the_wide_header_and_nowhere_narrow() {
let source = sine(440.0, 12_000.0, 20_000);
let at = codec::TAIL_FLOATS_AT[1];
for layout in [Layout::V2, Layout::V3, Layout::V4] {
let one = zone(&source, 60, 127, 1);
let made = made("Decay", Predictor::Plain, layout);
let base = multi_zone(made, &[one]).unwrap();
let slower = multi_zone(
made,
&[NewZone {
loop_decay: 60.0,
..one
}],
)
.unwrap();
let (_, a) = base.stroke_streams()[0];
let (_, b) = slower.stroke_streams()[0];
let wide = layout != Layout::V2;
assert_eq!(
codec::loop_decay(a, layout),
wide.then_some(DEFAULT_LOOP_DECAY),
"{layout:?}"
);
assert_eq!(
codec::loop_decay(b, layout),
wide.then_some(60.0),
"{layout:?}"
);
match wide {
false => assert_eq!(a, b),
true => {
assert_eq!(a[..at], b[..at], "{layout:?}");
assert_eq!(a[at + 4..], b[at + 4..], "{layout:?}");
}
}
}
}
#[test]
fn a_zone_gain_past_the_measured_range_is_refused() {
let source = sine(440.0, 12_000.0, 20_000);
for layout in [Layout::V2, Layout::V3, Layout::V4] {
for gain in [MAX_ZONE_GAIN * 2.0, f64::NAN, f64::INFINITY] {
let loud = NewZone {
gain,
..zone(&source, 60, 127, 1)
};
assert!(
multi_zone(made("Gain", Predictor::Plain, layout), &[loud]).is_err(),
"{layout:?} at {gain}"
);
}
let wrapping = NewZone {
gain: MAX_ZONE_GAIN,
..zone(&source, 60, 127, 1)
};
assert!(multi_zone(made("Gain", Predictor::Plain, layout), &[wrapping]).is_ok());
}
}
#[test]
fn the_wide_plan_tiles_the_lattice_in_its_own_units() {
for frames in [4096, 10_000, 44_100, 100_000] {
for layout in [Layout::V3, Layout::V4] {
let p =
Plan::new(layout, frames, 1, default_secondary_start(frames, None)).unwrap();
assert_eq!(p.cell(), 32, "{layout:?} {frames} frames");
assert_eq!(
p.warmup + p.cell() * p.cells_before + p.resync + p.cell() * p.cells_after,
p.fields,
"{layout:?} {frames} frames"
);
for run in chunks(p.warmup, p.chunk())
.into_iter()
.chain(chunks(p.resync, p.chunk()))
{
assert!(
(32..=48).contains(&run),
"{layout:?} {frames} frames: {run}"
);
}
}
}
}
#[test]
fn a_wide_statistic_b_carries_the_extremes_sign() {
let frames = 20_000;
let mut down = vec![0i16; frames];
down[10_000] = -13;
for layout in [Layout::V2, Layout::V3, Layout::V4] {
let file = instrument(&down, &Options::new("Peak").layout(layout)).unwrap();
let (_, stroke) = file.stroke_streams()[0];
let want = if layout.signed_peak() { -3 } else { 3 };
assert_eq!(codec::peak(stroke, layout), Some(want), "{layout:?}");
}
}
#[test]
fn silence_codes_at_the_draft_width_throughout() {
let file = encoded(&vec![0i16; 44_100], Predictor::Plain);
let (at, stroke) = file.stroke_streams()[0];
let stream = codec::walk(stroke, at, codec::Layout::V2).unwrap();
assert!(stream.records.iter().all(|r| r.width == MIN_WIDTH));
assert!(stream
.records
.iter()
.all(|r| r.values.iter().all(|&v| v == 0)));
assert_eq!(codec::peak(stroke, codec::Layout::V2), Some(0));
assert!(codec::decode(stroke, at, codec::Layout::V2)
.unwrap()
.samples
.iter()
.all(|&s| s == 0));
}
}