use crate::engine::{Automaton, NoAction, Range, Time};
use rill_core::traits::ParamValue;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct Step {
pub duration: f64,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PlayMode {
OneShot,
Loop,
PingPong,
Random,
Brownian,
}
#[derive(Debug, Clone)]
pub struct SequencerAutomaton {
name: String,
steps: Vec<Step>,
mode: PlayMode,
tempo: f64,
duration_scale: f64,
interpolate: bool,
range: Range,
}
impl SequencerAutomaton {
pub fn new(name: &str, steps: Vec<Step>) -> Self {
Self {
name: name.to_string(),
steps,
mode: PlayMode::Loop,
tempo: 120.0,
duration_scale: 1.0,
interpolate: false,
range: Range::unipolar(),
}
}
pub fn with_mode(mut self, mode: PlayMode) -> Self {
self.mode = mode;
self
}
pub fn with_tempo(mut self, bpm: f64) -> Self {
self.tempo = bpm.max(1.0);
self
}
pub fn with_interpolation(mut self, interpolate: bool) -> Self {
self.interpolate = interpolate;
self
}
pub fn with_range(mut self, range: Range) -> Self {
self.range = range;
self
}
fn step_duration(&self, step: &Step) -> f64 {
step.duration * 60.0 / self.tempo * self.duration_scale
}
fn xorshift(&self, rng: &mut u64) -> u64 {
let mut x = *rng;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
*rng = x;
x
}
fn random_index(&self, rng: &mut u64) -> usize {
let x = self.xorshift(rng);
(x as usize) % self.steps.len()
}
fn next_step(&self, current_step: usize, direction: i8, rng_state: &mut u64) -> (usize, i8) {
match self.mode {
PlayMode::OneShot => {
if current_step < self.steps.len() - 1 {
(current_step + 1, direction)
} else {
(current_step, direction)
}
}
PlayMode::Loop => ((current_step + 1) % self.steps.len(), direction),
PlayMode::PingPong => {
let next = current_step as i32 + direction as i32;
if next < 0 {
(1, 1)
} else if next >= self.steps.len() as i32 {
(self.steps.len() - 2, -1)
} else {
(next as usize, direction)
}
}
PlayMode::Random => (self.random_index(rng_state), direction),
PlayMode::Brownian => {
let mut candidates = vec![current_step];
if current_step > 0 {
candidates.push(current_step - 1);
}
if current_step < self.steps.len() - 1 {
candidates.push(current_step + 1);
}
let idx = self.random_index(rng_state) % candidates.len();
(candidates[idx], direction)
}
}
}
}
impl Automaton for SequencerAutomaton {
type Internal = (usize, f64, i8, u64);
type Action = NoAction;
fn step(
&self,
internal: &mut Self::Internal,
_current: &ParamValue,
time: Time,
_action: &Self::Action,
) -> ParamValue {
let (current_step, step_start_time, direction, mut rng_state) = *internal;
if self.steps.is_empty() {
return ParamValue::Int(0);
}
let current_step_data = &self.steps[current_step];
let step_dur = self.step_duration(current_step_data);
let elapsed = time - step_start_time;
if elapsed >= step_dur {
let (next, new_dir) = self.next_step(current_step, direction, &mut rng_state);
*internal = (next, time, new_dir, rng_state);
ParamValue::Int(next as i32)
} else {
ParamValue::Int(current_step as i32)
}
}
fn initial_internal(&self) -> Self::Internal {
(0, 0.0, 1, 123456789)
}
fn name(&self) -> &str {
&self.name
}
}
pub fn simple_sequence(count: usize, duration: f64) -> Vec<Step> {
(0..count).map(|_| Step { duration }).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sequencer() {
let steps = simple_sequence(4, 1.0);
let seq = SequencerAutomaton::new("Test", steps);
let mut internal = seq.initial_internal();
let current = ParamValue::Float(0.0);
assert_eq!(internal.0, 0);
let _value = seq.step(&mut internal, ¤t, 0.6, &NoAction);
assert_eq!(internal.0, 1);
}
}