// Copyright (c) 2021 Weird Constructor // This is a part of HexoDSP. Released under (A)GPLv3 or any later. // See README.md and COPYING for details. use crate::nodes::NodeAudioContext; use crate::dsp::helpers::TriggerClock; use crate::dsp::{SAtom, ProcBuf, DspNode, LedPhaseVals}; use crate::dsp::tracker::TrackerBackend; use crate::dsp::MAX_BLOCK_SIZE; /// A tracker based sequencer #[derive(Debug)] pub struct TSeq { backend: Option>, clock: TriggerClock, srate: f64, } impl Clone for TSeq { fn clone(&self) -> Self { Self::new() } } impl TSeq { pub fn new() -> Self { Self { backend: None, srate: 48000.0, clock: TriggerClock::new(), } } pub fn set_backend(&mut self, backend: TrackerBackend) { self.backend = Some(Box::new(backend)); } pub const clock : &'static str = "TSeq clock\nClock input\nRange: (0..1)\n"; pub const cmode : &'static str = "TSeq cmode\n'clock' input signal mode:\n\ - RowT: Trigger = advance row\n\ - PatT: Trigger = pattern rate\n\ - Phase: Phase to pattern index\n\ \n"; pub const trk1 : &'static str = "TSeq trk1\nTrack 1 signal output\nRange: (-1..1)\n"; pub const trk2 : &'static str = "TSeq trk2\nTrack 2 signal output\nRange: (-1..1)\n"; pub const trk3 : &'static str = "TSeq trk3\nTrack 3 signal output\nRange: (-1..1)\n"; pub const trk4 : &'static str = "TSeq trk4\nTrack 4 signal output\nRange: (-1..1)\n"; pub const trk5 : &'static str = "TSeq trk5\nTrack 5 signal output\nRange: (-1..1)\n"; pub const trk6 : &'static str = "TSeq trk6\nTrack 6 signal output\nRange: (-1..1)\n"; } impl DspNode for TSeq { fn outputs() -> usize { 1 } fn set_sample_rate(&mut self, srate: f32) { self.srate = srate as f64; } fn reset(&mut self) { self.backend = None; self.clock.reset(); } #[inline] fn process( &mut self, ctx: &mut T, atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { use crate::dsp::{out, inp, at}; let clock = inp::TSeq::clock(inputs); let cmode = at::TSeq::cmode(atoms); let backend = if let Some(backend) = &mut self.backend { backend } else { return; }; backend.check_updates(); let mut phase_out : [f32; MAX_BLOCK_SIZE] = [0.0; MAX_BLOCK_SIZE]; let cmode = cmode.i(); for frame in 0..ctx.nframes() { let mut clock_phase = if cmode < 2 { self.clock.next_phase(clock.read(frame)) } else { clock.read(frame).abs() as f64 }; let phase = match cmode { // RowT 0 => { let plen = backend.pattern_len() as f64; while clock_phase >= plen { clock_phase -= plen; } clock_phase / plen }, // 1 | 2 PatT, Phase _ => { clock_phase = clock_phase.fract(); clock_phase }, }; phase_out[frame] = phase as f32; } // println!("PHASE {}", phase_out[0]); let mut col_out : [f32; MAX_BLOCK_SIZE] = [0.0; MAX_BLOCK_SIZE]; let col_out_slice = &mut col_out[0..ctx.nframes()]; let phase_out_slice = &phase_out[0..ctx.nframes()]; let out_t1 = out::TSeq::trk1(outputs); backend.get_col_at_phase( 0, phase_out_slice, col_out_slice); out_t1.write_from(col_out_slice); ctx_vals[0].set(col_out_slice[col_out_slice.len() - 1]); let out_t2 = out::TSeq::trk2(outputs); backend.get_col_at_phase( 1, phase_out_slice, col_out_slice); out_t2.write_from(col_out_slice); let out_t3 = out::TSeq::trk3(outputs); backend.get_col_at_phase( 2, phase_out_slice, col_out_slice); out_t3.write_from(col_out_slice); let out_t4 = out::TSeq::trk4(outputs); backend.get_col_at_phase( 3, phase_out_slice, col_out_slice); out_t4.write_from(col_out_slice); let out_t5 = out::TSeq::trk5(outputs); backend.get_col_at_phase( 4, phase_out_slice, col_out_slice); out_t5.write_from(col_out_slice); let out_t6 = out::TSeq::trk6(outputs); backend.get_col_at_phase( 5, phase_out_slice, col_out_slice); out_t6.write_from(col_out_slice); ctx_vals[1].set(phase_out_slice[phase_out_slice.len() - 1]); } }