From bbe49ad463bde288b3cc0d6af982814ec0505e4d Mon Sep 17 00:00:00 2001 From: unspeaker Date: Wed, 22 Jan 2025 00:10:31 +0100 Subject: [PATCH] more sensible port creation --- midi/src/midi_player.rs | 44 ++++++++------------- tek/src/audio.rs | 84 ++++++++++++++++++++--------------------- tek/src/cli.rs | 32 +++++++++------- tek/src/keys.rs | 13 ++++--- tek/src/model.rs | 37 +++++++++++------- tek/src/view.rs | 4 +- time/src/clock.rs | 44 +++++++++++---------- 7 files changed, 133 insertions(+), 125 deletions(-) diff --git a/midi/src/midi_player.rs b/midi/src/midi_player.rs index 9bd8d19c..bcbf7d08 100644 --- a/midi/src/midi_player.rs +++ b/midi/src/midi_player.rs @@ -62,20 +62,28 @@ impl Default for MidiPlayer { } impl MidiPlayer { pub fn new ( - jack: &Jack, - name: impl AsRef, - clip: Option<&Arc>>, + name: impl AsRef, + jack: &Jack, + clock: Option<&Clock>, + clip: Option<&Arc>>, midi_from: &[PortConnect], midi_to: &[PortConnect], ) -> Usually { let name = name.as_ref(); - let clock = Clock::from(jack); + let clock = clock.cloned().unwrap_or_default(); Ok(Self { - midi_ins: vec![JackMidiIn::new(jack, format!("M/{name}"), midi_from)?,], - midi_outs: vec![JackMidiOut::new(jack, format!("{name}/M"), midi_to)?, ], - play_clip: Some((Moment::zero(&clock.timebase), clip.cloned())), + midi_ins: vec![JackMidiIn::new(jack, format!("M/{name}"), midi_from)?,], + midi_outs: vec![JackMidiOut::new(jack, format!("{name}/M"), midi_to)?, ], + play_clip: clip.map(|clip|(Moment::zero(&clock.timebase), Some(clip.clone()))), clock, - ..Default::default() + note_buf: vec![0;8], + reset: true, + recording: false, + monitoring: false, + overdub: false, + next_clip: None, + notes_in: RwLock::new([false;128]).into(), + notes_out: RwLock::new([false;128]).into(), }) } } @@ -88,26 +96,6 @@ impl std::fmt::Debug for MidiPlayer { .finish() } } -from!(|clock: &Clock| MidiPlayer = Self { - clock: clock.clone(), - midi_ins: vec![], - midi_outs: vec![], - note_buf: vec![0;8], - reset: true, - recording: false, - monitoring: false, - overdub: false, - play_clip: None, - next_clip: None, - notes_in: RwLock::new([false;128]).into(), - notes_out: RwLock::new([false;128]).into(), -}); -from!(|state: (&Clock, &Arc>)|MidiPlayer = { - let (clock, clip) = state; - let mut model = Self::from(clock); - model.play_clip = Some((Moment::zero(&clock.timebase), Some(clip.clone()))); - model -}); has_clock!(|self: MidiPlayer|self.clock); impl HasMidiIns for MidiPlayer { fn midi_ins (&self) -> &Vec { &self.midi_ins } diff --git a/tek/src/audio.rs b/tek/src/audio.rs index 206beadd..80b41d2e 100644 --- a/tek/src/audio.rs +++ b/tek/src/audio.rs @@ -1,7 +1,6 @@ use crate::*; impl HasJack for Tek { fn jack (&self) -> &Jack { &self.jack } } audio!( - |self: Tek, client, scope|{ // Start profiling cycle let t0 = self.perf.get_t0(); @@ -14,20 +13,20 @@ audio!( .collect::>()) .collect::>(); // Update standalone MIDI sequencer - if let Some(player) = self.player.as_mut() { - if Control::Quit == PlayerAudio( - player, - &mut self.note_buf, - &mut self.midi_buf, - ).process(client, scope) { - return Control::Quit - } - } + //if let Some(player) = self.player.as_mut() { + //if Control::Quit == PlayerAudio( + //player, + //&mut self.note_buf, + //&mut self.midi_buf, + //).process(client, scope) { + //return Control::Quit + //} + //} // Update standalone sampler - if let Some(sampler) = self.sampler.as_mut() { - if Control::Quit == SamplerAudio(sampler).process(client, scope) { - return Control::Quit - } + //if let Some(sampler) = self.sampler.as_mut() { + //if Control::Quit == SamplerAudio(sampler).process(client, scope) { + //return Control::Quit + //} //for port in midi_in.iter() { //for message in port.iter() { //match message { @@ -35,30 +34,30 @@ audio!( //} //} //} - } + //} // TODO move these to editor and sampler?: - for port in midi_in.iter() { - for event in port.iter() { - match event { - (time, Ok(LiveEvent::Midi {message, ..})) => match message { - MidiMessage::NoteOn {ref key, ..} if let Some(editor) = self.editor.as_ref() => { - editor.set_note_pos(key.as_int() as usize); - }, - MidiMessage::Controller {controller, value} if let (Some(editor), Some(sampler)) = ( - self.editor.as_ref(), - self.sampler.as_ref(), - ) => { - // TODO: give sampler its own cursor - if let Some(sample) = &sampler.mapped[editor.note_pos()] { - sample.write().unwrap().handle_cc(*controller, *value) - } - } - _ =>{} - }, - _ =>{} - } - } - } + //for port in midi_in.iter() { + //for event in port.iter() { + //match event { + //(time, Ok(LiveEvent::Midi {message, ..})) => match message { + //MidiMessage::NoteOn {ref key, ..} if let Some(editor) = self.editor.as_ref() => { + //editor.set_note_pos(key.as_int() as usize); + //}, + //MidiMessage::Controller {controller, value} if let (Some(editor), Some(sampler)) = ( + //self.editor.as_ref(), + //self.sampler.as_ref(), + //) => { + //// TODO: give sampler its own cursor + //if let Some(sample) = &sampler.mapped[editor.note_pos()] { + //sample.write().unwrap().handle_cc(*controller, *value) + //} + //} + //_ =>{} + //}, + //_ =>{} + //} + //} + //} // Update track sequencers for track in self.tracks.iter_mut() { if PlayerAudio( @@ -71,20 +70,19 @@ audio!( self.perf.update(t0, scope); Control::Continue }; - |self, event|{ use JackEvent::*; match event { SampleRate(sr) => { self.clock.timebase.sr.set(sr as f64); }, PortRegistration(id, true) => - {}, + { println!("\rport add: {id}") }, PortRegistration(id, false) => - {}, + { println!("\rport del: {id}") }, PortsConnected(a, b, true) => - {}, + { println!("\rport conn: {a} {b}") }, PortsConnected(a, b, false) => - {}, + { println!("\rport disc: {a} {b}") }, ClientRegistration(id, true) => {}, ClientRegistration(id, false) => @@ -93,6 +91,8 @@ audio!( {}, XRun => {}, + GraphReorder => + {}, _ => { panic!("{event:?}"); } } } diff --git a/tek/src/cli.rs b/tek/src/cli.rs index e2624b67..f2018e40 100644 --- a/tek/src/cli.rs +++ b/tek/src/cli.rs @@ -103,9 +103,9 @@ impl Tek { view: SourceIter(include_str!("./view_transport.edn")), jack: jack.clone(), color: ItemPalette::random(), - clock: Clock::new(jack, bpm), - midi_ins: vec![JackMidiIn::new(jack, "GlobalI", midi_froms)?], - midi_outs: vec![JackMidiOut::new(jack, "GlobalO", midi_tos)?], + clock: Clock::new(jack, bpm)?, + midi_ins: vec![], + midi_outs: vec![], keys: SourceIter(KEYS_APP), keys_clip: SourceIter(KEYS_CLIP), keys_track: SourceIter(KEYS_TRACK), @@ -129,14 +129,16 @@ impl Tek { ) -> Usually { let clip = MidiClip::new("Clip", true, 384usize, None, Some(ItemColor::random().into())); let clip = Arc::new(RwLock::new(clip)); + let this = Self::new_clock(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)?; Ok(Self { view: SourceIter(include_str!("./view_sequencer.edn")), pool: Some((&clip).into()), editor: Some((&clip).into()), editing: false.into(), midi_buf: vec![vec![];65536], - player: Some(MidiPlayer::new(&jack, "sequencer", Some(&clip), &midi_froms, &midi_tos)?), - ..Self::new_clock(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)? + tracks: vec![Track::default()], + //player: Some(MidiPlayer::new("sequencer", &jack, Some(&this.clock), Some(&clip), &midi_froms, &midi_tos)?), + ..this }) } pub fn new_groovebox ( @@ -147,12 +149,15 @@ impl Tek { ) -> Usually { let app = Self { view: SourceIter(include_str!("./view_groovebox.edn")), - sampler: Some(Sampler::new(jack, &"sampler", midi_froms, audio_froms, audio_tos)?), + tracks: vec![Track { + devices: vec![Sampler::new(jack, &"sampler", midi_froms, audio_froms, audio_tos)?.boxed()], + ..Track::default() + }], ..Self::new_sequencer(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)? }; - if let Some(sampler) = app.sampler.as_ref().unwrap().midi_in.as_ref() { - app.player.as_ref().unwrap().midi_outs[0].connect_to(sampler.port())?; - } + //if let Some(sampler) = app.sampler.as_ref().unwrap().midi_in.as_ref() { + //app.player.as_ref().unwrap().midi_outs[0].connect_to(sampler.port())?; + //} Ok(app) } pub fn new_arranger ( @@ -164,10 +169,11 @@ impl Tek { ) -> Usually { let mut arranger = Self { view: SourceIter(include_str!("./view_arranger.edn")), - ..Self::new_groovebox( - jack, bpm, sync_lead, sync_follow, - midi_froms, midi_tos, audio_froms, audio_tos, - )? + pool: Some(Default::default()), + editor: Some(Default::default()), + editing: false.into(), + midi_buf: vec![vec![];65536], + ..Self::new_clock(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)? }; arranger.scenes_add(scenes); arranger.tracks_add(tracks, track_width, midi_froms, midi_tos); diff --git a/tek/src/keys.rs b/tek/src/keys.rs index 5f85c485..7688dd0e 100644 --- a/tek/src/keys.rs +++ b/tek/src/keys.rs @@ -69,8 +69,8 @@ atom_command!(TekCommand: |app: Tek| { MidiEditCommand::try_from_expr(app.editor.as_ref().expect("no editor"), a).expect("invalid command"))) ("pool" [,..a] Self::Pool( PoolCommand::try_from_expr(app.pool.as_ref().expect("no pool"), a).expect("invalid command"))) - ("sampler" [,..a] Self::Sampler( - SamplerCommand::try_from_expr(app.sampler.as_ref().expect("no sampler"), a).expect("invalid command"))) + //("sampler" [,..a] Self::Sampler( + //SamplerCommand::try_from_expr(app.sampler().as_ref().expect("no sampler"), a).expect("invalid command"))) ("scene" [,..a] Self::Scene( SceneCommand::try_from_expr(app, a).expect("invalid command"))) ("track" [,..a] Self::Track( @@ -133,10 +133,10 @@ command!(|self: TekCommand, app: Tek|match self { Self::Clip(cmd) => cmd.delegate(app, Self::Clip)?, Self::Editor(cmd) => app.editor.as_mut() .map(|editor|cmd.delegate(editor, Self::Editor)).transpose()?.flatten(), - Self::Sampler(cmd) => app.sampler.as_mut() - .map(|sampler|cmd.delegate(sampler, Self::Sampler)).transpose()?.flatten(), - Self::Enqueue(clip) => app.player.as_mut() - .map(|player|{player.enqueue_next(clip.as_ref());None}).flatten(), + //Self::Sampler(cmd) => app.sampler.as_mut() + //.map(|sampler|cmd.delegate(sampler, Self::Sampler)).transpose()?.flatten(), + //Self::Enqueue(clip) => app.player.as_mut() + //.map(|player|{player.enqueue_next(clip.as_ref());None}).flatten(), Self::Color(palette) => { use Selection::*; Some(Self::Color(match app.selected { @@ -190,6 +190,7 @@ command!(|self: TekCommand, app: Tek|match self { } else { None }, + _ => todo!() }); #[derive(Clone, Debug)] pub enum TrackCommand { Add, diff --git a/tek/src/model.rs b/tek/src/model.rs index e44701f8..c04be5e5 100644 --- a/tek/src/model.rs +++ b/tek/src/model.rs @@ -8,8 +8,6 @@ use crate::*; pub color: ItemPalette, pub pool: Option, pub editor: Option, - pub player: Option, - pub sampler: Option, pub midi_buf: Vec>>, pub midi_ins: Vec, pub midi_outs: Vec, @@ -39,9 +37,9 @@ use crate::*; has_size!(|self: Tek|&self.size); has_clock!(|self: Tek|self.clock); has_clips!(|self: Tek|self.pool.as_ref().expect("no clip pool").clips); -has_sampler!(|self: Tek|{ - sampler = self.sampler; - index = self.editor.as_ref().map(|e|e.note_pos()).unwrap_or(0); }); +//has_sampler!(|self: Tek|{ + //sampler = self.sampler; + //index = self.editor.as_ref().map(|e|e.note_pos()).unwrap_or(0); }); has_editor!(|self: Tek|{ editor = self.editor; editor_w = { @@ -108,24 +106,33 @@ impl Tek { } pub fn track_add ( &mut self, name: Option<&str>, color: Option, - midi_from: &[PortConnect], midi_to: &[PortConnect], + midi_froms: &[PortConnect], + midi_tos: &[PortConnect], ) -> Usually<(usize, &mut Track)> { let name = name.map_or_else(||self.track_next_name(), |x|x.to_string().into()); let mut track = Track { width: (name.len() + 2).max(9), color: color.unwrap_or_else(ItemPalette::random), - player: MidiPlayer::from(self.clock()), + player: MidiPlayer::new( + &format!("{name}"), + self.jack(), + Some(self.clock()), + None, + midi_froms, + midi_tos + )?, name, ..Default::default() }; - let midi_in = JackMidiIn::new(&self.jack, &format!("{}I", &track.name), midi_from)?; - midi_in.connect_to_matching()?; - track.player.midi_ins.push(midi_in); + //let midi_in = JackMidiIn::new(&self.jack, &format!("{}I", &track.name), midi_from)?; + //midi_in.connect_to_matching()?; + //track.player.midi_ins.push(midi_in); + + //let midi_out = JackMidiOut::new(&self.jack, &format!("{}O", &track.name), midi_to)?; + //midi_out.connect_to_matching()?; + //track.player.midi_outs.push(midi_out); - let midi_out = JackMidiOut::new(&self.jack, &format!("{}O", &track.name), midi_to)?; - midi_out.connect_to_matching()?; - track.player.midi_outs.push(midi_out); self.tracks_mut().push(track); let len = self.tracks().len(); let index = len - 1; @@ -293,7 +300,9 @@ pub trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync self.selected().track().and_then(|s|self.tracks_mut().get_mut(s)) } } -pub trait Device: Send + Sync + std::fmt::Debug {} +pub trait Device: Send + Sync + std::fmt::Debug { + fn boxed <'a> (self) -> Box where Self: Sized + 'a { Box::new(self) } +} impl Device for Sampler {} impl Device for Plugin {} #[derive(Debug, Default)] pub struct Scene { diff --git a/tek/src/view.rs b/tek/src/view.rs index d59944ad..7a78ad99 100644 --- a/tek/src/view.rs +++ b/tek/src/view.rs @@ -53,8 +53,8 @@ impl Default for ViewCache { view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); { ":editor" => (&self.editor).boxed(), ":pool" => self.view_pool().boxed(), - ":sample" => self.view_sample(self.is_editing()).boxed(), - ":sampler" => self.view_sampler(self.is_editing(), &self.editor).boxed(), + //":sample" => self.view_sample(self.is_editing()).boxed(), + //":sampler" => self.view_sampler(self.is_editing(), &self.editor).boxed(), ":status" => self.view_editor().boxed(), ":toolbar" => self.view_clock().boxed(), ":tracks" => self.view_tracks().boxed(), diff --git a/time/src/clock.rs b/time/src/clock.rs index d3dfa077..ed877852 100644 --- a/time/src/clock.rs +++ b/time/src/clock.rs @@ -80,23 +80,12 @@ pub struct Clock { pub sync: Arc, /// Size of buffer in samples pub chunk: Arc, -} -impl From<&Jack> for Clock { - fn from (jack: &Jack) -> Self { - let (chunk, transport) = jack.with_client(|c|(c.buffer_size(), c.transport())); - let timebase = Arc::new(Timebase::default()); - Self { - quant: Arc::new(24.into()), - sync: Arc::new(384.into()), - transport: Arc::new(Some(transport)), - chunk: Arc::new((chunk as usize).into()), - global: Arc::new(Moment::zero(&timebase)), - playhead: Arc::new(Moment::zero(&timebase)), - offset: Arc::new(Moment::zero(&timebase)), - started: RwLock::new(None).into(), - timebase, - } - } + /// For syncing the clock to an external source + pub midi_in: Arc>>, + /// For syncing other devices to this clock + pub midi_out: Arc>>, + /// For emitting a metronome + pub click_out: Arc>>, } impl std::fmt::Debug for Clock { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { @@ -112,12 +101,27 @@ impl std::fmt::Debug for Clock { } } impl Clock { - pub fn new (jack: &Jack, bpm: Option) -> Self { - let clock = Self::from(jack); + pub fn new (jack: &Jack, bpm: Option) -> Usually { + let (chunk, transport) = jack.with_client(|c|(c.buffer_size(), c.transport())); + let timebase = Arc::new(Timebase::default()); + let clock = Self { + quant: Arc::new(24.into()), + sync: Arc::new(384.into()), + transport: Arc::new(Some(transport)), + chunk: Arc::new((chunk as usize).into()), + global: Arc::new(Moment::zero(&timebase)), + playhead: Arc::new(Moment::zero(&timebase)), + offset: Arc::new(Moment::zero(&timebase)), + started: RwLock::new(None).into(), + timebase, + midi_in: Arc::new(RwLock::new(Some(JackMidiIn::new(jack, "M/clock", &[])?))), + midi_out: Arc::new(RwLock::new(Some(JackMidiOut::new(jack, "clock/M", &[])?))), + click_out: Arc::new(RwLock::new(Some(JackAudioOut::new(jack, "click", &[])?))), + }; if let Some(bpm) = bpm { clock.timebase.bpm.set(bpm); } - clock + Ok(clock) } pub fn timebase (&self) -> &Arc { &self.timebase