mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-03-13 11:50:44 +01:00
move the jack stuff into tengri
This commit is contained in:
parent
9ae35830c3
commit
6295f2e601
12 changed files with 107 additions and 440 deletions
311
app/tek_impls.rs
311
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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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<Browse>> { todo!() }
|
||||
/// FIXME: implement
|
||||
pub fn browser_target (&self) -> Option<&BrowseTarget> { todo!() }
|
||||
}
|
||||
impl_audio!(App: tek_jack_process, tek_jack_event);
|
||||
}
|
||||
|
||||
#[cfg(feature = "vst2")] impl<E: Engine> ::vst::host::Host for Plugin<E> {}
|
||||
|
|
@ -1703,8 +1631,6 @@ impl AsRef<Arc<[MenuItem]>> for MenuItems { fn as_ref (&self) -> &Arc<[MenuItem]
|
|||
impl<T: TracksView + ScenesView + Send + Sync> ClipsView for T {}
|
||||
impl HasClipsSize for App { fn clips_size (&self) -> &Measure<TuiOut> { &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<J: HasJack<'static>> RegisterPorts for J {
|
|||
#[cfg(feature = "port")] impl_has!(Vec<MidiInput>: |self: Sequencer|self.midi_ins);
|
||||
#[cfg(feature = "port")] impl_has!(Vec<MidiOutput>: |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<TuiOut>: |self: App|self.size);
|
||||
|
|
@ -1838,141 +1762,6 @@ namespace!(App: Option<Arc<RwLock<MidiClip>>> {
|
|||
};
|
||||
});
|
||||
|
||||
|
||||
mod draw {
|
||||
|
||||
use crate::*;
|
||||
|
||||
impl Draw<TuiOut> for MidiEditor {
|
||||
fn draw (&self, to: &mut TuiOut) { self.content().draw(to) }
|
||||
}
|
||||
|
||||
impl Draw<TuiOut> 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 <T: HasJack<'j> + Audio + Send + Sync + 'static> (
|
||||
name: &impl AsRef<str>,
|
||||
init: impl FnOnce(Jack<'j>)->Usually<T>
|
||||
) -> Usually<Arc<RwLock<T>>> {
|
||||
Jack::new(name)?.run(init)
|
||||
}
|
||||
|
||||
pub fn new (name: &impl AsRef<str>) -> Usually<Self> {
|
||||
let client = Client::new(name.as_ref(), ClientOptions::NO_START_SERVER)?.0;
|
||||
Ok(Jack(Arc::new(RwLock::new(JackState::Inactive(client)))))
|
||||
}
|
||||
|
||||
pub fn run <T: HasJack<'j> + Audio + Send + Sync + 'static>
|
||||
(self, init: impl FnOnce(Self)->Usually<T>) -> Usually<Arc<RwLock<T>>>
|
||||
{
|
||||
let client_state = self.0.clone();
|
||||
let app: Arc<RwLock<T>> = 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 <T> (&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<T: Fn(JackEvent) + Send> NotificationHandler for JackNotify<T> {
|
||||
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<u64>, 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<TuiOut> for MidiEditor {
|
||||
fn draw (&self, to: &mut TuiOut) { self.content().draw(to) }
|
||||
}
|
||||
impl Draw<TuiOut> 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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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<Browse>> { 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
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue