diff --git a/.gitmodules b/.gitmodules index 50fe8c6d..04bb5796 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,8 +6,8 @@ path = tengri url = https://codeberg.org/unspeaker/tengri [submodule "deps/rust-jack"] - path = rust-jack + path = tengri/rust-jack url = https://codeberg.org/unspeaker/rust-jack [submodule "deps/dizzle"] - path = dizzle + path = tengri/dizzle url = ssh://git@codeberg.org/unspeaker/dizzle.git diff --git a/Cargo.lock b/Cargo.lock index 50c81b5a..a1080f3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2992,9 +2992,7 @@ dependencies = [ "builder-pattern", "bumpalo", "clap", - "dizzle", "gtk", - "jack", "konst", "livi", "midly", @@ -3035,6 +3033,7 @@ dependencies = [ "better-panic", "crossterm 0.29.0", "dizzle", + "jack", "palette", "quanta", "rand 0.8.5", diff --git a/Cargo.toml b/Cargo.toml index 43c3ded8..f4e2a106 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,6 @@ rustflags = ["-C", "link-arg=-fuse-ld=mold"] [dependencies] tengri = { path = "./tengri", features = [ "tui", "dsl" ] } -dizzle = { path = "./dizzle" } -jack = { path = "./rust-jack" } ansi_term = { version = "0.12.1" } atomic_float = { version = "1.0.0" } diff --git a/app/Cargo.toml b/app/Cargo.toml deleted file mode 100644 index e69de29b..00000000 diff --git a/app/tek.rs b/app/tek.rs index 8cd455fc..178070da 100644 --- a/app/tek.rs +++ b/app/tek.rs @@ -13,8 +13,8 @@ extern crate xdg; pub(crate) use ::xdg::BaseDirectories; pub extern crate atomic_float; pub(crate) use atomic_float::AtomicF64; -pub extern crate jack; -pub(crate) use ::jack::{*, contrib::{*, ClosureProcessHandler}}; +//pub extern crate jack; +//pub(crate) use ::jack::{*, contrib::{*, ClosureProcessHandler}}; pub extern crate midly; pub(crate) use ::midly::{Smf, TrackEventKind, MidiMessage, Error as MidiError, num::*, live::*}; pub extern crate tengri; @@ -301,10 +301,10 @@ pub fn tek_print_status (project: &Arrangement) { } /// Return boxed iterator of MIDI events -pub fn parse_midi_input <'a> (input: ::jack::MidiIter<'a>) +pub fn parse_midi_input <'a> (input: ::tengri::jack::MidiIter<'a>) -> Box, &'a [u8])> + 'a> { - Box::new(input.map(|::jack::RawMidi { time, bytes }|( + Box::new(input.map(|::tengri::jack::RawMidi { time, bytes }|( time as usize, LiveEvent::parse(bytes).unwrap(), bytes @@ -426,24 +426,10 @@ impl Device { } } -audio!(|self: DeviceAudio<'a>, client, scope|{ - use Device::*; - match self.0 { - Mute => { Control::Continue }, - Bypass => { /*TODO*/ Control::Continue }, - #[cfg(feature = "sampler")] Sampler(sampler) => sampler.process(client, scope), - #[cfg(feature = "lv2")] Lv2(lv2) => lv2.process(client, scope), - #[cfg(feature = "vst2")] Vst2 => { todo!() }, // TODO - #[cfg(feature = "vst3")] Vst3 => { todo!() }, // TODO - #[cfg(feature = "clap")] Clap => { todo!() }, // TODO - #[cfg(feature = "sf2")] Sf2 => { todo!() }, // TODO - } -}); //take!(DeviceCommand|state: Arrangement, iter|state.selected_device().as_ref() //.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten())); - #[macro_export] macro_rules! has_clip { (|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => { impl $(<$($L),*$($T $(: $U)?),*>)? HasMidiClip for $Struct $(<$($L),*$($T),*>)? { diff --git a/app/tek_impls.rs b/app/tek_impls.rs index 12987408..53ec57cc 100644 --- a/app/tek_impls.rs +++ b/app/tek_impls.rs @@ -280,80 +280,8 @@ mod app { } } } -} -mod dialog { - use crate::*; - - impl Dialog { - /// ``` - /// let _ = tek::Dialog::welcome(); - /// ``` - pub fn welcome () -> Self { - Self::Menu(1, MenuItems([ - MenuItem("Resume session".into(), Arc::new(Box::new(|_|Ok(())))), - MenuItem("Create new session".into(), Arc::new(Box::new(|app|Ok({ - app.dialog = Dialog::None; - app.mode = app.config.modes.clone().read().unwrap().get(":arranger").cloned().unwrap(); - })))), - MenuItem("Load old session".into(), Arc::new(Box::new(|_|Ok(())))), - ].into())) - } - /// FIXME: generalize - /// ``` - /// let _ = tek::Dialog::welcome().menu_selected(); - /// ``` - pub fn menu_selected (&self) -> Option { - if let Self::Menu(selected, _) = self { Some(*selected) } else { None } - } - /// FIXME: generalize - /// ``` - /// let _ = tek::Dialog::welcome().menu_next(); - /// ``` - pub fn menu_next (&self) -> Self { - match self { - Self::Menu(index, items) => Self::Menu(wrap_inc(*index, items.0.len()), items.clone()), - _ => Self::None - } - } - /// FIXME: generalize - /// ``` - /// let _ = tek::Dialog::welcome().menu_prev(); - /// ``` - pub fn menu_prev (&self) -> Self { - match self { - Self::Menu(index, items) => Self::Menu(wrap_dec(*index, items.0.len()), items.clone()), - _ => Self::None - } - } - /// FIXME: generalize - /// ``` - /// let _ = tek::Dialog::welcome().device_kind(); - /// ``` - pub fn device_kind (&self) -> Option { - if let Self::Device(index) = self { Some(*index) } else { None } - } - /// FIXME: generalize - /// ``` - /// let _ = tek::Dialog::welcome().device_kind_next(); - /// ``` - pub fn device_kind_next (&self) -> Option { - self.device_kind().map(|index|(index + 1) % device_kinds().len()) - } - /// FIXME: generalize - /// ``` - /// let _ = tek::Dialog::welcome().device_kind_prev(); - /// ``` - pub fn device_kind_prev (&self) -> Option { - self.device_kind().map(|index|index.overflowing_sub(1).0.min(device_kinds().len().saturating_sub(1))) - } - /// FIXME: implement - pub fn message (&self) -> Option<&str> { todo!() } - /// FIXME: implement - pub fn browser (&self) -> Option<&Arc> { todo!() } - /// FIXME: implement - pub fn browser_target (&self) -> Option<&BrowseTarget> { todo!() } - } + impl_audio!(App: tek_jack_process, tek_jack_event); } #[cfg(feature = "vst2")] impl ::vst::host::Host for Plugin {} @@ -1703,8 +1631,6 @@ impl AsRef> for MenuItems { fn as_ref (&self) -> &Arc<[MenuItem] impl ClipsView for T {} impl HasClipsSize for App { fn clips_size (&self) -> &Measure { &self.project.size_inner } } -impl<'j> HasJack<'j> for Jack<'j> { fn jack (&self) -> &Jack<'j> { self } } -impl<'j> HasJack<'j> for &Jack<'j> { fn jack (&self) -> &Jack<'j> { self } } impl HasJack<'static> for MidiInput { fn jack (&self) -> &Jack<'static> { &self.jack } } impl HasJack<'static> for MidiOutput { fn jack (&self) -> &Jack<'static> { &self.jack } } impl HasJack<'static> for AudioInput { fn jack (&self) -> &Jack<'static> { &self.jack } } @@ -1735,8 +1661,6 @@ impl> RegisterPorts for J { #[cfg(feature = "port")] impl_has!(Vec: |self: Sequencer|self.midi_ins); #[cfg(feature = "port")] impl_has!(Vec: |self: Sequencer|self.midi_outs); -audio!(App: tek_jack_process, tek_jack_event); - impl_has!(Jack<'static>: |self: App|self.jack); impl_has!(Dialog: |self: App|self.dialog); impl_has!(Measure: |self: App|self.size); @@ -1838,141 +1762,6 @@ namespace!(App: Option>> { }; }); - -mod draw { - - use crate::*; - - impl Draw for MidiEditor { - fn draw (&self, to: &mut TuiOut) { self.content().draw(to) } - } - - impl Draw for PianoHorizontal { - fn draw (&self, to: &mut TuiOut) { self.content().draw(to) } - } - -} - -mod jack { - use crate::*; - - /// Implement [Jack] constructor and methods - impl<'j> Jack<'j> { - /// Register new [Client] and wrap it for shared use. - pub fn new_run + Audio + Send + Sync + 'static> ( - name: &impl AsRef, - init: impl FnOnce(Jack<'j>)->Usually - ) -> Usually>> { - Jack::new(name)?.run(init) - } - - pub fn new (name: &impl AsRef) -> Usually { - let client = Client::new(name.as_ref(), ClientOptions::NO_START_SERVER)?.0; - Ok(Jack(Arc::new(RwLock::new(JackState::Inactive(client))))) - } - - pub fn run + Audio + Send + Sync + 'static> - (self, init: impl FnOnce(Self)->Usually) -> Usually>> - { - let client_state = self.0.clone(); - let app: Arc> = Arc::new(RwLock::new(init(self)?)); - let mut state = Activating; - std::mem::swap(&mut*client_state.write().unwrap(), &mut state); - if let Inactive(client) = state { - // This is the misc notifications handler. It's a struct that wraps a [Box] - // which performs type erasure on a callback that takes [JackEvent], which is - // one of the available misc notifications. - let notify = JackNotify(Box::new({ - let app = app.clone(); - move|event|(&mut*app.write().unwrap()).handle(event) - }) as BoxedJackEventHandler); - // This is the main processing handler. It's a struct that wraps a [Box] - // which performs type erasure on a callback that takes [Client] and [ProcessScope] - // and passes them down to the `app`'s `process` callback, which in turn - // implements audio and MIDI input and output on a realtime basis. - let process = ClosureProcessHandler::new(Box::new({ - let app = app.clone(); - move|c: &_, s: &_|if let Ok(mut app) = app.write() { - app.process(c, s) - } else { - Control::Quit - } - }) as BoxedAudioHandler); - // Launch a client with the two handlers. - *client_state.write().unwrap() = Active( - client.activate_async(notify, process)? - ); - } else { - unreachable!(); - } - Ok(app) - } - - /// Run something with the client. - pub fn with_client (&self, op: impl FnOnce(&Client)->T) -> T { - match &*self.0.read().unwrap() { - Inert => panic!("jack client not activated"), - Inactive(client) => op(client), - Activating => panic!("jack client has not finished activation"), - Active(client) => op(client.as_client()), - } - } - } - - impl NotificationHandler for JackNotify { - fn thread_init(&self, _: &Client) { - self.0(JackEvent::ThreadInit); - } - unsafe 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 - } - } - - impl JackPerfModel for PerfModel { - fn update_from_jack_scope (&self, t0: Option, scope: &ProcessScope) { - if let Some(t0) = t0 { - let t1 = self.clock.raw(); - self.used.store( - self.clock.delta_as_nanos(t0, t1) as f64, - Relaxed, - ); - self.window.store( - scope.cycle_times().unwrap().period_usecs as f64, - Relaxed, - ); - } - } - } -} - mod time { use crate::*; impl Moment { @@ -3062,6 +2851,12 @@ mod audio { } } } + impl Draw for MidiEditor { + fn draw (&self, to: &mut TuiOut) { self.content().draw(to) } + } + impl Draw for PianoHorizontal { + fn draw (&self, to: &mut TuiOut) { self.content().draw(to) } + } } #[cfg(feature = "sampler")] mod sampler { @@ -3565,7 +3360,7 @@ mod audio { }) } - audio!(Sampler: sampler_jack_process); + impl_audio!(Sampler: sampler_jack_process); pub(crate) fn sampler_jack_process (state: &mut Sampler, _: &Client, scope: &ProcessScope) -> Control { if let Some(midi_in) = &state.midi_in { for midi in midi_in.port().iter(scope) { @@ -3619,7 +3414,7 @@ mod audio { use crate::*; - audio!(Lv2: lv2_jack_process); + impl_audio!(Lv2: lv2_jack_process); impl Lv2 { const INPUT_BUFFER: usize = 1024; @@ -4246,3 +4041,91 @@ mod config { } } } + +mod dialog { + use crate::*; + + impl Dialog { + /// ``` + /// let _ = tek::Dialog::welcome(); + /// ``` + pub fn welcome () -> Self { + Self::Menu(1, MenuItems([ + MenuItem("Resume session".into(), Arc::new(Box::new(|_|Ok(())))), + MenuItem("Create new session".into(), Arc::new(Box::new(|app|Ok({ + app.dialog = Dialog::None; + app.mode = app.config.modes.clone().read().unwrap().get(":arranger").cloned().unwrap(); + })))), + MenuItem("Load old session".into(), Arc::new(Box::new(|_|Ok(())))), + ].into())) + } + /// FIXME: generalize + /// ``` + /// let _ = tek::Dialog::welcome().menu_selected(); + /// ``` + pub fn menu_selected (&self) -> Option { + if let Self::Menu(selected, _) = self { Some(*selected) } else { None } + } + /// FIXME: generalize + /// ``` + /// let _ = tek::Dialog::welcome().menu_next(); + /// ``` + pub fn menu_next (&self) -> Self { + match self { + Self::Menu(index, items) => Self::Menu(wrap_inc(*index, items.0.len()), items.clone()), + _ => Self::None + } + } + /// FIXME: generalize + /// ``` + /// let _ = tek::Dialog::welcome().menu_prev(); + /// ``` + pub fn menu_prev (&self) -> Self { + match self { + Self::Menu(index, items) => Self::Menu(wrap_dec(*index, items.0.len()), items.clone()), + _ => Self::None + } + } + /// FIXME: generalize + /// ``` + /// let _ = tek::Dialog::welcome().device_kind(); + /// ``` + pub fn device_kind (&self) -> Option { + if let Self::Device(index) = self { Some(*index) } else { None } + } + /// FIXME: generalize + /// ``` + /// let _ = tek::Dialog::welcome().device_kind_next(); + /// ``` + pub fn device_kind_next (&self) -> Option { + self.device_kind().map(|index|(index + 1) % device_kinds().len()) + } + /// FIXME: generalize + /// ``` + /// let _ = tek::Dialog::welcome().device_kind_prev(); + /// ``` + pub fn device_kind_prev (&self) -> Option { + self.device_kind().map(|index|index.overflowing_sub(1).0.min(device_kinds().len().saturating_sub(1))) + } + /// FIXME: implement + pub fn message (&self) -> Option<&str> { todo!() } + /// FIXME: implement + pub fn browser (&self) -> Option<&Arc> { todo!() } + /// FIXME: implement + pub fn browser_target (&self) -> Option<&BrowseTarget> { todo!() } + } +} + +impl_audio!(|self: DeviceAudio<'a>, client, scope|{ + use Device::*; + match self.0 { + Mute => { Control::Continue }, + Bypass => { /*TODO*/ Control::Continue }, + #[cfg(feature = "sampler")] Sampler(sampler) => sampler.process(client, scope), + #[cfg(feature = "lv2")] Lv2(lv2) => lv2.process(client, scope), + #[cfg(feature = "vst2")] Vst2 => { todo!() }, // TODO + #[cfg(feature = "vst3")] Vst3 => { todo!() }, // TODO + #[cfg(feature = "clap")] Clap => { todo!() }, // TODO + #[cfg(feature = "sf2")] Sf2 => { todo!() }, // TODO + } +}); diff --git a/app/tek_struct.rs b/app/tek_struct.rs index b4e0ea4b..bf1fa768 100644 --- a/app/tek_struct.rs +++ b/app/tek_struct.rs @@ -2,58 +2,6 @@ use crate::*; use clap::{self, Parser, Subcommand}; use builder_pattern::Builder; -/// Wraps [JackState], and through it [jack::Client] when connected. -/// -/// ``` -/// let jack = tek::Jack::default(); -/// ``` -#[derive(Clone, Debug, Default)] pub struct Jack<'j> ( - pub(crate) Arc>> -); - -/// This is a connection which may be [Inactive], [Activating], or [Active]. -/// In the [Active] and [Inactive] states, [JackState::client] returns a -/// [jack::Client], which you can use to talk to the JACK API. -/// -/// ``` -/// let state = tek::JackState::default(); -/// ``` -#[derive(Debug, Default)] pub enum JackState<'j> { - /// Unused - #[default] Inert, - /// Before activation. - Inactive(Client), - /// During activation. - Activating, - /// After activation. Must not be dropped for JACK thread to persist. - Active(DynamicAsyncClient<'j>), -} - -/// Event enum for JACK events. -/// -/// ``` -/// let event = tek::JackEvent::XRun; -/// ``` -#[derive(Debug, Clone, PartialEq)] pub enum JackEvent { - ThreadInit, - Shutdown(ClientStatus, Arc), - Freewheel(bool), - SampleRate(Frames), - ClientRegistration(Arc, bool), - PortRegistration(PortId, bool), - PortRename(PortId, Arc, Arc), - PortsConnected(PortId, PortId, bool), - GraphReorder, - XRun, -} - -/// Generic notification handler that emits [JackEvent] -/// -/// ``` -/// let notify = tek::JackNotify(|_|{}); -/// ``` -pub struct JackNotify(pub T); - /// Total state /// /// ``` diff --git a/app/tek_trait.rs b/app/tek_trait.rs index a7887992..4e738b82 100644 --- a/app/tek_trait.rs +++ b/app/tek_trait.rs @@ -1,125 +1,6 @@ use crate::*; use std::sync::atomic::Ordering; -/// Things that can provide a [jack::Client] reference. -/// -/// ``` -/// use tek::{Jack, HasJack}; -/// -/// let jack: &Jack = Jacked::default().jack(); -/// -/// #[derive(Default)] struct Jacked<'j>(Jack<'j>); -/// -/// impl<'j> tek::HasJack<'j> for Jacked<'j> { -/// fn jack (&self) -> &Jack<'j> { &self.0 } -/// } -/// ``` -pub trait HasJack<'j>: Send + Sync { - - /// Return the internal [jack::Client] handle - /// that lets you call the JACK API. - fn jack (&self) -> &Jack<'j>; - - fn with_client (&self, op: impl FnOnce(&Client)->T) -> T { - self.jack().with_client(op) - } - - fn port_by_name (&self, name: &str) -> Option> { - self.with_client(|client|client.port_by_name(name)) - } - - fn port_by_id (&self, id: u32) -> Option> { - self.with_client(|c|c.port_by_id(id)) - } - - fn register_port (&self, name: impl AsRef) -> Usually> { - self.with_client(|client|Ok(client.register_port(name.as_ref(), PS::default())?)) - } - - fn sync_lead (&self, enable: bool, callback: impl Fn(TimebaseInfo)->Position) -> Usually<()> { - if enable { - self.with_client(|client|match client.register_timebase_callback(false, callback) { - Ok(_) => Ok(()), - Err(e) => Err(e) - })? - } - Ok(()) - } - - fn sync_follow (&self, _enable: bool) -> Usually<()> { - // TODO: sync follow - Ok(()) - } -} - -/// Trait for thing that has a JACK process callback. -pub trait Audio { - - /// Handle a JACK event. - fn handle (&mut self, _event: JackEvent) {} - - /// Projecss a JACK chunk. - fn process (&mut self, _: &Client, _: &ProcessScope) -> Control { - Control::Continue - } - - /// The JACK process callback function passed to the server. - fn callback ( - state: &Arc>, client: &Client, scope: &ProcessScope - ) -> Control where Self: Sized { - if let Ok(mut state) = state.write() { - state.process(client, scope) - } else { - Control::Quit - } - } - -} - -/// Implement [Audio]: provide JACK callbacks. -#[macro_export] macro_rules! audio { - - (| - $self1:ident: - $Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?,$c:ident,$s:ident - |$cb:expr$(;|$self2:ident,$e:ident|$cb2:expr)?) => { - impl $(<$($L),*$($T $(: $U)?),*>)? Audio for $Struct $(<$($L),*$($T),*>)? { - #[inline] fn process (&mut $self1, $c: &Client, $s: &ProcessScope) -> Control { $cb } - $(#[inline] fn handle (&mut $self2, $e: JackEvent) { $cb2 })? - } - }; - - ($Struct:ident: $process:ident, $handle:ident) => { - impl Audio for $Struct { - #[inline] fn process (&mut self, c: &Client, s: &ProcessScope) -> Control { - $process(self, c, s) - } - #[inline] fn handle (&mut self, e: JackEvent) { - $handle(self, e) - } - } - }; - - ($Struct:ident: $process:ident) => { - impl Audio for $Struct { - #[inline] fn process (&mut self, c: &Client, s: &ProcessScope) -> Control { - $process(self, c, s) - } - } - }; - -} - -pub trait JackPerfModel { - fn update_from_jack_scope (&self, t0: Option, scope: &ProcessScope); -} - - -pub trait HasN: Send + Sync { - fn get_nth (&self, key: usize) -> &T; - fn get_nth_mut (&mut self, key: usize) -> &mut T; -} - pub trait Gettable { /// Returns current value fn get (&self) -> T; @@ -249,7 +130,7 @@ pub trait HasClock: AsRef + AsMut { } pub trait HasDevices: AsRef> + AsMut> { fn devices_mut (&mut self) -> &mut Vec { self.as_mut() } - fn devices (&self) -> &Vec { self.as_reF() } + fn devices (&self) -> &Vec { self.as_ref() } } pub trait HasSelection: AsRef + AsMut { fn selection_mut (&mut self) -> &mut Selection { self.as_mut() } @@ -264,7 +145,7 @@ pub trait HasScene: AsRef> + AsMut> { fn scene (&self) -> Option<&Scene> { self.as_ref() } } pub trait HasScenes: AsRef> + AsMut> { - fn scenes (&self) -> &Vec { self.as_reF() } + fn scenes (&self) -> &Vec { self.as_ref() } fn scenes_mut (&mut self) -> &mut Vec { self.as_mut() } /// Generate the default name for a new scene fn scene_default_name (&self) -> Arc { format!("s{:3>}", self.scenes().len() + 1).into() } diff --git a/app/tek_type.rs b/app/tek_type.rs index bcc90fb8..fd2c7d9d 100644 --- a/app/tek_type.rs +++ b/app/tek_type.rs @@ -1,31 +1,5 @@ use crate::*; -/// Running JACK [AsyncClient] with maximum type erasure. -/// -/// One [Box] contains function that handles [JackEvent]s. -/// -/// Another [Box] containing a function that handles realtime IO. -/// -/// That's all it knows about them. -pub type DynamicAsyncClient<'j> - = AsyncClient, DynamicAudioHandler<'j>>; - -/// Notification handler wrapper for [BoxedAudioHandler]. -pub type DynamicAudioHandler<'j> = - ClosureProcessHandler<(), BoxedAudioHandler<'j>>; - -/// Boxed realtime callback. -pub type BoxedAudioHandler<'j> = - Box Control + Send + Sync + 'j>; - -/// Notification handler wrapper for [BoxedJackEventHandler]. -pub type DynamicNotifications<'j> = - JackNotify>; - -/// Boxed [JackEvent] callback. -pub type BoxedJackEventHandler<'j> = - Box; - pub type MidiData = Vec>; diff --git a/dizzle b/dizzle deleted file mode 160000 index 909b94cb..00000000 --- a/dizzle +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 909b94cbd4beffb49be314724dc79db9374bcc99 diff --git a/rust-jack b/rust-jack deleted file mode 160000 index 764a38a8..00000000 --- a/rust-jack +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 764a38a880ab4749ea60aa7e53cd814b858e606c diff --git a/tengri b/tengri index 5d0dc40f..c1b727ba 160000 --- a/tengri +++ b/tengri @@ -1 +1 @@ -Subproject commit 5d0dc40fdcd7cc022d1e468d9bf59de722949ace +Subproject commit c1b727bafc27dc715d7239a0bc63a1cdfe962bc2