diff --git a/src/device/launcher/handle.rs b/src/device/launcher/handle.rs index cf5a7f42..e6b20f76 100644 --- a/src/device/launcher/handle.rs +++ b/src/device/launcher/handle.rs @@ -204,23 +204,14 @@ fn play_toggle (s: &mut Launcher) -> Usually { //unimplemented!() //} fn record_toggle (s: &mut Launcher) -> Usually { - s.recording = !s.recording; - for track in s.tracks.iter() { - track.sequencer.state().recording = s.recording; - } + s.sequencer().map(|mut s|s.recording = !s.recording); Ok(true) } fn overdub_toggle (s: &mut Launcher) -> Usually { - s.overdub = !s.overdub; - for track in s.tracks.iter() { - track.sequencer.state().overdub = s.overdub; - } + s.sequencer().map(|mut s|s.overdub = !s.overdub); Ok(true) } fn monitor_toggle (s: &mut Launcher) -> Usually { - s.monitoring = !s.monitoring; - for track in s.tracks.iter() { - track.sequencer.state().monitoring = s.monitoring; - } + s.sequencer().map(|mut s|s.monitoring = !s.monitoring); Ok(true) } diff --git a/src/device/launcher/mod.rs b/src/device/launcher/mod.rs index 6619ad08..0704bff9 100644 --- a/src/device/launcher/mod.rs +++ b/src/device/launcher/mod.rs @@ -57,7 +57,7 @@ impl Launcher { recording: false, overdub: true, cursor: (2, 2), - current_frame: 0, + current_frame: 0, scenes: scenes.unwrap_or_else(||vec![Scene::new(&"Scene 1", &[None])]), tracks: if let Some(tracks) = tracks { tracks } else { vec![ Track::new("Track 1", &timebase, None, Some(vec![ @@ -172,8 +172,8 @@ impl DynamicDevice { } impl PortList for Launcher {} pub fn process (state: &mut Launcher, _: &Client, _: &ProcessScope) -> Control { - let transport = state.transport.query().unwrap(); - state.playing = transport.state; + let transport = state.transport.query().unwrap(); + state.playing = transport.state; state.current_frame = transport.pos.frame() as usize; Control::Continue } @@ -184,9 +184,9 @@ pub fn render (state: &Launcher, buf: &mut Buffer, mut area: Rect) -> Usually Usually> { let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?; let transport = client.transport(); - 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())?, - - timebase: timebase.clone(), - sequence: Some(0), - phrases: phrases.unwrap_or_else(||vec![ - Phrase::new("Phrase0", 4 * timebase.ppq() as usize, None) - ]), + DynamicDevice::new(render, handle, Self::process, Self { + name: name.into(), + timebase: timebase.clone(), + phrases: phrases.unwrap_or_else(||vec![Phrase::default()]), + sequence: Some(0), transport, - playing: TransportState::Starting, + midi_in: client.register_port("in", MidiIn::default())?, monitoring: true, recording: true, + midi_out: client.register_port("out", MidiOut::default())?, + playing: TransportState::Starting, overdub: true, view: SequencerView::Horizontal, @@ -88,6 +84,61 @@ impl Sequencer { pub fn phrase <'a> (&'a self) -> Option<&'a Phrase> { self.phrases.get(self.sequence?) } + + pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { + // Get currently playing phrase + if self.sequence.is_none() { + return Control::Continue + } + let phrase = self.phrases.get_mut(self.sequence.unwrap()); + if phrase.is_none() { + return Control::Continue + } + let phrase = phrase.unwrap(); + // Prepare output buffer and transport + let frame = scope.last_frame_time() as usize;//transport.pos.frame() as usize; + let frames = scope.n_frames() as usize; + let mut output: Vec>>> = vec![None;frames]; + // Check transport self. If starting or stopping, send "all notes off": + let transport = self.transport.query().unwrap(); + if transport.state != self.playing { + all_notes_off(&mut output); + } + self.playing = transport.state; + // Play from phrase into output buffer + if self.playing == TransportState::Rolling { + phrase.process_out( + &mut output, + &mut self.notes_on, + &self.timebase, + frame, + frames + ); + } + // Play from input to monitor, and record into phrase. + phrase.process_in( + self.midi_in.iter(scope), + &mut self.notes_on, + if self.monitoring { Some(&mut output) } else { None }, + self.recording && self.playing == TransportState::Rolling, + &self.timebase, + frame, + ); + // 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 { fn midi_ins (&self) -> Usually> { Ok(vec![self.midi_in.name()?]) } @@ -168,3 +219,14 @@ pub fn contains_note_on (sequence: &Phrase, k: u7, start: usize, end: usize) -> } return false } +/// Add "all notes off" to the start of a buffer. +pub 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); + } +} diff --git a/src/device/sequencer/phrase.rs b/src/device/sequencer/phrase.rs index 51497629..af1e495b 100644 --- a/src/device/sequencer/phrase.rs +++ b/src/device/sequencer/phrase.rs @@ -17,7 +17,7 @@ impl Phrase { Self { name: name.to_string(), length, notes: notes.unwrap_or(BTreeMap::new()) } } /** Write a chunk of MIDI events to an output port. */ - pub fn chunk_out ( + pub fn process_out ( &self, output: &mut MIDIChunk, notes_on: &mut Vec, @@ -56,7 +56,7 @@ impl Phrase { } } /** Read a chunk of MIDI events from an input port. */ - pub fn chunk_in ( + pub fn process_in ( &mut self, input: ::jack::MidiIter, notes_on: &mut Vec, @@ -111,3 +111,9 @@ impl Phrase { } } + +impl Default for Phrase { + fn default () -> Self { + Self::new("", 0, None) + } +} diff --git a/src/device/sequencer/process.rs b/src/device/sequencer/process.rs deleted file mode 100644 index 1b123397..00000000 --- a/src/device/sequencer/process.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::core::*; -use super::*; - -pub fn process (state: &mut Sequencer, _: &Client, scope: &ProcessScope) -> Control { - // Get currently playing phrase - if state.sequence.is_none() { - return Control::Continue - } - let phrase = state.phrases.get_mut(state.sequence.unwrap()); - if phrase.is_none() { - return Control::Continue - } - let phrase = phrase.unwrap(); - // Prepare output buffer and transport - let frame = scope.last_frame_time() as usize;//transport.pos.frame() as usize; - let frames = scope.n_frames() as usize; - let mut output: Vec>>> = vec![None;frames]; - // Check transport state. If starting or stopping, send "all notes off": - let transport = state.transport.query().unwrap(); - if transport.state != state.playing { - all_notes_off(&mut output); - } - state.playing = transport.state; - // Play from phrase into output buffer - if state.playing == TransportState::Rolling { - phrase.chunk_out( - &mut output, - &mut state.notes_on, - &state.timebase, - frame, - frames - ); - } - // Play from input to monitor, and record into phrase. - //let usecs = state.timebase.frames_usecs(frame); - //let steps = usecs / state.timebase.usec_per_step(state.time_zoom as usize); - //let step = steps % state.steps; - //let tick = (step * state.timebase.ppq() / state.time_zoom) as u32; - phrase.chunk_in( - state.midi_in.iter(scope), - &mut state.notes_on, - Some(&mut output), - state.recording && state.playing == TransportState::Rolling, - &state.timebase, - frame, - ); - // 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 -} - - -/// Add "all notes off" to the start of a buffer. -pub 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); - } -}