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
}
pub struct AppContainer<E, M, V, C, A, S>
pub struct AppView<E, A, C>
where
E: Engine,
M: Send + Sync,
V: Widget<Engine = E> + Handle<E>,
C: Command<V>,
A: Audio,
S: StatusBar<E>
A: Widget<Engine = E> + Handle<E> + Audio,
C: Command<A>,
{
pub app: A,
pub cursor: (usize, usize),
pub entered: bool,
pub menu_bar: Option<MenuBar<E, V, C>>,
pub status_bar: Option<S>,
pub menu_bar: Option<MenuBar<E, A, C>>,
pub status_bar: Option<Box<dyn Widget<Engine = E>>>,
pub history: Vec<C>,
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
E: Engine,
M: Send + Sync,
V: Widget<Engine = E> + Handle<E>,
C: Command<V>,
A: Audio,
S: StatusBar<E>
A: Widget<Engine = E> + Handle<E> + Audio,
C: Command<A>
{
pub fn new (
model: &Arc<RwLock<M>>,
view: V,
audio: A,
menu_bar: Option<MenuBar<E, V, C>>,
status_bar: Option<S>,
app: A,
menu_bar: Option<MenuBar<E, A, C>>,
status_bar: Option<Box<dyn Widget<Engine = E>>>,
) -> Self {
Self {
app,
cursor: (0, 0),
entered: false,
history: vec![],
size: Measure::new(),
model: model.clone(),
view,
audio,
menu_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
M: Send + Sync,
V: Widget<Engine = Tui> + Handle<Tui>,
C: Command<V>,
A: Audio,
S: StatusBar<Tui>,
A: Widget<Engine = Tui> + Handle<Tui> + Audio,
C: Command<A>
{
type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> {
@ -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<T: std::fmt::Debug + Copy + Clone> {
pub enum AppViewCommand<T: std::fmt::Debug + Copy + Clone> {
Focus(FocusCommand),
Undo,
Redo,
@ -139,20 +124,17 @@ pub enum AppContainerCommand<T: std::fmt::Debug + Copy + Clone> {
}
#[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,
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
T: Send + Sync,
U: From<Arc<RwLock<T>>> + Widget<Engine = Tui> + Handle<Tui> + FocusGrid,
C: Command<U>,
A: From<Arc<RwLock<T>>> + Audio,
S: From<Arc<RwLock<T>>> + StatusBar<Tui>
A: Widget<Engine = Tui> + Handle<Tui> + Audio + FocusGrid,
C: Command<A>
{
type Item = AppContainerFocus<<U as FocusGrid>::Item>;
type Item = AppViewFocus<<A as FocusGrid>::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) {

View file

@ -1,18 +1,11 @@
use crate::*;
pub type ArrangerApp = AppContainer<
Tui,
ArrangerModel,
ArrangerView<Tui>,
ArrangerViewCommand,
ArrangerAudio,
ArrangerStatusBar
>;
pub type ArrangerApp<E: Engine> = AppView<E, ArrangerView<E>, ArrangerViewCommand>;
impl TryFrom<&Arc<RwLock<JackClient>>> for ArrangerApp {
impl TryFrom<&Arc<RwLock<JackClient>>> for ArrangerApp<Tui> {
type Error = Box<dyn std::error::Error>;
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())),
phrases: vec![],
scenes: vec![],
@ -23,22 +16,15 @@ impl TryFrom<&Arc<RwLock<JackClient>>> 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<E: Engine> From<&Arc<RwLock<ArrangerModel>>> for ArrangerView<E> {
fn from (model: &Arc<RwLock<ArrangerModel>>) -> Self {
impl<E: Engine> From<ArrangerModel> for ArrangerView<E> {
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<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.
impl Content for ArrangerView<Tui> {
type Engine = Tui;
@ -172,11 +133,13 @@ impl<E: Engine> ArrangerView<E> {
}
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<E: Engine> ArrangerView<E> {
//}
},
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<E: Engine> ArrangerView<E> {
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<E: Engine> ArrangerView<E> {
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> {
_engine: PhantomData<E>,
pub model: PhrasePool,
/// Collection of phrases
pub model: Vec<Arc<RwLock<Phrase>>>,
/// Selected phrase
pub phrase: usize,
/// Scroll offset
@ -24,7 +25,7 @@ pub enum PhrasePoolMode {
}
impl<E: Engine> PhrasePoolView<E> {
pub fn new (model: PhrasePool) -> Self {
pub fn new (model: Vec<Arc<RwLock<Phrase>>>) -> Self {
Self {
_engine: Default::default(),
scroll: 0,

View file

@ -1,64 +1,39 @@
use crate::*;
pub type SequencerApp = AppContainer<
Tui,
SequencerModel,
SequencerView<Tui>,
SequencerViewCommand,
SequencerAudio,
SequencerStatusBar
>;
pub type SequencerApp<E: Engine> = AppView<E, SequencerView<E>, SequencerViewCommand>;
impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerApp {
impl TryFrom<&Arc<RwLock<JackClient>>> for SequencerApp<Tui> {
type Error = Box<dyn std::error::Error>;
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
let clock = Arc::new(Clock::from(Instant::default()));
let transport = Arc::new(RwLock::new(tek_api::Transport {
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()
}));
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,
))
},
}.into(), None, None))
}
}
impl<E: Engine> From<&Arc<RwLock<SequencerModel>>> for SequencerView<E> {
fn from (model: &Arc<RwLock<SequencerModel>>) -> Self {
impl<E: Engine> From<SequencerModel> for SequencerView<E> {
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<E: Engine> {
pub model: Arc<RwLock<SequencerModel>>,
pub model: SequencerModel,
/// Displays the JACK transport.
pub transport: TransportView<E>,
/// Displays the phrase pool
@ -74,7 +49,16 @@ impl Content for SequencerView<Tui> {
fn content (&self) -> impl Widget<Engine = Tui> {
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<Tui> {
fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
self.model.process(client, scope)
}
}

View file

@ -1,30 +1,16 @@
use crate::*;
pub type TransportApp = AppContainer<
Tui,
Transport,
TransportView<Tui>,
TransportViewCommand,
TransportAudio,
TransportStatusBar
>;
pub type TransportApp<E: Engine> = AppView<E, TransportView<E>, TransportViewCommand>;
impl TryFrom<&Arc<RwLock<JackClient>>> for TransportApp {
impl TryFrom<&Arc<RwLock<JackClient>>> for TransportApp<Tui> {
type Error = Box<dyn std::error::Error>;
fn try_from (jack: &Arc<RwLock<JackClient>>) -> Usually<Self> {
let model = Arc::new(RwLock::new(TransportModel {
Ok(Self::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,
))
}.into(), None, None))
}
}
@ -38,42 +24,14 @@ pub struct TransportView<E: Engine> {
pub size: Measure<E>,
}
impl<E: Engine> From<&Arc<RwLock<Transport>>> for TransportView<E> {
fn from (model: &Arc<RwLock<Transport>>) -> Self {
impl<E: Engine> From<TransportModel> for TransportView<E> {
fn from (model: TransportModel) -> Self {
Self {
_engine: Default::default(),
focused: false,
focus: TransportViewFocus::PlayPause,
size: Measure::new(),
model: model.clone()
}
}
}
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,
})
}
}
model
}
}
}
@ -84,7 +42,7 @@ impl Content for TransportView<Tui> {
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<Tui> {
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<Tui> {
fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
self.model.process(client, scope)
}
}