diff --git a/src/device/chain/sampler.rs b/src/device/chain/sampler.rs index 953ea912..9363daa2 100644 --- a/src/device/chain/sampler.rs +++ b/src/device/chain/sampler.rs @@ -75,7 +75,7 @@ impl Sampler { let mut mixed = vec![vec![0.0;scope.n_frames() as usize];channel_count]; // Emit next chunk of each currently playing voice, - // removing voices that have completed playing. + // dropping voices that have reached their ends. let mut voices = vec![]; std::mem::swap(&mut voices, &mut self.voices); loop { @@ -93,6 +93,11 @@ impl Sampler { self.voices.push(voice); } } + + // process midi in + // add new voices + // emit new voices starting from midi event frames + // for (i, port) in self.audio_outs.iter_mut().enumerate() { let buffer = &mixed[i]; for (i, value) in port.as_mut_slice(scope).iter_mut().enumerate() { @@ -100,9 +105,6 @@ impl Sampler { } } Control::Continue - // process midi in - // add new voices - // emit new voices starting from midi event frames //for event in self.midi_in.iter(scope) { //let len = 3.min(event.bytes.len()); diff --git a/src/device/launcher/mod.rs b/src/device/launcher/mod.rs index d1c386b2..3465c1c0 100644 --- a/src/device/launcher/mod.rs +++ b/src/device/launcher/mod.rs @@ -184,7 +184,7 @@ pub fn render (state: &Launcher, buf: &mut Buffer, mut area: Rect) -> Usually>; +pub type PhraseData = BTreeMap>; pub struct Phrase { pub name: String, @@ -82,7 +84,7 @@ impl Sequencer { let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?; let transport = client.transport(); let ppq = timebase.ppq() as u32; - DynamicDevice::new(render, handle, Self::process, Self { + DynamicDevice::new(render, handle, process, Self { name: name.into(), midi_in: client.register_port("in", MidiIn::default())?, midi_out: client.register_port("out", MidiOut::default())?, @@ -111,129 +113,6 @@ impl Sequencer { pub fn phrase <'a> (&'a self) -> Option<&'a Phrase> { self.sequences.get(self.sequence) } - - pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { - - // Prepare output buffer - let frames = scope.n_frames() as usize; - let mut output: Vec>>> = vec![None;frames]; - - // Update time - let sequence = self.sequences.get_mut(self.sequence); - if sequence.is_none() { - return Control::Continue - } - let sequence = &mut sequence.unwrap().notes; - let transport = self.transport.query().unwrap(); - // If starting or stopping, send "all notes off": - if transport.state != self.playing { - output[0] = Some(vec![]); - if let Some(Some(frame)) = output.get_mut(0) { - let mut buf = vec![]; - LiveEvent::Midi { - channel: 0.into(), - message: MidiMessage::Controller { controller: 123.into(), value: 0.into() } - }.write(&mut buf).unwrap(); - frame.push(buf); - } - } - self.playing = transport.state; - let pos = &transport.pos; - let usecs = self.timebase.frame_to_usec(pos.frame() as usize); - let steps = usecs / self.timebase.usec_per_step(self.resolution as usize); - let step = steps % self.steps; - let tick = step * self.timebase.ppq() / self.resolution; - - // Read from sequence into output buffer - if self.playing == TransportState::Rolling { - let frame = transport.pos.frame() as usize; - let quant = self.timebase.frames_per_beat() as usize * self.steps / self.resolution; - let ticks = self.timebase.frames_to_ticks(frame, frame + frames, quant); - for (time, tick) in ticks.iter() { - if let Some(events) = sequence.get(&(*tick as u32)) { - for message in events.iter() { - let mut buf = vec![]; - let channel = 0.into(); - let message = *message; - LiveEvent::Midi { channel, message }.write(&mut buf).unwrap(); - let t = *time as usize; - if output[t].is_none() { - output[t] = Some(vec![]); - } - if let Some(Some(frame)) = output.get_mut(t) { - frame.push(buf); - } - } - } - } - } - - // Read from input, write inputs to sequence and/or output buffer - for event in self.midi_in.iter(scope) { - let tick = tick as u32; - let msg = midly::live::LiveEvent::parse(event.bytes).unwrap(); - match msg { - midly::live::LiveEvent::Midi { channel: _, message } => match message { - midly::MidiMessage::NoteOn { key, vel: _ } => { - self.notes_on[key.as_int() as usize] = true; - if self.monitoring { - let t = event.time as usize; - if output[t].is_none() { - output[t] = Some(vec![]); - } - if let Some(Some(frame)) = output.get_mut(t) { - frame.push(event.bytes.into()) - } - } - if self.recording && self.playing == TransportState::Rolling { - let contains = sequence.contains_key(&tick); - if contains { - sequence.get_mut(&tick).unwrap().push(message.clone()); - } else { - sequence.insert(tick, vec![message.clone()]); - } - } - }, - midly::MidiMessage::NoteOff { key, vel: _ } => { - self.notes_on[key.as_int() as usize] = false; - if self.monitoring { - let t = event.time as usize; - if output[t].is_none() { - output[t] = Some(vec![]); - } - if let Some(Some(frame)) = output.get_mut(t) { - frame.push(event.bytes.into()) - } - } - if self.recording && self.playing == TransportState::Rolling { - let contains = sequence.contains_key(&tick); - if contains { - sequence.get_mut(&tick).unwrap().push(message.clone()); - } else { - sequence.insert(tick, vec![message.clone()]); - } - } - }, - _ => {} - }, - _ => {} - } - } - - // Write to port from output buffer - // (containing notes from sequence and/or monitor) - let mut writer = self.midi_out.writer(scope); - for time in 0..scope.n_frames() { - if let Some(Some(frame)) = output.get_mut(time as usize) { - for event in frame.iter() { - writer.write(&::jack::RawMidi { time, bytes: &event }) - .expect(&format!("{event:?}")); - } - } - } - - Control::Continue - } } impl PortList for Sequencer { diff --git a/src/device/sequencer/process.rs b/src/device/sequencer/process.rs new file mode 100644 index 00000000..286c3476 --- /dev/null +++ b/src/device/sequencer/process.rs @@ -0,0 +1,176 @@ +use crate::prelude::*; +use super::*; + +pub fn process (state: &mut Sequencer, _: &Client, scope: &ProcessScope) -> Control { + + // Get currently playing sequence + let sequence = state.sequences.get_mut(state.sequence); + if sequence.is_none() { + return Control::Continue + } + let sequence = sequence.unwrap(); + + // Prepare output buffer and transport + let frames = scope.n_frames() as usize; + let mut output: Vec>>> = vec![None;frames]; + let transport = state.transport.query().unwrap(); + + // If starting or stopping, send "all notes off": + if transport.state != state.playing { + all_notes_off(&mut output); + } + + // Update time + state.playing = transport.state; + let frame = transport.pos.frame() as usize; + let usecs = state.timebase.frame_to_usec(frame); + let steps = usecs / state.timebase.usec_per_step(state.resolution as usize); + let step = steps % state.steps; + let tick = step * state.timebase.ppq() / state.resolution; + + // Play from sequence into output buffer + if state.playing == TransportState::Rolling { + play_from_sequence( + &mut output, + &sequence.notes, + &state.timebase, + state.steps, + state.resolution, + frame, + frames, + ); + } + + // Play from input to monitor, and record into sequence. + monitor_and_record( + &scope, + &mut output, + &mut sequence.notes, + &state.midi_in, + &mut state.notes_on, + state.playing, + state.monitoring, + state.recording, + tick as u32 + ); + + // Write to port from output buffer + // (containing notes from sequence and/or monitor) + let mut writer = state.midi_out.writer(scope); + for time in 0..scope.n_frames() { + if let Some(Some(frame)) = output.get_mut(time as usize) { + for event in frame.iter() { + writer.write(&::jack::RawMidi { time, bytes: &event }) + .expect(&format!("{event:?}")); + } + } + } + + Control::Continue +} + +type MIDIChunk = [Option>>]; + +/// Add "all notes off" to the start of a buffer. +fn all_notes_off (output: &mut MIDIChunk) { + output[0] = Some(vec![]); + if let Some(Some(frame)) = output.get_mut(0) { + let mut buf = vec![]; + let msg = MidiMessage::Controller { controller: 123.into(), value: 0.into() }; + let evt = LiveEvent::Midi { channel: 0.into(), message: msg }; + evt.write(&mut buf).unwrap(); + frame.push(buf); + } +} + +fn play_from_sequence ( + output: &mut MIDIChunk, + sequence: &PhraseData, + timebase: &Arc, + steps: usize, + resolution: usize, + frame: usize, + frames: usize, +) { + let quant = timebase.frames_per_beat() as usize * steps / resolution; + let ticks = timebase.frames_to_ticks(frame, frame + frames, quant); + for (time, tick) in ticks.iter() { + if let Some(events) = sequence.get(&(*tick as u32)) { + for message in events.iter() { + let mut buf = vec![]; + let channel = 0.into(); + let message = *message; + LiveEvent::Midi { channel, message }.write(&mut buf).unwrap(); + let t = *time as usize; + if output[t].is_none() { + output[t] = Some(vec![]); + } + if let Some(Some(frame)) = output.get_mut(t) { + frame.push(buf); + } + } + } + } +} + +fn monitor_and_record ( + scope: &ProcessScope, + output: &mut MIDIChunk, + sequence: &mut PhraseData, + midi_in: &Port, + notes_on: &mut Vec, + playing: TransportState, + monitoring: bool, + recording: bool, + tick: u32, +) { + for event in midi_in.iter(scope) { + let msg = midly::live::LiveEvent::parse(event.bytes).unwrap(); + match msg { + midly::live::LiveEvent::Midi { channel: _, message } => match message { + midly::MidiMessage::NoteOn { key, vel: _ } => { + notes_on[key.as_int() as usize] = true; + if monitoring { + let t = event.time as usize; + if output[t].is_none() { + output[t] = Some(vec![]); + } + if let Some(Some(frame)) = output.get_mut(t) { + frame.push(event.bytes.into()) + } + } + if recording && playing == TransportState::Rolling { + let contains = sequence.contains_key(&tick); + if contains { + sequence.get_mut(&tick).unwrap().push(message.clone()); + } else { + sequence.insert(tick, vec![message.clone()]); + } + } + }, + midly::MidiMessage::NoteOff { key, vel: _ } => { + notes_on[key.as_int() as usize] = false; + if monitoring { + let t = event.time as usize; + if output[t].is_none() { + output[t] = Some(vec![]); + } + if let Some(Some(frame)) = output.get_mut(t) { + frame.push(event.bytes.into()) + } + } + if recording && playing == TransportState::Rolling { + let contains = sequence.contains_key(&tick); + if contains { + sequence.get_mut(&tick).unwrap().push(message.clone()); + } else { + sequence.insert(tick, vec![message.clone()]); + } + } + }, + _ => {} + }, + _ => {} + } + } +}