mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
merge tek_jack into tek_core
This commit is contained in:
parent
a659062dbc
commit
278b3caad3
23 changed files with 285 additions and 456 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
|
@ -2512,7 +2512,6 @@ dependencies = [
|
|||
"clojure-reader",
|
||||
"microxdg",
|
||||
"tek_core",
|
||||
"tek_jack",
|
||||
"tek_mixer",
|
||||
"tek_sequencer",
|
||||
]
|
||||
|
|
@ -2527,20 +2526,13 @@ dependencies = [
|
|||
"clap",
|
||||
"clojure-reader",
|
||||
"crossterm",
|
||||
"jack",
|
||||
"midly",
|
||||
"once_cell",
|
||||
"ratatui",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tek_jack"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"jack",
|
||||
"tek_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tek_mixer"
|
||||
version = "0.1.0"
|
||||
|
|
@ -2549,7 +2541,6 @@ dependencies = [
|
|||
"suil-rs",
|
||||
"symphonia",
|
||||
"tek_core",
|
||||
"tek_jack",
|
||||
"vst",
|
||||
"wavers",
|
||||
"winit",
|
||||
|
|
@ -2560,7 +2551,6 @@ name = "tek_sequencer"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"tek_core",
|
||||
"tek_jack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ clojure-reader = "0.1.0"
|
|||
microxdg = "0.1.2"
|
||||
|
||||
tek_core = { path = "../tek_core" }
|
||||
tek_jack = { path = "../tek_jack" }
|
||||
tek_sequencer = { path = "../tek_sequencer" }
|
||||
tek_mixer = { path = "../tek_mixer" }
|
||||
#jack = "0.10"
|
||||
|
|
|
|||
139
crates/tek/example.edn
Normal file
139
crates/tek/example.edn
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
;(bpm 150)
|
||||
|
||||
;(midi-in "nanoKEY Studio.*capture.*")
|
||||
;(midi-in "nanoKEY Studio.*capture.*")
|
||||
;(audio-out "Built-.+:playback_FL", "Built-.+:playback_FR")
|
||||
|
||||
;(scene { :name "Intro" } 0 0 _ _)
|
||||
;(scene { :name "Hook" } 1 1 0 _)
|
||||
;(scene { :name "Verse" } 2 2 1 _)
|
||||
;(scene { :name "Chorus" } 3 3 2 _)
|
||||
;(scene { :name "Bridge" } _ 4 3 _)
|
||||
;(scene { :name "Outro" } 4 1 4 _)
|
||||
|
||||
;(track { :name "Drums" :gain +0.0 }
|
||||
;(phrase { :name "4 kicks" :beats 4 :steps 16 }
|
||||
;(:00 (36 128))
|
||||
;(:04 (36 100))
|
||||
;(:08 (36 100))
|
||||
;(:12 (36 100)))
|
||||
;(phrase { :name "5 kicks" :beats 4 :steps 16 }
|
||||
;(:00 (36 128))
|
||||
;(:04 (36 100))
|
||||
;(:08 (36 128))
|
||||
;(:12 (36 100))
|
||||
;(:14 (36 110)))
|
||||
;(phrase { :name "D Beat" :beats 8 :steps 32 }
|
||||
;(:00 (44 70) (36 128) (49 110))
|
||||
;(:02 (44 30))
|
||||
;(:04 (44 80) (40 100))
|
||||
;(:06 (44 50))
|
||||
;(:08 (44 30) (36 100))
|
||||
;(:10 (44 50) (36 100))
|
||||
;(:12 (44 80) (40 100))
|
||||
;(:14 (44 50))
|
||||
;(:15 (36 50))
|
||||
;(:16 (44 60) (36 80))
|
||||
;(:18 (44 60) (36 80))
|
||||
;(:20 (44 60) (40 80))
|
||||
;(:22 (44 60))
|
||||
;(:24 (44 60))
|
||||
;(:26 (44 30) (36 80))
|
||||
;(:27 (44 60))
|
||||
;(:28 (44 60) (40 80))
|
||||
;(:30 (44 60)))
|
||||
;(phrase { :name "Garage" :beats 4 :steps 16 }
|
||||
;(:00 (44 100) (36 100) (35 100))
|
||||
;(:01 (44 100))
|
||||
;(:02 (44 100) (35 100))
|
||||
;(:03 (44 100))
|
||||
;(:04 (44 100) (40 100))
|
||||
;(:06 (44 100))
|
||||
;(:07 (44 100) (34 100))
|
||||
;(:09 (44 100))
|
||||
;(:10 (44 100))
|
||||
;(:11 (35 100) (36 100))
|
||||
;(:12 (44 100) (40 100))
|
||||
;(:14 (44 100)))
|
||||
|
||||
;(phrase { :name "Trap Pinging" :beats 8 :steps 96 }
|
||||
;(:00 (42 100) (36 100) (34 120) (49 100))
|
||||
;(:01 (42 100))
|
||||
;(:02 (42 100))
|
||||
;(:06 (42 100) (35 80) (36 80) (49 100))
|
||||
;(:07 (42 100))
|
||||
;(:08 (42 100))
|
||||
;(:12 (42 100))
|
||||
;(:15 (39 100) (34 100))
|
||||
;(:18 (42 100))
|
||||
;(:24 (42 100) (38 50) (40 50))
|
||||
;(:27 (42 100) (36 50))
|
||||
;(:30 (42 100))
|
||||
;(:33 (42 100) (36 50) (34 100))
|
||||
;(:36 (42 90))
|
||||
;(:39 (42 80))
|
||||
;(:42 (42 70))
|
||||
;(:45 (42 60))
|
||||
|
||||
;(:48 (42 100) (36 100) (34 100))
|
||||
;(:50 (42 100))
|
||||
;(:52 (42 110))
|
||||
;(:54 (46 50) (42 120))
|
||||
;(:56 (42 90))
|
||||
;(:58 (42 100))
|
||||
;(:60 (42 100) (35 100))
|
||||
;(:64 (39 100))
|
||||
;(:66 (42 100) (34 100))
|
||||
|
||||
;(:70 (42 100))
|
||||
;(:71 (42 100))
|
||||
;(:72 (42 100) (38 50) (40 50))
|
||||
;(:75 (42 100) (36 50) (34 80))
|
||||
;(:78 (42 100))
|
||||
;(:81 (42 100) (36 50))
|
||||
;(:84 (38 40) (40 50) (34 90))
|
||||
;(:87 (42 90) (35 40))
|
||||
;(:90 (42 70)))
|
||||
|
||||
;(sampler { :name "DrumKit1" :dir "/home/user/Lab/Music/pak" }
|
||||
;(sample { :midi 34 :name "808 D" :file "808.wav" })
|
||||
;(sample { :midi 35 :name "Kick 1" :file "kik.wav" })
|
||||
;(sample { :midi 36 :name "Kick 2" :file "kik2.wav" })
|
||||
;(sample { :midi 37 :name "Rim" :file "rim.wav" })
|
||||
;(sample { :midi 38 :name "Snare 1" :file "sna.wav" })
|
||||
;(sample { :midi 39 :name "Shaker" :file "shk.wav" })
|
||||
;(sample { :midi 40 :name "Snare 2" :file "sna2.wav" })
|
||||
;(sample { :midi 42 :name "Closed HH 1" :file "chh.wav" })
|
||||
;(sample { :midi 44 :name "Closed HH 2" :file "chh2.wav" })
|
||||
;(sample { :midi 45 :name "Open HH 0" :file "ohh.wav" })
|
||||
;(sample { :midi 46 :name "Open HH 1" :file "ohh1.wav" })
|
||||
;(sample { :midi 47 :name "Open HH 2" :file "ohh2.wav" })
|
||||
;(sample { :midi 49 :name "Crash" :file "crs.wav" })))
|
||||
|
||||
;(track { :name "Bass" :gain +0.0 }
|
||||
;(phrase { :name "Bass 1" :beats 4 })
|
||||
;(phrase { :name "Bass 2" :beats 4 })
|
||||
;(phrase { :name "Bass 3" :beats 4 })
|
||||
;(phrase { :name "Bass 4" :beats 4 })
|
||||
;(phrase { :name "Bass 5" :beats 4 })
|
||||
;(phrase { :name "Bass 6" :beats 4 })
|
||||
;(phrase { :name "Bass 7" :beats 4 })
|
||||
;(phrase { :name "Bass 8" :beats 4 })
|
||||
;(lv2 {
|
||||
;:name "Odin2"
|
||||
;:path "file:///home/user/.lv2/Odin2.lv2"
|
||||
;}))
|
||||
|
||||
;(track { :name "Lead" :gain +0.0 }
|
||||
;(phrase { :name "Lead 1" :beats 4 })
|
||||
;(phrase { :name "Lead 2" :beats 4 })
|
||||
;(phrase { :name "Lead 3" :beats 4 })
|
||||
;(phrase { :name "Lead 4" :beats 4 })
|
||||
;(phrase { :name "Lead 5" :beats 4 })
|
||||
;(phrase { :name "Lead 6" :beats 4 })
|
||||
;(phrase { :name "Lead 7" :beats 4 })
|
||||
;(phrase { :name "Lead 8" :beats 4 })
|
||||
;(lv2 {
|
||||
;:name "Odin2"
|
||||
;:path "file:///home/user/.lv2/Odin2.lv2"
|
||||
;}))
|
||||
|
|
@ -37,17 +37,18 @@ fn handle_modal (e: &AppEvent) -> Usually<bool> {
|
|||
}
|
||||
|
||||
fn handle_focused (state: &mut App, e: &AppEvent) -> Usually<bool> {
|
||||
match state.section {
|
||||
AppFocus::Transport => state.transport.handle(e),
|
||||
AppFocus::Arranger => state.arranger.sequencer_mut().map(|s|s.handle(e)),
|
||||
AppFocus::Sequencer => state.arranger.sequencer_mut().map(|s|s.handle(e)),
|
||||
AppFocus::Chain => Ok(false)/*if state.entered {
|
||||
handle_device(state, e)? ||
|
||||
handle_keymap(state, e, crate::control::KEYMAP_CHAIN)?
|
||||
} else {
|
||||
handle_keymap(state, e, crate::control::KEYMAP_CHAIN)? || handle_device(state, e)?
|
||||
})*/
|
||||
}
|
||||
unimplemented!()
|
||||
//match state.section {
|
||||
//AppFocus::Transport => state.transport.handle(e),
|
||||
//AppFocus::Arranger => state.arranger.sequencer_mut().map(|s|s.handle(e)),
|
||||
//AppFocus::Sequencer => state.arranger.sequencer_mut().map(|s|s.handle(e)),
|
||||
//AppFocus::Chain => Ok(false)[>if state.entered {
|
||||
//handle_device(state, e)? ||
|
||||
//handle_keymap(state, e, crate::control::KEYMAP_CHAIN)?
|
||||
//} else {
|
||||
//handle_keymap(state, e, crate::control::KEYMAP_CHAIN)? || handle_device(state, e)?
|
||||
//})*/
|
||||
//}
|
||||
}
|
||||
|
||||
fn handle_device (state: &mut App, e: &AppEvent) -> Usually<bool> {
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@
|
|||
#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]
|
||||
#![allow(ambiguous_glob_reexports)]
|
||||
|
||||
pub(crate) use tek_core::*;
|
||||
pub(crate) use tek_jack::{*, jack::*};
|
||||
pub(crate) use tek_core::{*, jack::*};
|
||||
pub(crate) use tek_sequencer::*;
|
||||
pub(crate) use tek_mixer::*;
|
||||
pub(crate) use microxdg::XdgApp;
|
||||
|
||||
submod! {
|
||||
|
|
@ -29,7 +29,7 @@ pub static MODAL: Lazy<Arc<Mutex<Option<Box<dyn ExitableComponent>>>>> =
|
|||
|
||||
/// Application entrypoint.
|
||||
pub fn main () -> Usually<()> {
|
||||
run(App::from_edn(include_str!("../../../demos/project.edn"))?
|
||||
run(App::from_edn(include_str!("../example.edn"))?
|
||||
.activate(Some(|app: &Arc<RwLock<App>>|Ok({
|
||||
let (midi_in, mut midi_outs) = {
|
||||
let app = app.read().unwrap();
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@ edition = "2021"
|
|||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
crossterm = "0.27"
|
||||
ratatui = { version = "0.26.3", features = [ "unstable-widget-ref", "underline-color" ] }
|
||||
atomic_float = "1.0.0"
|
||||
backtrace = "0.3.72"
|
||||
toml = "0.8.12"
|
||||
better-panic = "0.3.0"
|
||||
midly = "0.5"
|
||||
clap = { version = "4.5.4", features = [ "derive" ] }
|
||||
clojure-reader = "0.1.0"
|
||||
crossterm = "0.27"
|
||||
jack = "0.10"
|
||||
midly = "0.5"
|
||||
once_cell = "1.19.0"
|
||||
atomic_float = "1.0.0"
|
||||
ratatui = { version = "0.26.3", features = [ "unstable-widget-ref", "underline-color" ] }
|
||||
toml = "0.8.12"
|
||||
|
|
|
|||
|
|
@ -1,39 +1,4 @@
|
|||
//! Audio engine.
|
||||
|
||||
pub use jack;
|
||||
|
||||
use std::sync::{Arc, RwLock, LockResult, RwLockReadGuard, RwLockWriteGuard};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
submod!( device event factory ports );
|
||||
|
||||
pub(crate) use tek_core::*;
|
||||
pub(crate) use tek_core::ratatui::prelude::{Buffer, Rect};
|
||||
pub(crate) use ::jack::{
|
||||
AsyncClient,
|
||||
AudioIn,
|
||||
AudioOut,
|
||||
Client,
|
||||
ClientOptions,
|
||||
ClientStatus,
|
||||
ClosureProcessHandler,
|
||||
Control,
|
||||
//CycleTimes,
|
||||
Frames,
|
||||
MidiIn,
|
||||
//MidiIter,
|
||||
MidiOut,
|
||||
NotificationHandler,
|
||||
Port,
|
||||
//PortFlags,
|
||||
PortId,
|
||||
PortSpec,
|
||||
ProcessScope,
|
||||
//RawMidi,
|
||||
Transport,
|
||||
//TransportState,
|
||||
Unowned
|
||||
};
|
||||
use crate::{*, jack::*};
|
||||
|
||||
/// A UI component that may be associated with a JACK client by the `Jack` factory.
|
||||
pub trait Device: Render + Handle + Process + Send + Sync {
|
||||
|
|
@ -156,3 +121,109 @@ pub fn jack_run <T: Sync> (name: &str, app: &Arc<RwLock<T>>) -> Usually<DynamicA
|
|||
)?)
|
||||
}
|
||||
|
||||
/// `JackDevice` factory. Creates JACK `Client`s, performs port registration
|
||||
/// and activation, and encapsulates a `Device` into a `JackDevice`.
|
||||
pub struct Jack {
|
||||
pub client: Client,
|
||||
pub midi_ins: Vec<String>,
|
||||
pub audio_ins: Vec<String>,
|
||||
pub midi_outs: Vec<String>,
|
||||
pub audio_outs: Vec<String>,
|
||||
}
|
||||
impl Jack {
|
||||
pub fn new (name: &str) -> Usually<Self> {
|
||||
Ok(Self {
|
||||
midi_ins: vec![],
|
||||
audio_ins: vec![],
|
||||
midi_outs: vec![],
|
||||
audio_outs: vec![],
|
||||
client: Client::new(
|
||||
name,
|
||||
ClientOptions::NO_START_SERVER
|
||||
)?.0,
|
||||
})
|
||||
}
|
||||
pub fn run <T: Device + Process + Sized + 'static> (
|
||||
self, state: impl FnOnce(JackPorts)->Box<T>
|
||||
)
|
||||
-> Usually<JackDevice>
|
||||
{
|
||||
let owned_ports = JackPorts {
|
||||
audio_ins: register_ports(&self.client, self.audio_ins, AudioIn)?,
|
||||
audio_outs: register_ports(&self.client, self.audio_outs, AudioOut)?,
|
||||
midi_ins: register_ports(&self.client, self.midi_ins, MidiIn)?,
|
||||
midi_outs: register_ports(&self.client, self.midi_outs, MidiOut)?,
|
||||
};
|
||||
let midi_outs = owned_ports.midi_outs.values()
|
||||
.map(|p|Ok(p.name()?)).collect::<Usually<Vec<_>>>()?;
|
||||
let midi_ins = owned_ports.midi_ins.values()
|
||||
.map(|p|Ok(p.name()?)).collect::<Usually<Vec<_>>>()?;
|
||||
let audio_outs = owned_ports.audio_outs.values()
|
||||
.map(|p|Ok(p.name()?)).collect::<Usually<Vec<_>>>()?;
|
||||
let audio_ins = owned_ports.audio_ins.values()
|
||||
.map(|p|Ok(p.name()?)).collect::<Usually<Vec<_>>>()?;
|
||||
let state = Arc::new(RwLock::new(state(owned_ports) as Box<dyn Device>));
|
||||
let client = self.client.activate_async(
|
||||
Notifications(Box::new({
|
||||
let _state = state.clone();
|
||||
move|_event|{
|
||||
// FIXME: this deadlocks
|
||||
//state.lock().unwrap().handle(&event).unwrap();
|
||||
}
|
||||
}) as Box<dyn Fn(JackEvent) + Send + Sync>),
|
||||
ClosureProcessHandler::new(Box::new({
|
||||
let state = state.clone();
|
||||
move|c: &Client, s: &ProcessScope|{
|
||||
state.write().unwrap().process(c, s)
|
||||
}
|
||||
}) as BoxedProcessHandler)
|
||||
)?;
|
||||
Ok(JackDevice {
|
||||
ports: UnownedJackPorts {
|
||||
audio_ins: query_ports(&client.as_client(), audio_ins),
|
||||
audio_outs: query_ports(&client.as_client(), audio_outs),
|
||||
midi_ins: query_ports(&client.as_client(), midi_ins),
|
||||
midi_outs: query_ports(&client.as_client(), midi_outs),
|
||||
},
|
||||
client,
|
||||
state,
|
||||
})
|
||||
}
|
||||
pub fn audio_in (mut self, name: &str) -> Self {
|
||||
self.audio_ins.push(name.to_string());
|
||||
self
|
||||
}
|
||||
pub fn audio_out (mut self, name: &str) -> Self {
|
||||
self.audio_outs.push(name.to_string());
|
||||
self
|
||||
}
|
||||
pub fn midi_in (mut self, name: &str) -> Self {
|
||||
self.midi_ins.push(name.to_string());
|
||||
self
|
||||
}
|
||||
pub fn midi_out (mut self, name: &str) -> Self {
|
||||
self.midi_outs.push(name.to_string());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn register_ports <T: PortSpec + Copy> (
|
||||
client: &Client, names: Vec<String>, spec: T
|
||||
) -> Usually<BTreeMap<String, Port<T>>> {
|
||||
names.into_iter().try_fold(BTreeMap::new(), |mut ports, name|{
|
||||
let port = client.register_port(&name, spec)?;
|
||||
ports.insert(name, port);
|
||||
Ok(ports)
|
||||
})
|
||||
}
|
||||
|
||||
fn query_ports (
|
||||
client: &Client, names: Vec<String>
|
||||
) -> BTreeMap<String, Port<Unowned>> {
|
||||
names.into_iter().fold(BTreeMap::new(), |mut ports, name|{
|
||||
let port = client.port_by_name(&name).unwrap();
|
||||
ports.insert(name, port);
|
||||
ports
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -1,7 +1,4 @@
|
|||
//! Wrap JACK-enabled [Device]s.
|
||||
|
||||
use super::*;
|
||||
use tek_core::ratatui::prelude::{Buffer, Rect};
|
||||
use crate::{*, jack::*};
|
||||
|
||||
/// A [Device] bound to a JACK client and a set of ports.
|
||||
pub struct JackDevice {
|
||||
|
|
@ -52,3 +49,4 @@ impl JackDevice {
|
|||
Ok(self.client.as_client().connect_ports(self.audio_outs()?[index], port)?)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,6 +1,4 @@
|
|||
//! JACK event handling.
|
||||
|
||||
use super::*;
|
||||
use crate::{*, jack::*};
|
||||
|
||||
/// Notification handler used by the [Jack] factory
|
||||
/// when constructing [JackDevice]s.
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
//! Handling JACK ports.
|
||||
use super::*;
|
||||
use crate::{*, jack::*};
|
||||
|
||||
/// Collection of JACK ports as [AudioIn]/[AudioOut]/[MidiIn]/[MidiOut].
|
||||
#[derive(Default, Debug)]
|
||||
|
|
@ -92,3 +91,4 @@ pub trait Ports {
|
|||
)?}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
pub use ratatui;
|
||||
pub use crossterm;
|
||||
pub use jack;
|
||||
pub use midly;
|
||||
pub use clap;
|
||||
pub use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
pub use std::sync::{Arc, Mutex, LockResult, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
pub use std::collections::BTreeMap;
|
||||
pub use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers};
|
||||
pub use ratatui::prelude::{Rect, Style, Color, Buffer};
|
||||
|
|
@ -16,7 +17,6 @@ pub(crate) use std::io::{stdout};
|
|||
pub(crate) use std::thread::{spawn, JoinHandle};
|
||||
pub(crate) use std::time::Duration;
|
||||
pub(crate) use atomic_float::*;
|
||||
//, LockResult, RwLockReadGuard, RwLockWriteGuard};
|
||||
//pub(crate) use std::path::PathBuf;
|
||||
//pub(crate) use std::fs::read_dir;
|
||||
//pub(crate) use std::ffi::OsString;
|
||||
|
|
@ -48,6 +48,10 @@ submod! {
|
|||
time_base
|
||||
time_note
|
||||
time_tick
|
||||
jack_core
|
||||
jack_device
|
||||
jack_event
|
||||
jack_ports
|
||||
}
|
||||
|
||||
/// EDN parsing helper.
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ impl Render for () {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Render> Render for Option<T> {
|
||||
impl<T: Render + Send + Sync> Render for Option<&T> {
|
||||
fn render (&self, b: &mut Buffer, a: Rect) -> Usually<Rect> {
|
||||
match self {
|
||||
Some(widget) => widget.render(b, a),
|
||||
|
|
|
|||
|
|
@ -46,63 +46,3 @@ mod test {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
/// (pulses, name)
|
||||
pub const NOTE_DURATIONS: [(usize, &str);26] = [
|
||||
(1, "1/384"),
|
||||
(2, "1/192"),
|
||||
(3, "1/128"),
|
||||
(4, "1/96"),
|
||||
(6, "1/64"),
|
||||
(8, "1/48"),
|
||||
(12, "1/32"),
|
||||
(16, "1/24"),
|
||||
(24, "1/16"),
|
||||
(32, "1/12"),
|
||||
(48, "1/8"),
|
||||
(64, "1/6"),
|
||||
(96, "1/4"),
|
||||
(128, "1/3"),
|
||||
(192, "1/2"),
|
||||
(256, "2/3"),
|
||||
(384, "1/1"),
|
||||
(512, "4/3"),
|
||||
(576, "3/2"),
|
||||
(768, "2/1"),
|
||||
(1152, "3/1"),
|
||||
(1536, "4/1"),
|
||||
(2304, "6/1"),
|
||||
(3072, "8/1"),
|
||||
(3456, "9/1"),
|
||||
(6144, "16/1"),
|
||||
];
|
||||
|
||||
/// Returns the next shorter length
|
||||
pub fn prev_note_length (ppq: usize) -> usize {
|
||||
for i in 1..=16 {
|
||||
let length = NOTE_DURATIONS[16-i].0;
|
||||
if length < ppq {
|
||||
return length
|
||||
}
|
||||
}
|
||||
ppq
|
||||
}
|
||||
|
||||
/// Returns the next longer length
|
||||
pub fn next_note_length (ppq: usize) -> usize {
|
||||
for (length, _) in &NOTE_DURATIONS {
|
||||
if *length > ppq {
|
||||
return *length
|
||||
}
|
||||
}
|
||||
ppq
|
||||
}
|
||||
|
||||
pub fn ppq_to_name (ppq: usize) -> &'static str {
|
||||
for (length, name) in &NOTE_DURATIONS {
|
||||
if *length == ppq {
|
||||
return name
|
||||
}
|
||||
}
|
||||
""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
[package]
|
||||
name = "tek_jack"
|
||||
edition = "2021"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
tek_core = { path = "../tek_core" }
|
||||
jack = "0.10"
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
# `tek_jack`
|
||||
|
||||
This crate interfaces with the JACK Audio Connection Kit API.
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
//! Encapsulate and run [Device]s as [JackDevice]s.
|
||||
use super::*;
|
||||
|
||||
/// `JackDevice` factory. Creates JACK `Client`s, performs port registration
|
||||
/// and activation, and encapsulates a `Device` into a `JackDevice`.
|
||||
pub struct Jack {
|
||||
pub client: Client,
|
||||
pub midi_ins: Vec<String>,
|
||||
pub audio_ins: Vec<String>,
|
||||
pub midi_outs: Vec<String>,
|
||||
pub audio_outs: Vec<String>,
|
||||
}
|
||||
impl Jack {
|
||||
pub fn new (name: &str) -> Usually<Self> {
|
||||
Ok(Self {
|
||||
midi_ins: vec![],
|
||||
audio_ins: vec![],
|
||||
midi_outs: vec![],
|
||||
audio_outs: vec![],
|
||||
client: Client::new(
|
||||
name,
|
||||
ClientOptions::NO_START_SERVER
|
||||
)?.0,
|
||||
})
|
||||
}
|
||||
pub fn run <T: Device + Process + Sized + 'static> (
|
||||
self, state: impl FnOnce(JackPorts)->Box<T>
|
||||
)
|
||||
-> Usually<JackDevice>
|
||||
{
|
||||
let owned_ports = JackPorts {
|
||||
audio_ins: register_ports(&self.client, self.audio_ins, AudioIn)?,
|
||||
audio_outs: register_ports(&self.client, self.audio_outs, AudioOut)?,
|
||||
midi_ins: register_ports(&self.client, self.midi_ins, MidiIn)?,
|
||||
midi_outs: register_ports(&self.client, self.midi_outs, MidiOut)?,
|
||||
};
|
||||
let midi_outs = owned_ports.midi_outs.values()
|
||||
.map(|p|Ok(p.name()?)).collect::<Usually<Vec<_>>>()?;
|
||||
let midi_ins = owned_ports.midi_ins.values()
|
||||
.map(|p|Ok(p.name()?)).collect::<Usually<Vec<_>>>()?;
|
||||
let audio_outs = owned_ports.audio_outs.values()
|
||||
.map(|p|Ok(p.name()?)).collect::<Usually<Vec<_>>>()?;
|
||||
let audio_ins = owned_ports.audio_ins.values()
|
||||
.map(|p|Ok(p.name()?)).collect::<Usually<Vec<_>>>()?;
|
||||
let state = Arc::new(RwLock::new(state(owned_ports) as Box<dyn Device>));
|
||||
let client = self.client.activate_async(
|
||||
Notifications(Box::new({
|
||||
let _state = state.clone();
|
||||
move|_event|{
|
||||
// FIXME: this deadlocks
|
||||
//state.lock().unwrap().handle(&event).unwrap();
|
||||
}
|
||||
}) as Box<dyn Fn(JackEvent) + Send + Sync>),
|
||||
ClosureProcessHandler::new(Box::new({
|
||||
let state = state.clone();
|
||||
move|c: &Client, s: &ProcessScope|{
|
||||
state.write().unwrap().process(c, s)
|
||||
}
|
||||
}) as BoxedProcessHandler)
|
||||
)?;
|
||||
Ok(JackDevice {
|
||||
ports: UnownedJackPorts {
|
||||
audio_ins: query_ports(&client.as_client(), audio_ins),
|
||||
audio_outs: query_ports(&client.as_client(), audio_outs),
|
||||
midi_ins: query_ports(&client.as_client(), midi_ins),
|
||||
midi_outs: query_ports(&client.as_client(), midi_outs),
|
||||
},
|
||||
client,
|
||||
state,
|
||||
})
|
||||
}
|
||||
pub fn audio_in (mut self, name: &str) -> Self {
|
||||
self.audio_ins.push(name.to_string());
|
||||
self
|
||||
}
|
||||
pub fn audio_out (mut self, name: &str) -> Self {
|
||||
self.audio_outs.push(name.to_string());
|
||||
self
|
||||
}
|
||||
pub fn midi_in (mut self, name: &str) -> Self {
|
||||
self.midi_ins.push(name.to_string());
|
||||
self
|
||||
}
|
||||
pub fn midi_out (mut self, name: &str) -> Self {
|
||||
self.midi_outs.push(name.to_string());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn register_ports <T: PortSpec + Copy> (
|
||||
client: &Client, names: Vec<String>, spec: T
|
||||
) -> Usually<BTreeMap<String, Port<T>>> {
|
||||
names.into_iter().try_fold(BTreeMap::new(), |mut ports, name|{
|
||||
let port = client.register_port(&name, spec)?;
|
||||
ports.insert(name, port);
|
||||
Ok(ports)
|
||||
})
|
||||
}
|
||||
|
||||
fn query_ports (
|
||||
client: &Client, names: Vec<String>
|
||||
) -> BTreeMap<String, Port<Unowned>> {
|
||||
names.into_iter().fold(BTreeMap::new(), |mut ports, name|{
|
||||
let port = client.port_by_name(&name).unwrap();
|
||||
ports.insert(name, port);
|
||||
ports
|
||||
})
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ version = "0.1.0"
|
|||
|
||||
[dependencies]
|
||||
tek_core = { path = "../tek_core" }
|
||||
tek_jack = { path = "../tek_jack" }
|
||||
|
||||
livi = "0.7.4"
|
||||
suil-rs = { path = "../suil" }
|
||||
|
|
@ -32,4 +31,4 @@ path = "src/sampler_main.rs"
|
|||
|
||||
[[bin]]
|
||||
name = "tek_plugin"
|
||||
path = "src/sampler_main.rs"
|
||||
path = "src/plugin_main.rs"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ pub(crate) use tek_core::*;
|
|||
pub(crate) use tek_core::ratatui::prelude::*;
|
||||
pub(crate) use tek_core::crossterm::event::{KeyCode, KeyModifiers};
|
||||
pub(crate) use tek_core::midly::{num::u7, live::LiveEvent, MidiMessage};
|
||||
pub(crate) use tek_jack::{*, jack::*};
|
||||
pub(crate) use tek_core::jack::*;
|
||||
|
||||
pub(crate) use std::collections::BTreeMap;
|
||||
pub(crate) use std::sync::{Arc, Mutex, RwLock};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ version = "0.1.0"
|
|||
|
||||
[dependencies]
|
||||
tek_core = { path = "../tek_core" }
|
||||
tek_jack = { path = "../tek_jack" }
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
|
|
|||
|
|
@ -27,12 +27,12 @@ impl Arranger {
|
|||
arr.transport = Some(Arc::new(RwLock::new(TransportToolbar::new(None))));
|
||||
}
|
||||
if let Some(tracks) = args.tracks {
|
||||
for track in 0..tracks {
|
||||
for _ in 0..tracks {
|
||||
arr.track_add(None)?;
|
||||
}
|
||||
}
|
||||
if let Some(scenes) = args.scenes {
|
||||
for scene in 0..scenes {
|
||||
for _ in 0..scenes {
|
||||
arr.scene_add(None)?;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ pub(crate) use tek_core::*;
|
|||
pub(crate) use tek_core::ratatui::prelude::*;
|
||||
pub(crate) use tek_core::crossterm::event::{KeyCode, KeyModifiers};
|
||||
pub(crate) use tek_core::midly::{num::u7, live::LiveEvent, MidiMessage};
|
||||
pub(crate) use tek_jack::{*, jack::*};
|
||||
pub(crate) use tek_core::jack::*;
|
||||
pub(crate) use std::sync::{Arc, RwLock};
|
||||
|
||||
submod! {
|
||||
|
|
|
|||
|
|
@ -1,139 +0,0 @@
|
|||
(bpm 150)
|
||||
|
||||
(midi-in "nanoKEY Studio.*capture.*")
|
||||
(midi-in "nanoKEY Studio.*capture.*")
|
||||
(audio-out "Built-.+:playback_FL", "Built-.+:playback_FR")
|
||||
|
||||
(scene { :name "Intro" } 0 0 _ _)
|
||||
(scene { :name "Hook" } 1 1 0 _)
|
||||
(scene { :name "Verse" } 2 2 1 _)
|
||||
(scene { :name "Chorus" } 3 3 2 _)
|
||||
(scene { :name "Bridge" } _ 4 3 _)
|
||||
(scene { :name "Outro" } 4 1 4 _)
|
||||
|
||||
(track { :name "Drums" :gain +0.0 }
|
||||
(phrase { :name "4 kicks" :beats 4 :steps 16 }
|
||||
(:00 (36 128))
|
||||
(:04 (36 100))
|
||||
(:08 (36 100))
|
||||
(:12 (36 100)))
|
||||
(phrase { :name "5 kicks" :beats 4 :steps 16 }
|
||||
(:00 (36 128))
|
||||
(:04 (36 100))
|
||||
(:08 (36 128))
|
||||
(:12 (36 100))
|
||||
(:14 (36 110)))
|
||||
(phrase { :name "D Beat" :beats 8 :steps 32 }
|
||||
(:00 (44 70) (36 128) (49 110))
|
||||
(:02 (44 30))
|
||||
(:04 (44 80) (40 100))
|
||||
(:06 (44 50))
|
||||
(:08 (44 30) (36 100))
|
||||
(:10 (44 50) (36 100))
|
||||
(:12 (44 80) (40 100))
|
||||
(:14 (44 50))
|
||||
(:15 (36 50))
|
||||
(:16 (44 60) (36 80))
|
||||
(:18 (44 60) (36 80))
|
||||
(:20 (44 60) (40 80))
|
||||
(:22 (44 60))
|
||||
(:24 (44 60))
|
||||
(:26 (44 30) (36 80))
|
||||
(:27 (44 60))
|
||||
(:28 (44 60) (40 80))
|
||||
(:30 (44 60)))
|
||||
(phrase { :name "Garage" :beats 4 :steps 16 }
|
||||
(:00 (44 100) (36 100) (35 100))
|
||||
(:01 (44 100))
|
||||
(:02 (44 100) (35 100))
|
||||
(:03 (44 100))
|
||||
(:04 (44 100) (40 100))
|
||||
(:06 (44 100))
|
||||
(:07 (44 100) (34 100))
|
||||
(:09 (44 100))
|
||||
(:10 (44 100))
|
||||
(:11 (35 100) (36 100))
|
||||
(:12 (44 100) (40 100))
|
||||
(:14 (44 100)))
|
||||
|
||||
(phrase { :name "Trap Pinging" :beats 8 :steps 96 }
|
||||
(:00 (42 100) (36 100) (34 120) (49 100))
|
||||
(:01 (42 100))
|
||||
(:02 (42 100))
|
||||
(:06 (42 100) (35 80) (36 80) (49 100))
|
||||
(:07 (42 100))
|
||||
(:08 (42 100))
|
||||
(:12 (42 100))
|
||||
(:15 (39 100) (34 100))
|
||||
(:18 (42 100))
|
||||
(:24 (42 100) (38 50) (40 50))
|
||||
(:27 (42 100) (36 50))
|
||||
(:30 (42 100))
|
||||
(:33 (42 100) (36 50) (34 100))
|
||||
(:36 (42 90))
|
||||
(:39 (42 80))
|
||||
(:42 (42 70))
|
||||
(:45 (42 60))
|
||||
|
||||
(:48 (42 100) (36 100) (34 100))
|
||||
(:50 (42 100))
|
||||
(:52 (42 110))
|
||||
(:54 (46 50) (42 120))
|
||||
(:56 (42 90))
|
||||
(:58 (42 100))
|
||||
(:60 (42 100) (35 100))
|
||||
(:64 (39 100))
|
||||
(:66 (42 100) (34 100))
|
||||
|
||||
(:70 (42 100))
|
||||
(:71 (42 100))
|
||||
(:72 (42 100) (38 50) (40 50))
|
||||
(:75 (42 100) (36 50) (34 80))
|
||||
(:78 (42 100))
|
||||
(:81 (42 100) (36 50))
|
||||
(:84 (38 40) (40 50) (34 90))
|
||||
(:87 (42 90) (35 40))
|
||||
(:90 (42 70)))
|
||||
|
||||
(sampler { :name "DrumKit1" :dir "/home/user/Lab/Music/pak" }
|
||||
(sample { :midi 34 :name "808 D" :file "808.wav" })
|
||||
(sample { :midi 35 :name "Kick 1" :file "kik.wav" })
|
||||
(sample { :midi 36 :name "Kick 2" :file "kik2.wav" })
|
||||
(sample { :midi 37 :name "Rim" :file "rim.wav" })
|
||||
(sample { :midi 38 :name "Snare 1" :file "sna.wav" })
|
||||
(sample { :midi 39 :name "Shaker" :file "shk.wav" })
|
||||
(sample { :midi 40 :name "Snare 2" :file "sna2.wav" })
|
||||
(sample { :midi 42 :name "Closed HH 1" :file "chh.wav" })
|
||||
(sample { :midi 44 :name "Closed HH 2" :file "chh2.wav" })
|
||||
(sample { :midi 45 :name "Open HH 0" :file "ohh.wav" })
|
||||
(sample { :midi 46 :name "Open HH 1" :file "ohh1.wav" })
|
||||
(sample { :midi 47 :name "Open HH 2" :file "ohh2.wav" })
|
||||
(sample { :midi 49 :name "Crash" :file "crs.wav" })))
|
||||
|
||||
(track { :name "Bass" :gain +0.0 }
|
||||
(phrase { :name "Bass 1" :beats 4 })
|
||||
(phrase { :name "Bass 2" :beats 4 })
|
||||
(phrase { :name "Bass 3" :beats 4 })
|
||||
(phrase { :name "Bass 4" :beats 4 })
|
||||
(phrase { :name "Bass 5" :beats 4 })
|
||||
(phrase { :name "Bass 6" :beats 4 })
|
||||
(phrase { :name "Bass 7" :beats 4 })
|
||||
(phrase { :name "Bass 8" :beats 4 })
|
||||
(lv2 {
|
||||
:name "Odin2"
|
||||
:path "file:///home/user/.lv2/Odin2.lv2"
|
||||
}))
|
||||
|
||||
(track { :name "Lead" :gain +0.0 }
|
||||
(phrase { :name "Lead 1" :beats 4 })
|
||||
(phrase { :name "Lead 2" :beats 4 })
|
||||
(phrase { :name "Lead 3" :beats 4 })
|
||||
(phrase { :name "Lead 4" :beats 4 })
|
||||
(phrase { :name "Lead 5" :beats 4 })
|
||||
(phrase { :name "Lead 6" :beats 4 })
|
||||
(phrase { :name "Lead 7" :beats 4 })
|
||||
(phrase { :name "Lead 8" :beats 4 })
|
||||
(lv2 {
|
||||
:name "Odin2"
|
||||
:path "file:///home/user/.lv2/Odin2.lv2"
|
||||
}))
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
|
||||
|
||||
|
||||
;(:bpm 150)
|
||||
|
||||
;(:track#01 "Drums")
|
||||
;(:track#02 "Bass")
|
||||
;(:track#03 "Lead")
|
||||
|
||||
;(:scene#01 "Intro" (:01 --) (:02 :01) (:03 :01) )
|
||||
;(:scene#02 "Hook" )
|
||||
;(:scene#03 "Verse" )
|
||||
;(:scene#04 "Chorus")
|
||||
;(:scene#05 "Bridge")
|
||||
|
||||
;(midi-in "nanoKEY Studio.*capture.*")
|
||||
;(audio-out ["Komplete.+:playback_FL", "Komplete.+:playback_FR"])
|
||||
|
||||
|
||||
;(sampler { :name "DrumKit1" :dir "/home/user/Lab/Music/pak" }
|
||||
;(sample { :midi 34 :name "808" :file "808.wav" })
|
||||
;(sample { :midi 35 :name "KC1" :file "kik.wav" })
|
||||
;(sample { :midi 36 :name "KC2" :file "kik2.wav" })
|
||||
;(sample { :midi 38 :name "SN1" :file "sna.wav" })
|
||||
;(sample { :midi 40 :name "SN2" :file "sna2.wav" })
|
||||
;(sample { :midi 42 :name "HH1" :file "chh.wav" })
|
||||
;(sample { :midi 44 :name "HH2" :file "chh2.wav" }))
|
||||
|
||||
;(phrase { :name "4 kicks" :beats 4 :steps 16 }
|
||||
;(:00 (36 128))
|
||||
;(:04 (36 128))
|
||||
;(:08 (36 128))
|
||||
;(:12 (36 128))))
|
||||
|
||||
;(phrase {
|
||||
;:name "5 kicks"
|
||||
;:beats 4
|
||||
;} (:00 (36 128))
|
||||
;(:04 (36 128))
|
||||
;(:08 (36 128))
|
||||
;(:12 (36 128))
|
||||
;(:14 (36 128)))
|
||||
|
||||
;(track "Bass"
|
||||
;(lv2-plugin "Odin2")
|
||||
;(phrase { :name "Empty" :beats 4 })
|
||||
;(phrase { :name "Empty" :beats 4 }))
|
||||
|
||||
;(track "Lead"
|
||||
;(lv2-plugin "Odin2")
|
||||
;(phrase { :name "Empty" :beats 4 })
|
||||
;(phrase { :name "Empty" :beats 4 }))
|
||||
Loading…
Add table
Add a link
Reference in a new issue