From bc679ef8bda99856ee9df8cc3d6fe368f2146d66 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 4 Nov 2024 21:01:16 +0200 Subject: [PATCH] wip: refresh mixer components --- crates/tek_core/src/audio.rs | 28 +++++-- crates/tek_mixer/src/lib.rs | 13 +--- crates/tek_mixer/src/mixer.rs | 5 -- crates/tek_mixer/src/mixer_snd.rs | 6 ++ crates/tek_mixer/src/plugin.rs | 83 +++----------------- crates/tek_mixer/src/plugin_edn.rs | 20 +++++ crates/tek_mixer/src/plugin_lv2.rs | 48 +++--------- crates/tek_mixer/src/plugin_snd.rs | 58 ++++++++++++++ crates/tek_mixer/src/sampler.rs | 108 +++++++++------------------ crates/tek_mixer/src/sampler_cli.rs | 1 + crates/tek_mixer/src/sampler_edn.rs | 60 +++++++++++++++ crates/tek_mixer/src/sampler_snd.rs | 10 +++ crates/tek_mixer/src/track.rs | 58 +------------- crates/tek_mixer/src/track_edn.rs | 54 ++++++++++++++ crates/tek_mixer/src/voice.rs | 29 ------- crates/tek_sequencer/src/arranger.rs | 2 +- 16 files changed, 296 insertions(+), 287 deletions(-) create mode 100644 crates/tek_mixer/src/mixer_snd.rs create mode 100644 crates/tek_mixer/src/plugin_edn.rs create mode 100644 crates/tek_mixer/src/plugin_snd.rs create mode 100644 crates/tek_mixer/src/sampler_edn.rs create mode 100644 crates/tek_mixer/src/sampler_snd.rs create mode 100644 crates/tek_mixer/src/track_edn.rs delete mode 100644 crates/tek_mixer/src/voice.rs diff --git a/crates/tek_core/src/audio.rs b/crates/tek_core/src/audio.rs index e904ed96..6b2c245b 100644 --- a/crates/tek_core/src/audio.rs +++ b/crates/tek_core/src/audio.rs @@ -2,20 +2,38 @@ use crate::*; use jack::*; /// Trait for things that wrap a JACK client. pub trait AudioEngine { - fn activate( + fn activate ( self, process: impl FnMut(&Arc>, &Client, &ProcessScope) -> Control + Send + 'static ) -> Usually>> where Self: Send + Sync + 'static; - fn client(&self) -> &Client; - fn transport(&self) -> Transport { + fn client (&self) -> &Client; + fn transport (&self) -> Transport { self.client().transport() } - fn port_by_name(&self, name: &str) -> Option> { + fn port_by_name (&self, name: &str) -> Option> { self.client().port_by_name(name) } - fn register_port(&self, name: &str, spec: PS) -> Usually> { + fn register_port (&self, name: &str, spec: PS) -> Usually> { Ok(self.client().register_port(name, spec)?) } + 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 + } } /// Wraps [Client] or [DynamicAsyncClient] in place. pub enum JackClient { diff --git a/crates/tek_mixer/src/lib.rs b/crates/tek_mixer/src/lib.rs index 107263d8..ca66359b 100644 --- a/crates/tek_mixer/src/lib.rs +++ b/crates/tek_mixer/src/lib.rs @@ -10,13 +10,8 @@ pub(crate) use std::ffi::OsString; pub(crate) use std::fs::read_dir; submod! { - mixer - plugin - plugin_lv2 - plugin_lv2_gui - plugin_vst2 - plugin_vst3 - sampler - track - voice + mixer mixer_snd + track track_edn + plugin plugin_snd plugin_edn plugin_lv2 plugin_lv2_gui plugin_vst2 plugin_vst3 + sampler sampler_snd sampler_edn } diff --git a/crates/tek_mixer/src/mixer.rs b/crates/tek_mixer/src/mixer.rs index bcc02d0a..21384ea7 100644 --- a/crates/tek_mixer/src/mixer.rs +++ b/crates/tek_mixer/src/mixer.rs @@ -27,11 +27,6 @@ impl Mixer { self.tracks.get(self.selected_track) } } -impl Audio for Mixer { - fn process (&mut self, _: &Client, _: &ProcessScope) -> Control { - Control::Continue - } -} impl Content for Mixer { type Engine = Tui; fn content (&self) -> impl Widget { diff --git a/crates/tek_mixer/src/mixer_snd.rs b/crates/tek_mixer/src/mixer_snd.rs new file mode 100644 index 00000000..eea362e2 --- /dev/null +++ b/crates/tek_mixer/src/mixer_snd.rs @@ -0,0 +1,6 @@ +use crate::*; +impl Audio for Mixer { + fn process (&mut self, _: &Client, _: &ProcessScope) -> Control { + Control::Continue + } +} diff --git a/crates/tek_mixer/src/plugin.rs b/crates/tek_mixer/src/plugin.rs index b10d4499..8a54a769 100644 --- a/crates/tek_mixer/src/plugin.rs +++ b/crates/tek_mixer/src/plugin.rs @@ -32,82 +32,23 @@ impl Plugin { } } -impl Plugin { +impl Plugin { pub fn new_lv2 ( jack: &Arc>, name: &str, path: &str, - ) -> Usually> { + ) -> Usually> { let plugin = LV2Plugin::new(path)?; - jack_from_lv2(name, &plugin.plugin)? - .run(|ports|Box::new(Self { - _engine: Default::default(), - jack: jack.clone(), - name: name.into(), - path: Some(String::from(path)), - plugin: Some(PluginKind::LV2(plugin)), - selected: 0, - mapping: false, - ports - })) - } -} - -impl Audio for Plugin { - fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { - match self.plugin.as_mut() { - Some(PluginKind::LV2(LV2Plugin { - features, - ref mut instance, - ref mut input_buffer, - .. - })) => { - let urid = features.midi_urid(); - input_buffer.clear(); - for port in self.ports.midi_ins.values() { - let mut atom = ::livi::event::LV2AtomSequence::new( - &features, - scope.n_frames() as usize - ); - for event in port.iter(scope) { - match event.bytes.len() { - 3 => atom.push_midi_event::<3>( - event.time as i64, - urid, - &event.bytes[0..3] - ).unwrap(), - _ => {} - } - } - input_buffer.push(atom); - } - let mut outputs = vec![]; - for _ in self.ports.midi_outs.iter() { - outputs.push(::livi::event::LV2AtomSequence::new( - &features, - scope.n_frames() as usize - )); - } - let ports = ::livi::EmptyPortConnections::new() - .with_atom_sequence_inputs( - input_buffer.iter() - ) - .with_atom_sequence_outputs( - outputs.iter_mut() - ) - .with_audio_inputs( - self.ports.audio_ins.values().map(|o|o.as_slice(scope)) - ) - .with_audio_outputs( - self.ports.audio_outs.values_mut().map(|o|o.as_mut_slice(scope)) - ); - unsafe { - instance.run(scope.n_frames() as usize, ports).unwrap() - }; - }, - _ => {} - } - Control::Continue + jack_from_lv2(name, &plugin.plugin)?.run(|ports|Box::new(Self { + _engine: Default::default(), + jack: jack.clone(), + name: name.into(), + path: Some(String::from(path)), + plugin: Some(PluginKind::LV2(plugin)), + selected: 0, + mapping: false, + ports + })) } } diff --git a/crates/tek_mixer/src/plugin_edn.rs b/crates/tek_mixer/src/plugin_edn.rs new file mode 100644 index 00000000..9dc6299f --- /dev/null +++ b/crates/tek_mixer/src/plugin_edn.rs @@ -0,0 +1,20 @@ +use crate::*; + +impl LV2Plugin { + pub fn from_edn <'e, E: Engine> (jack: &Arc>, args: &[Edn<'e>]) -> Usually> { + 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) + } +} diff --git a/crates/tek_mixer/src/plugin_lv2.rs b/crates/tek_mixer/src/plugin_lv2.rs index e918c8b9..a71fea12 100644 --- a/crates/tek_mixer/src/plugin_lv2.rs +++ b/crates/tek_mixer/src/plugin_lv2.rs @@ -22,53 +22,25 @@ pub struct LV2Plugin { } impl LV2Plugin { - pub fn from_edn <'e> (args: &[Edn<'e>]) -> Usually> { - 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(&name, &path) - } + const INPUT_BUFFER: usize = 1024; pub fn new (uri: &str) -> Usually { // Get 1st plugin at URI let world = World::with_load_bundle(&uri); let features = FeaturesBuilder { min_block_length: 1, max_block_length: 65536 }; let features = world.build_features(features); let mut plugin = None; - if let Some(p) = world.iter_plugins().next() { - plugin = Some(p); + if let Some(p) = world.iter_plugins().next() { plugin = Some(p); } + let plugin = plugin.expect("plugin not found"); + let err = &format!("init {uri}"); + let instance = unsafe { plugin.instantiate(features.clone(), 48000.0).expect(&err) }; + let mut port_list = vec![]; + for port in plugin.ports() { + port_list.push(port); } - let plugin = plugin.unwrap(); - let err = &format!("init {uri}"); - + let input_buffer = Vec::with_capacity(Self::INPUT_BUFFER); // Instantiate Ok(Self { - world, - instance: unsafe { - plugin - .instantiate(features.clone(), 48000.0) - .expect(&err) - }, - port_list: { - let mut port_list = vec![]; - for port in plugin.ports() { - port_list.push(port); - } - port_list - }, - plugin, - features, - input_buffer: Vec::with_capacity(1024), - ui_thread: None + world, instance, port_list, plugin, features, input_buffer, ui_thread: None }) } } diff --git a/crates/tek_mixer/src/plugin_snd.rs b/crates/tek_mixer/src/plugin_snd.rs new file mode 100644 index 00000000..1adba53e --- /dev/null +++ b/crates/tek_mixer/src/plugin_snd.rs @@ -0,0 +1,58 @@ +use crate::*; +impl Audio for Plugin { + fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { + match self.plugin.as_mut() { + Some(PluginKind::LV2(LV2Plugin { + features, + ref mut instance, + ref mut input_buffer, + .. + })) => { + let urid = features.midi_urid(); + input_buffer.clear(); + for port in self.ports.midi_ins.values() { + let mut atom = ::livi::event::LV2AtomSequence::new( + &features, + scope.n_frames() as usize + ); + for event in port.iter(scope) { + match event.bytes.len() { + 3 => atom.push_midi_event::<3>( + event.time as i64, + urid, + &event.bytes[0..3] + ).unwrap(), + _ => {} + } + } + input_buffer.push(atom); + } + let mut outputs = vec![]; + for _ in self.ports.midi_outs.iter() { + outputs.push(::livi::event::LV2AtomSequence::new( + &features, + scope.n_frames() as usize + )); + } + let ports = ::livi::EmptyPortConnections::new() + .with_atom_sequence_inputs( + input_buffer.iter() + ) + .with_atom_sequence_outputs( + outputs.iter_mut() + ) + .with_audio_inputs( + self.ports.audio_ins.values().map(|o|o.as_slice(scope)) + ) + .with_audio_outputs( + self.ports.audio_outs.values_mut().map(|o|o.as_mut_slice(scope)) + ); + unsafe { + instance.run(scope.n_frames() as usize, ports).unwrap() + }; + }, + _ => {} + } + Control::Continue + } +} diff --git a/crates/tek_mixer/src/sampler.rs b/crates/tek_mixer/src/sampler.rs index 05dfcce7..9d8565fd 100644 --- a/crates/tek_mixer/src/sampler.rs +++ b/crates/tek_mixer/src/sampler.rs @@ -17,38 +17,9 @@ pub struct Sampler { } impl Sampler { - pub fn from_edn <'e> (args: &[Edn<'e>]) -> Usually> { - 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.get(0) { - Some(Edn::Symbol("sample")) => { - let (midi, sample) = Sample::from_edn(&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:?}") - }); - Self::new(&name, Some(samples)) - } - pub fn new ( name: &str, mapped: Option>>> - ) -> Usually> { + ) -> Usually> { Jack::new(name)? .midi_in("midi") .audio_in("recL") @@ -57,6 +28,7 @@ impl Sampler { .audio_out("outR") .run(|ports|Box::new(Self { _engine: Default::default(), + jack: (), name: name.into(), cursor: (0, 0), editing: None, @@ -69,7 +41,6 @@ impl Sampler { modal: Default::default() })) } - /// Immutable reference to sample at cursor. pub fn sample (&self) -> Option<&Arc>> { for (i, sample) in self.mapped.values().enumerate() { @@ -84,9 +55,8 @@ impl Sampler { } None } - /// Create [Voice]s from [Sample]s in response to MIDI input. - fn process_midi_in (&mut self, scope: &ProcessScope) { + pub fn process_midi_in (&mut self, scope: &ProcessScope) { for RawMidi { time, bytes } in self.ports.midi_ins.get("midi").unwrap().iter(scope) { if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() { if let MidiMessage::NoteOn { ref key, ref vel } = message { @@ -97,16 +67,14 @@ impl Sampler { } } } - /// Zero the output buffer. - fn clear_output_buffer (&mut self) { + pub fn clear_output_buffer (&mut self) { for buffer in self.buffer.iter_mut() { buffer.fill(0.0); } } - /// Mix all currently playing samples into the output. - fn process_audio_out (&mut self, scope: &ProcessScope) { + pub fn process_audio_out (&mut self, scope: &ProcessScope) { let channel_count = self.buffer.len(); self.voices.write().unwrap().retain_mut(|voice|{ for index in 0..scope.n_frames() as usize { @@ -126,9 +94,8 @@ impl Sampler { return true }); } - /// Write output buffer to output ports. - fn write_output_buffer (&mut self, scope: &ProcessScope) { + pub fn write_output_buffer (&mut self, scope: &ProcessScope) { for (i, port) in self.ports.audio_outs.values_mut().enumerate() { let buffer = &self.buffer[i]; for (i, value) in port.as_mut_slice(scope).iter_mut().enumerate() { @@ -137,16 +104,6 @@ impl Sampler { } } } - -impl Audio for Sampler { - fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { - self.process_midi_in(scope); - self.clear_output_buffer(); - self.process_audio_out(scope); - self.write_output_buffer(scope); - Control::Continue - } -} impl Handle for Sampler { fn handle (&mut self, from: &TuiInput) -> Perhaps { match from.event() { @@ -257,31 +214,6 @@ pub struct Sample { } impl Sample { - pub fn from_edn <'e> (dir: &str, args: &[Edn<'e>]) -> Usually<(Option, Arc>)> { - 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) = read_sample_data(&format!("{dir}/{file}"))?; - Ok((midi, Arc::new(RwLock::new(Self::new(&name, start, end, data))))) - } pub fn new (name: &str, start: usize, end: usize, channels: Vec>) -> Self { Self { name: name.to_string(), start, end, channels, rate: None } } @@ -615,3 +547,31 @@ impl Sample { Ok(sample) } } + +/// A currently playing instance of a sample. +pub struct Voice { + pub sample: Arc>, + pub after: usize, + pub position: usize, + pub velocity: f32, +} + +impl Iterator for Voice { + type Item = [f32;2]; + fn next (&mut self) -> Option { + if self.after > 0 { + self.after = self.after - 1; + return Some([0.0, 0.0]) + } + let sample = self.sample.read().unwrap(); + if self.position < sample.end { + let position = self.position; + self.position = self.position + 1; + return sample.channels[0].get(position).map(|_amplitude|[ + sample.channels[0][position] * self.velocity, + sample.channels[0][position] * self.velocity, + ]) + } + None + } +} diff --git a/crates/tek_mixer/src/sampler_cli.rs b/crates/tek_mixer/src/sampler_cli.rs index 3b9f42fa..95e0f81a 100644 --- a/crates/tek_mixer/src/sampler_cli.rs +++ b/crates/tek_mixer/src/sampler_cli.rs @@ -14,6 +14,7 @@ impl SamplerCli { let mut plugin = Sampler::new( jack, self.name.as_ref().map(|x|x.as_str()).unwrap_or("mixer"), + None, )?; Ok(plugin) })?)?; diff --git a/crates/tek_mixer/src/sampler_edn.rs b/crates/tek_mixer/src/sampler_edn.rs new file mode 100644 index 00000000..5a65783b --- /dev/null +++ b/crates/tek_mixer/src/sampler_edn.rs @@ -0,0 +1,60 @@ +use crate::*; + +impl Sampler { + pub fn from_edn <'e> (jack: &Arc>, args: &[Edn<'e>]) -> Usually> { + 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.get(0) { + Some(Edn::Symbol("sample")) => { + let (midi, sample) = Sample::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:?}") + }); + Self::new(&name, Some(samples)) + } +} + +impl Sample { + pub fn from_edn <'e> (jack: &Arc>, dir: &str, args: &[Edn<'e>]) -> Usually<(Option, Arc>)> { + 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) = read_sample_data(&format!("{dir}/{file}"))?; + Ok((midi, Arc::new(RwLock::new(Self::new(&name, start, end, data))))) + } +} diff --git a/crates/tek_mixer/src/sampler_snd.rs b/crates/tek_mixer/src/sampler_snd.rs new file mode 100644 index 00000000..880c1116 --- /dev/null +++ b/crates/tek_mixer/src/sampler_snd.rs @@ -0,0 +1,10 @@ +use crate::*; +impl Audio for Sampler { + fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { + self.process_midi_in(scope); + self.clear_output_buffer(); + self.process_audio_out(scope); + self.write_output_buffer(scope); + Control::Continue + } +} diff --git a/crates/tek_mixer/src/track.rs b/crates/tek_mixer/src/track.rs index 6d30821a..21d7b168 100644 --- a/crates/tek_mixer/src/track.rs +++ b/crates/tek_mixer/src/track.rs @@ -33,6 +33,9 @@ impl Track { let index = self.devices.len() - 1; Ok(&mut self.devices[index]) } + pub fn add_device (&mut self, device: JackDevice) { + self.devices.push(device); + } //pub fn connect_first_device (&self) -> Usually<()> { //if let (Some(port), Some(device)) = (&self.midi_out, self.devices.get(0)) { //device.client.as_client().connect_ports(&port, &device.midi_ins()?[0])?; @@ -50,61 +53,6 @@ impl Track { //}) //} } -impl Track { - pub fn from_edn <'a, 'e> (args: &[Edn<'e>]) -> Usually> { - let mut _gain = 0.0f64; - let mut track = Self::new("")?; - #[allow(unused_mut)] - let mut devices: Vec> = vec![]; - edn!(edn in args { - Edn::Map(map) => { - if let Some(Edn::Str(n)) = map.get(&Edn::Key(SYM_NAME)) { - track.name = n.to_string(); - } - if let Some(Edn::Double(g)) = map.get(&Edn::Key(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)) => { - devices.push(Sampler::from_edn(&args[1..])?); - 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(SYM_LV2)) => { - devices.push(LV2Plugin::from_edn(&args[1..])?); - 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()) - }, - _ => {} - }); - for device in devices { - track.add_device(device); - } - Ok(track) - } - pub fn add_device (&mut self, device: JackDevice) { - self.devices.push(device); - } -} - -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 Handle for Track { fn handle (&mut self, from: &TuiInput) -> Perhaps { diff --git a/crates/tek_mixer/src/track_edn.rs b/crates/tek_mixer/src/track_edn.rs new file mode 100644 index 00000000..6533df13 --- /dev/null +++ b/crates/tek_mixer/src/track_edn.rs @@ -0,0 +1,54 @@ +use crate::*; + +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 Track { + pub fn from_edn <'a, 'e> (jack: &Arc>, args: &[Edn<'e>]) -> Usually { + let mut _gain = 0.0f64; + let mut track = Self::new("")?; + #[allow(unused_mut)] + let mut devices: Vec> = vec![]; + edn!(edn in args { + Edn::Map(map) => { + if let Some(Edn::Str(n)) = map.get(&Edn::Key(SYM_NAME)) { + track.name = n.to_string(); + } + if let Some(Edn::Double(g)) = map.get(&Edn::Key(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)) => { + devices.push(Sampler::from_edn(jack, &args[1..])?); + 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(SYM_LV2)) => { + devices.push(LV2Plugin::from_edn(jack, &args[1..])?); + 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()) + }, + _ => {} + }); + for device in devices { + track.add_device(device); + } + Ok(track) + } +} diff --git a/crates/tek_mixer/src/voice.rs b/crates/tek_mixer/src/voice.rs deleted file mode 100644 index 120cfb55..00000000 --- a/crates/tek_mixer/src/voice.rs +++ /dev/null @@ -1,29 +0,0 @@ -use super::*; - -/// A currently playing instance of a sample. -pub struct Voice { - pub sample: Arc>, - pub after: usize, - pub position: usize, - pub velocity: f32, -} - -impl Iterator for Voice { - type Item = [f32;2]; - fn next (&mut self) -> Option { - if self.after > 0 { - self.after = self.after - 1; - return Some([0.0, 0.0]) - } - let sample = self.sample.read().unwrap(); - if self.position < sample.end { - let position = self.position; - self.position = self.position + 1; - return sample.channels[0].get(position).map(|_amplitude|[ - sample.channels[0][position] * self.velocity, - sample.channels[0][position] * self.velocity, - ]) - } - None - } -} diff --git a/crates/tek_sequencer/src/arranger.rs b/crates/tek_sequencer/src/arranger.rs index 108bf9a0..1ac1041a 100644 --- a/crates/tek_sequencer/src/arranger.rs +++ b/crates/tek_sequencer/src/arranger.rs @@ -157,7 +157,7 @@ impl Arranger { if let ArrangementFocus::Clip(track, scene) = self.arrangement.selected { let track_color = self.arrangement.tracks[track].color; let scene_color = self.arrangement.scenes[scene].color; - track_color.mix(scene_color, 0.5).mix(ItemColor::random(), 0.5) + track_color.mix(scene_color, 0.5).mix(ItemColor::random(), 0.25) } else { panic!("could not compute next color") }