From da074eb5fae2de524b2c66fd1b2147d4b050d122 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Thu, 14 Nov 2024 15:09:30 +0100 Subject: [PATCH] wip: refactor pt.24: 45 errors --- crates/tek_tui/src/lib.rs | 68 ++++++++------------- crates/tek_tui/src/tui_arranger.rs | 94 +++++++++++++---------------- crates/tek_tui/src/tui_pool.rs | 5 +- crates/tek_tui/src/tui_sequencer.rs | 72 +++++++++------------- crates/tek_tui/src/tui_transport.rs | 80 +++++++----------------- 5 files changed, 120 insertions(+), 199 deletions(-) diff --git a/crates/tek_tui/src/lib.rs b/crates/tek_tui/src/lib.rs index 9c5da899..3f4e31b1 100644 --- a/crates/tek_tui/src/lib.rs +++ b/crates/tek_tui/src/lib.rs @@ -52,63 +52,48 @@ submod! { tui_transport_foc } -pub struct AppContainer +pub struct AppView where E: Engine, - M: Send + Sync, - V: Widget + Handle, - C: Command, - A: Audio, - S: StatusBar + A: Widget + Handle + Audio, + C: Command, { + pub app: A, pub cursor: (usize, usize), pub entered: bool, - pub menu_bar: Option>, - pub status_bar: Option, + pub menu_bar: Option>, + pub status_bar: Option>>, pub history: Vec, pub size: Measure, - pub model: Arc>, - pub view: V, - pub audio: A, } -impl AppContainer +impl AppView where E: Engine, - M: Send + Sync, - V: Widget + Handle, - C: Command, - A: Audio, - S: StatusBar + A: Widget + Handle + Audio, + C: Command { pub fn new ( - model: &Arc>, - view: V, - audio: A, - menu_bar: Option>, - status_bar: Option, + app: A, + menu_bar: Option>, + status_bar: Option>>, ) -> Self { Self { + app, cursor: (0, 0), entered: false, history: vec![], size: Measure::new(), - model: model.clone(), - view, - audio, menu_bar, status_bar, } } } -impl Content for AppContainer +impl Content for AppView where - M: Send + Sync, - V: Widget + Handle, - C: Command, - A: Audio, - S: StatusBar, + A: Widget + Handle + Audio, + C: Command { type Engine = Tui; fn content (&self) -> impl Widget { @@ -124,14 +109,14 @@ where Split::up( if self.status_bar.is_some() { 1 } else { 0 }, widget(&self.status_bar), - widget(&self.view) + widget(&self.app) ) ) } } #[derive(Debug, Copy, Clone)] -pub enum AppContainerCommand { +pub enum AppViewCommand { Focus(FocusCommand), Undo, Redo, @@ -139,20 +124,17 @@ pub enum AppContainerCommand { } #[derive(Debug, Copy, Clone, PartialEq)] -pub enum AppContainerFocus { +pub enum AppViewFocus { Menu, Content(F), } -impl FocusGrid for AppContainer +impl FocusGrid for AppView where - T: Send + Sync, - U: From>> + Widget + Handle + FocusGrid, - C: Command, - A: From>> + Audio, - S: From>> + StatusBar + A: Widget + Handle + Audio + FocusGrid, + C: Command { - type Item = AppContainerFocus<::Item>; + type Item = AppViewFocus<::Item>; fn cursor (&self) -> (usize, usize) { self.cursor } @@ -181,9 +163,9 @@ where } fn layout (&self) -> &[&[Self::Item]] { &[ - &[AppContainerFocus::Menu], + &[AppViewFocus::Menu], FocusGrid::layout(&self.ui) - //&[AppContainerFocus::Content(())], + //&[AppViewFocus::Content(())], ] } fn update_focus (&mut self) { diff --git a/crates/tek_tui/src/tui_arranger.rs b/crates/tek_tui/src/tui_arranger.rs index 81bf4fe3..8fded36f 100644 --- a/crates/tek_tui/src/tui_arranger.rs +++ b/crates/tek_tui/src/tui_arranger.rs @@ -1,18 +1,11 @@ use crate::*; -pub type ArrangerApp = AppContainer< - Tui, - ArrangerModel, - ArrangerView, - ArrangerViewCommand, - ArrangerAudio, - ArrangerStatusBar ->; +pub type ArrangerApp = AppView, ArrangerViewCommand>; -impl TryFrom<&Arc>> for ArrangerApp { +impl TryFrom<&Arc>> for ArrangerApp { type Error = Box; fn try_from (jack: &Arc>) -> Usually { - let model = Arc::new(RwLock::new(ArrangerModel { + Ok(Self::new(ArrangerModel { name: Arc::new(RwLock::new(String::new())), phrases: vec![], scenes: vec![], @@ -23,22 +16,15 @@ impl TryFrom<&Arc>> for ArrangerApp { clock: Arc::new(Clock::from(Instant::default())), jack: jack.clone(), }, - })); - Ok(Self::new( - &model, - ArrangerView::from(&model), - ArrangerAudio(model.clone()), - None, - None - )) + }.into(), None, None)) } } -impl From<&Arc>> for ArrangerView { - fn from (model: &Arc>) -> Self { +impl From for ArrangerView { + fn from (model: ArrangerModel) -> Self { let mut view = Self { - model: model.clone(), - sequencer: SequencerView::from(&model.read().unwrap().sequencer), + model, + sequencer: SequencerView::from(&model.sequencer), split: 20, selected: ArrangerFocus::Clip(0, 0), mode: ArrangerMode::Vertical(2), @@ -96,31 +82,6 @@ impl ArrangerMode { } } -impl Audio for ArrangerView { - #[inline] fn process (&mut self, _: &Client, _: &ProcessScope) -> Control { - // FIXME: one of these per playing track - if let ArrangerFocus::Clip(t, s) = self.selected { - let phrase = self.model.scenes().get(s).map(|scene|scene.clips.get(t)); - if let Some(Some(Some(phrase))) = phrase { - if let Some(track) = self.model.tracks().get(t) { - if let Some((ref started_at, Some(ref playing))) = track.player.phrase { - let phrase = phrase.read().unwrap(); - if *playing.read().unwrap() == *phrase { - let pulse = self.sequencer.transport.model.clock().current.pulse.get(); - let start = started_at.pulse.get(); - let now = (pulse - start) % phrase.length as f64; - self.sequencer.editor.now.set(now); - return Control::Continue - } - } - } - } - } - self.sequencer.editor.now.set(0.); - return Control::Continue - } -} - /// Layout for standalone arranger app. impl Content for ArrangerView { type Engine = Tui; @@ -172,11 +133,13 @@ impl ArrangerView { } pub fn activate (&mut self) { + let scenes = self.model.scenes(); + let tracks = self.model.tracks_mut(); match self.selected { ArrangerFocus::Scene(s) => { - for (t, track) in self.model.tracks_mut().iter_mut().enumerate() { + for (t, track) in tracks.iter_mut().enumerate() { let player = &mut track.player; - let clip = self.model.scenes()[s].clips[t].as_ref(); + let clip = scenes[s].clips[t].as_ref(); if player.phrase.is_some() || clip.is_some() { player.enqueue_next(clip); } @@ -188,8 +151,7 @@ impl ArrangerView { //} }, ArrangerFocus::Clip(t, s) => { - let clip = self.model.scenes()[s].clips[t].as_ref(); - self.model.tracks_mut()[t].player.enqueue_next(clip); + tracks[t].player.enqueue_next(scenes[s].clips[t]); }, _ => {} } @@ -221,7 +183,7 @@ impl ArrangerView { self.color = ItemColor::random_dark() }, ArrangerFocus::Track(t) => { - self.model.tracks_mu()[t].color = ItemColor::random() + self.model.tracks_mut()[t].color = ItemColor::random() }, ArrangerFocus::Scene(s) => { self.model.scenes_mut()[s].color = ItemColor::random() @@ -243,3 +205,31 @@ impl ArrangerView { self.selected_scene()?.clips.get(self.selected.track()?)?.clone() } } + +impl Audio for ArrangerView { + #[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { + if self.model.process(client, scope) == Control::Quit { + return Control::Quit + } + // FIXME: one of these per playing track + if let ArrangerFocus::Clip(t, s) = self.selected { + let phrase = self.model.scenes().get(s).map(|scene|scene.clips.get(t)); + if let Some(Some(Some(phrase))) = phrase { + if let Some(track) = self.model.tracks().get(t) { + if let Some((ref started_at, Some(ref playing))) = track.player.phrase { + let phrase = phrase.read().unwrap(); + if *playing.read().unwrap() == *phrase { + let pulse = self.sequencer.transport.model.clock().current.pulse.get(); + let start = started_at.pulse.get(); + let now = (pulse - start) % phrase.length as f64; + self.sequencer.editor.now.set(now); + return Control::Continue + } + } + } + } + } + self.sequencer.editor.now.set(0.); + return Control::Continue + } +} diff --git a/crates/tek_tui/src/tui_pool.rs b/crates/tek_tui/src/tui_pool.rs index d33a94b8..2534e860 100644 --- a/crates/tek_tui/src/tui_pool.rs +++ b/crates/tek_tui/src/tui_pool.rs @@ -2,7 +2,8 @@ use crate::*; pub struct PhrasePoolView { _engine: PhantomData, - pub model: PhrasePool, + /// Collection of phrases + pub model: Vec>>, /// Selected phrase pub phrase: usize, /// Scroll offset @@ -24,7 +25,7 @@ pub enum PhrasePoolMode { } impl PhrasePoolView { - pub fn new (model: PhrasePool) -> Self { + pub fn new (model: Vec>>) -> Self { Self { _engine: Default::default(), scroll: 0, diff --git a/crates/tek_tui/src/tui_sequencer.rs b/crates/tek_tui/src/tui_sequencer.rs index 2369ecfa..7a5a8f74 100644 --- a/crates/tek_tui/src/tui_sequencer.rs +++ b/crates/tek_tui/src/tui_sequencer.rs @@ -1,64 +1,39 @@ use crate::*; -pub type SequencerApp = AppContainer< - Tui, - SequencerModel, - SequencerView, - SequencerViewCommand, - SequencerAudio, - SequencerStatusBar ->; +pub type SequencerApp = AppView, SequencerViewCommand>; -impl TryFrom<&Arc>> for SequencerApp { +impl TryFrom<&Arc>> for SequencerApp { type Error = Box; fn try_from (jack: &Arc>) -> Usually { let clock = Arc::new(Clock::from(Instant::default())); - - let transport = Arc::new(RwLock::new(tek_api::Transport { - metronome: false, - transport: jack.read().unwrap().transport(), - jack: jack.clone(), - clock: clock.clone() - })); - - let phrases = Arc::new(RwLock::new(PhrasePool { - phrases: vec![] // FIXME - })); - - let player = Arc::new(RwLock::new(MIDIPlayer::new(jack, &clock, "preview")?)); - - let model = Arc::new(RwLock::new(SequencerModel { - transport: transport.clone(), - phrases: phrases.clone(), - player: player.clone() - })); - - Ok(Self::new( - &model, - SequencerView::from(&model), - SequencerAudio(transport.clone(), player.clone()), - None, - None, - )) + Ok(Self::new(SequencerModel { + phrases: vec![], + player: MIDIPlayer::new(jack, &clock, "preview")?, + transport: TransportModel { + metronome: false, + transport: jack.read().unwrap().transport(), + jack: jack.clone(), + clock: clock.clone() + }, + }.into(), None, None)) } } -impl From<&Arc>> for SequencerView { - fn from (model: &Arc>) -> Self { +impl From for SequencerView { + fn from (model: SequencerModel) -> Self { Self { split: 20, - transport: TransportView::from(&model.read().unwrap().transport), - phrases: PhrasePoolView::from(&model.read().unwrap().phrases), + transport: TransportView::from(&model.transport), + phrases: PhrasePoolView::from(&model.phrases), editor: PhraseEditor::new(), - model: model.clone(), + model, } } } /// Root level object for standalone `tek_sequencer`. -/// Also embeddable, in which case the `player` is used for preview. pub struct SequencerView { - pub model: Arc>, + pub model: SequencerModel, /// Displays the JACK transport. pub transport: TransportView, /// Displays the phrase pool @@ -74,7 +49,16 @@ impl Content for SequencerView { fn content (&self) -> impl Widget { col!( self.transport, - Split::right(20, widget(&self.phrases), widget(&self.editor)).min_y(20) + Split::right(20, + widget(&self.phrases), + widget(&self.editor) + ).min_y(20) ) } } + +impl Audio for SequencerView { + fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { + self.model.process(client, scope) + } +} diff --git a/crates/tek_tui/src/tui_transport.rs b/crates/tek_tui/src/tui_transport.rs index af5bc988..97ccde76 100644 --- a/crates/tek_tui/src/tui_transport.rs +++ b/crates/tek_tui/src/tui_transport.rs @@ -1,30 +1,16 @@ use crate::*; -pub type TransportApp = AppContainer< - Tui, - Transport, - TransportView, - TransportViewCommand, - TransportAudio, - TransportStatusBar ->; +pub type TransportApp = AppView, TransportViewCommand>; -impl TryFrom<&Arc>> for TransportApp { +impl TryFrom<&Arc>> for TransportApp { type Error = Box; fn try_from (jack: &Arc>) -> Usually { - let model = Arc::new(RwLock::new(TransportModel { - metronome: false, - transport: jack.read().unwrap().transport(), - jack: jack.clone(), - clock: Arc::new(Clock::from(Instant::default())) - })); - Ok(Self::new( - &model, - TransportView::from(&model), - TransportAudio(model.clone()), - None, - None, - )) + Ok(Self::new(TransportModel { + metronome: false, + transport: jack.read().unwrap().transport(), + jack: jack.clone(), + clock: Arc::new(Clock::from(Instant::default())) + }.into(), None, None)) } } @@ -38,42 +24,14 @@ pub struct TransportView { pub size: Measure, } -impl From<&Arc>> for TransportView { - fn from (model: &Arc>) -> Self { +impl From for TransportView { + fn from (model: TransportModel) -> Self { Self { _engine: Default::default(), focused: false, focus: TransportViewFocus::PlayPause, size: Measure::new(), - model: model.clone() - } - } -} - -impl TransportView { - pub fn new (jack: &Arc>, clock: Option<&Arc>) -> Self { - Self { - _engine: Default::default(), - focused: false, - focus: TransportViewFocus::PlayPause, - size: Measure::new(), - model: Transport { - metronome: false, - transport: jack.read().unwrap().transport(), - jack: jack.clone(), - clock: if let Some(clock) = clock { - clock.clone() - } else { - let current = Instant::default(); - Arc::new(Clock { - playing: Some(TransportState::Stopped).into(), - started: None.into(), - quant: 24.into(), - sync: (current.timebase.ppq.get() * 4.).into(), - current, - }) - } - } + model } } } @@ -84,7 +42,7 @@ impl Content for TransportView { lay!( self.focus.wrap(self.focused, TransportViewFocus::PlayPause, &Styled( None, - match *self.model.clock.playing.read().unwrap() { + match *self.model.clock().playing.read().unwrap() { Some(TransportState::Rolling) => "▶ PLAYING", Some(TransportState::Starting) => "READY ...", Some(TransportState::Stopped) => "⏹ STOPPED", @@ -94,23 +52,29 @@ impl Content for TransportView { row!( self.focus.wrap(self.focused, TransportViewFocus::Bpm, &Outset::X(1u16, { - let bpm = self.model.clock.timebase().bpm.get(); + let bpm = self.model.clock().timebase().bpm.get(); row! { "BPM ", format!("{}.{:03}", bpm as usize, (bpm * 1000.0) % 1000.0) } })), //let quant = self.focus.wrap(self.focused, TransportViewFocus::Quant, &Outset::X(1u16, row! { //"QUANT ", ppq_to_name(self.quant as usize) //})), self.focus.wrap(self.focused, TransportViewFocus::Sync, &Outset::X(1u16, row! { - "SYNC ", pulses_to_name(self.model.clock.sync.get() as usize) + "SYNC ", pulses_to_name(self.model.clock().sync.get() as usize) })) ).align_w().fill_x(), self.focus.wrap(self.focused, TransportViewFocus::Clock, &{ - let time1 = self.model.clock.current.format_beat(); - let time2 = self.model.clock.current.usec.format_msu(); + let time1 = self.model.clock().current.format_beat(); + let time2 = self.model.clock().current.usec.format_msu(); row!("B" ,time1.as_str(), " T", time2.as_str()).outset_x(1) }).align_e().fill_x(), ).fill_x().bg(Color::Rgb(40, 50, 30)) } } + +impl Audio for TransportView { + fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { + self.model.process(client, scope) + } +}