refactor app/jack init

This commit is contained in:
🪞👃🪞 2024-07-10 13:15:53 +03:00
parent 117f4d5363
commit 23d9910399
7 changed files with 166 additions and 80 deletions

View file

@ -166,12 +166,12 @@ fn focus_prev (app: &mut App) -> Usually<bool> {
Ok(true) Ok(true)
} }
fn increment (app: &mut App) -> Usually<bool> { fn increment (_: &mut App) -> Usually<bool> {
Ok(false) Ok(false)
} }
fn decrement (app: &mut App) -> Usually<bool> { fn decrement (_: &mut App) -> Usually<bool> {
Ok(false) Ok(false)
} }
@ -186,7 +186,7 @@ fn delete_track (app: &mut App) -> Usually<bool> {
if app.tracks.len() > 0 { if app.tracks.len() > 0 {
let track = app.tracks.remove(app.track_cursor); let track = app.tracks.remove(app.track_cursor);
app.track_cursor = app.track_cursor.saturating_sub(1); app.track_cursor = app.track_cursor.saturating_sub(1);
app.jack.as_ref().unwrap().as_client().unregister_port(track.midi_out)?; app.client().unregister_port(track.midi_out)?;
return Ok(true) return Ok(true)
} }
Ok(false) Ok(false)

View file

@ -65,24 +65,22 @@ pub use crate::{submod, pubmod, render, handle, process, phrase, keymap, ports};
// Reexport JACK proto-lib: // Reexport JACK proto-lib:
pub use crate::jack::*; pub use crate::jack::*;
impl<T: Render + Handle + Send + Sync + Sized + 'static> Run for T {} pub fn run <T> (state: Arc<RwLock<T>>) -> Usually<Arc<RwLock<T>>>
where T: Render + Handle + Send + Sync + Sized + 'static
pub trait Run: Render + Handle + Send + Sync + Sized + 'static { {
fn run (self, init: Option<impl FnOnce(Arc<RwLock<Self>>)->Usually<()>>) -> Usually<()> { let exited = Arc::new(AtomicBool::new(false));
let app = Arc::new(RwLock::new(self)); let _input_thread = input_thread(&exited, &state);
let exited = Arc::new(AtomicBool::new(false)); terminal_setup()?;
let _input_thread = input_thread(&exited, &app); panic_hook_setup();
terminal_setup()?; let main_thread = main_thread(&exited, &state)?;
panic_hook_setup(); main_thread.join().unwrap();
let main_thread = main_thread(&exited, &app)?; terminal_teardown()?;
if let Some(init) = init { Ok(state)
init(app)?;
}
main_thread.join().unwrap();
terminal_teardown()?;
Ok(())
}
} }
pub trait Run: Render + Handle + Send + Sync + Sized + 'static {
fn run (self) -> Usually<Arc<RwLock<Self>>> { run(Arc::new(RwLock::new(self))) }
}
impl<T: Render + Handle + Send + Sync + Sized + 'static> Run for T {}
/// Set up panic hook /// Set up panic hook
pub fn panic_hook_setup () { pub fn panic_hook_setup () {

View file

@ -63,7 +63,7 @@ pub trait Render: Send {
} }
impl Render for () { impl Render for () {
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> { fn render (&self, _: &mut Buffer, a: Rect) -> Usually<Rect> {
Ok(Rect { x: a.x, y: a.y, width: 0, height: 0 }) Ok(Rect { x: a.x, y: a.y, width: 0, height: 0 })
} }
} }

View file

@ -14,7 +14,7 @@ macro_rules! edn {
} }
impl App { impl App {
pub fn load_edn (&mut self, mut src: &str) -> Usually<()> { pub fn load_edn (&mut self, mut src: &str) -> Usually<&mut Self> {
loop { loop {
match clojure_reader::edn::read(src) { match clojure_reader::edn::read(src) {
Ok((edn, rest)) => { Ok((edn, rest)) => {
@ -33,7 +33,7 @@ impl App {
}, },
} }
} }
Ok(()) Ok(self)
} }
fn load_edn_one <'e> (&mut self, edn: Edn<'e>) -> Usually<()> { fn load_edn_one <'e> (&mut self, edn: Edn<'e>) -> Usually<()> {
match edn { match edn {
@ -96,7 +96,7 @@ impl Track {
fn load_edn <'a, 'e> (app: &'a mut App, args: &[Edn<'e>]) -> Usually<&'a mut Self> { fn load_edn <'a, 'e> (app: &'a mut App, args: &[Edn<'e>]) -> Usually<&'a mut Self> {
let ppq = app.timebase.ppq() as usize; let ppq = app.timebase.ppq() as usize;
let mut name = app.new_track_name(); let mut name = app.new_track_name();
let mut gain = 0.0f64; let mut _gain = 0.0f64;
let mut devices: Vec<JackDevice> = vec![]; let mut devices: Vec<JackDevice> = vec![];
let mut phrases: Vec<Phrase> = vec![]; let mut phrases: Vec<Phrase> = vec![];
edn!(edn in args { edn!(edn in args {
@ -105,7 +105,7 @@ impl Track {
name = String::from(*n); name = String::from(*n);
} }
if let Some(Edn::Double(g)) = map.get(&Edn::Key(":gain")) { if let Some(Edn::Double(g)) = map.get(&Edn::Key(":gain")) {
gain = f64::from(*g) _gain = f64::from(*g)
} }
}, },
Edn::List(args) => match args.get(0) { Edn::List(args) => match args.get(0) {
@ -132,7 +132,7 @@ impl Track {
track.add_device(device)?; track.add_device(device)?;
} }
if let Some(device) = track.devices.get(0) { if let Some(device) = track.devices.get(0) {
device.connect_midi_in(0, &track.midi_out.clone_unowned())?; //device.connect_midi_in(0, &track.midi_out.clone_unowned())?;
} }
if let Some(device) = track.devices.get(track.devices.len() - 1) { if let Some(device) = track.devices.get(track.devices.len() - 1) {
if let Some(ref left) = left { if let Some(ref left) = left {

View file

@ -30,6 +30,55 @@ pub use ::_jack::{
Unowned Unowned
}; };
pub enum JackClient {
Inactive(Client),
Active(DynamicAsyncClient),
}
impl JackClient {
pub fn client (&self) -> &Client {
match self {
Self::Inactive(ref client) =>
client,
Self::Active(ref client) =>
client.as_client(),
}
}
pub fn transport (&self) -> Transport {
match self {
Self::Inactive(client) =>
client.transport(),
Self::Active(client) =>
client.as_client().transport(),
}
}
fn register_port <PS: PortSpec> (&self, name: &str, spec: PS) -> Usually<Port<PS>> {
Ok(match self {
Self::Inactive(client) =>
client.register_port(name, spec)?,
Self::Active(client) =>
client.as_client().register_port(name, spec)?,
})
}
pub fn activate <T: Send + Sync + 'static> (
self,
state: &Arc<RwLock<T>>,
mut process: impl FnMut(&Arc<RwLock<T>>, &Client, &ProcessScope)->Control + Send + 'static
) -> Usually<Self> {
Ok(match self {
Self::Active(_) => self,
Self::Inactive(client) => Self::Active(client.activate_async(
Notifications(Box::new(move|_|{/*TODO*/})
as Box<dyn Fn(AppEvent) + Send + Sync>),
ClosureProcessHandler::new(Box::new({
let state = state.clone();
move|c: &Client, s: &ProcessScope|process(&state, c, s)
}) as BoxedProcessHandler)
)?)
})
}
}
pub type DynamicAsyncClient = pub type DynamicAsyncClient =
AsyncClient<DynamicNotifications, DynamicProcessHandler>; AsyncClient<DynamicNotifications, DynamicProcessHandler>;

View file

@ -21,43 +21,25 @@ mod edn;
use crate::{core::*, model::*}; use crate::{core::*, model::*};
/// Application entrypoint. /// Application entrypoint.
fn main () -> Usually<()> { pub fn main () -> Usually<()> {
let controller = ["nanoKEY Studio.*capture.*"]; let mut app = App::new()?;
let soundsystem = ["Komplete.+:playback_FL", "Komplete.+:playback_FR"]; app
.connect_to_midi_ins(&["nanoKEY Studio.*capture.*"])?
.connect_to_audio_outs(&["Komplete.+:playback_FL", "Komplete.+:playback_FR"])?
.load_edn(include_str!("../demos/project.edn"))?;
run(app.activate()?);
// Start main loop // Start main loop
App::new()?.run(Some(|app: Arc<RwLock<App>>|{ //App::new()?.run(Some(|app: Arc<RwLock<App>>|{
let mut state = app.write().unwrap(); //let mut state = app.write().unwrap();
// Start JACK and setup device graph //// Start JACK and setup device graph
let jack = jack_run("tek", &app)?; //let jack = jack_run("tek", &app)?;
let client = jack.as_client(); //let client = jack.as_client();
state.transport = Some(client.transport()); //state.transport = Some(client.transport());
state.midi_in = Some(client.register_port("midi-in", MidiIn)?); //state.midi_in = Some(client.register_port("midi-in", MidiIn)?);
controller //state.jack = Some(jack);
.iter() //// Load project
.map(|name|client //state.load_edn(include_str!("../demos/project.edn"))?;
.ports(Some(name), None, PortFlags::empty()) //Ok(())
.iter() //}))
.map(|name|{ Ok(())
if let Some(port) = client.port_by_name(name) {
client.connect_ports(&port, &state.midi_in.as_ref().unwrap())?;
}
Ok(())
})
.collect::<Usually<()>>())
.collect::<Usually<()>>()?;
state.audio_outs = soundsystem
.iter()
.map(|name|client
.ports(Some(name), None, PortFlags::empty())
.get(0)
.map(|name|client.port_by_name(name)))
.flatten()
.filter_map(|x|x)
.map(Arc::new)
.collect();
state.jack = Some(jack);
// Load project
state.load_edn(include_str!("../demos/project.edn"))?;
Ok(())
}))
} }

View file

@ -15,12 +15,11 @@ pub use self::plugin::{Plugin, PluginKind, lv2::LV2Plugin};
use crate::core::*; use crate::core::*;
#[derive(Default)]
pub struct App { pub struct App {
/// Paths to user directories /// Paths to user directories
pub xdg: Option<Arc<XdgApp>>, pub xdg: Option<Arc<XdgApp>>,
/// Main JACK client. /// Main JACK client.
pub jack: Option<DynamicAsyncClient>, pub jack: Option<JackClient>,
/// Main MIDI controller. /// Main MIDI controller.
pub midi_in: Option<Port<MidiIn>>, pub midi_in: Option<Port<MidiIn>>,
/// Main audio outputs. /// Main audio outputs.
@ -77,15 +76,36 @@ impl App {
pub fn new () -> Usually<Self> { pub fn new () -> Usually<Self> {
let xdg = Arc::new(microxdg::XdgApp::new("tek")?); let xdg = Arc::new(microxdg::XdgApp::new("tek")?);
let first_run = crate::config::AppPaths::new(&xdg)?.should_create(); let first_run = crate::config::AppPaths::new(&xdg)?.should_create();
let jack = JackClient::Inactive(Client::new("tek", ClientOptions::NO_START_SERVER)?.0);
let transport = jack.transport();
Ok(Self { Ok(Self {
arranger_mode: false,
audio_outs: vec![],
chain_mode: false,
chunk_size: 0,
entered: true,
jack: Some(jack),
metronome: false,
midi_in: None,
modal: first_run.then(||crate::config::SetupModal(Some(xdg.clone())).boxed()), modal: first_run.then(||crate::config::SetupModal(Some(xdg.clone())).boxed()),
xdg: Some(xdg), note_cursor: 0,
track_cursor: 1, note_start: 2,
play_started: None,
playhead: 0,
playing: None,
quant: 24,
scene_cursor: 1, scene_cursor: 1,
note_start: 2, scenes: vec![],
time_zoom: 12, section: AppSection::default(),
quant: 24, seq_mode: false,
..Self::default() time_cursor: 0,
time_start: 0,
time_zoom: 12,
timebase: Arc::new(Timebase::default()),
track_cursor: 1,
tracks: vec![],
transport: Some(transport),
xdg: Some(xdg),
}) })
} }
} }
@ -111,14 +131,23 @@ process!(App |self, _client, scope| {
Control::Continue Control::Continue
}); });
impl App { impl App {
pub fn activate (mut self) -> Usually<Arc<RwLock<Self>>> {
let jack = self.jack.take().expect("no jack client");
let state = Arc::new(RwLock::new(self));
state.write().unwrap().jack = Some(jack.activate(&state, Self::process)?);
Ok(state)
}
pub fn process (state: &Arc<RwLock<Self>>, client: &Client, scope: &ProcessScope) -> Control {
state.write().unwrap().process(client, scope)
}
pub fn process_update_time (&mut self, scope: &ProcessScope) -> (bool, usize, usize, usize, f64) { pub fn process_update_time (&mut self, scope: &ProcessScope) -> (bool, usize, usize, usize, f64) {
self.chunk_size = scope.n_frames() as usize;
let CycleTimes { let CycleTimes {
current_frames, current_frames,
current_usecs, current_usecs,
next_usecs, next_usecs,
period_usecs period_usecs
} = scope.cycle_times().unwrap(); } = scope.cycle_times().unwrap();
self.chunk_size = scope.n_frames() as usize;
let transport = self.transport.as_ref().unwrap().query().unwrap(); let transport = self.transport.as_ref().unwrap().query().unwrap();
self.playhead = transport.pos.frame() as usize; self.playhead = transport.pos.frame() as usize;
let mut reset = false; let mut reset = false;
@ -146,15 +175,10 @@ impl App {
self.audio_outs.get(index).map(|x|x.clone()) self.audio_outs.get(index).map(|x|x.clone())
} }
pub fn client (&self) -> &Client { pub fn client (&self) -> &Client {
self.jack.as_ref().unwrap().as_client() self.jack.as_ref().unwrap().client()
}
pub fn connect_ports <A: ::_jack::PortSpec, B: ::_jack::PortSpec> (
&self, a: &Port<A>, b: &Port<B>
) -> Usually<()> {
Ok(self.client().connect_ports(a, b)?)
} }
pub fn toggle_play (&mut self) -> Usually<()> { pub fn toggle_play (&mut self) -> Usually<()> {
self.playing = match self.playing.expect("after jack init") { self.playing = match self.playing.expect("1st frame has not been processed yet") {
TransportState::Stopped => { TransportState::Stopped => {
self.transport.as_ref().unwrap().start()?; self.transport.as_ref().unwrap().start()?;
Some(TransportState::Starting) Some(TransportState::Starting)
@ -308,6 +332,39 @@ impl App {
//phrase //phrase
//} //}
//} //}
pub fn connect_to_midi_ins (&mut self, ports: &[&str]) -> Usually<&mut Self> {
let client = self.client();
let midi_in = client.register_port("midi-in", MidiIn)?;
ports
.iter()
.map(|name|client
.ports(Some(name), None, PortFlags::empty())
.iter()
.map(|name|{
if let Some(port) = client.port_by_name(name) {
client.connect_ports(&port, &midi_in)?;
}
Ok(())
})
.collect::<Usually<()>>())
.collect::<Usually<()>>()?;
self.midi_in = Some(midi_in);
Ok(self)
}
pub fn connect_to_audio_outs (&mut self, ports: &[&str]) -> Usually<&mut Self> {
let client = self.client();
self.audio_outs = ports
.iter()
.map(|name|client
.ports(Some(name), None, PortFlags::empty())
.get(0)
.map(|name|client.port_by_name(name)))
.flatten()
.filter_map(|x|x)
.map(Arc::new)
.collect();
Ok(self)
}
} }
struct Selection<'a> { struct Selection<'a> {