wip: refactor: cli, snd, tui

This commit is contained in:
🪞👃🪞 2024-11-09 21:27:04 +01:00
parent 70fc3c97d1
commit 84aacfea82
58 changed files with 416 additions and 191 deletions

View file

@ -6,3 +6,6 @@ version = "0.1.0"
[dependencies]
tek_core = { path = "../tek_core" }
uuid = { version = "1.10.0", features = [ "v4" ] }
vst = "0.4.0"
livi = "0.7.4"
symphonia = { version = "0.5.4", features = [ "all" ] }

View file

@ -1,4 +1,5 @@
use crate::*;
use crate::midly::num::u7;
impl Scene {
pub fn from_edn <'a, 'e> (args: &[Edn<'e>]) -> Usually<Self> {
@ -26,29 +27,29 @@ impl Scene {
}
}
const SYM_NAME: &'static str = ":name";
const SYM_GAIN: &'static str = ":gain";
const SYM_SAMPLER: &'static str = "sampler";
const SYM_LV2: &'static str = "lv2";
impl<E: Engine> Track<E> {
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 = Self::new("")?;
#[allow(unused_mut)]
let mut devices: Vec<JackDevice<E>> = vec![];
let mut devices: Vec<JackDevice> = vec![];
edn!(edn in args {
Edn::Map(map) => {
if let Some(Edn::Str(n)) = map.get(&Edn::Key(SYM_NAME)) {
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(SYM_GAIN)) {
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(SYM_SAMPLER)) => {
Some(Edn::Symbol(Self::SYM_SAMPLER)) => {
devices.push(Sampler::from_edn(jack, &args[1..])?);
panic!(
"unsupported in track {}: {:?}; tek_mixer not compiled with feature \"sampler\"",
@ -57,7 +58,7 @@ impl<E: Engine> Track<E> {
)
},
// Add a LV2 plugin to the track.
Some(Edn::Symbol(SYM_LV2)) => {
Some(Edn::Symbol(Self::SYM_LV2)) => {
devices.push(LV2Plugin::from_edn(jack, &args[1..])?);
panic!(
"unsupported in track {}: {:?}; tek_mixer not compiled with feature \"plugin\"",
@ -80,7 +81,7 @@ impl<E: Engine> Track<E> {
}
impl LV2Plugin {
pub fn from_edn <'e, E: Engine> (jack: &Arc<RwLock<JackClient>>, args: &[Edn<'e>]) -> Usually<JackDevice<E>> {
pub fn from_edn <'e> (jack: &Arc<RwLock<JackClient>>, args: &[Edn<'e>]) -> Usually<JackDevice> {
let mut name = String::new();
let mut path = String::new();
edn!(edn in args {
@ -98,8 +99,8 @@ impl LV2Plugin {
}
}
impl<E: Engine> Sampler<E> {
pub fn from_edn <'e> (jack: &Arc<RwLock<JackClient>>, args: &[Edn<'e>]) -> Usually<JackDevice<E>> {
impl Sampler {
pub fn from_edn <'e> (jack: &Arc<RwLock<JackClient>>, args: &[Edn<'e>]) -> Usually<JackDevice> {
let mut name = String::new();
let mut dir = String::new();
let mut samples = BTreeMap::new();

View file

@ -1,28 +0,0 @@
use crate::*;
pub(crate) use tek_core::midly::MidiMessage;
/// MIDI message structural
pub type PhraseData = Vec<Vec<MidiMessage>>;
/// A MIDI sequence.
#[derive(Debug, Clone)] pub struct Phrase {
pub uuid: uuid::Uuid,
/// Name of phrase
pub name: String,
/// Temporal resolution in pulses per quarter note
pub ppq: usize,
/// Length of phrase in pulses
pub length: usize,
/// Notes in phrase
pub notes: PhraseData,
/// Whether to loop the phrase or play it once
pub loop_on: bool,
/// Start of loop
pub loop_start: usize,
/// Length of loop
pub loop_length: usize,
/// All notes are displayed with minimum length
pub percussive: bool,
/// Identifying color of phrase
pub color: ItemColorTriplet,
}

View file

@ -1,6 +1,242 @@
pub(crate) use tek_core::*;
pub(crate) use tek_core::jack::{TransportState, Port, MidiIn, MidiOut};
pub(crate) use tek_core::midly::{MidiMessage, num::u7};
pub(crate) use std::thread::JoinHandle;
submod! {
api_cmd
api_edn
api_midi
}
/// A timer with starting point, current time, and quantization
#[derive(Default, Debug)]
pub struct Clock {
/// Playback state
pub playing: RwLock<Option<TransportState>>,
/// Global sample and usec at which playback started
pub started: RwLock<Option<(usize, usize)>>,
/// Current moment in time
pub current: Instant,
/// Note quantization factor
pub quant: Quantize,
/// Launch quantization factor
pub sync: LaunchSync,
}
impl Clock {
#[inline] pub fn timebase (&self) -> &Arc<Timebase> {
&self.current.timebase
}
#[inline] pub fn pulse (&self) -> f64 {
self.current.pulse.get()
}
#[inline] pub fn quant (&self) -> f64 {
self.quant.get()
}
#[inline] pub fn sync (&self) -> f64 {
self.sync.get()
}
#[inline] pub fn next_launch_pulse (&self) -> usize {
let sync = self.sync.get() as usize;
let pulse = self.current.pulse.get() as usize;
if pulse % sync == 0 { pulse } else { (pulse / sync + 1) * sync }
}
}
pub struct TransportToolbar {
pub jack: Arc<RwLock<JackClient>>,
/// JACK transport handle.
pub transport: jack::Transport,
/// Current sample rate, tempo, and PPQ.
pub clock: Arc<Clock>,
/// Enable metronome?
pub metronome: bool,
}
pub struct Arrangement {
/// JACK client handle (needs to not be dropped for standalone mode to work).
pub jack: Arc<RwLock<JackClient>>,
/// Global timebase
pub clock: Arc<Clock>,
/// Name of arranger
pub name: Arc<RwLock<String>>,
/// Collection of phrases.
pub phrases: Arc<RwLock<Vec<Phrase>>>,
/// Collection of tracks.
pub tracks: Vec<Sequencer>,
/// Collection of scenes.
pub scenes: Vec<Scene>,
}
#[derive(Debug)]
pub struct Sequencer {
/// Name of track
pub name: Arc<RwLock<String>>,
/// Preferred width of track column
pub width: usize,
/// Identifying color of track
pub color: ItemColor,
/// MIDI player/recorder
pub player: PhrasePlayer,
}
/// Phrase player.
#[derive(Debug)]
pub struct PhrasePlayer {
/// Global timebase
pub clock: Arc<Clock>,
/// Start time and phrase being played
pub phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
/// Start time and next phrase
pub next_phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
/// Play input through output.
pub monitoring: bool,
/// Write input to sequence.
pub recording: bool,
/// Overdub input to sequence.
pub overdub: bool,
/// Send all notes off
pub reset: bool, // TODO?: after Some(nframes)
/// Record from MIDI ports to current sequence.
pub midi_inputs: Vec<Port<MidiIn>>,
/// Play from current sequence to MIDI ports
pub midi_outputs: Vec<Port<MidiOut>>,
/// MIDI output buffer
pub midi_note: Vec<u8>,
/// MIDI output buffer
pub midi_chunk: Vec<Vec<Vec<u8>>>,
/// Notes currently held at input
pub notes_in: Arc<RwLock<[bool; 128]>>,
/// Notes currently held at output
pub notes_out: Arc<RwLock<[bool; 128]>>,
}
#[derive(Default, Debug, Clone)]
pub struct Scene {
/// Name of scene
pub name: Arc<RwLock<String>>,
/// Clips in scene, one per track
pub clips: Vec<Option<Arc<RwLock<Phrase>>>>,
/// Identifying color of scene
pub color: ItemColor,
}
/// A MIDI sequence.
#[derive(Debug, Clone, Default)]
pub struct Phrase {
pub uuid: uuid::Uuid,
/// Name of phrase
pub name: String,
/// Temporal resolution in pulses per quarter note
pub ppq: usize,
/// Length of phrase in pulses
pub length: usize,
/// Notes in phrase
pub notes: PhraseData,
/// Whether to loop the phrase or play it once
pub loop_on: bool,
/// Start of loop
pub loop_start: usize,
/// Length of loop
pub loop_length: usize,
/// All notes are displayed with minimum length
pub percussive: bool,
/// Identifying color of phrase
pub color: ItemColorTriplet,
}
/// MIDI message structural
pub type PhraseData = Vec<Vec<MidiMessage>>;
#[derive(Default, Debug, Clone)]
pub struct Mixer {
/// JACK client handle (needs to not be dropped for standalone mode to work).
pub jack: Arc<RwLock<JackClient>>,
pub name: String,
pub tracks: Vec<MixerTrack>,
pub selected_track: usize,
pub selected_column: usize,
}
/// A mixer track.
#[derive(Default, Debug, Clone)]
pub struct MixerTrack {
pub name: String,
/// Inputs and outputs of 1st and last device
pub ports: JackPorts,
/// Device chain
pub devices: Vec<JackDevice>,
/// Device selector
pub device: usize,
}
/// The sampler plugin plays sounds.
#[derive(Default, Debug, Clone)]
pub struct Sampler {
pub jack: Arc<RwLock<JackClient>>,
pub name: String,
pub cursor: (usize, usize),
pub editing: Option<Arc<RwLock<Sample>>>,
pub mapped: BTreeMap<u7, Arc<RwLock<Sample>>>,
pub unmapped: Vec<Arc<RwLock<Sample>>>,
pub voices: Arc<RwLock<Vec<Voice>>>,
pub ports: JackPorts,
pub buffer: Vec<Vec<f32>>,
pub modal: Arc<Mutex<Option<Box<dyn Exit + Send>>>>,
pub output_gain: f32
}
/// A sound sample.
#[derive(Default, Debug)]
pub struct Sample {
pub name: String,
pub start: usize,
pub end: usize,
pub channels: Vec<Vec<f32>>,
pub rate: Option<usize>,
}
/// A currently playing instance of a sample.
#[derive(Default, Debug, Clone)]
pub struct Voice {
pub sample: Arc<RwLock<Sample>>,
pub after: usize,
pub position: usize,
pub velocity: f32,
}
/// A plugin device.
#[derive(Debug)]
pub struct Plugin {
/// JACK client handle (needs to not be dropped for standalone mode to work).
pub jack: Arc<RwLock<JackClient>>,
pub name: String,
pub path: Option<String>,
pub plugin: Option<PluginKind>,
pub selected: usize,
pub mapping: bool,
pub ports: JackPorts,
}
/// Supported plugin formats.
#[derive(Default, Debug)]
pub enum PluginKind {
#[default] None,
LV2(LV2Plugin),
VST2 {
instance: ::vst::host::PluginInstance
},
VST3,
}
/// A LV2 plugin.
#[derive(Debug)]
pub struct LV2Plugin {
pub world: livi::World,
pub instance: livi::Instance,
pub plugin: livi::LiviPlugin,
pub features: Arc<livi::Features>,
pub port_list: Vec<livi::Port>,
pub input_buffer: Vec<livi::event::LV2AtomSequence>,
pub ui_thread: Option<JoinHandle<()>>,
}