wip: multi-crate refactor

This commit is contained in:
🪞👃🪞 2024-12-27 14:46:35 +01:00
parent 911c47fc7c
commit e08a79b507
25 changed files with 311 additions and 265 deletions

View file

@ -9,7 +9,6 @@ version = "0.2.0"
atomic_float = "1.0.0"
backtrace = "0.3.72"
better-panic = "0.3.0"
clap = { version = "4.5.4", features = [ "derive" ] }
clojure-reader = "0.1.0"
crossterm = "0.27"
jack = "0.13"
@ -29,37 +28,4 @@ wavers = "1.4.3"
#winit = { version = "0.30.4", features = [ "x11" ] }
[lib]
name = "tek_lib"
path = "src/lib.rs"
[[bin]]
name = "tek_arranger"
path = "src/cli/cli_arranger.rs"
[[bin]]
name = "tek_sequencer"
path = "src/cli/cli_sequencer.rs"
[[bin]]
name = "tek_groovebox"
path = "src/cli/cli_groovebox.rs"
[[bin]]
name = "tek_transport"
path = "src/cli/cli_transport.rs"
[[bin]]
name = "tek_sampler"
path = "src/cli/cli_sampler.rs"
#[[bin]]
#name = "tek_mixer"
#path = "src/cli_mixer.rs"
#[[bin]]
#name = "tek_track"
#path = "src/cli_track.rs"
#[[bin]]
#name = "tek_plugin"
#path = "src/cli_plugin.rs"

View file

@ -1,3 +1,9 @@
pub(crate) mod audio_in;
pub(crate) mod audio_out;
pub(crate) mod sampler; pub(crate) use sampler::*;
use crate::*;
mod audio_in;
mod audio_out;
mod sampler;
pub(crate) use sampler::*;
pub use self::sampler::{Sampler, Sample, Voice};

View file

@ -16,62 +16,6 @@ pub struct MixerTrack {
//impl MixerTrackDevice for LV2Plugin {}
impl MixerTrack {
const SYM_NAME: &'static str = ":name";
const SYM_GAIN: &'static str = ":gain";
const SYM_SAMPLER: &'static str = "sampler";
const SYM_LV2: &'static str = "lv2";
pub fn from_edn <'a, 'e> (jack: &Arc<RwLock<JackClient>>, args: &[Edn<'e>]) -> Usually<Self> {
let mut _gain = 0.0f64;
let mut track = MixerTrack {
name: String::new(),
audio_ins: vec![],
audio_outs: vec![],
devices: vec![],
};
edn!(edn in args {
Edn::Map(map) => {
if let Some(Edn::Str(n)) = map.get(&Edn::Key(Self::SYM_NAME)) {
track.name = n.to_string();
}
if let Some(Edn::Double(g)) = map.get(&Edn::Key(Self::SYM_GAIN)) {
_gain = f64::from(*g);
}
},
Edn::List(args) => match args.get(0) {
// Add a sampler device to the track
Some(Edn::Symbol(Self::SYM_SAMPLER)) => {
track.devices.push(
Box::new(Sampler::from_edn(jack, &args[1..])?) as Box<dyn MixerTrackDevice>
);
panic!(
"unsupported in track {}: {:?}; tek_mixer not compiled with feature \"sampler\"",
&track.name,
args.get(0).unwrap()
)
},
// Add a LV2 plugin to the track.
Some(Edn::Symbol(Self::SYM_LV2)) => {
track.devices.push(
Box::new(LV2Plugin::from_edn(jack, &args[1..])?) as Box<dyn MixerTrackDevice>
);
panic!(
"unsupported in track {}: {:?}; tek_mixer not compiled with feature \"plugin\"",
&track.name,
args.get(0).unwrap()
)
},
None =>
panic!("empty list track {}", &track.name),
_ =>
panic!("unexpected in track {}: {:?}", &track.name, args.get(0).unwrap())
},
_ => {}
});
Ok(track)
}
}
pub trait MixerTrackDevice: Debug + Send + Sync {
fn boxed (self) -> Box<dyn MixerTrackDevice> where Self: Sized + 'static {
Box::new(self)

View file

@ -1,137 +0,0 @@
#![allow(unused)]
#![allow(clippy::unit_arg)]
include!("../lib.rs");
pub fn main () -> Usually<()> {
ArrangerCli::parse().run()
}
/// Launches an interactive MIDI arranger.
#[derive(Debug, Parser)]
#[command(version, about, long_about = None)]
pub struct ArrangerCli {
/// Name of JACK client
#[arg(short, long)]
name: Option<String>,
/// Whether to include a transport toolbar (default: true)
#[arg(short, long, default_value_t = true)]
transport: bool,
/// Number of tracks
#[arg(short = 'x', long, default_value_t = 4)]
tracks: usize,
/// Number of scenes
#[arg(short, long, default_value_t = 8)]
scenes: usize,
/// MIDI outs to connect each track to.
#[arg(short='i', long)]
midi_from: Vec<String>,
/// MIDI ins to connect each track to.
#[arg(short='o', long)]
midi_to: Vec<String>,
}
impl ArrangerCli {
/// Run the arranger TUI from CLI arguments.
fn run (&self) -> Usually<()> {
let mut client_name = String::from("tek_arranger");
if let Some(name) = self.name.as_ref() {
client_name = name.clone();
}
Tui::run(JackClient::new(client_name.as_str())?.activate_with(|jack|{
let mut app = ArrangerTui::try_from(jack)?;
let jack = jack.read().unwrap();
app.color = ItemPalette::random();
add_tracks(&jack, &mut app, self)?;
add_scenes(&mut app, self.scenes)?;
Ok(app)
})?)?;
Ok(())
}
}
fn add_tracks (jack: &JackClient, app: &mut ArrangerTui, cli: &ArrangerCli) -> Usually<()> {
let n = cli.tracks;
let track_color_1 = ItemColor::random();
let track_color_2 = ItemColor::random();
for i in 0..n {
let track = app.track_add(None, Some(
track_color_1.mix(track_color_2, i as f32 / n as f32).into()
))?;
track.width = 8;
let name = track.name.read().unwrap();
track.player.midi_ins.push(
jack.register_port(&format!("{}I", &name), MidiIn::default())?
);
track.player.midi_outs.push(
jack.register_port(&format!("{}O", &name), MidiOut::default())?
);
}
for connection in cli.midi_from.iter() {
let mut split = connection.split("=");
let number = split.next().unwrap().trim();
if let Ok(track) = number.parse::<usize>() {
if track < 1 {
panic!("Tracks are zero-indexed")
}
if track > n {
panic!("Tried to connect track {track} or {n}. Pass -t {track} to increase number of tracks.")
}
if let Some(port) = split.next() {
if let Some(port) = jack.port_by_name(port).as_ref() {
jack.client().connect_ports(port, &app.tracks[track-1].player.midi_ins[0])?;
} else {
panic!("Missing MIDI output: {port}. Use jack_lsp to list all port names.");
}
} else {
panic!("No port specified for track {track}. Format is TRACK_NUMBER=PORT_NAME")
}
} else {
panic!("Failed to parse track number: {number}")
}
}
for connection in cli.midi_to.iter() {
let mut split = connection.split("=");
let number = split.next().unwrap().trim();
if let Ok(track) = number.parse::<usize>() {
if track < 1 {
panic!("Tracks are zero-indexed")
}
if track > n {
panic!("Tried to connect track {track} or {n}. Pass -t {track} to increase number of tracks.")
}
if let Some(port) = split.next() {
if let Some(port) = jack.port_by_name(port).as_ref() {
jack.client().connect_ports(&app.tracks[track-1].player.midi_outs[0], port)?;
} else {
panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names.");
}
} else {
panic!("No port specified for track {track}. Format is TRACK_NUMBER=PORT_NAME")
}
} else {
panic!("Failed to parse track number: {number}")
}
}
Ok(())
}
fn add_scenes (app: &mut ArrangerTui, n: usize) -> Usually<()> {
let scene_color_1 = ItemColor::random();
let scene_color_2 = ItemColor::random();
for i in 0..n {
let _scene = app.scene_add(None, Some(
scene_color_1.mix(scene_color_2, i as f32 / n as f32).into()
))?;
}
Ok(())
}
#[test] fn verify_arranger_cli () {
use clap::CommandFactory;
ArrangerCli::command().debug_assert();
}

View file

@ -1,27 +0,0 @@
#![allow(unused)]
#![allow(clippy::unit_arg)]
include!("../lib.rs");
pub fn main () -> Usually<()> {
GrooveboxCli::parse().run()
}
#[derive(Debug, Parser)]
#[command(version, about, long_about = None)]
pub struct GrooveboxCli;
impl GrooveboxCli {
fn run (&self) -> Usually<()> {
Tui::run(JackClient::new("tek_groovebox")?.activate_with(|jack|{
let app = GrooveboxTui::try_from(jack)?;
let midi_out = jack.read().unwrap().register_port("out", MidiOut::default())?;
let midi_in_1 = jack.read().unwrap().register_port("in1", MidiIn::default())?;
let midi_in_2 = jack.read().unwrap().register_port("in2", MidiIn::default())?;
let audio_in_1 = jack.read().unwrap().register_port("inL", AudioIn::default())?;
let audio_in_2 = jack.read().unwrap().register_port("inR", AudioIn::default())?;
let audio_out_1 = jack.read().unwrap().register_port("out1", AudioOut::default())?;
let audio_out_2 = jack.read().unwrap().register_port("out2", AudioOut::default())?;
Ok(app)
})?)?;
Ok(())
}
}

View file

@ -1,23 +0,0 @@
#![allow(unused)]
#![allow(clippy::unit_arg)]
include!("../lib.rs");
pub fn main () -> Usually<()> {
SamplerCli::parse().run()
}
#[derive(Debug, Parser)] #[command(version, about, long_about = None)] pub struct SamplerCli {
/// Name of JACK client
#[arg(short, long)] name: Option<String>,
/// Path to plugin
#[arg(short, long)] path: Option<String>,
}
impl SamplerCli {
fn run (&self) -> Usually<()> {
Tui::run(JackClient::new("tek_sampler")?.activate_with(|x|{
let sampler = SamplerTui::try_from(x)?;
Ok(sampler)
})?)?;
Ok(())
}
}

View file

@ -1,72 +0,0 @@
#![allow(unused)]
#![allow(clippy::unit_arg)]
include!("../lib.rs");
pub fn main () -> Usually<()> {
SequencerCli::parse().run()
}
/// Launches a single interactive MIDI sequencer.
#[derive(Debug, Parser)]
#[command(version, about, long_about = None)]
pub struct SequencerCli {
/// Name of JACK client
#[arg(short, long)]
name: Option<String>,
/// Whether to include a transport toolbar (default: true)
#[arg(short, long, default_value_t = true)]
transport: bool,
/// MIDI outs to connect to (multiple instances accepted)
#[arg(short='i', long)]
midi_from: Vec<String>,
/// MIDI ins to connect to (multiple instances accepted)
#[arg(short='o', long)]
midi_to: Vec<String>,
}
impl SequencerCli {
fn run (&self) -> Usually<()> {
let name = self.name.as_deref().unwrap_or("tek_sequencer");
Tui::run(JackClient::new(name)?.activate_with(|jack|{
let mut app = SequencerTui::try_from(jack)?;
let jack = jack.read().unwrap();
let midi_in = jack.register_port("i", MidiIn::default())?;
let midi_out = jack.register_port("o", MidiOut::default())?;
connect_from(&jack, &midi_in, &self.midi_from)?;
connect_to(&jack, &midi_out, &self.midi_to)?;
app.player.midi_ins.push(midi_in);
app.player.midi_outs.push(midi_out);
Ok(app)
})?)?;
Ok(())
}
}
fn connect_from (jack: &JackClient, input: &Port<MidiIn>, ports: &[String]) -> Usually<()> {
for port in ports.iter() {
if let Some(port) = jack.port_by_name(port).as_ref() {
jack.client().connect_ports(port, input)?;
} else {
panic!("Missing MIDI output: {port}. Use jack_lsp to list all port names.");
}
}
Ok(())
}
fn connect_to (jack: &JackClient, output: &Port<MidiOut>, ports: &[String]) -> Usually<()> {
for port in ports.iter() {
if let Some(port) = jack.port_by_name(port).as_ref() {
jack.client().connect_ports(output, port)?;
} else {
panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names.");
}
}
Ok(())
}
#[test] fn verify_sequencer_cli () {
use clap::CommandFactory;
SequencerCli::command().debug_assert();
}

View file

@ -1,10 +0,0 @@
#![allow(unused)]
#![allow(clippy::unit_arg)]
include!("../lib.rs");
/// Application entrypoint.
pub fn main () -> Usually<()> {
Tui::run(JackClient::new("tek_transport")?.activate_with(|jack|{
TransportTui::try_from(jack)
})?)?;
Ok(())
}

View file

@ -1,25 +0,0 @@
use crate::*;
pub fn main () -> Usually<()> {
MixerCli::parse().run()
}
#[derive(Debug, Parser)] #[command(version, about, long_about = None)] pub struct MixerCli {
/// Name of JACK client
#[arg(short, long)] name: Option<String>,
/// Number of tracks
#[arg(short, long)] channels: Option<usize>,
}
impl MixerCli {
fn run (&self) -> Usually<()> {
Tui::run(JackClient::new("tek_mixer")?.activate_with(|jack|{
let mut mixer = Mixer::new(jack, self.name.as_ref().map(|x|x.as_str()).unwrap_or("mixer"))?;
for channel in 0..self.channels.unwrap_or(8) {
mixer.track_add(&format!("Track {}", channel + 1), 1)?;
}
Ok(mixer)
})?)?;
Ok(())
}
}

View file

@ -1,26 +0,0 @@
use crate::*;
pub fn main () -> Usually<()> {
PluginCli::parse().run()
}
#[derive(Debug, Parser)] #[command(version, about, long_about = None)] pub struct PluginCli {
/// Name of JACK client
#[arg(short, long)] name: Option<String>,
/// Path to plugin
#[arg(short, long)] path: Option<String>,
}
impl PluginCli {
fn run (&self) -> Usually<()> {
Tui::run(JackClient::new("tek_plugin")?.activate_with(|jack|{
let mut plugin = Plugin::new_lv2(
jack,
self.name.as_ref().map(|x|x.as_str()).unwrap_or("mixer"),
self.path.as_ref().expect("pass --path /to/lv2/plugin.so")
)?;
Ok(plugin)
})?)?;
Ok(())
}
}

View file

@ -1,26 +0,0 @@
use crate::*;
pub fn main () -> Usually<()> {
SamplerCli::parse().run()
}
#[derive(Debug, Parser)] #[command(version, about, long_about = None)] pub struct SamplerCli {
/// Name of JACK client
#[arg(short, long)] name: Option<String>,
/// Path to plugin
#[arg(short, long)] path: Option<String>,
}
impl SamplerCli {
fn run (&self) -> Usually<()> {
Tui::run(JackClient::new("tek_sampler")?.activate_with(|jack|{
let mut plugin = Sampler::new(
jack,
self.name.as_ref().map(|x|x.as_str()).unwrap_or("mixer"),
None,
)?;
Ok(plugin)
})?)?;
Ok(())
}
}

View file

@ -1,139 +0,0 @@
#![allow(unused)]
use crate::*;
pub use clojure_reader::edn::Edn;
//pub use clojure_reader::{edn::{read, Edn}, error::Error as EdnError};
pub trait FromEdn<C>: Sized {
const ID: &'static str;
fn from_edn (context: C, expr: &[Edn<'_>]) -> Usually<Self>;
}
/// Implements the [FromEdn] trait.
#[macro_export] macro_rules! from_edn {
(|$context:pat = $Context:ty, $id:expr, $args:ident| -> $T:ty $body:block) => {
impl FromEdn<$Context> for $T {
const ID: &'static str = $id;
fn from_edn <'e> ($context: $Context, $args: &[Edn<'e>]) -> Usually<Self> {
$body
}
}
}
}
/// EDN parsing helper.
#[macro_export] macro_rules! edn {
($edn:ident { $($pat:pat => $expr:expr),* $(,)? }) => {
match $edn { $($pat => $expr),* }
};
($edn:ident in $args:ident { $($pat:pat => $expr:expr),* $(,)? }) => {
for $edn in $args {
edn!($edn { $($pat => $expr),* })
}
};
}
from_edn!(|jack = &Arc<RwLock<JackClient>>, "sampler", args| -> crate::Sampler {
let mut name = String::new();
let mut dir = String::new();
let mut samples = BTreeMap::new();
edn!(edn in args {
Edn::Map(map) => {
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
name = String::from(*n);
}
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":dir")) {
dir = String::from(*n);
}
},
Edn::List(args) => match args.first() {
Some(Edn::Symbol("sample")) => {
let (midi, sample) = MidiSample::from_edn((jack, &dir), &args[1..])?;
if let Some(midi) = midi {
samples.insert(midi, sample);
} else {
panic!("sample without midi binding: {}", sample.read().unwrap().name);
}
},
_ => panic!("unexpected in sampler {name}: {args:?}")
},
_ => panic!("unexpected in sampler {name}: {edn:?}")
});
let midi_in = jack.read().unwrap().client().register_port("in", MidiIn::default())?;
Ok(Self {
jack: jack.clone(),
mapped: samples,
unmapped: Default::default(),
voices: Default::default(),
buffer: Default::default(),
audio_outs: vec![],
output_gain: 0.,
midi_in,
name,
})
});
type MidiSample = (Option<u7>, Arc<RwLock<crate::Sample>>);
from_edn!(|(jack, dir) = (&Arc<RwLock<JackClient>>, &str), "sample", args| -> MidiSample {
let mut name = String::new();
let mut file = String::new();
let mut midi = None;
let mut start = 0usize;
edn!(edn in args {
Edn::Map(map) => {
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
name = String::from(*n);
}
if let Some(Edn::Str(f)) = map.get(&Edn::Key(":file")) {
file = String::from(*f);
}
if let Some(Edn::Int(i)) = map.get(&Edn::Key(":start")) {
start = *i as usize;
}
if let Some(Edn::Int(m)) = map.get(&Edn::Key(":midi")) {
midi = Some(u7::from(*m as u8));
}
},
_ => panic!("unexpected in sample {name}"),
});
let (end, data) = Sample::read_data(&format!("{dir}/{file}"))?;
Ok((midi, Arc::new(RwLock::new(crate::Sample {
name,
start,
end,
channels: data,
rate: None
}))))
});
//impl ArrangerScene {
////TODO
////pub fn from_edn <'a, 'e> (args: &[Edn<'e>]) -> Usually<Self> {
////let mut name = None;
////let mut clips = vec![];
////edn!(edn in args {
////Edn::Map(map) => {
////let key = map.get(&Edn::Key(":name"));
////if let Some(Edn::Str(n)) = key {
////name = Some(*n);
////} else {
////panic!("unexpected key in scene '{name:?}': {key:?}")
////}
////},
////Edn::Symbol("_") => {
////clips.push(None);
////},
////Edn::Int(i) => {
////clips.push(Some(*i as usize));
////},
////_ => panic!("unexpected in scene '{name:?}': {edn:?}")
////});
////Ok(ArrangerScene {
////name: Arc::new(name.unwrap_or("").to_string().into()),
////color: ItemColor::random(),
////clips,
////})
////}
//}

View file

@ -1,10 +1,7 @@
use crate::*;
pub use ::jack as libjack;
pub(crate) mod activate; #[allow(unused)] pub(crate) use self::activate::*;
pub(crate) mod audio; pub(crate) use self::audio::*;
pub(crate) mod client; pub(crate) use self::client::*;
pub(crate) mod jack_event; pub(crate) use self::jack_event::*;
pub(crate) mod ports; pub(crate) use self::ports::*;
pub(crate) use ::jack::{
pub use ::jack::{
contrib::ClosureProcessHandler, NotificationHandler,
Client, AsyncClient, ClientOptions, ClientStatus,
ProcessScope, Control, CycleTimes, Frames,
@ -12,6 +9,20 @@ pub(crate) use ::jack::{
Transport, TransportState, MidiIter, MidiWriter, RawMidi,
};
pub mod activate;
pub(crate) use self::activate::*;
pub use self::activate::JackActivate;
pub mod client;
pub(crate) use self::client::*;
pub use self::client::JackClient;
pub mod jack_event;
pub(crate) use self::jack_event::*;
pub mod ports;
pub(crate) use self::ports::*;
/// Implement [TryFrom<&Arc<RwLock<JackClient>>>]: create app state from wrapped JACK handle.
#[macro_export] macro_rules! from_jack {
(|$jack:ident|$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)? $cb:expr) => {
@ -24,6 +35,83 @@ pub(crate) use ::jack::{
};
}
/// Implement [Audio]: provide JACK callbacks.
#[macro_export] macro_rules! audio {
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?,$c:ident,$s:ident|$cb:expr) => {
impl $(<$($L),*$($T $(: $U)?),*>)? Audio for $Struct $(<$($L),*$($T),*>)? {
#[inline] fn process (&mut $self, $c: &Client, $s: &ProcessScope) -> Control { $cb }
}
}
}
/// Trait for thing that has a JACK process callback.
pub trait Audio: Send + Sync {
fn process (&mut self, _: &Client, _: &ProcessScope) -> Control {
Control::Continue
}
fn callback (
state: &Arc<RwLock<Self>>, client: &Client, scope: &ProcessScope
) -> Control where Self: Sized {
if let Ok(mut state) = state.write() {
state.process(client, scope)
} else {
Control::Quit
}
}
}
/// Trait for things that wrap a JACK client.
pub trait AudioEngine {
fn transport (&self) -> Transport {
self.client().transport()
}
fn port_by_name (&self, name: &str) -> Option<Port<Unowned>> {
self.client().port_by_name(name)
}
fn register_port <PS: PortSpec> (&self, name: &str, spec: PS) -> Usually<Port<PS>> {
Ok(self.client().register_port(name, spec)?)
}
fn client (&self) -> &Client;
fn activate (
self,
process: impl FnMut(&Arc<RwLock<Self>>, &Client, &ProcessScope) -> Control + Send + 'static
) -> Usually<Arc<RwLock<Self>>> where Self: Send + Sync + 'static;
fn thread_init (&self, _: &Client) {}
unsafe fn shutdown (&mut self, _status: ClientStatus, _reason: &str) {}
fn freewheel (&mut self, _: &Client, _enabled: bool) {}
fn client_registration (&mut self, _: &Client, _name: &str, _reg: bool) {}
fn port_registration (&mut self, _: &Client, _id: PortId, _reg: bool) {}
fn ports_connected (&mut self, _: &Client, _a: PortId, _b: PortId, _are: bool) {}
fn sample_rate (&mut self, _: &Client, _frames: Frames) -> Control {
Control::Continue
}
fn port_rename (&mut self, _: &Client, _id: PortId, _old: &str, _new: &str) -> Control {
Control::Continue
}
fn graph_reorder (&mut self, _: &Client) -> Control {
Control::Continue
}
fn xrun (&mut self, _: &Client) -> Control {
Control::Continue
}
}
////////////////////////////////////////////////////////////////////////////////////
///// A [AudioComponent] bound to a JACK client and a set of ports.

View file

@ -1,78 +0,0 @@
use crate::*;
/// Implement [Audio]: provide JACK callbacks.
#[macro_export] macro_rules! audio {
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?,$c:ident,$s:ident|$cb:expr) => {
impl $(<$($L),*$($T $(: $U)?),*>)? Audio for $Struct $(<$($L),*$($T),*>)? {
#[inline] fn process (&mut $self, $c: &Client, $s: &ProcessScope) -> Control { $cb }
}
}
}
/// Trait for thing that has a JACK process callback.
pub trait Audio: Send + Sync {
fn process (&mut self, _: &Client, _: &ProcessScope) -> Control {
Control::Continue
}
fn callback (
state: &Arc<RwLock<Self>>, client: &Client, scope: &ProcessScope
) -> Control where Self: Sized {
if let Ok(mut state) = state.write() {
state.process(client, scope)
} else {
Control::Quit
}
}
}
/// Trait for things that wrap a JACK client.
pub trait AudioEngine {
fn transport (&self) -> Transport {
self.client().transport()
}
fn port_by_name (&self, name: &str) -> Option<Port<Unowned>> {
self.client().port_by_name(name)
}
fn register_port <PS: PortSpec> (&self, name: &str, spec: PS) -> Usually<Port<PS>> {
Ok(self.client().register_port(name, spec)?)
}
fn client (&self) -> &Client;
fn activate (
self,
process: impl FnMut(&Arc<RwLock<Self>>, &Client, &ProcessScope) -> Control + Send + 'static
) -> Usually<Arc<RwLock<Self>>> where Self: Send + Sync + 'static;
fn thread_init (&self, _: &Client) {}
unsafe fn shutdown (&mut self, _status: ClientStatus, _reason: &str) {}
fn freewheel (&mut self, _: &Client, _enabled: bool) {}
fn client_registration (&mut self, _: &Client, _name: &str, _reg: bool) {}
fn port_registration (&mut self, _: &Client, _id: PortId, _reg: bool) {}
fn ports_connected (&mut self, _: &Client, _a: PortId, _b: PortId, _are: bool) {}
fn sample_rate (&mut self, _: &Client, _frames: Frames) -> Control {
Control::Continue
}
fn port_rename (&mut self, _: &Client, _id: PortId, _old: &str, _new: &str) -> Control {
Control::Continue
}
fn graph_reorder (&mut self, _: &Client) -> Control {
Control::Continue
}
fn xrun (&mut self, _: &Client) -> Control {
Control::Continue
}
}

View file

@ -1,16 +1,22 @@
const FOO: () = ();
#![allow(unused)]
#![allow(clippy::unit_arg)]
pub mod core; pub use self::core::*;
pub mod time; pub(crate) use self::time::*;
pub mod space; pub(crate) use self::space::*;
pub mod tui; pub(crate) use self::tui::*;
pub mod edn;
pub mod jack; pub(crate) use self::jack::*;
pub mod midi; pub(crate) use self::midi::*;
pub mod audio; pub(crate) use self::audio::*;
//pub mod plugin; pub(crate) use self::plugin::*;
pub(crate) use clap::{self, Parser};
pub mod tui; pub(crate) use self::tui::*;
pub use tui::{Tui, TransportTui, SequencerTui, SamplerTui, GrooveboxTui, ArrangerTui};
pub mod jack; pub(crate) use self::jack::*;
pub use jack::JackClient;
pub mod midi; pub(crate) use self::midi::*;
pub mod audio; pub(crate) use self::audio::*;
pub use audio::{Sampler, Sample, Voice};
//pub mod plugin; pub(crate) use self::plugin::*;
pub use ::better_panic;
pub(crate) use better_panic::{Settings, Verbosity};
@ -44,13 +50,12 @@ pub(crate) use ratatui::{
backend::{Backend, CrosstermBackend, ClearType}
};
pub use ::midly;
pub use ::midly::{self, num::u7};
pub(crate) use ::midly::{
Smf,
MidiMessage,
TrackEventKind,
live::LiveEvent,
num::u7
};
pub use ::palette;

View file

@ -40,22 +40,3 @@ impl LV2Plugin {
})
}
}
impl LV2Plugin {
pub fn from_edn <'e> (jack: &Arc<RwLock<JackClient>>, args: &[Edn<'e>]) -> Usually<Plugin> {
let mut name = String::new();
let mut path = String::new();
edn!(edn in args {
Edn::Map(map) => {
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
name = String::from(*n);
}
if let Some(Edn::Str(p)) = map.get(&Edn::Key(":path")) {
path = String::from(*p);
}
},
_ => panic!("unexpected in lv2 '{name}'"),
});
Plugin::new_lv2(jack, &name, &path)
}
}

View file

@ -15,10 +15,19 @@ mod tui_border; pub(crate) use self::tui_border::*;
////////////////////////////////////////////////////////
mod app_transport; #[allow(unused)] pub(crate) use self::app_transport::*;
pub use self::app_transport::TransportTui;
mod app_sequencer; #[allow(unused)] pub(crate) use self::app_sequencer::*;
pub use self::app_sequencer::SequencerTui;
mod app_sampler; #[allow(unused)] pub(crate) use self::app_sampler::*;
pub use self::app_sampler::SamplerTui;
mod app_groovebox; #[allow(unused)] pub(crate) use self::app_groovebox::*;
pub use self::app_groovebox::GrooveboxTui;
mod app_arranger; #[allow(unused)] pub(crate) use self::app_arranger::*;
pub use self::app_arranger::ArrangerTui;
///////////////////////////////////////////////////////

View file

@ -13,7 +13,6 @@ use symphonia::{
},
default::get_codecs,
};
pub struct SamplerTui {
pub state: Sampler,
pub cursor: (usize, usize),