wip: assigning steps to frames

This commit is contained in:
🪞👃🪞 2024-06-03 17:53:10 +03:00
parent 7c1dc9ce9b
commit faac61180b
6 changed files with 389 additions and 13 deletions

View file

@ -72,7 +72,7 @@ fn run_one (command: &cli::Command) -> Result<(), Box<dyn Error>> {
),
cli::Command::Sequencer => engine.run(
&mut sequencer::Sequencer::new(engine.jack_client.as_client())?,
&mut sequencer::Sequencer::new()?,
|state, stdout, mut offset| {
let (w, h) = render::render_toolbar_vertical(stdout, offset, &sequencer::ACTIONS)?;
offset.0 = offset.0 + w + 2;
@ -122,7 +122,7 @@ fn run_all () -> Result<(), Box<dyn Error>> {
exited: false,
mode: Mode::Sequencer,
transport: transport::Transport::new(engine.jack_client.as_client())?,
sequencer: sequencer::Sequencer::new(engine.jack_client.as_client())?,
sequencer: sequencer::Sequencer::new()?,
mixer: mixer::Mixer::new()?,
looper: looper::Looper::new()?,
sampler: sampler::Sampler::new()?,

View file

@ -51,9 +51,10 @@ pub fn handle (state: &mut Sequencer, event: &Event) -> Result<(), Box<dyn Error
let row = state.cursor.0 as usize;
let step = state.cursor.1 as usize;
let duration = state.cursor.2 as usize;
state.sequence[row][step] = Some(super::Event::NoteOn(35, 128));
let mut sequence = state.sequence.lock().unwrap();
sequence[row][step] = Some(super::Event::NoteOn(35, 128));
if state.cursor.2 > 0 {
state.sequence[row][step + duration] = Some(super::Event::NoteOff(35));
sequence[row][step + duration] = Some(super::Event::NoteOff(35));
}
},
_ => {

View file

@ -14,12 +14,13 @@ pub const ACTIONS: [(&'static str, &'static str);4] = [
];
pub struct Sequencer {
exited: bool,
exited: Arc<AtomicBool>,
cursor: (u16, u16, u16),
sequence: Vec<Vec<Option<Event>>>,
sequence: Arc<Mutex<Vec<Vec<Option<Event>>>>>,
transport: ::jack::Transport,
bpm: f64,
timesig: (f32, f32),
pub jack_client: Jack<self::jack::Notifications>,
}
#[derive(Clone)]
@ -29,24 +30,179 @@ pub enum Event {
}
impl Sequencer {
pub fn new (client: &Client) -> Result<Self, Box<dyn Error>> {
pub fn new () -> Result<Self, Box<dyn Error>> {
let exited = Arc::new(AtomicBool::new(false));
let sequence: Arc<Mutex<Vec<Vec<Option<Event>>>>> = Arc::new(Mutex::new(vec![vec![None;64];4]));
let (client, _status) = Client::new(
"blinkenlive-sequencer",
ClientOptions::NO_START_SERVER
)?;
let transport = client.transport();
let mut port = client.register_port("sequence", ::jack::MidiOut::default())?;
let beats = 4;
let steps = 16;
let bpm = 120;
let rate = 44100; // Hz
let frame = 1f64 / rate as f64; // msec
let buf = 512; // frames
let t_beat = 60.0 / bpm as f64; // msec
let t_loop = t_beat * beats as f64; // msec
let t_step = t_beat / steps as f64; // msec
let mut step_frames = vec![];
for step in 0..beats*steps {
let step_index = (step as f64 * t_step / frame) as usize;
step_frames.push(step_index);
}
let loop_frames = (t_loop*rate as f64) as usize;
let mut frame_steps: Vec<Option<usize>> = vec![None;loop_frames];
for (index, frame) in step_frames.iter().enumerate() {
frame_steps[*frame] = Some(index);
}
let handler: BoxedProcessHandler = Box::new({
let transport = client.transport();
let exited = exited.clone();
let sequence = sequence.clone();
move |client: &Client, scope: &ProcessScope| -> Control {
if exited.fetch_and(true, Ordering::Relaxed) {
return Control::Quit
}
if transport.query_state().unwrap() == TransportState::Rolling {
let chunk_start = scope.last_frame_time();
let chunk_size = scope.n_frames();
let chunk_end = chunk_start + chunk_size;
let start_looped = chunk_start as usize % loop_frames;
let end_looped = chunk_end as usize % loop_frames;
let mut writer = port.writer(scope);
let sequence = sequence.lock().unwrap();
for frame in 0..chunk_size {
let value = frame_steps[(start_looped + frame as usize) % loop_frames];
if let Some(step) = value {
for track in sequence.iter() {
for event in track[step].iter() {
writer.write(&::jack::RawMidi {
time: frame as u32,
bytes: &match event {
Event::NoteOn(pitch, velocity) => [
0b10010000,
*pitch,
*velocity
],
Event::NoteOff(pitch) => [
0b10000000,
*pitch,
0b00000000
],
}
}).unwrap()
}
}
}
}
}
Control::Continue
}
});
Ok(Self {
exited: false,
exited,
cursor: (0, 0, 0),
transport,
sequence: vec![vec![None;64];4],
sequence,
bpm: 120.0,
timesig: (4.0, 4.0)
timesig: (4.0, 4.0),
jack_client: client.activate_async(
self::jack::Notifications,
ClosureProcessHandler::new(handler)
)?
})
}
}
impl Exitable for Sequencer {
fn exit (&mut self) {
self.exited = true
self.exited.store(true, Ordering::Relaxed)
}
fn exited (&self) -> bool {
self.exited
self.exited.fetch_and(true, Ordering::Relaxed)
}
}
#[cfg(test)]
mod test {
#[test]
fn test_midi_frames () {
let beats = 4;
let steps = 16;
let bpm = 120;
let rate = 44100; // Hz
let frame = 1f64 / rate as f64; // msec
let buf = 512; // frames
let t_beat = 60.0 / bpm as f64; // msec
let t_loop = t_beat * beats as f64; // msec
let t_step = t_beat / steps as f64; // msec
let assign = |chunk: usize| {
let start = chunk * buf; // frames
let end = (chunk + 1) * buf; // frames
println!("{chunk}: {start} .. {end}");
let mut steps: Vec<(usize, usize, f64)> = vec![];
for frame_index in start..end {
let frame_msec = frame_index as f64 * frame;
let offset = (frame_msec * 1000.0) % (t_step * 1000.0);
if offset < 0.1 {
let time = frame_index - start;
let step_index = (frame_msec % t_loop / t_step) as usize;
println!("{chunk}: {frame_index} ({time}) -> {step_index} ({frame_msec} % {t_step} = {offset})");
steps.push((time, step_index, offset));
}
}
steps
};
for chunk in 0..10 {
let chunk = assign(chunk);
//println!("{chunk} {:#?}", assign(chunk));
}
}
#[test]
fn test_midi_frames_2 () {
let beats = 4;
let steps = 16;
let bpm = 120;
let rate = 44100; // Hz
let frame = 1f64 / rate as f64; // msec
let buf = 512; // frames
let t_beat = 60.0 / bpm as f64; // msec
let t_loop = t_beat * beats as f64; // msec
let t_step = t_beat / steps as f64; // msec
let mut step_frames = vec![];
for step in 0..beats*steps {
let step_index = (step as f64 * t_step / frame) as usize;
step_frames.push(step_index);
}
let loop_frames = (t_loop*rate as f64) as usize;
let mut frame_steps: Vec<Option<usize>> = vec![None;loop_frames];
for (index, frame) in step_frames.iter().enumerate() {
println!("{index} {frame}");
frame_steps[*frame] = Some(index);
}
let assign = |chunk: usize| {
let (start, end) = (chunk * buf, (chunk + 1) * buf); // frames
let (start_looped, end_looped) = (start % loop_frames, end % loop_frames);
println!("{chunk}: {start} .. {end} ({start_looped} .. {end_looped})");
let mut steps: Vec<Option<usize>> = vec![None;buf];
for frame in 0..buf {
let value = frame_steps[(start_looped + frame) as usize % loop_frames];
if value.is_some() { println!("{frame:03} = {value:?}, ") };
steps[frame as usize] = value;
}
steps
};
for chunk in 0..1000 {
let chunk = assign(chunk);
//println!("{chunk} {:#?}", assign(chunk));
}
}
}

View file

@ -41,7 +41,8 @@ fn render_grid (
.queue(move_to(17, 3))?.queue(Print("1.2"))?
.queue(move_to(33, 3))?.queue(Print("1.3"))?
.queue(move_to(49, 3))?.queue(Print("1.4"))?;
for (index, row) in state.sequence.iter().enumerate() {
let sequence = state.sequence.lock().unwrap();
for (index, row) in sequence.iter().enumerate() {
let y = index as u16 + 4;
for x in 0u16..64 {
let bg = if x as u32 == (beat - 1) * 16 + (beat_sub * 16.0) as u32 {