mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
refactor app/jack init
This commit is contained in:
parent
117f4d5363
commit
23d9910399
7 changed files with 166 additions and 80 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
32
src/core.rs
32
src/core.rs
|
|
@ -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 () {
|
||||||
|
|
|
||||||
|
|
@ -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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
src/edn.rs
10
src/edn.rs
|
|
@ -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 {
|
||||||
|
|
|
||||||
49
src/jack.rs
49
src/jack.rs
|
|
@ -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>;
|
||||||
|
|
||||||
|
|
|
||||||
58
src/main.rs
58
src/main.rs
|
|
@ -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(())
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
89
src/model.rs
89
src/model.rs
|
|
@ -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> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue