wip: refactor pt.24: 45 errors

This commit is contained in:
🪞👃🪞 2024-11-14 15:09:30 +01:00
parent ff4698d046
commit da074eb5fa
5 changed files with 120 additions and 199 deletions

View file

@ -52,63 +52,48 @@ submod! {
tui_transport_foc tui_transport_foc
} }
pub struct AppContainer<E, M, V, C, A, S> pub struct AppView<E, A, C>
where where
E: Engine, E: Engine,
M: Send + Sync, A: Widget<Engine = E> + Handle<E> + Audio,
V: Widget<Engine = E> + Handle<E>, C: Command<A>,
C: Command<V>,
A: Audio,
S: StatusBar<E>
{ {
pub app: A,
pub cursor: (usize, usize), pub cursor: (usize, usize),
pub entered: bool, pub entered: bool,
pub menu_bar: Option<MenuBar<E, V, C>>, pub menu_bar: Option<MenuBar<E, A, C>>,
pub status_bar: Option<S>, pub status_bar: Option<Box<dyn Widget<Engine = E>>>,
pub history: Vec<C>, pub history: Vec<C>,
pub size: Measure<E>, pub size: Measure<E>,
pub model: Arc<RwLock<M>>,
pub view: V,
pub audio: A,
} }
impl<E, M, V, C, A, S> AppContainer<E, M, V, C, A, S> impl<E, A, C> AppView<E, A, C>
where where
E: Engine, E: Engine,
M: Send + Sync, A: Widget<Engine = E> + Handle<E> + Audio,
V: Widget<Engine = E> + Handle<E>, C: Command<A>
C: Command<V>,
A: Audio,
S: StatusBar<E>
{ {
pub fn new ( pub fn new (
model: &Arc<RwLock<M>>, app: A,
view: V, menu_bar: Option<MenuBar<E, A, C>>,
audio: A, status_bar: Option<Box<dyn Widget<Engine = E>>>,
menu_bar: Option<MenuBar<E, V, C>>,
status_bar: Option<S>,
) -> Self { ) -> Self {
Self { Self {
app,
cursor: (0, 0), cursor: (0, 0),
entered: false, entered: false,
history: vec![], history: vec![],
size: Measure::new(), size: Measure::new(),
model: model.clone(),
view,
audio,
menu_bar, menu_bar,
status_bar, status_bar,
} }
} }
} }
impl<M, V, C, A, S> Content for AppContainer<Tui, M, V, C, A, S> impl<A, C> Content for AppView<Tui, A, C>
where where
M: Send + Sync, A: Widget<Engine = Tui> + Handle<Tui> + Audio,
V: Widget<Engine = Tui> + Handle<Tui>, C: Command<A>
C: Command<V>,
A: Audio,
S: StatusBar<Tui>,
{ {
type Engine = Tui; type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> { fn content (&self) -> impl Widget<Engine = Tui> {
@ -124,14 +109,14 @@ where
Split::up( Split::up(
if self.status_bar.is_some() { 1 } else { 0 }, if self.status_bar.is_some() { 1 } else { 0 },
widget(&self.status_bar), widget(&self.status_bar),
widget(&self.view) widget(&self.app)
) )
) )
} }
} }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum AppContainerCommand<T: std::fmt::Debug + Copy + Clone> { pub enum AppViewCommand<T: std::fmt::Debug + Copy + Clone> {
Focus(FocusCommand), Focus(FocusCommand),
Undo, Undo,
Redo, Redo,
@ -139,20 +124,17 @@ pub enum AppContainerCommand<T: std::fmt::Debug + Copy + Clone> {
} }
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub enum AppContainerFocus<F: std::fmt::Debug + Copy + Clone + PartialEq> { pub enum AppViewFocus<F: std::fmt::Debug + Copy + Clone + PartialEq> {
Menu, Menu,
Content(F), Content(F),
} }
impl<T, U, C, A, S> FocusGrid for AppContainer<Tui, T, U, C, A, S> impl<A, C> FocusGrid for AppView<Tui, A, C>
where where
T: Send + Sync, A: Widget<Engine = Tui> + Handle<Tui> + Audio + FocusGrid,
U: From<Arc<RwLock<T>>> + Widget<Engine = Tui> + Handle<Tui> + FocusGrid, C: Command<A>
C: Command<U>,
A: From<Arc<RwLock<T>>> + Audio,
S: From<Arc<RwLock<T>>> + StatusBar<Tui>
{ {
type Item = AppContainerFocus<<U as FocusGrid>::Item>; type Item = AppViewFocus<<A as FocusGrid>::Item>;
fn cursor (&self) -> (usize, usize) { fn cursor (&self) -> (usize, usize) {
self.cursor self.cursor
} }
@ -181,9 +163,9 @@ where
} }
fn layout (&self) -> &[&[Self::Item]] { fn layout (&self) -> &[&[Self::Item]] {
&[ &[
&[AppContainerFocus::Menu], &[AppViewFocus::Menu],
FocusGrid::layout(&self.ui) FocusGrid::layout(&self.ui)
//&[AppContainerFocus::Content(())], //&[AppViewFocus::Content(())],
] ]
} }
fn update_focus (&mut self) { fn update_focus (&mut self) {

View file

@ -1,18 +1,11 @@
use crate::*; use crate::*;
pub type ArrangerApp = AppContainer< pub type ArrangerApp<E: Engine> = AppView<E, ArrangerView<E>, ArrangerViewCommand>;
Tui,
ArrangerModel,
ArrangerView<Tui>,
ArrangerViewCommand,
ArrangerAudio,
ArrangerStatusBar
>;
impl TryFrom<&Arc<RwLock<JackClient>>> for ArrangerApp { impl TryFrom<&Arc<RwLock<JackClient>>> for ArrangerApp<Tui> {
type Error = Box<dyn std::error::Error>; type Error = Box<dyn std::error::Error>;
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> { fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
let model = Arc::new(RwLock::new(ArrangerModel { Ok(Self::new(ArrangerModel {
name: Arc::new(RwLock::new(String::new())), name: Arc::new(RwLock::new(String::new())),
phrases: vec![], phrases: vec![],
scenes: vec![], scenes: vec![],
@ -23,22 +16,15 @@ impl TryFrom<&Arc<RwLock<JackClient>>> for ArrangerApp {
clock: Arc::new(Clock::from(Instant::default())), clock: Arc::new(Clock::from(Instant::default())),
jack: jack.clone(), jack: jack.clone(),
}, },
})); }.into(), None, None))
Ok(Self::new(
&model,
ArrangerView::from(&model),
ArrangerAudio(model.clone()),
None,
None
))
} }
} }
impl<E: Engine> From<&Arc<RwLock<ArrangerModel>>> for ArrangerView<E> { impl<E: Engine> From<ArrangerModel> for ArrangerView<E> {
fn from (model: &Arc<RwLock<ArrangerModel>>) -> Self { fn from (model: ArrangerModel) -> Self {
let mut view = Self { let mut view = Self {
model: model.clone(), model,
sequencer: SequencerView::from(&model.read().unwrap().sequencer), sequencer: SequencerView::from(&model.sequencer),
split: 20, split: 20,
selected: ArrangerFocus::Clip(0, 0), selected: ArrangerFocus::Clip(0, 0),
mode: ArrangerMode::Vertical(2), mode: ArrangerMode::Vertical(2),
@ -96,31 +82,6 @@ impl ArrangerMode {
} }
} }
impl<E: Engine> Audio for ArrangerView<E> {
#[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. /// Layout for standalone arranger app.
impl Content for ArrangerView<Tui> { impl Content for ArrangerView<Tui> {
type Engine = Tui; type Engine = Tui;
@ -172,11 +133,13 @@ impl<E: Engine> ArrangerView<E> {
} }
pub fn activate (&mut self) { pub fn activate (&mut self) {
let scenes = self.model.scenes();
let tracks = self.model.tracks_mut();
match self.selected { match self.selected {
ArrangerFocus::Scene(s) => { 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 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() { if player.phrase.is_some() || clip.is_some() {
player.enqueue_next(clip); player.enqueue_next(clip);
} }
@ -188,8 +151,7 @@ impl<E: Engine> ArrangerView<E> {
//} //}
}, },
ArrangerFocus::Clip(t, s) => { ArrangerFocus::Clip(t, s) => {
let clip = self.model.scenes()[s].clips[t].as_ref(); tracks[t].player.enqueue_next(scenes[s].clips[t]);
self.model.tracks_mut()[t].player.enqueue_next(clip);
}, },
_ => {} _ => {}
} }
@ -221,7 +183,7 @@ impl<E: Engine> ArrangerView<E> {
self.color = ItemColor::random_dark() self.color = ItemColor::random_dark()
}, },
ArrangerFocus::Track(t) => { ArrangerFocus::Track(t) => {
self.model.tracks_mu()[t].color = ItemColor::random() self.model.tracks_mut()[t].color = ItemColor::random()
}, },
ArrangerFocus::Scene(s) => { ArrangerFocus::Scene(s) => {
self.model.scenes_mut()[s].color = ItemColor::random() self.model.scenes_mut()[s].color = ItemColor::random()
@ -243,3 +205,31 @@ impl<E: Engine> ArrangerView<E> {
self.selected_scene()?.clips.get(self.selected.track()?)?.clone() self.selected_scene()?.clips.get(self.selected.track()?)?.clone()
} }
} }
impl Audio for ArrangerView<Tui> {
#[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
}
}

View file

@ -2,7 +2,8 @@ use crate::*;
pub struct PhrasePoolView<E: Engine> { pub struct PhrasePoolView<E: Engine> {
_engine: PhantomData<E>, _engine: PhantomData<E>,
pub model: PhrasePool, /// Collection of phrases
pub model: Vec<Arc<RwLock<Phrase>>>,
/// Selected phrase /// Selected phrase
pub phrase: usize, pub phrase: usize,
/// Scroll offset /// Scroll offset
@ -24,7 +25,7 @@ pub enum PhrasePoolMode {
} }
impl<E: Engine> PhrasePoolView<E> { impl<E: Engine> PhrasePoolView<E> {
pub fn new (model: PhrasePool) -> Self { pub fn new (model: Vec<Arc<RwLock<Phrase>>>) -> Self {
Self { Self {
_engine: Default::default(), _engine: Default::default(),
scroll: 0, scroll: 0,

View file

@ -1,64 +1,39 @@
use crate::*; use crate::*;
pub type SequencerApp = AppContainer< pub type SequencerApp<E: Engine> = AppView<E, SequencerView<E>, SequencerViewCommand>;
Tui,
SequencerModel,
SequencerView<Tui>,
SequencerViewCommand,
SequencerAudio,
SequencerStatusBar
>;
impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerApp { impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerApp<Tui> {
type Error = Box<dyn std::error::Error>; type Error = Box<dyn std::error::Error>;
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> { fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
let clock = Arc::new(Clock::from(Instant::default())); let clock = Arc::new(Clock::from(Instant::default()));
Ok(Self::new(SequencerModel {
let transport = Arc::new(RwLock::new(tek_api::Transport { phrases: vec![],
metronome: false, player: MIDIPlayer::new(jack, &clock, "preview")?,
transport: jack.read().unwrap().transport(), transport: TransportModel {
jack: jack.clone(), metronome: false,
clock: clock.clone() transport: jack.read().unwrap().transport(),
})); jack: jack.clone(),
clock: clock.clone()
let phrases = Arc::new(RwLock::new(PhrasePool { },
phrases: vec![] // FIXME }.into(), None, None))
}));
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,
))
} }
} }
impl<E: Engine> From<&Arc<RwLock<SequencerModel>>> for SequencerView<E> { impl<E: Engine> From<SequencerModel> for SequencerView<E> {
fn from (model: &Arc<RwLock<SequencerModel>>) -> Self { fn from (model: SequencerModel) -> Self {
Self { Self {
split: 20, split: 20,
transport: TransportView::from(&model.read().unwrap().transport), transport: TransportView::from(&model.transport),
phrases: PhrasePoolView::from(&model.read().unwrap().phrases), phrases: PhrasePoolView::from(&model.phrases),
editor: PhraseEditor::new(), editor: PhraseEditor::new(),
model: model.clone(), model,
} }
} }
} }
/// Root level object for standalone `tek_sequencer`. /// Root level object for standalone `tek_sequencer`.
/// Also embeddable, in which case the `player` is used for preview.
pub struct SequencerView<E: Engine> { pub struct SequencerView<E: Engine> {
pub model: Arc<RwLock<SequencerModel>>, pub model: SequencerModel,
/// Displays the JACK transport. /// Displays the JACK transport.
pub transport: TransportView<E>, pub transport: TransportView<E>,
/// Displays the phrase pool /// Displays the phrase pool
@ -74,7 +49,16 @@ impl Content for SequencerView<Tui> {
fn content (&self) -> impl Widget<Engine = Tui> { fn content (&self) -> impl Widget<Engine = Tui> {
col!( col!(
self.transport, 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<Tui> {
fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
self.model.process(client, scope)
}
}

View file

@ -1,30 +1,16 @@
use crate::*; use crate::*;
pub type TransportApp = AppContainer< pub type TransportApp<E: Engine> = AppView<E, TransportView<E>, TransportViewCommand>;
Tui,
Transport,
TransportView<Tui>,
TransportViewCommand,
TransportAudio,
TransportStatusBar
>;
impl TryFrom<&Arc<RwLock<JackClient>>> for TransportApp { impl TryFrom<&Arc<RwLock<JackClient>>> for TransportApp<Tui> {
type Error = Box<dyn std::error::Error>; type Error = Box<dyn std::error::Error>;
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> { fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
let model = Arc::new(RwLock::new(TransportModel { Ok(Self::new(TransportModel {
metronome: false, metronome: false,
transport: jack.read().unwrap().transport(), transport: jack.read().unwrap().transport(),
jack: jack.clone(), jack: jack.clone(),
clock: Arc::new(Clock::from(Instant::default())) clock: Arc::new(Clock::from(Instant::default()))
})); }.into(), None, None))
Ok(Self::new(
&model,
TransportView::from(&model),
TransportAudio(model.clone()),
None,
None,
))
} }
} }
@ -38,42 +24,14 @@ pub struct TransportView<E: Engine> {
pub size: Measure<E>, pub size: Measure<E>,
} }
impl<E: Engine> From<&Arc<RwLock<Transport>>> for TransportView<E> { impl<E: Engine> From<TransportModel> for TransportView<E> {
fn from (model: &Arc<RwLock<Transport>>) -> Self { fn from (model: TransportModel) -> Self {
Self { Self {
_engine: Default::default(), _engine: Default::default(),
focused: false, focused: false,
focus: TransportViewFocus::PlayPause, focus: TransportViewFocus::PlayPause,
size: Measure::new(), size: Measure::new(),
model: model.clone() model
}
}
}
impl<E: Engine> TransportView<E> {
pub fn new (jack: &Arc<RwLock<JackClient>>, clock: Option<&Arc<Clock>>) -> 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,
})
}
}
} }
} }
} }
@ -84,7 +42,7 @@ impl Content for TransportView<Tui> {
lay!( lay!(
self.focus.wrap(self.focused, TransportViewFocus::PlayPause, &Styled( self.focus.wrap(self.focused, TransportViewFocus::PlayPause, &Styled(
None, None,
match *self.model.clock.playing.read().unwrap() { match *self.model.clock().playing.read().unwrap() {
Some(TransportState::Rolling) => "▶ PLAYING", Some(TransportState::Rolling) => "▶ PLAYING",
Some(TransportState::Starting) => "READY ...", Some(TransportState::Starting) => "READY ...",
Some(TransportState::Stopped) => "⏹ STOPPED", Some(TransportState::Stopped) => "⏹ STOPPED",
@ -94,23 +52,29 @@ impl Content for TransportView<Tui> {
row!( row!(
self.focus.wrap(self.focused, TransportViewFocus::Bpm, &Outset::X(1u16, { 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) } 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! { //let quant = self.focus.wrap(self.focused, TransportViewFocus::Quant, &Outset::X(1u16, row! {
//"QUANT ", ppq_to_name(self.quant as usize) //"QUANT ", ppq_to_name(self.quant as usize)
//})), //})),
self.focus.wrap(self.focused, TransportViewFocus::Sync, &Outset::X(1u16, row! { 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(), ).align_w().fill_x(),
self.focus.wrap(self.focused, TransportViewFocus::Clock, &{ self.focus.wrap(self.focused, TransportViewFocus::Clock, &{
let time1 = self.model.clock.current.format_beat(); let time1 = self.model.clock().current.format_beat();
let time2 = self.model.clock.current.usec.format_msu(); let time2 = self.model.clock().current.usec.format_msu();
row!("B" ,time1.as_str(), " T", time2.as_str()).outset_x(1) row!("B" ,time1.as_str(), " T", time2.as_str()).outset_x(1)
}).align_e().fill_x(), }).align_e().fill_x(),
).fill_x().bg(Color::Rgb(40, 50, 30)) ).fill_x().bg(Color::Rgb(40, 50, 30))
} }
} }
impl Audio for TransportView<Tui> {
fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
self.model.process(client, scope)
}
}