From cd8a808c217dec7bd680932619f1609294460d58 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Tue, 10 Sep 2024 11:45:18 +0300 Subject: [PATCH] Process -> Audio; Layers2 -> Layers --- crates/tek_core/src/audio.rs | 294 ++++++++++++++++--- crates/tek_core/src/audio/device.rs | 70 ----- crates/tek_core/src/audio/event.rs | 71 ----- crates/tek_core/src/audio/ports.rs | 94 ------ crates/tek_core/src/engine.rs | 4 + crates/tek_core/src/engine/collect.rs | 25 +- crates/tek_core/src/engine/component.rs | 10 +- crates/tek_core/src/tui.rs | 10 + crates/tek_core/src/tui/tui_layout.rs | 58 +--- crates/tek_mixer/src/mixer.rs | 2 +- crates/tek_mixer/src/plugin.rs | 2 +- crates/tek_mixer/src/sample_add.rs | 6 +- crates/tek_mixer/src/sampler.rs | 19 +- crates/tek_mixer/src/sampler_view.rs | 6 +- crates/tek_sequencer/src/arranger_view_h.rs | 10 +- crates/tek_sequencer/src/arranger_view_v.rs | 35 +-- crates/tek_sequencer/src/sequencer_view_h.rs | 14 +- crates/tek_sequencer/src/transport.rs | 2 +- crates/tek_sequencer/src/transport_view.rs | 2 +- crates/tek_test/src/main.rs | 14 +- 20 files changed, 334 insertions(+), 414 deletions(-) delete mode 100644 crates/tek_core/src/audio/device.rs delete mode 100644 crates/tek_core/src/audio/event.rs delete mode 100644 crates/tek_core/src/audio/ports.rs diff --git a/crates/tek_core/src/audio.rs b/crates/tek_core/src/audio.rs index 79c78fe8..27ca19c6 100644 --- a/crates/tek_core/src/audio.rs +++ b/crates/tek_core/src/audio.rs @@ -1,10 +1,31 @@ use jack::*; use crate::*; -submod! { device event ports } +/// Trait for things that have a JACK process callback. +pub trait Audio { + fn process (&mut self, _: &Client, _: &ProcessScope) -> Control { + Control::Continue + } +} + +/// Trait for things that may expose JACK ports. +pub trait Ports { + fn audio_ins (&self) -> Usually>> { + Ok(vec![]) + } + fn audio_outs (&self) -> Usually>> { + Ok(vec![]) + } + fn midi_ins (&self) -> Usually>> { + Ok(vec![]) + } + fn midi_outs (&self) -> Usually>> { + Ok(vec![]) + } +} /// A UI component that may be associated with a JACK client by the `Jack` factory. -pub trait Device: Component + Process { +pub trait Device: Component + Audio { /// Perform type erasure for collecting heterogeneous devices. fn boxed (self) -> Box> where Self: Sized + 'static { Box::new(self) @@ -12,13 +33,7 @@ pub trait Device: Component + Process { } /// All things that implement the required traits can be treated as `Device`. -impl + Process> Device for W {} - -//impl<'a, E: Engine> Render for Box + 'a> { - //fn render (&self, to: &mut E) -> Perhaps { - //(**self).render(to) - //} -//} +impl + Audio> Device for W {} /// Wraps [Client] or [DynamicAsyncClient] in place. pub enum JackClient { @@ -57,52 +72,24 @@ impl JackClient { ClosureProcessHandler::new(Box::new({ let state = state.clone(); move|c: &Client, s: &ProcessScope|process(&state, c, s) - }) as BoxedProcessHandler) + }) as BoxedAudioHandler) )?) }) } } pub type DynamicAsyncClient = - AsyncClient; + AsyncClient; -type DynamicProcessHandler = - ClosureProcessHandler; +type DynamicAudioHandler = + ClosureProcessHandler; -pub type BoxedProcessHandler = +pub type BoxedAudioHandler = Box Control + Send>; -/// Trait for things that have a JACK process callback. -pub trait Process { - fn process (&mut self, _: &Client, _: &ProcessScope) -> Control { - Control::Continue - } -} - -/// Define the JACK process callback associated with a struct. -#[macro_export] macro_rules! process { - ($T:ty) => { - impl Process for $T {} - }; - ($T:ty |$self:ident, $c:ident, $s:ident|$block:tt) => { - impl Process for $T { - fn process (&mut $self, $c: &Client, $s: &ProcessScope) -> Control { - $block - } - } - }; - ($T:ty = $process:path) => { - impl Process for $T { - fn process (&mut self, c: &Client, s: &ProcessScope) -> Control { - $process(self, c, s) - } - } - } -} - /// Just run thing with JACK. Returns the activated client. pub fn jack_run (name: &str, app: &Arc>) -> Usually - where T: Handle + Process + Send + Sync + 'static + where T: Handle + Audio + Send + Sync + 'static { let options = ClientOptions::NO_START_SERVER; let (client, _status) = Client::new(name, options)?; @@ -120,7 +107,7 @@ pub fn jack_run (name: &str, app: &Arc>) -> Usually, pub audio_outs: Vec, } + impl Jack { pub fn new (name: &str) -> Usually { Ok(Self { @@ -180,7 +168,7 @@ impl Jack { move|c: &Client, s: &ProcessScope|{ state.write().unwrap().process(c, s) } - }) as BoxedProcessHandler) + }) as BoxedAudioHandler) )?; Ok(JackDevice { ports: UnownedJackPorts { @@ -230,3 +218,219 @@ fn query_ports ( ports }) } + +/// Notification handler used by the [Jack] factory +/// when constructing [JackDevice]s. +pub type DynamicNotifications = + Notifications>; + +#[derive(Debug)] +/// Event enum for JACK events. +pub enum JackEvent { + ThreadInit, + Shutdown(ClientStatus, String), + Freewheel(bool), + SampleRate(Frames), + ClientRegistration(String, bool), + PortRegistration(PortId, bool), + PortRename(PortId, String, String), + PortsConnected(PortId, PortId, bool), + GraphReorder, + XRun, +} + +/// Generic notification handler that emits [JackEvent] +pub struct Notifications(pub T); + +impl NotificationHandler for Notifications { + fn thread_init (&self, _: &Client) { + self.0(JackEvent::ThreadInit); + } + + fn shutdown (&mut self, status: ClientStatus, reason: &str) { + self.0(JackEvent::Shutdown(status, reason.into())); + } + + fn freewheel (&mut self, _: &Client, enabled: bool) { + self.0(JackEvent::Freewheel(enabled)); + } + + fn sample_rate (&mut self, _: &Client, frames: Frames) -> Control { + self.0(JackEvent::SampleRate(frames)); + Control::Quit + } + + fn client_registration (&mut self, _: &Client, name: &str, reg: bool) { + self.0(JackEvent::ClientRegistration(name.into(), reg)); + } + + fn port_registration (&mut self, _: &Client, id: PortId, reg: bool) { + self.0(JackEvent::PortRegistration(id, reg)); + } + + fn port_rename (&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control { + self.0(JackEvent::PortRename(id, old.into(), new.into())); + Control::Continue + } + + fn ports_connected (&mut self, _: &Client, a: PortId, b: PortId, are: bool) { + self.0(JackEvent::PortsConnected(a, b, are)); + } + + fn graph_reorder (&mut self, _: &Client) -> Control { + self.0(JackEvent::GraphReorder); + Control::Continue + } + + fn xrun (&mut self, _: &Client) -> Control { + self.0(JackEvent::XRun); + Control::Continue + } +} + +/// A [Device] bound to a JACK client and a set of ports. +pub struct JackDevice { + /// The active JACK client of this device. + pub client: DynamicAsyncClient, + /// The device state, encapsulated for sharing between threads. + pub state: Arc>>>, + /// Unowned copies of the device's JACK ports, for connecting to the device. + /// The "real" readable/writable `Port`s are owned by the `state`. + pub ports: UnownedJackPorts, +} + +impl std::fmt::Debug for JackDevice { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("JackDevice").field("ports", &self.ports).finish() + } +} + +impl Widget for JackDevice { + type Engine = E; + fn layout (&self, to: E::Area) -> Perhaps { + self.state.read().unwrap().layout(to) + } + fn render (&self, to: &mut E) -> Perhaps { + self.state.read().unwrap().render(to) + } +} + +impl Handle for JackDevice { + fn handle (&mut self, from: &E::HandleInput) -> Perhaps { + self.state.write().unwrap().handle(from) + } +} + +impl Ports for JackDevice { + fn audio_ins (&self) -> Usually>> { + Ok(self.ports.audio_ins.values().collect()) + } + fn audio_outs (&self) -> Usually>> { + Ok(self.ports.audio_outs.values().collect()) + } + fn midi_ins (&self) -> Usually>> { + Ok(self.ports.midi_ins.values().collect()) + } + fn midi_outs (&self) -> Usually>> { + Ok(self.ports.midi_outs.values().collect()) + } +} + +impl JackDevice { + /// Returns a locked mutex of the state's contents. + pub fn state (&self) -> LockResult>>> { + self.state.read() + } + /// Returns a locked mutex of the state's contents. + pub fn state_mut (&self) -> LockResult>>> { + self.state.write() + } + pub fn connect_midi_in (&self, index: usize, port: &Port) -> Usually<()> { + Ok(self.client.as_client().connect_ports(port, self.midi_ins()?[index])?) + } + pub fn connect_midi_out (&self, index: usize, port: &Port) -> Usually<()> { + Ok(self.client.as_client().connect_ports(self.midi_outs()?[index], port)?) + } + pub fn connect_audio_in (&self, index: usize, port: &Port) -> Usually<()> { + Ok(self.client.as_client().connect_ports(port, self.audio_ins()?[index])?) + } + pub fn connect_audio_out (&self, index: usize, port: &Port) -> Usually<()> { + Ok(self.client.as_client().connect_ports(self.audio_outs()?[index], port)?) + } +} + +/// Collection of JACK ports as [AudioIn]/[AudioOut]/[MidiIn]/[MidiOut]. +#[derive(Default, Debug)] +pub struct JackPorts { + pub audio_ins: BTreeMap>, + pub midi_ins: BTreeMap>, + pub audio_outs: BTreeMap>, + pub midi_outs: BTreeMap>, +} + +/// Collection of JACK ports as [Unowned]. +#[derive(Default, Debug)] +pub struct UnownedJackPorts { + pub audio_ins: BTreeMap>, + pub midi_ins: BTreeMap>, + pub audio_outs: BTreeMap>, + pub midi_outs: BTreeMap>, +} + +impl JackPorts { + pub fn clone_unowned (&self) -> UnownedJackPorts { + let mut unowned = UnownedJackPorts::default(); + for (name, port) in self.midi_ins.iter() { + unowned.midi_ins.insert(name.clone(), port.clone_unowned()); + } + for (name, port) in self.midi_outs.iter() { + unowned.midi_outs.insert(name.clone(), port.clone_unowned()); + } + for (name, port) in self.audio_ins.iter() { + unowned.audio_ins.insert(name.clone(), port.clone_unowned()); + } + for (name, port) in self.audio_outs.iter() { + unowned.audio_outs.insert(name.clone(), port.clone_unowned()); + } + unowned + } +} + +/// Implement the `Ports` trait. +#[macro_export] macro_rules! ports { + ($T:ty $({ $(audio: { + $(ins: |$ai_arg:ident|$ai_impl:expr,)? + $(outs: |$ao_arg:ident|$ao_impl:expr,)? + })? $(midi: { + $(ins: |$mi_arg:ident|$mi_impl:expr,)? + $(outs: |$mo_arg:ident|$mo_impl:expr,)? + })?})?) => { + impl Ports for $T {$( + $( + $(fn audio_ins <'a> (&'a self) -> Usually>> { + let cb = |$ai_arg:&'a Self|$ai_impl; + cb(self) + })? + )? + $( + $(fn audio_outs <'a> (&'a self) -> Usually>> { + let cb = (|$ao_arg:&'a Self|$ao_impl); + cb(self) + })? + )? + )? $( + $( + $(fn midi_ins <'a> (&'a self) -> Usually>> { + let cb = (|$mi_arg:&'a Self|$mi_impl); + cb(self) + })? + )? + $( + $(fn midi_outs <'a> (&'a self) -> Usually>> { + let cb = (|$mo_arg:&'a Self|$mo_impl); + cb(self) + })? + )? + )?} + }; +} diff --git a/crates/tek_core/src/audio/device.rs b/crates/tek_core/src/audio/device.rs deleted file mode 100644 index d34f24b1..00000000 --- a/crates/tek_core/src/audio/device.rs +++ /dev/null @@ -1,70 +0,0 @@ -use ::jack::*; -use crate::*; - -/// A [Device] bound to a JACK client and a set of ports. -pub struct JackDevice { - /// The active JACK client of this device. - pub client: DynamicAsyncClient, - /// The device state, encapsulated for sharing between threads. - pub state: Arc>>>, - /// Unowned copies of the device's JACK ports, for connecting to the device. - /// The "real" readable/writable `Port`s are owned by the `state`. - pub ports: UnownedJackPorts, -} -impl std::fmt::Debug for JackDevice { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("JackDevice").field("ports", &self.ports).finish() - } -} -impl Widget for JackDevice { - type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { - self.state.read().unwrap().layout(to) - } - fn render (&self, to: &mut E) -> Perhaps { - self.state.read().unwrap().render(to) - } -} -impl Handle for JackDevice { - fn handle (&mut self, from: &E::HandleInput) -> Perhaps { - self.state.write().unwrap().handle(from) - } -} -impl Ports for JackDevice { - fn audio_ins (&self) -> Usually>> { - Ok(self.ports.audio_ins.values().collect()) - } - fn audio_outs (&self) -> Usually>> { - Ok(self.ports.audio_outs.values().collect()) - } - fn midi_ins (&self) -> Usually>> { - Ok(self.ports.midi_ins.values().collect()) - } - fn midi_outs (&self) -> Usually>> { - Ok(self.ports.midi_outs.values().collect()) - } -} -impl JackDevice { - /// Returns a locked mutex of the state's contents. - pub fn state (&self) -> LockResult>>> { - self.state.read() - } - /// Returns a locked mutex of the state's contents. - pub fn state_mut (&self) -> LockResult>>> { - self.state.write() - } - pub fn connect_midi_in (&self, index: usize, port: &Port) -> Usually<()> { - Ok(self.client.as_client().connect_ports(port, self.midi_ins()?[index])?) - } - pub fn connect_midi_out (&self, index: usize, port: &Port) -> Usually<()> { - Ok(self.client.as_client().connect_ports(self.midi_outs()?[index], port)?) - } - pub fn connect_audio_in (&self, index: usize, port: &Port) -> Usually<()> { - Ok(self.client.as_client().connect_ports(port, self.audio_ins()?[index])?) - } - pub fn connect_audio_out (&self, index: usize, port: &Port) -> Usually<()> { - Ok(self.client.as_client().connect_ports(self.audio_outs()?[index], port)?) - } -} - - diff --git a/crates/tek_core/src/audio/event.rs b/crates/tek_core/src/audio/event.rs deleted file mode 100644 index 1edc007e..00000000 --- a/crates/tek_core/src/audio/event.rs +++ /dev/null @@ -1,71 +0,0 @@ -use ::jack::*; -use crate::*; - -/// Notification handler used by the [Jack] factory -/// when constructing [JackDevice]s. -pub type DynamicNotifications = - Notifications>; - -#[derive(Debug)] -/// Event enum for JACK events. -pub enum JackEvent { - ThreadInit, - Shutdown(ClientStatus, String), - Freewheel(bool), - SampleRate(Frames), - ClientRegistration(String, bool), - PortRegistration(PortId, bool), - PortRename(PortId, String, String), - PortsConnected(PortId, PortId, bool), - GraphReorder, - XRun, -} - -/// Generic notification handler that emits [JackEvent] -pub struct Notifications(pub T); - -impl NotificationHandler for Notifications { - fn thread_init (&self, _: &Client) { - self.0(JackEvent::ThreadInit); - } - - fn shutdown (&mut self, status: ClientStatus, reason: &str) { - self.0(JackEvent::Shutdown(status, reason.into())); - } - - fn freewheel (&mut self, _: &Client, enabled: bool) { - self.0(JackEvent::Freewheel(enabled)); - } - - fn sample_rate (&mut self, _: &Client, frames: Frames) -> Control { - self.0(JackEvent::SampleRate(frames)); - Control::Quit - } - - fn client_registration (&mut self, _: &Client, name: &str, reg: bool) { - self.0(JackEvent::ClientRegistration(name.into(), reg)); - } - - fn port_registration (&mut self, _: &Client, id: PortId, reg: bool) { - self.0(JackEvent::PortRegistration(id, reg)); - } - - fn port_rename (&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control { - self.0(JackEvent::PortRename(id, old.into(), new.into())); - Control::Continue - } - - fn ports_connected (&mut self, _: &Client, a: PortId, b: PortId, are: bool) { - self.0(JackEvent::PortsConnected(a, b, are)); - } - - fn graph_reorder (&mut self, _: &Client) -> Control { - self.0(JackEvent::GraphReorder); - Control::Continue - } - - fn xrun (&mut self, _: &Client) -> Control { - self.0(JackEvent::XRun); - Control::Continue - } -} diff --git a/crates/tek_core/src/audio/ports.rs b/crates/tek_core/src/audio/ports.rs deleted file mode 100644 index eac8ff39..00000000 --- a/crates/tek_core/src/audio/ports.rs +++ /dev/null @@ -1,94 +0,0 @@ -use ::jack::*; -use crate::*; - -/// Collection of JACK ports as [AudioIn]/[AudioOut]/[MidiIn]/[MidiOut]. -#[derive(Default, Debug)] -pub struct JackPorts { - pub audio_ins: BTreeMap>, - pub midi_ins: BTreeMap>, - pub audio_outs: BTreeMap>, - pub midi_outs: BTreeMap>, -} - -/// Collection of JACK ports as [Unowned]. -#[derive(Default, Debug)] -pub struct UnownedJackPorts { - pub audio_ins: BTreeMap>, - pub midi_ins: BTreeMap>, - pub audio_outs: BTreeMap>, - pub midi_outs: BTreeMap>, -} - -impl JackPorts { - pub fn clone_unowned (&self) -> UnownedJackPorts { - let mut unowned = UnownedJackPorts::default(); - for (name, port) in self.midi_ins.iter() { - unowned.midi_ins.insert(name.clone(), port.clone_unowned()); - } - for (name, port) in self.midi_outs.iter() { - unowned.midi_outs.insert(name.clone(), port.clone_unowned()); - } - for (name, port) in self.audio_ins.iter() { - unowned.audio_ins.insert(name.clone(), port.clone_unowned()); - } - for (name, port) in self.audio_outs.iter() { - unowned.audio_outs.insert(name.clone(), port.clone_unowned()); - } - unowned - } -} - -/// Trait for things that may expose JACK ports. -pub trait Ports { - fn audio_ins (&self) -> Usually>> { - Ok(vec![]) - } - fn audio_outs (&self) -> Usually>> { - Ok(vec![]) - } - fn midi_ins (&self) -> Usually>> { - Ok(vec![]) - } - fn midi_outs (&self) -> Usually>> { - Ok(vec![]) - } -} - -/// Implement the `Ports` trait. -#[macro_export] macro_rules! ports { - ($T:ty $({ $(audio: { - $(ins: |$ai_arg:ident|$ai_impl:expr,)? - $(outs: |$ao_arg:ident|$ao_impl:expr,)? - })? $(midi: { - $(ins: |$mi_arg:ident|$mi_impl:expr,)? - $(outs: |$mo_arg:ident|$mo_impl:expr,)? - })?})?) => { - impl Ports for $T {$( - $( - $(fn audio_ins <'a> (&'a self) -> Usually>> { - let cb = |$ai_arg:&'a Self|$ai_impl; - cb(self) - })? - )? - $( - $(fn audio_outs <'a> (&'a self) -> Usually>> { - let cb = (|$ao_arg:&'a Self|$ao_impl); - cb(self) - })? - )? - )? $( - $( - $(fn midi_ins <'a> (&'a self) -> Usually>> { - let cb = (|$mi_arg:&'a Self|$mi_impl); - cb(self) - })? - )? - $( - $(fn midi_outs <'a> (&'a self) -> Usually>> { - let cb = (|$mo_arg:&'a Self|$mo_impl); - cb(self) - })? - )? - )?} - }; -} diff --git a/crates/tek_core/src/engine.rs b/crates/tek_core/src/engine.rs index 0958fbef..9f04011b 100644 --- a/crates/tek_core/src/engine.rs +++ b/crates/tek_core/src/engine.rs @@ -25,6 +25,10 @@ pub trait Engine: Send + Sync + Sized { // FIXME fn with_area (&mut self, x: Self::Unit, y: Self::Unit, w: Self::Unit, h: Self::Unit) -> &mut Self; + // FIXME + fn render_in ( + &mut self, area: Self::Area, widget: &impl Widget + ) -> Perhaps; } submod! { diff --git a/crates/tek_core/src/engine/collect.rs b/crates/tek_core/src/engine/collect.rs index b831cfbb..ab89d41f 100644 --- a/crates/tek_core/src/engine/collect.rs +++ b/crates/tek_core/src/engine/collect.rs @@ -52,9 +52,7 @@ impl<'a, E: Engine> Collect<'a, E> for Collection<'a, E> { } } -pub struct Layers<'a, E: Engine, const N: usize>(pub [&'a dyn Widget;N]); - -pub struct Layers2< +pub struct Layers< E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget)->Usually<()>)->Usually<()> >(pub F, PhantomData); @@ -62,7 +60,7 @@ pub struct Layers2< impl< E: Engine, F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget)->Usually<()>)->Usually<()> -> Layers2 { +> Layers { pub fn new (build: F) -> Self { Self(build, Default::default()) } @@ -73,25 +71,6 @@ impl< //pub &'a I //); -pub struct Layered<'a, E: Engine>(pub Collection<'a, E>); - -impl<'a, E: Engine> Layered<'a, E> { - pub fn new () -> Self { - Self(Collection::new()) - } -} - -impl<'a, E: Engine> Collect<'a, E> for Layered<'a, E> { - fn add_box (mut self, item: Box + 'a>) -> Self { - self.0 = self.0.add_box(item); - self - } - fn add_ref (mut self, item: &'a dyn Widget) -> Self { - self.0 = self.0.add_ref(item); - self - } -} - #[derive(Copy, Clone)] pub enum Direction { Up, Down, Left, Right } diff --git a/crates/tek_core/src/engine/component.rs b/crates/tek_core/src/engine/component.rs index 1198261d..3f059407 100644 --- a/crates/tek_core/src/engine/component.rs +++ b/crates/tek_core/src/engine/component.rs @@ -3,7 +3,10 @@ use crate::*; pub trait Widget: Send + Sync { type Engine: Engine; fn layout (&self, to: <::Engine as Engine>::Area) -> - Perhaps<<::Engine as Engine>::Area>; + Perhaps<<::Engine as Engine>::Area> + { + Ok(Some(to)) + } fn render (&self, to: &mut Self::Engine) -> Perhaps<<::Engine as Engine>::Area>; } @@ -86,7 +89,10 @@ impl> Widget for W { self.content().layout(to) } fn render (&self, to: &mut E) -> Perhaps { - self.content().render(to) + match self.layout(to.area())? { + Some(area) => to.render_in(area, &self.content()), + None => Ok(None) + } } } diff --git a/crates/tek_core/src/tui.rs b/crates/tek_core/src/tui.rs index 55d2c084..d98c8d15 100644 --- a/crates/tek_core/src/tui.rs +++ b/crates/tek_core/src/tui.rs @@ -58,6 +58,16 @@ impl Engine for Tui { self.with_rect([x, y, w, h]); self } + #[inline] + fn render_in ( + &mut self, area: [u16;4], widget: &impl Widget + ) -> Perhaps<[u16;4]> { + let last = self.area; + self.area = area; + let next = widget.render(self)?; + self.area = last; + Ok(next) + } } impl Tui { /// Run the main loop. diff --git a/crates/tek_core/src/tui/tui_layout.rs b/crates/tek_core/src/tui/tui_layout.rs index 62937def..427a6f74 100644 --- a/crates/tek_core/src/tui/tui_layout.rs +++ b/crates/tek_core/src/tui/tui_layout.rs @@ -53,56 +53,21 @@ impl<'a> Split<'a, Tui> { } } -impl<'a, const N: usize> Widget for Layers<'a, Tui, N> { - type Engine = Tui; - fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { - let [x, y, ..] = area; - let mut w = 0; - let mut h = 0; - for layer in self.0.iter() { - if let Some(layer_area) = layer.layout(area)? { - w = w.max(layer_area.w()); - h = h.max(layer_area.h()); - } - } - Ok(Some([x, y, w, h])) - } - fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { - self.layout(to.area())? - .map(|area| { - for layer in self.0.iter() { - layer.render(to.with_rect(area))?; - } - Ok(area) - }) - .transpose() - } -} - -impl Widget for Layers2 +impl Widget for Layers where F: Send + Sync + Fn(&mut dyn FnMut(&dyn Widget)->Usually<()>)->Usually<()> { type Engine = Tui; - fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { - let [x, y, ..] = area; - let mut w = 0; - let mut h = 0; - (self.0)(&mut |layer| { - if let Some(layer_area) = layer.layout(area)? { - w = w.max(layer_area.w()); - h = h.max(layer_area.h()); - } - Ok(()) - })?; - Ok(Some([x, y, w, h])) - } fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { - self.layout(to.area())? - .map(|area|(self.0)(&mut |layer| { - layer.render(to.with_rect(area))?; - Ok(()) - }).map(|_|area)) + let area = to.area(); + to.blit(&format!("L1 {area:?}"), 10, 10, None)?; + let area = self.layout(to.area())?; + to.blit(&format!("L2 {area:?}"), 10, 11, None)?; + area.map(|area|(self.0)(&mut |layer| { + to.blit(&format!("L3 {area:?}"), 10, 12, None)?; + to.render_in(area, &layer)?; + Ok(()) + }).map(|_|area)) .transpose() } } @@ -129,8 +94,7 @@ impl> Widget for Align { } fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { self.layout(to.area())? - .map(|area|to.with_area(area.x(), area.y(), area.w(), area.h())) - .map(|to|self.inner().render(to)) + .map(|area|to.render_in(area, self.inner())) .transpose() .map(|x|x.flatten()) } diff --git a/crates/tek_mixer/src/mixer.rs b/crates/tek_mixer/src/mixer.rs index a5a54fa5..1875b790 100644 --- a/crates/tek_mixer/src/mixer.rs +++ b/crates/tek_mixer/src/mixer.rs @@ -25,7 +25,7 @@ impl Mixer { self.tracks.get(self.selected_track) } } -impl Process for Mixer { +impl Audio for Mixer { fn process (&mut self, _: &Client, _: &ProcessScope) -> Control { Control::Continue } diff --git a/crates/tek_mixer/src/plugin.rs b/crates/tek_mixer/src/plugin.rs index 5b014c6c..4ddbc939 100644 --- a/crates/tek_mixer/src/plugin.rs +++ b/crates/tek_mixer/src/plugin.rs @@ -39,7 +39,7 @@ impl Plugin { } } -impl Process for Plugin { +impl Audio for Plugin { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { match self.plugin.as_mut() { Some(PluginKind::LV2(LV2Plugin { diff --git a/crates/tek_mixer/src/sample_add.rs b/crates/tek_mixer/src/sample_add.rs index 29657d3f..1df09006 100644 --- a/crates/tek_mixer/src/sample_add.rs +++ b/crates/tek_mixer/src/sample_add.rs @@ -22,7 +22,11 @@ pub struct AddSampleModal { exit!(AddSampleModal); -impl Render for AddSampleModal { +impl Widget for AddSampleModal { + type Engine = Tui; + fn layout (&self, to: [u16;4]) -> Perhaps<[u16;4]> { + todo!() + } fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { let area = to.area(); to.make_dim(); diff --git a/crates/tek_mixer/src/sampler.rs b/crates/tek_mixer/src/sampler.rs index bda32d27..08b8b620 100644 --- a/crates/tek_mixer/src/sampler.rs +++ b/crates/tek_mixer/src/sampler.rs @@ -13,7 +13,6 @@ pub struct Sampler { pub modal: Arc>>>, pub output_gain: f32 } -process!(Sampler = Sampler::process); impl Sampler { pub fn from_edn <'e> (args: &[Edn<'e>]) -> Usually> { @@ -83,14 +82,6 @@ impl Sampler { None } - pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { - self.process_midi_in(scope); - self.clear_output_buffer(); - self.process_audio_out(scope); - self.write_output_buffer(scope); - Control::Continue - } - /// Create [Voice]s from [Sample]s in response to MIDI input. fn process_midi_in (&mut self, scope: &ProcessScope) { for RawMidi { time, bytes } in self.ports.midi_ins.get("midi").unwrap().iter(scope) { @@ -144,8 +135,12 @@ impl Sampler { } } -impl Layout for Sampler { - fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { - todo!() +impl Audio for Sampler { + fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { + self.process_midi_in(scope); + self.clear_output_buffer(); + self.process_audio_out(scope); + self.write_output_buffer(scope); + Control::Continue } } diff --git a/crates/tek_mixer/src/sampler_view.rs b/crates/tek_mixer/src/sampler_view.rs index 80e187a7..058119a2 100644 --- a/crates/tek_mixer/src/sampler_view.rs +++ b/crates/tek_mixer/src/sampler_view.rs @@ -1,6 +1,10 @@ use crate::*; -impl Render for Sampler { +impl Widget for Sampler { + type Engine = Tui; + fn layout (&self, to: [u16;4]) -> Perhaps<[u16;4]> { + todo!() + } fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { tui_render_sampler(self, to) } diff --git a/crates/tek_sequencer/src/arranger_view_h.rs b/crates/tek_sequencer/src/arranger_view_h.rs index 13ba6114..589bcb81 100644 --- a/crates/tek_sequencer/src/arranger_view_h.rs +++ b/crates/tek_sequencer/src/arranger_view_h.rs @@ -4,17 +4,17 @@ pub fn draw (state: &Arranger, to: &mut Tui) -> Perhaps<[u16;4]> { let area = to.area(); let area = [area.x(), area.y(), area.w(), area.h().min((2 + state.tracks.len() * 2) as u16)]; let tracks = state.tracks.as_slice(); - Layers([ - &state.focused.then_some(FillBg(COLOR_BG0)), - &Split::right() + Layers::new(|add|{ + add(&state.focused.then_some(FillBg(COLOR_BG0)))?; + add(&Split::right() .add(TrackNameColumn(tracks, state.selected)) .add(TrackMonitorColumn(tracks)) .add(TrackRecordColumn(tracks)) .add(TrackOverdubColumn(tracks)) .add(TrackEraseColumn(tracks)) .add(TrackGainColumn(tracks)) - .add(TrackScenesColumn(tracks, state.scenes.as_slice(), state.selected)) - ]).render(to.with_rect(area)) + .add(TrackScenesColumn(tracks, state.scenes.as_slice(), state.selected))) + }).render(to.with_rect(area)) } struct TrackNameColumn<'a>(&'a [Sequencer], ArrangerFocus); diff --git a/crates/tek_sequencer/src/arranger_view_v.rs b/crates/tek_sequencer/src/arranger_view_v.rs index 9a71a777..dd079028 100644 --- a/crates/tek_sequencer/src/arranger_view_v.rs +++ b/crates/tek_sequencer/src/arranger_view_v.rs @@ -38,24 +38,21 @@ pub fn draw <'a, 'b> ( let offset = 3 + scene_name_max_len(state.scenes.as_ref()) as u16; let tracks = state.tracks.as_ref(); let scenes = state.scenes.as_ref(); - Layers([ + Layers::new(|add|{ //.add_ref(&FillBg(Color::Rgb(30, 33, 36)))//COLOR_BG1))//bg_lo(state.focused, state.entered))) - &ColumnSeparators(offset, cols), - &RowSeparators(rows), - &CursorFocus(state.selected, offset, cols, rows), - &Split::down() + add(&ColumnSeparators(offset, cols))?; + add(&RowSeparators(rows))?; + add(&CursorFocus(state.selected, offset, cols, rows))?; + add(&Split::down() .add_ref(&TracksHeader(offset, cols, tracks)) - .add_ref(&SceneRows(offset, cols, rows, tracks, scenes)) - ]).render(to.with_rect(area)) + .add_ref(&SceneRows(offset, cols, rows, tracks, scenes))) + }).render(to.with_rect(area)) } struct ColumnSeparators<'a>(u16, &'a [(usize, usize)]); impl<'a> Widget for ColumnSeparators<'a> { type Engine = Tui; - fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { - todo!() - } fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { let area = to.area(); let Self(offset, cols) = self; @@ -74,9 +71,6 @@ struct RowSeparators<'a>(&'a [(usize, usize)]); impl<'a> Widget for RowSeparators<'a> { type Engine = Tui; - fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { - todo!() - } fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { let area = to.area(); let Self(rows) = self; @@ -101,9 +95,6 @@ struct CursorFocus<'a>( impl<'a> Widget for CursorFocus<'a> { type Engine = Tui; - fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { - todo!() - } fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { let area = to.area(); let Self(selected, offset, cols, rows) = *self; @@ -171,9 +162,6 @@ struct TracksHeader<'a>(u16, &'a[(usize, usize)], &'a [Sequencer]); impl<'a> Widget for TracksHeader<'a> { type Engine = Tui; - fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { - todo!() - } fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { let area = to.area(); let Self(offset, track_cols, tracks) = *self; @@ -195,9 +183,6 @@ struct SceneRows<'a>(u16, &'a[(usize, usize)], &'a[(usize, usize)], &'a[Sequence impl<'a> Widget for SceneRows<'a> { type Engine = Tui; - fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { - todo!() - } fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { let area = to.area(); let Self(offset, track_cols, scene_rows, tracks, scenes) = *self; @@ -228,9 +213,6 @@ struct SceneRow<'a>(&'a[Sequencer], &'a Scene, &'a[(usize, usize)], u16); impl<'a> Widget for SceneRow<'a> { type Engine = Tui; - fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { - todo!() - } fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { let area = to.area(); let Self(tracks, scene, track_cols, offset) = self; @@ -258,9 +240,6 @@ struct SceneClip<'a>(&'a Sequencer, usize); impl<'a> Widget for SceneClip<'a> { type Engine = Tui; - fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { - todo!() - } fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { let area = to.area(); let Self(track, clip) = self; diff --git a/crates/tek_sequencer/src/sequencer_view_h.rs b/crates/tek_sequencer/src/sequencer_view_h.rs index cfef4ad3..3ef2c683 100644 --- a/crates/tek_sequencer/src/sequencer_view_h.rs +++ b/crates/tek_sequencer/src/sequencer_view_h.rs @@ -15,13 +15,13 @@ impl Sequencer { let area = [area.x() + 10, area.y(), area.w().saturating_sub(10), area.h().min(66)]; Lozenge(Style::default().fg(Nord::BG2)).draw(to.with_rect(area))?; let area = [area.x() + 1, area.y(), area.w().saturating_sub(1), area.h()]; - Layers([ - &SequenceKeys(&self), - &self.phrase.as_ref().map(|phrase|SequenceTimer(&self, phrase.clone())), - &SequenceNotes(&self), - &SequenceCursor(&self), - &SequenceZoom(&self), - ]).render(to.with_rect(area))?; + Layers::new(|add|{ + add(&SequenceKeys(&self))?; + add(&self.phrase.as_ref().map(|phrase|SequenceTimer(&self, phrase.clone())))?; + add(&SequenceNotes(&self))?; + add(&SequenceCursor(&self))?; + add(&SequenceZoom(&self)) + }).render(to.with_rect(area))?; Ok(()) } diff --git a/crates/tek_sequencer/src/transport.rs b/crates/tek_sequencer/src/transport.rs index fcb9d1b4..cfd77022 100644 --- a/crates/tek_sequencer/src/transport.rs +++ b/crates/tek_sequencer/src/transport.rs @@ -181,7 +181,7 @@ impl Focusable for TransportToolbar { self.focused = focused } } -impl Process for TransportToolbar { +impl Audio for TransportToolbar { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { self.update(&scope); Control::Continue diff --git a/crates/tek_sequencer/src/transport_view.rs b/crates/tek_sequencer/src/transport_view.rs index 79a8a0bd..bcf3146d 100644 --- a/crates/tek_sequencer/src/transport_view.rs +++ b/crates/tek_sequencer/src/transport_view.rs @@ -17,7 +17,7 @@ impl Content for TransportToolbar { impl Content for TransportPlayPauseButton { type Engine = Tui; fn content (&self) -> impl Widget { - Layers2::new(|add|{ + Layers::new(|add|{ //add(&self.focused.then_some(CORNERS))?; add(&Styled(match self.value { Some(TransportState::Stopped) => Some(GRAY_DIM.bold()), diff --git a/crates/tek_test/src/main.rs b/crates/tek_test/src/main.rs index 5b7c5127..0c24e3c3 100644 --- a/crates/tek_test/src/main.rs +++ b/crates/tek_test/src/main.rs @@ -34,10 +34,16 @@ impl Demo { impl Content for Demo { type Engine = Tui; fn content (&self) -> impl Widget { - Layers2::new(|add|{ - add(&Align::Center("FOOBAR"))?; - add(&Align::Center("FOO"))?; - Ok(()) + //Align::Center("FOO") + //Layers2::new(|_|Ok(())) + //Layers2::new(|add|add(&Align::Center("FOO"))) + Layers::new(|add|{ + add(&FillBg(Color::Rgb(0,128,128)))?; + add(&Layers::new(|add|{ + add(&Align::Center("....."))?; + add(&Align::Center("FOO"))?; + Ok(()) + })) }) //Align::Center(&self.items[self.index] as &dyn Widget) }