diff --git a/crates/tek/src/core/color.rs b/crates/tek/src/core/color.rs index 0c96d043..93f086b6 100644 --- a/crates/tek/src/core/color.rs +++ b/crates/tek/src/core/color.rs @@ -50,41 +50,35 @@ impl ItemColor { self.okhsl.mix(other.okhsl, distance).into() } } -impl From for ItemPalette { - fn from (base: Color) -> Self { - Self::from(ItemColor::from(base)) - } -} -impl From for ItemPalette { - fn from (base: ItemColor) -> Self { - let mut light = base.okhsl.clone(); - light.lightness = (light.lightness * 1.3).min(Okhsl::::max_lightness()); - let mut lighter = light.clone(); - lighter.lightness = (lighter.lightness * 1.3).min(Okhsl::::max_lightness()); - let mut lightest = lighter.clone(); - lightest.lightness = (lightest.lightness * 1.3).min(Okhsl::::max_lightness()); +from!(|base: Color|ItemPalette = Self::from(ItemColor::from(base))); +from!(|base: ItemColor|ItemPalette = { + let mut light = base.okhsl.clone(); + light.lightness = (light.lightness * 1.3).min(Okhsl::::max_lightness()); + let mut lighter = light.clone(); + lighter.lightness = (lighter.lightness * 1.3).min(Okhsl::::max_lightness()); + let mut lightest = lighter.clone(); + lightest.lightness = (lightest.lightness * 1.3).min(Okhsl::::max_lightness()); - let mut dark = base.okhsl.clone(); - dark.lightness = (dark.lightness * 0.75).max(Okhsl::::min_lightness()); - dark.saturation = (dark.saturation * 0.75).max(Okhsl::::min_saturation()); - let mut darker = dark.clone(); - darker.lightness = (darker.lightness * 0.66).max(Okhsl::::min_lightness()); - darker.saturation = (darker.saturation * 0.66).max(Okhsl::::min_saturation()); - let mut darkest = darker.clone(); - darkest.lightness = (darkest.lightness * 0.50).max(Okhsl::::min_lightness()); - darkest.saturation = (darkest.saturation * 0.50).max(Okhsl::::min_saturation()); + let mut dark = base.okhsl.clone(); + dark.lightness = (dark.lightness * 0.75).max(Okhsl::::min_lightness()); + dark.saturation = (dark.saturation * 0.75).max(Okhsl::::min_saturation()); + let mut darker = dark.clone(); + darker.lightness = (darker.lightness * 0.66).max(Okhsl::::min_lightness()); + darker.saturation = (darker.saturation * 0.66).max(Okhsl::::min_saturation()); + let mut darkest = darker.clone(); + darkest.lightness = (darkest.lightness * 0.50).max(Okhsl::::min_lightness()); + darkest.saturation = (darkest.saturation * 0.50).max(Okhsl::::min_saturation()); - Self { - base, - light: light.into(), - lighter: lighter.into(), - lightest: lightest.into(), - dark: dark.into(), - darker: darker.into(), - darkest: darkest.into(), - } + Self { + base, + light: light.into(), + lighter: lighter.into(), + lightest: lightest.into(), + dark: dark.into(), + darker: darker.into(), + darkest: darkest.into(), } -} +}); impl ItemPalette { pub fn random () -> Self { ItemColor::random().into() diff --git a/crates/tek/src/jack.rs b/crates/tek/src/jack.rs index 44332681..0dd51dc1 100644 --- a/crates/tek/src/jack.rs +++ b/crates/tek/src/jack.rs @@ -1,10 +1,31 @@ - -pub(crate) mod activate; +pub use ::jack as libjack; +pub(crate) mod activate; pub(crate) use self::activate::*; pub(crate) mod audio; pub(crate) use self::audio::*; pub(crate) mod client; pub(crate) use self::client::*; -pub(crate) mod from_jack; pub(crate) mod jack_event; pub(crate) use self::jack_event::*; pub(crate) mod ports; pub(crate) use self::ports::*; +pub(crate) use ::jack::{ + contrib::ClosureProcessHandler, + Client, AsyncClient, ClientOptions, ClientStatus, + ProcessScope, Control, CycleTimes, + Port, PortId, + PortSpec, MidiIn, MidiOut, AudioIn, AudioOut, Unowned, + Transport, TransportState, MidiIter, RawMidi, + Frames, + NotificationHandler, +}; + +/// Implement [TryFrom<&Arc>>]: create app state from wrapped JACK handle. +#[macro_export] macro_rules! from_jack { + (|$jack:ident|$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)? $cb:expr) => { + impl $(<$($L),*$($T $(: $U)?),*>)? TryFrom<&Arc>> for $Struct $(<$($L),*$($T),*>)? { + type Error = Box; + fn try_from ($jack: &Arc>) -> Usually { + Ok($cb) + } + } + }; +} //////////////////////////////////////////////////////////////////////////////////// diff --git a/crates/tek/src/jack/client.rs b/crates/tek/src/jack/client.rs index 046990bd..be18cb0f 100644 --- a/crates/tek/src/jack/client.rs +++ b/crates/tek/src/jack/client.rs @@ -1,5 +1,7 @@ use crate::*; - +pub type DynamicAsyncClient = AsyncClient; +pub type DynamicAudioHandler = ClosureProcessHandler<(), BoxedAudioHandler>; +pub type BoxedAudioHandler = Box Control + Send>; /// Wraps [Client] or [DynamicAsyncClient] in place. #[derive(Debug)] pub enum JackClient { @@ -10,7 +12,17 @@ pub enum JackClient { /// After activation. Must not be dropped for JACK thread to persist. Active(DynamicAsyncClient), } - +from!(|jack: JackClient|Client = match jack { + JackClient::Inactive(client) => client, + JackClient::Activating => panic!("jack client still activating"), + JackClient::Active(_) => panic!("jack client already activated"), +}); +impl JackClient { + pub fn new (name: &str) -> Usually { + let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?; + Ok(Self::Inactive(client)) + } +} impl AudioEngine for JackClient { fn client(&self) -> &Client { match self { @@ -36,26 +48,3 @@ impl AudioEngine for JackClient { Ok(state) } } - -pub type DynamicAsyncClient = AsyncClient; - -pub type DynamicAudioHandler = ClosureProcessHandler<(), BoxedAudioHandler>; - -pub type BoxedAudioHandler = Box Control + Send>; - -impl JackClient { - pub fn new (name: &str) -> Usually { - let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?; - Ok(Self::Inactive(client)) - } -} - -impl From for Client { - fn from (jack: JackClient) -> Client { - match jack { - JackClient::Inactive(client) => client, - JackClient::Activating => panic!("jack client still activating"), - JackClient::Active(_) => panic!("jack client already activated"), - } - } -} diff --git a/crates/tek/src/jack/from_jack.rs b/crates/tek/src/jack/from_jack.rs deleted file mode 100644 index bdd1fd2e..00000000 --- a/crates/tek/src/jack/from_jack.rs +++ /dev/null @@ -1,13 +0,0 @@ - - -/// Implement [TryFrom<&Arc>>]: create app state from wrapped JACK handle. -#[macro_export] macro_rules! from_jack { - (|$jack:ident|$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)? $cb:expr) => { - impl $(<$($L),*$($T $(: $U)?),*>)? TryFrom<&Arc>> for $Struct $(<$($L),*$($T),*>)? { - type Error = Box; - fn try_from ($jack: &Arc>) -> Usually { - Ok($cb) - } - } - }; -} diff --git a/crates/tek/src/lib.rs b/crates/tek/src/lib.rs index a13ac2f9..4cf3080e 100644 --- a/crates/tek/src/lib.rs +++ b/crates/tek/src/lib.rs @@ -65,18 +65,6 @@ pub(crate) use ratatui::{ backend::{Backend, CrosstermBackend, ClearType} }; -pub use ::jack as libjack; -pub(crate) use ::jack::{ - contrib::ClosureProcessHandler, - Client, AsyncClient, ClientOptions, ClientStatus, - ProcessScope, Control, CycleTimes, - Port, PortId, - PortSpec, MidiIn, MidiOut, AudioOut, Unowned, - Transport, TransportState, MidiIter, RawMidi, - Frames, - NotificationHandler, -}; - pub use ::midly; pub(crate) use ::midly::{ Smf, diff --git a/crates/tek/src/time/clock.rs b/crates/tek/src/time/clock.rs index 8636852a..e9c0d2d2 100644 --- a/crates/tek/src/time/clock.rs +++ b/crates/tek/src/time/clock.rs @@ -63,25 +63,23 @@ pub struct ClockModel { pub chunk: Arc, } -impl From<&Arc>> for ClockModel { - fn from (jack: &Arc>) -> Self { - let jack = jack.read().unwrap(); - let chunk = jack.client().buffer_size(); - let transport = jack.client().transport(); - let timebase = Arc::new(Timebase::default()); - Self { - quant: Arc::new(24.into()), - sync: Arc::new(384.into()), - transport: Arc::new(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, - } +from!(|jack: &Arc>| ClockModel = { + let jack = jack.read().unwrap(); + let chunk = jack.client().buffer_size(); + let transport = jack.client().transport(); + let timebase = Arc::new(Timebase::default()); + Self { + quant: Arc::new(24.into()), + sync: Arc::new(384.into()), + transport: Arc::new(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, } -} +}); impl std::fmt::Debug for ClockModel { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { diff --git a/crates/tek/src/tui.rs b/crates/tek/src/tui.rs index 787d40e2..9f231045 100644 --- a/crates/tek/src/tui.rs +++ b/crates/tek/src/tui.rs @@ -21,7 +21,7 @@ pub(crate) use tui_border::*; mod app_transport; pub(crate) use app_transport::*; mod app_sequencer; pub(crate) use app_sequencer::*; mod app_sampler; pub(crate) use app_sampler::*; -mod app_groovebox; +mod app_groovebox; pub(crate) use app_groovebox::*; mod app_arranger; pub(crate) use app_arranger::*; /////////////////////////////////////////////////////// diff --git a/crates/tek/src/tui/app_arranger.rs b/crates/tek/src/tui/app_arranger.rs index 90fd03ea..470a1391 100644 --- a/crates/tek/src/tui/app_arranger.rs +++ b/crates/tek/src/tui/app_arranger.rs @@ -27,8 +27,8 @@ from_jack!(|jack| ArrangerTui { ))); Self { clock, - phrases: PhraseListModel::from(&phrase), - editor: PhraseEditorModel::from(&phrase), + phrases: (&phrase).into(), + editor: (&phrase).into(), selected: ArrangerSelection::Clip(0, 0), scenes: vec![], tracks: vec![], @@ -282,13 +282,13 @@ fn any_size (_: E::Size) -> Perhaps{ Ok(Some([0.into(),0.into()].into())) } impl ArrangerTui { - fn selected (&self) -> ArrangerSelection { + pub fn selected (&self) -> ArrangerSelection { self.selected } - fn selected_mut (&mut self) -> &mut ArrangerSelection { + pub fn selected_mut (&mut self) -> &mut ArrangerSelection { &mut self.selected } - fn activate (&mut self) -> Usually<()> { + pub fn activate (&mut self) -> Usually<()> { if let ArrangerSelection::Scene(s) = self.selected { for (t, track) in self.tracks.iter_mut().enumerate() { let phrase = self.scenes[s].clips[t].clone(); @@ -305,15 +305,15 @@ impl ArrangerTui { }; Ok(()) } - fn selected_phrase (&self) -> Option>> { + pub fn selected_phrase (&self) -> Option>> { self.selected_scene()?.clips.get(self.selected.track()?)?.clone() } - fn toggle_loop (&mut self) { + pub fn toggle_loop (&mut self) { if let Some(phrase) = self.selected_phrase() { phrase.write().unwrap().toggle_loop() } } - fn randomize_color (&mut self) { + pub fn randomize_color (&mut self) { match self.selected { ArrangerSelection::Mix => { self.color = ItemPalette::random() diff --git a/crates/tek/src/tui/app_transport.rs b/crates/tek/src/tui/app_transport.rs index 7b00645d..7c058152 100644 --- a/crates/tek/src/tui/app_transport.rs +++ b/crates/tek/src/tui/app_transport.rs @@ -9,14 +9,14 @@ pub struct TransportTui { pub clock: ClockModel, pub size: Measure, pub cursor: (usize, usize), - pub focus: FocusState, + pub focus: TransportFocus, } from_jack!(|jack|TransportTui Self { jack: jack.clone(), clock: ClockModel::from(jack), size: Measure::new(), cursor: (0, 0), - focus: FocusState::Entered(TransportFocus::PlayPause) + focus: TransportFocus::PlayPause }); has_clock!(|self:TransportTui|&self.clock); audio!(|self:TransportTui,client,scope|ClockAudio(self).process(client, scope)); @@ -131,10 +131,10 @@ render!(|self: PlayPause|Tui::bg( impl HasFocus for TransportTui { type Item = TransportFocus; fn focused (&self) -> Self::Item { - self.focus.inner() + self.focus } fn set_focused (&mut self, to: Self::Item) { - self.focus.set_inner(to) + self.focus = to } } @@ -147,8 +147,6 @@ pub enum TransportFocus { Clock, Quant, } -from!(|state: &TransportTui|Option = Some(state.focus.inner())); - impl FocusWrap for TransportFocus { fn wrap <'a, W: Render> (self, focus: TransportFocus, content: &'a W) -> impl Render + 'a @@ -177,7 +175,7 @@ pub trait TransportControl: HasClock + { impl TransportControl for TransportTui { fn transport_focused (&self) -> Option { - Some(self.focus.inner()) + Some(self.focus) } } diff --git a/crates/tek/src/tui/arranger_mode_v.rs b/crates/tek/src/tui/arranger_mode_v.rs index 8056f3a2..d4980ece 100644 --- a/crates/tek/src/tui/arranger_mode_v.rs +++ b/crates/tek/src/tui/arranger_mode_v.rs @@ -48,11 +48,11 @@ pub struct ArrangerVCursor { header_h: u16, } from!(|args:(&ArrangerTui, usize)|ArrangerVCursor = Self { - cols: track_widths(state.tracks()), + cols: track_widths(args.0.tracks()), rows: ArrangerScene::ppqs(args.0.scenes(), args.1), focused: true, - selected: state.selected, - scenes_w: 3 + ArrangerScene::longest_name(state.scenes()) as u16, + selected: args.0.selected(), + scenes_w: 3 + ArrangerScene::longest_name(args.0.scenes()) as u16, header_h: 3, }); render!(|self: ArrangerVCursor|render(move|to: &mut TuiOutput|{ diff --git a/crates/tek/src/tui/phrase_editor.rs b/crates/tek/src/tui/phrase_editor.rs index d78745e7..bcbaa652 100644 --- a/crates/tek/src/tui/phrase_editor.rs +++ b/crates/tek/src/tui/phrase_editor.rs @@ -207,22 +207,18 @@ impl PhraseEditorModel { } } -impl From<&Arc>> for PhraseEditorModel { - fn from (phrase: &Arc>) -> Self { - let mut model = Self::from(Some(phrase.clone())); - model.redraw(); - model - } -} +from!(|phrase: &Arc>|PhraseEditorModel = { + let mut model = Self::from(Some(phrase.clone())); + model.redraw(); + model +}); -impl From>>> for PhraseEditorModel { - fn from (phrase: Option>>) -> Self { - let mut model = Self::default(); - *model.phrase_mut() = phrase; - model.redraw(); - model - } -} +from!(|phrase: Option>>|PhraseEditorModel = { + let mut model = Self::default(); + *model.phrase_mut() = phrase; + model.redraw(); + model +}); impl std::fmt::Debug for PhraseEditorModel { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { diff --git a/crates/tek/src/tui/phrase_list.rs b/crates/tek/src/tui/phrase_list.rs index f8b58e8f..5d211284 100644 --- a/crates/tek/src/tui/phrase_list.rs +++ b/crates/tek/src/tui/phrase_list.rs @@ -167,7 +167,7 @@ impl Default for PhraseListModel { } } } -from!(|phrase:<&Arc>>| PhraseListModel = { +from!(|phrase:&Arc>|PhraseListModel = { let mut model = Self::default(); model.phrases.push(phrase.clone()); model.phrase.store(1, Relaxed); diff --git a/crates/tek/src/tui/tui_output.rs b/crates/tek/src/tui/tui_output.rs index 6e4070c0..b346b535 100644 --- a/crates/tek/src/tui/tui_output.rs +++ b/crates/tek/src/tui/tui_output.rs @@ -91,11 +91,7 @@ impl BigBuffer { } } -impl From<(usize, usize)> for BigBuffer { // cuteness overload - fn from ((width, height): (usize, usize)) -> Self { - Self::new(width, height) - } -} +from!(|size:(usize, usize)| BigBuffer = Self::new(size.0, size.1)); pub fn buffer_update (buf: &mut Buffer, area: [u16;4], callback: &impl Fn(&mut Cell, u16, u16)) { for row in 0..area.h() {