From 5fab1af13893babb81c321a43226d72926b03a9e Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 10 May 2025 19:08:22 +0300 Subject: [PATCH] MidiPlayer -> Sequencer; connect sequencer to sampler in groovebox mode --- crates/app/src/api.rs | 4 +- crates/app/src/audio.rs | 2 +- crates/app/src/model.rs | 16 ++-- crates/app/src/model/scene.rs | 2 +- crates/app/src/model/track.rs | 104 ++++++++++++++--------- crates/app/src/view.rs | 10 +-- crates/cli/tek.rs | 13 ++- crates/device/src/device.rs | 2 +- crates/device/src/sequencer.rs | 4 +- crates/device/src/sequencer/seq_model.rs | 44 +++++----- 10 files changed, 120 insertions(+), 81 deletions(-) diff --git a/crates/app/src/api.rs b/crates/app/src/api.rs index 39d7f317..4e4ae4b0 100644 --- a/crates/app/src/api.rs +++ b/crates/app/src/api.rs @@ -469,7 +469,7 @@ impl<'state> Context<'state, SamplerCommand> for App { Ok(None) } fn stop (app: &mut App, index: usize) -> Perhaps { - app.tracks[index].player.enqueue_next(None); + app.tracks[index].sequencer.enqueue_next(None); Ok(None) } fn add (app: &mut App) -> Perhaps { @@ -535,7 +535,7 @@ impl<'state> Context<'state, SamplerCommand> for App { } fn enqueue (app: &mut App, a: usize, b: usize) -> Perhaps { //(Enqueue [t: usize, s: usize] - //cmd!(app.tracks[t].player.enqueue_next(app.scenes[s].clips[t].as_ref()))) + //cmd!(app.tracks[t].sequencer.enqueue_next(app.scenes[s].clips[t].as_ref()))) //("enqueue" [a: usize, b: usize] Some(Self::Enqueue(a.unwrap(), b.unwrap()))) todo!() } diff --git a/crates/app/src/audio.rs b/crates/app/src/audio.rs index b6db6a44..0081d093 100644 --- a/crates/app/src/audio.rs +++ b/crates/app/src/audio.rs @@ -57,7 +57,7 @@ audio!( // Update track sequencers and devices for track in self.tracks.iter_mut() { if Control::Quit == PlayerAudio( - track.player_mut(), &mut self.note_buf, &mut self.midi_buf + track.sequencer_mut(), &mut self.note_buf, &mut self.midi_buf ).process(client, scope) { return Control::Quit } diff --git a/crates/app/src/model.rs b/crates/app/src/model.rs index a18943f4..41c4c875 100644 --- a/crates/app/src/model.rs +++ b/crates/app/src/model.rs @@ -102,7 +102,7 @@ impl App { let mut track = Track { width: (name.len() + 2).max(12), color: color.unwrap_or_else(ItemTheme::random), - player: MidiPlayer::new( + sequencer: Sequencer::new( &format!("{name}"), self.jack(), Some(self.clock()), @@ -141,7 +141,7 @@ impl App { let exists = self.tracks().get(index).is_some(); if exists { let track = self.tracks_mut().remove(index); - let Track { player: MidiPlayer { midi_ins, midi_outs, .. }, .. } = track; + let Track { sequencer: Sequencer { midi_ins, midi_outs, .. }, .. } = track; for port in midi_ins.into_iter() { port.close()?; } @@ -196,7 +196,7 @@ impl App { /// Enqueue clips from a scene across all tracks pub fn scene_enqueue (&mut self, scene: usize) { for track in 0..self.tracks.len() { - self.tracks[track].player.enqueue_next(self.scenes[scene].clips[track].as_ref()); + self.tracks[track].sequencer.enqueue_next(self.scenes[scene].clips[track].as_ref()); } } @@ -315,7 +315,7 @@ impl App { /// Stop all playing clips pub(crate) fn stop_all (&mut self) { for track in 0..self.tracks.len() { - self.tracks[track].player.enqueue_next(None); + self.tracks[track].sequencer.enqueue_next(None); } } @@ -324,14 +324,14 @@ impl App { use Selection::*; match self.selected { Track(t) => { - self.tracks[t].player.enqueue_next(None) + self.tracks[t].sequencer.enqueue_next(None) }, TrackClip { track, scene } => { - self.tracks[track].player.enqueue_next(self.scenes[scene].clips[track].as_ref()) + self.tracks[track].sequencer.enqueue_next(self.scenes[scene].clips[track].as_ref()) }, Scene(s) => { for t in 0..self.tracks.len() { - self.tracks[t].player.enqueue_next(self.scenes[s].clips[t].as_ref()) + self.tracks[t].sequencer.enqueue_next(self.scenes[s].clips[t].as_ref()) } }, _ => {} @@ -417,7 +417,7 @@ impl App { fn device_add_sampler (&mut self) -> Usually<()> { let name = self.jack.with_client(|c|c.name().to_string()); - let midi = self.track().expect("no active track").player.midi_outs[0].name(); + let midi = self.track().expect("no active track").sequencer.midi_outs[0].name(); let sampler = if let Ok(sampler) = Sampler::new( &self.jack, &format!("{}/Sampler", &self.track().expect("no active track").name), diff --git a/crates/app/src/model/scene.rs b/crates/app/src/model/scene.rs index 353133ac..21d4f4ee 100644 --- a/crates/app/src/model/scene.rs +++ b/crates/app/src/model/scene.rs @@ -24,7 +24,7 @@ impl Scene { Some(c) => tracks .get(track_index) .map(|track|{ - if let Some((_, Some(clip))) = track.player().play_clip() { + if let Some((_, Some(clip))) = track.sequencer().play_clip() { *clip.read().unwrap() == *c.read().unwrap() } else { false diff --git a/crates/app/src/model/track.rs b/crates/app/src/model/track.rs index 6cf670b5..5e68b04d 100644 --- a/crates/app/src/model/track.rs +++ b/crates/app/src/model/track.rs @@ -7,8 +7,8 @@ use crate::*; pub width: usize, /// Identifying color of track pub color: ItemTheme, - /// MIDI player state - pub player: MidiPlayer, + /// MIDI sequencer state + pub sequencer: Sequencer, /// Device chain pub devices: Vec, /// Inputs of 1st device @@ -17,52 +17,60 @@ use crate::*; pub audio_outs: Vec, } -has_clock!(|self: Track|self.player.clock); +has_clock!(|self: Track|self.sequencer.clock); -has_player!(|self: Track|self.player); +has_sequencer!(|self: Track|self.sequencer); impl Track { - pub const MIN_WIDTH: usize = 9; - /// Create a new track with only the default [MidiPlayer]. - pub fn new () -> Self { - Self::default() + /// Create a new track with only the default [Sequencer]. + pub fn new ( + name: &impl AsRef, + color: Option, + jack: &Jack, + clock: Option<&Clock>, + midi_from: &[PortConnect], + midi_to: &[PortConnect], + ) -> Usually { + Ok(Self { + name: name.as_ref().into(), + color: color.unwrap_or_default(), + sequencer: Sequencer::new( + format!("{}/sequencer", name.as_ref()), + jack, + clock, + None, + midi_from, + midi_to + )?, + ..Default::default() + }) } - /// Create a new track connecting the [MidiPlayer] to a [Sampler]. + /// Create a new track connecting the [Sequencer] to a [Sampler]. pub fn new_with_sampler ( + name: &impl AsRef, + color: Option, jack: &Jack, + clock: Option<&Clock>, midi_from: &[PortConnect], + midi_to: &[PortConnect], audio_from: &[&[PortConnect];2], audio_to: &[&[PortConnect];2], ) -> Usually { - let mut track = Self::new_sequencer(); - let name = jack.with_client(|c|c.name().to_string()); - let midi = track.player.midi_outs[0].name(); - let port = PortConnect::exact(format!("{name}:{midi}")); - let sampler = Sampler::new(jack, &"sampler", &[port], audio_from, audio_to)?; - track.devices.push(Device::Sampler(sampler)); + let mut track = Self::new( + name, color, jack, clock, midi_from, midi_to + )?; + track.devices.push(Device::Sampler(Sampler::new( + jack, + &"sampler", + &[PortConnect::exact(format!("{}:{}", + jack.with_client(|c|c.name().to_string()), + track.sequencer.midi_outs[0].name() + ))], + audio_from, + audio_to + )?)); Ok(track) } - pub fn width_inc (&mut self) { - self.width += 1; - } - pub fn width_dec (&mut self) { - if self.width > Track::MIN_WIDTH { - self.width -= 1; - } - } - pub fn sequencer (&self, mut nth: usize) -> Option<&MidiPlayer> { - for device in self.devices.iter() { - match device { - Device::Sequencer(s) => if nth == 0 { - return Some(s); - } else { - nth -= 1; - }, - _ => {} - } - } - None - } pub fn sampler (&self, mut nth: usize) -> Option<&Sampler> { for device in self.devices.iter() { match device { @@ -91,6 +99,26 @@ impl Track { } } +pub trait HasWidth { + const MIN_WIDTH: usize; + /// Increment track width. + fn width_inc (&mut self); + /// Decrement track width, down to a hardcoded minimum of [Self::MIN_WIDTH]. + fn width_dec (&mut self); +} + +impl HasWidth for Track { + const MIN_WIDTH: usize = 9; + fn width_inc (&mut self) { + self.width += 1; + } + fn width_dec (&mut self) { + if self.width > Track::MIN_WIDTH { + self.width -= 1; + } + } +} + pub trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync { fn midi_ins (&self) -> &Vec; fn midi_outs (&self) -> &Vec; @@ -117,14 +145,14 @@ pub trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync fn track_toggle_record (&mut self) { if let Some(t) = self.selected().track() { let tracks = self.tracks_mut(); - tracks[t-1].player.recording = !tracks[t-1].player.recording; + tracks[t-1].sequencer.recording = !tracks[t-1].sequencer.recording; } } /// Toggle track monitoring fn track_toggle_monitor (&mut self) { if let Some(t) = self.selected().track() { let tracks = self.tracks_mut(); - tracks[t-1].player.monitoring = !tracks[t-1].player.monitoring; + tracks[t-1].sequencer.monitoring = !tracks[t-1].sequencer.monitoring; } } } diff --git a/crates/app/src/view.rs b/crates/app/src/view.rs index 271c8251..e0bb9340 100644 --- a/crates/app/src/view.rs +++ b/crates/app/src/view.rs @@ -336,8 +336,8 @@ impl<'a> ArrangerView<'a> { self.width_mid, ||self.tracks_with_sizes_scrolled(), move|t, track|{ - let rec = track.player.recording; - let mon = track.player.monitoring; + let rec = track.sequencer.recording; + let mon = track.sequencer.monitoring; let rec = if rec { White } else { track.color.darkest.rgb }; let mon = if mon { White } else { track.color.darkest.rgb }; let bg = if self.track_selected == Some(t) { @@ -377,10 +377,10 @@ impl<'a> ArrangerView<'a> { let label = Align::ne("Next clip:"); Tryptich::top(2).left(self.width_side, label).middle(self.width_mid, per_track_top( self.width_mid, ||self.tracks_with_sizes_scrolled(), |t, track|{ - let queued = track.player.next_clip.is_some(); + let queued = track.sequencer.next_clip.is_some(); let queued_blank = Thunk::new(||Tui::bg(Reset, " ------ ")); let queued_clip = Thunk::new(||{ - Tui::bg(Reset, if let Some((_, clip)) = track.player.next_clip.as_ref() { + Tui::bg(Reset, if let Some((_, clip)) = track.sequencer.next_clip.as_ref() { if let Some(clip) = clip { clip.read().unwrap().name.clone() } else { @@ -1224,7 +1224,7 @@ impl std::fmt::Debug for PianoHorizontal { } // Update sequencer playhead indicator //self.now().set(0.); - //if let Some((ref started_at, Some(ref playing))) = self.player.play_clip { + //if let Some((ref started_at, Some(ref playing))) = self.sequencer.play_clip { //let clip = clip.read().unwrap(); //if *playing.read().unwrap() == *clip { //let pulse = self.current().pulse.get(); diff --git a/crates/cli/tek.rs b/crates/cli/tek.rs index e6434a26..6d185221 100644 --- a/crates/cli/tek.rs +++ b/crates/cli/tek.rs @@ -136,11 +136,22 @@ impl Cli { }, tracks: match mode { LaunchMode::Sequencer => vec![ - Track::new() + Track::new( + &name, + None, + jack, + None, + midi_froms.as_slice(), + midi_tos.as_slice() + )? ], LaunchMode::Groovebox | LaunchMode::Sampler => vec![ Track::new_with_sampler( + &name, + None, jack, + None, + midi_froms.as_slice(), midi_froms.as_slice(), audio_froms, audio_tos, diff --git a/crates/device/src/device.rs b/crates/device/src/device.rs index 74d76488..60ecaf1a 100644 --- a/crates/device/src/device.rs +++ b/crates/device/src/device.rs @@ -2,7 +2,7 @@ use crate::*; #[derive(Debug)] pub enum Device { - #[cfg(feature = "sequencer")] Sequencer(MidiPlayer), + #[cfg(feature = "sequencer")] Sequencer(Sequencer), #[cfg(feature = "sampler")] Sampler(Sampler), #[cfg(feature = "lv2")] Lv2(Lv2), // TODO #[cfg(feature = "vst2")] Vst2, // TODO diff --git a/crates/device/src/sequencer.rs b/crates/device/src/sequencer.rs index ba8ab65e..6f8ca260 100644 --- a/crates/device/src/sequencer.rs +++ b/crates/device/src/sequencer.rs @@ -22,6 +22,6 @@ mod seq_view; pub use self::seq_view::*; } #[cfg(test)] #[test] fn test_midi_play () { - let player = MidiPlayer::default(); - println!("{player:?}"); + let sequencer = Sequencer::default(); + println!("{sequencer:?}"); } diff --git a/crates/device/src/sequencer/seq_model.rs b/crates/device/src/sequencer/seq_model.rs index 810b97b3..db65cd55 100644 --- a/crates/device/src/sequencer/seq_model.rs +++ b/crates/device/src/sequencer/seq_model.rs @@ -1,27 +1,27 @@ -//! MIDI player +//! MIDI sequencer use crate::*; use tek_engine::jack::*; -pub trait HasPlayer { - fn player (&self) -> &impl MidiPlayerApi; - fn player_mut (&mut self) -> &mut impl MidiPlayerApi; +pub trait HasSequencer { + fn sequencer (&self) -> &impl MidiPlayerApi; + fn sequencer_mut (&mut self) -> &mut impl MidiPlayerApi; } -#[macro_export] macro_rules! has_player { +#[macro_export] macro_rules! has_sequencer { (|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => { - impl $(<$($L),*$($T $(: $U)?),*>)? HasPlayer for $Struct $(<$($L),*$($T),*>)? { - fn player (&$self) -> &impl MidiPlayerApi { &$cb } - fn player_mut (&mut $self) -> &mut impl MidiPlayerApi { &mut$cb } + impl $(<$($L),*$($T $(: $U)?),*>)? HasSequencer for $Struct $(<$($L),*$($T),*>)? { + fn sequencer (&$self) -> &impl MidiPlayerApi { &$cb } + fn sequencer_mut (&mut $self) -> &mut impl MidiPlayerApi { &mut$cb } } } } pub trait MidiPlayerApi: MidiRecordApi + MidiPlaybackApi + Send + Sync {} -impl MidiPlayerApi for MidiPlayer {} +impl MidiPlayerApi for Sequencer {} /// Contains state for playing a clip -pub struct MidiPlayer { +pub struct Sequencer { /// State of clock and playhead pub clock: Clock, /// Start time and clip being played @@ -48,7 +48,7 @@ pub struct MidiPlayer { pub note_buf: Vec, } -impl Default for MidiPlayer { +impl Default for Sequencer { fn default () -> Self { Self { play_clip: None, @@ -69,7 +69,7 @@ impl Default for MidiPlayer { } } -impl MidiPlayer { +impl Sequencer { pub fn new ( name: impl AsRef, jack: &Jack, @@ -97,9 +97,9 @@ impl MidiPlayer { } } -impl std::fmt::Debug for MidiPlayer { +impl std::fmt::Debug for Sequencer { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - f.debug_struct("MidiPlayer") + f.debug_struct("Sequencer") .field("clock", &self.clock) .field("play_clip", &self.play_clip) .field("next_clip", &self.next_clip) @@ -107,20 +107,20 @@ impl std::fmt::Debug for MidiPlayer { } } -has_clock!(|self: MidiPlayer|self.clock); +has_clock!(|self: Sequencer|self.clock); -impl HasMidiIns for MidiPlayer { +impl HasMidiIns for Sequencer { fn midi_ins (&self) -> &Vec { &self.midi_ins } fn midi_ins_mut (&mut self) -> &mut Vec { &mut self.midi_ins } } -impl HasMidiOuts for MidiPlayer { +impl HasMidiOuts for Sequencer { fn midi_outs (&self) -> &Vec { &self.midi_outs } fn midi_outs_mut (&mut self) -> &mut Vec { &mut self.midi_outs } fn midi_note (&mut self) -> &mut Vec { &mut self.note_buf } } -/// Hosts the JACK callback for a single MIDI player +/// Hosts the JACK callback for a single MIDI sequencer pub struct PlayerAudio<'a, T: MidiPlayerApi>( /// Player pub &'a mut T, @@ -130,7 +130,7 @@ pub struct PlayerAudio<'a, T: MidiPlayerApi>( pub &'a mut Vec>>, ); -/// JACK process callback for a sequencer's clip player/recorder. +/// JACK process callback for a sequencer's clip sequencer/recorder. impl Audio for PlayerAudio<'_, T> { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { let model = &mut self.0; @@ -157,7 +157,7 @@ impl Audio for PlayerAudio<'_, T> { } } -impl MidiRecordApi for MidiPlayer { +impl MidiRecordApi for Sequencer { fn recording (&self) -> bool { self.recording } @@ -181,13 +181,13 @@ impl MidiRecordApi for MidiPlayer { } } -impl MidiPlaybackApi for MidiPlayer { +impl MidiPlaybackApi for Sequencer { fn notes_out (&self) -> &Arc> { &self.notes_out } } -impl HasPlayClip for MidiPlayer { +impl HasPlayClip for Sequencer { fn reset (&self) -> bool { self.reset }