HexoDSP/src/dsp/tracker/sequencer.rs
2021-08-04 03:58:43 +02:00

567 lines
18 KiB
Rust

// Copyright (c) 2021 Weird Constructor <weirdconstructor@gmail.com>
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
// See README.md and COPYING for details.
use super::MAX_PATTERN_LEN;
use super::MAX_COLS;
use crate::dsp::helpers::SplitMix64;
pub struct PatternSequencer {
rows: usize,
data: Vec<Vec<(f32, u8)>>,
rng: SplitMix64,
rand_vals: [(usize, f64); MAX_COLS],
}
const FRACT_16THS : [f32; 16] = [
1.0 / 16.0,
2.0 / 16.0,
3.0 / 16.0,
4.0 / 16.0,
5.0 / 16.0,
6.0 / 16.0,
7.0 / 16.0,
8.0 / 16.0,
9.0 / 16.0,
10.0 / 16.0,
11.0 / 16.0,
12.0 / 16.0,
13.0 / 16.0,
14.0 / 16.0,
15.0 / 16.0,
1.0
];
impl PatternSequencer {
pub fn new_default_seed(rows: usize) -> Self {
Self {
rows,
data: vec![vec![(0.0, 0); MAX_PATTERN_LEN]; MAX_COLS],
rng: SplitMix64::new(0x91234),
rand_vals: [(99999, 0.0); MAX_COLS],
}
}
pub fn new(rows: usize) -> Self {
use std::time::SystemTime;
let seed =
match SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
{
Ok(n) => n.as_nanos() as i64,
Err(_) => 1_234_567_890,
};
Self {
rows,
data: vec![vec![(0.0, 0); MAX_PATTERN_LEN]; MAX_COLS],
rng: SplitMix64::new_from_i64(seed),
rand_vals: [(99999, 0.0); MAX_COLS],
}
}
pub fn set_rows(&mut self, rows: usize) {
self.rows = rows;
}
pub fn rows(&self) -> usize { self.rows }
pub fn set_col(&mut self, col: usize, col_data: &[(f32, u8)]) {
for (out_cell, in_cell) in
self.data[col]
.iter_mut()
.zip(col_data.iter())
{
*out_cell = *in_cell;
}
}
pub fn col_interpolate_at_phase(
&self, col: usize, phase: &[f32], out: &mut [f32], out_gate: &mut [f32])
{
let col = &self.data[col][..];
let last_row_idx : f32 = (self.rows as f32) - 0.00001;
let rows = self.rows;
for ((phase, out), out_gate) in
phase.iter()
.zip(out.iter_mut())
.zip(out_gate.iter_mut())
{
let row_phase = phase * last_row_idx;
let phase_frac = row_phase.fract();
let line = row_phase.floor() as usize % rows;
let prev_line = if line == 0 { self.rows - 1 } else { line - 1 };
let prev = col[prev_line].0;
let next = col[line].0;
let gate = col[line].1;
// println!("INTERP: {}={:9.7}, {}={:9.7} | {:9.7}",
// prev_line, prev,
// line, next,
// phase_frac);
*out = prev * (1.0 - phase_frac) + next * phase_frac;
*out_gate = gate as f32;
}
}
pub fn col_get_at_phase(
&self, col: usize, phase: &[f32], out: &mut [f32], out_gate: &mut [f32])
{
let col = &self.data[col][..];
let last_row_idx : f32 = (self.rows as f32) - 0.00001;
let rows = self.rows;
for ((phase, out), out_gate) in
phase.iter()
.zip(out.iter_mut())
.zip(out_gate.iter_mut())
{
let row_phase = phase * last_row_idx;
let line = row_phase.floor() as usize % rows;
let (val, gate) = col[line];
*out = val;
*out_gate = gate as f32;
}
}
pub fn col_gate_at_phase(
&mut self, col_idx: usize, phase: &[f32], out: &mut [f32], out_gate: &mut [f32])
{
let col = &self.data[col_idx][..];
let last_row_idx : f32 = (self.rows as f32) - 0.00001;
let rows = self.rows;
for ((phase, out), out_gate) in
phase.iter()
.zip(out.iter_mut())
.zip(out_gate.iter_mut())
{
let row_phase = phase.clamp(0.0, 1.0) * last_row_idx;
let line = row_phase.floor() as usize % rows;
let phase_frac = row_phase.fract();
let gate : u32 = col[line].0.to_bits();
let ggate : u8 = col[line].1;
// pulse_width:
// 0xF - Gate is on for full row
// 0x0 - Gate is on for a very short burst
let pulse_width : f32 = FRACT_16THS[(gate & 0x00F) as usize];
// row_div:
// 0xF - Row has 1 Gate
// 0x0 - Row is divided up into 16 Gates
let row_div : f32 = (16 - ((gate & 0x0F0) >> 4)) as f32;
// probability:
// 0xF - Row is always triggered
// 0x7 - Row fires only in 50% of the cases
// 0x0 - Row fires only in ~6% of the cases
let probability : u8 = ((gate & 0xF00) >> 8) as u8;
let sub_frac = (phase_frac * row_div).fract();
//d// println!(
//d// "row_div={}, pw={}, phase={} / {}",
//d// row_div, pulse_width, sub_frac, phase_frac);
if probability < 0xF {
let rand_val =
if self.rand_vals[col_idx].0 != line {
let new_rand_val = self.rng.next_open01();
self.rand_vals[col_idx] = (line, new_rand_val);
new_rand_val
} else {
self.rand_vals[col_idx].1
};
//d// println!("RANDVAL: {:?} | {:9.7}", self.rand_vals[col_idx], FRACT_16THS[probability as usize]);
if rand_val > (FRACT_16THS[probability as usize] as f64) {
*out = 0.0;
if (gate & 0xF000) > 0 {
*out_gate = 0.0;
} else {
*out_gate = ggate as f32;
}
continue;
}
}
if (gate & 0xF000) > 0 {
*out = 0.0;
*out_gate = 0.0;
} else {
*out = if sub_frac <= pulse_width { 1.0 } else { 0.0 };
*out_gate = ggate as f32;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! assert_float_eq {
($a:expr, $b:expr) => {
if ($a - $b).abs() > 0.0001 {
panic!(r#"assertion failed: `(left == right)`
left: `{:?}`,
right: `{:?}`"#, $a, $b)
}
}
}
#[test]
fn check_seq_interpolate_1() {
let mut ps = PatternSequencer::new(2);
ps.set_col(0, &[(0.0, 0), (1.0, 1)]);
let mut out = [0.0; 6];
let mut out_gate = [0.0; 6];
ps.col_interpolate_at_phase(
0, &[0.0, 0.1, 0.50, 0.51, 0.9, 0.99999],
&mut out[..], &mut out_gate[..]);
assert_float_eq!(out[0], 1.0);
assert_float_eq!(out[1], 0.8);
assert_float_eq!(out[2], 0.0);
assert_float_eq!(out[3], 0.02);
assert_float_eq!(out[4], 0.8);
assert_float_eq!(out[5], 0.99999);
assert_float_eq!(out_gate[0], 0.0);
assert_float_eq!(out_gate[1], 0.0);
assert_float_eq!(out_gate[2], 0.0);
assert_float_eq!(out_gate[3], 1.0);
assert_float_eq!(out_gate[4], 1.0);
assert_float_eq!(out_gate[5], 1.0);
}
#[test]
fn check_seq_interpolate_buffer_end() {
let mut ps = PatternSequencer::new(256);
ps.set_col(0, &[(f32::from_bits(0xF000), 0); 256]);
let mut out = [0.0; 1];
let mut out_gate = [0.0; 1];
ps.col_gate_at_phase(
0, &[0.9999999999], &mut out[..], &mut out_gate[..]);
assert_float_eq!(out[0], 0.0);
assert_float_eq!(out_gate[0], 0.0);
let mut ps = PatternSequencer::new(256);
ps.set_col(0, &[(0.0, 0); 256]);
ps.col_get_at_phase(
0, &[0.9999999999], &mut out[..], &mut out_gate[..]);
assert_float_eq!(out[0], 0.0);
assert_float_eq!(out_gate[0], 0.0);
ps.col_interpolate_at_phase(
0, &[0.9999999999], &mut out[..], &mut out_gate[..]);
assert_float_eq!(out[0], 0.0);
assert_float_eq!(out_gate[0], 0.0);
}
#[test]
fn check_seq_step_1() {
let mut ps = PatternSequencer::new(2);
ps.set_col(0, &[(0.0, 0), (1.0, 1)]);
let mut out = [0.0; 3];
let mut out_gate = [0.0; 3];
ps.col_get_at_phase(
0, &[0.1, 0.51, 0.9], &mut out[..], &mut out_gate[..]);
assert_float_eq!(out[0], 0.0);
assert_float_eq!(out[1], 1.0);
assert_float_eq!(out[2], 1.0);
assert_float_eq!(out_gate[0], 0.0);
assert_float_eq!(out_gate[1], 1.0);
assert_float_eq!(out_gate[2], 1.0);
}
#[test]
fn check_seq_step_2() {
let mut ps = PatternSequencer::new(3);
ps.set_col(0, &[(0.0, 0), (0.3, 1), (1.0, 1)]);
let mut out = [0.0; 6];
let mut out_gate = [0.0; 6];
ps.col_get_at_phase(
0, &[0.1, 0.5, 0.51, 0.6, 0.9, 0.99],
&mut out[..], &mut out_gate[..]);
assert_float_eq!(out[0], 0.0);
assert_float_eq!(out[1], 0.3);
assert_float_eq!(out[2], 0.3);
assert_float_eq!(out[3], 0.3);
assert_float_eq!(out[4], 1.0);
assert_float_eq!(out[5], 1.0);
assert_float_eq!(out_gate[0], 0.0);
assert_float_eq!(out_gate[1], 1.0);
assert_float_eq!(out_gate[2], 1.0);
assert_float_eq!(out_gate[3], 1.0);
assert_float_eq!(out_gate[4], 1.0);
assert_float_eq!(out_gate[5], 1.0);
}
#[test]
fn check_seq_gate_1() {
let mut ps = PatternSequencer::new(2);
ps.set_col(0, &[
(f32::from_bits(0x0FFF), 1),
(f32::from_bits(0xF000), 0),
]);
let mut out = [0.0; 6];
let mut out_gate = [0.0; 6];
ps.col_gate_at_phase(
0, &[0.1, 0.5, 0.5001, 0.6, 0.9, 0.99],
&mut out[..], &mut out_gate[..]);
//d// println!("out: {:?}", out);
assert_float_eq!(out[0], 1.0);
assert_float_eq!(out[1], 1.0);
assert_float_eq!(out[2], 0.0);
assert_float_eq!(out[3], 0.0);
assert_float_eq!(out[4], 0.0);
assert_float_eq!(out[5], 0.0);
assert_float_eq!(out_gate[0], 1.0);
assert_float_eq!(out_gate[1], 1.0);
assert_float_eq!(out_gate[2], 0.0);
assert_float_eq!(out_gate[3], 0.0);
assert_float_eq!(out_gate[4], 0.0);
assert_float_eq!(out_gate[5], 0.0);
}
fn count_high(slice: &[f32]) -> usize {
let mut sum = 0;
for p in slice.iter() {
if *p > 0.5 { sum += 1; }
}
sum
}
fn count_up(slice: &[f32]) -> usize {
let mut sum = 0;
let mut cur = 0.0;
for p in slice.iter() {
if cur < 0.1 && *p > 0.5 {
sum += 1;
}
cur = *p;
}
sum
}
#[test]
fn check_seq_gate_2() {
let mut ps = PatternSequencer::new(3);
ps.set_col(0, &[
(f32::from_bits(0x0FF0), 1),
(f32::from_bits(0x0FF7), 0),
(f32::from_bits(0x0FFF), 1),
]);
let mut phase = vec![0.0; 96];
let inc = 1.0 / (96.0 - 1.0);
let mut phase_run = 0.0;
for p in phase.iter_mut() {
*p = phase_run;
phase_run += inc;
}
//d// println!("PHASE: {:?}", phase);
let mut out = [0.0; 96];
let mut out_gate = [0.0; 96];
ps.col_gate_at_phase(
0, &phase[..], &mut out[..], &mut out_gate[..]);
//d// println!("out: {:?}", &out[0..32]);
assert_eq!(count_high(&out[0..32]), 2);
assert_eq!(count_high(&out[32..64]), 16);
assert_eq!(count_high(&out[64..96]), 32);
assert_eq!(count_high(&out_gate[0..32]), 32);
assert_eq!(count_high(&out_gate[32..64]), 0);
assert_eq!(count_high(&out_gate[64..96]), 32);
assert_eq!(count_up(&out[0..32]), 1);
assert_eq!(count_up(&out[32..64]), 1);
assert_eq!(count_up(&out[64..96]), 1);
assert_eq!(count_up(&out_gate[0..32]), 1);
assert_eq!(count_up(&out_gate[32..64]), 0);
assert_eq!(count_up(&out_gate[64..96]), 1);
}
#[test]
fn check_seq_gate_div_1() {
let mut ps = PatternSequencer::new(3);
ps.set_col(0, &[
(f32::from_bits(0x0F80), 1),
(f32::from_bits(0x0F87), 0),
(f32::from_bits(0x0F8F), 1),
]);
let mut phase = vec![0.0; 3 * 64];
let inc = 1.0 / ((3.0 * 64.0) - 1.0);
let mut phase_run = 0.0;
for p in phase.iter_mut() {
*p = phase_run;
phase_run += inc;
}
//d// println!("PHASE: {:?}", phase);
let mut out = [0.0; 3 * 64];
let mut out_gate = [0.0; 3 * 64];
ps.col_gate_at_phase(
0, &phase[..], &mut out[..], &mut out_gate[..]);
assert_eq!(count_high(&out[0..64]), 8);
assert_eq!(count_up( &out[0..64]), 8);
assert_eq!(count_high(&out_gate[0..64]), 64);
assert_eq!(count_up( &out_gate[0..64]), 1);
assert_eq!(count_high(&out[64..128]), 32);
assert_eq!(count_up( &out[64..128]), 8);
assert_eq!(count_high(&out_gate[64..128]), 0);
assert_eq!(count_up( &out_gate[64..128]), 0);
assert_eq!(count_high(&out[128..192]), 64);
assert_eq!(count_up( &out[128..192]), 1);
assert_eq!(count_high(&out_gate[128..192]), 64);
assert_eq!(count_up( &out_gate[128..192]), 1);
}
#[test]
fn check_seq_gate_div_2() {
let mut ps = PatternSequencer::new(3);
ps.set_col(0, &[
(f32::from_bits(0x0F00), 1),
(f32::from_bits(0x0F07), 0),
(f32::from_bits(0x0F0F), 1),
]);
let mut phase = vec![0.0; 6 * 64];
let inc = 1.0 / ((6.0 * 64.0) - 1.0);
let mut phase_run = 0.0;
for p in phase.iter_mut() {
*p = phase_run;
phase_run += inc;
}
//d// println!("PHASE: {:?}", phase);
let mut out = [0.0; 6 * 64];
let mut out_gate = [0.0; 6 * 64];
ps.col_gate_at_phase(0, &phase[..], &mut out[..], &mut out_gate[..]);
assert_eq!(count_high(&out[0..128]), 16);
assert_eq!(count_up( &out[0..128]), 16);
assert_eq!(count_high(&out_gate[0..128]), 128);
assert_eq!(count_up( &out_gate[0..128]), 1);
assert_eq!(count_high(&out[128..256]), 64);
assert_eq!(count_up( &out[128..256]), 16);
assert_eq!(count_high(&out_gate[128..256]), 0);
assert_eq!(count_up( &out_gate[128..256]), 0);
assert_eq!(count_high(&out[256..384]), 128);
assert_eq!(count_up( &out[256..384]), 1);
assert_eq!(count_high(&out_gate[256..384]), 128);
assert_eq!(count_up( &out_gate[256..384]), 1);
}
#[test]
fn check_seq_gate_div_3() {
let mut ps = PatternSequencer::new(3);
ps.set_col(0, &[
(f32::from_bits(0x0FE0), 1),
(f32::from_bits(0x0FE7), 0),
(f32::from_bits(0x0FEF), 1),
]);
let mut phase = vec![0.0; 6 * 64];
let inc = 1.0 / ((6.0 * 64.0) - 1.0);
let mut phase_run = 0.0;
for p in phase.iter_mut() {
*p = phase_run;
phase_run += inc;
}
//d// println!("PHASE: {:?}", phase);
let mut out = [0.0; 6 * 64];
let mut out_gate = [0.0; 6 * 64];
ps.col_gate_at_phase(0, &phase[..], &mut out[..], &mut out_gate[..]);
assert_eq!(count_high(&out[0..128]), 8);
assert_eq!(count_up( &out[0..128]), 2);
assert_eq!(count_high(&out_gate[0..128]), 128);
assert_eq!(count_up( &out_gate[0..128]), 1);
assert_eq!(count_high(&out[128..256]), 64);
assert_eq!(count_up( &out[128..256]), 2);
assert_eq!(count_high(&out_gate[128..256]), 0);
assert_eq!(count_up( &out_gate[128..256]), 0);
assert_eq!(count_high(&out[256..384]), 128);
assert_eq!(count_up( &out[256..384]), 1);
assert_eq!(count_high(&out_gate[256..384]), 128);
assert_eq!(count_up( &out_gate[256..384]), 1);
}
fn run_probability_test_for(prob: u32) -> (usize, usize) {
let rows = 100;
let mut ps = PatternSequencer::new_default_seed(rows);
let mut coldata = vec![(0.0, 0); rows];
for i in 0..coldata.len() {
coldata[i] = (f32::from_bits(0x00FF | prob), 1);
}
ps.set_col(0, &coldata[..]);
let samples = rows;
let mut phase = vec![0.0; samples];
let inc = 1.0 / ((samples as f32) - 1.0);
let mut phase_run = 0.0;
for p in phase.iter_mut() {
*p = phase_run;
phase_run += inc;
}
let mut out = vec![0.0; samples];
let mut out_gate = vec![0.0; samples];
ps.col_gate_at_phase(0, &phase[..], &mut out[..], &mut out_gate[..]);
(count_high(&out[..]), count_up(&out[..]))
}
#[test]
fn check_seq_gate_div_rng() {
// XXX: The result numbers are highly dependent on the
// sampling rate inside run_probability_test_for().
assert_eq!(run_probability_test_for(0x000), (5, 5));
assert_eq!(run_probability_test_for(0x100), (12, 11));
assert_eq!(run_probability_test_for(0x200), (20, 18));
assert_eq!(run_probability_test_for(0x300), (26, 23));
assert_eq!(run_probability_test_for(0x400), (32, 26));
assert_eq!(run_probability_test_for(0x500), (38, 29));
assert_eq!(run_probability_test_for(0x600), (47, 29));
assert_eq!(run_probability_test_for(0x700), (56, 26));
assert_eq!(run_probability_test_for(0x800), (60, 25));
assert_eq!(run_probability_test_for(0x900), (66, 24));
assert_eq!(run_probability_test_for(0xA00), (70, 22));
assert_eq!(run_probability_test_for(0xB00), (79, 18));
assert_eq!(run_probability_test_for(0xC00), (84, 13));
assert_eq!(run_probability_test_for(0xD00), (93, 7));
assert_eq!(run_probability_test_for(0xE00), (96, 5));
assert_eq!(run_probability_test_for(0xF00), (100, 1));
}
}