flatten modules somewhat

This commit is contained in:
🪞👃🪞 2024-12-27 21:26:16 +01:00
parent cb7ba855ab
commit 0779560502
29 changed files with 442 additions and 474 deletions

View file

@ -1,9 +1,6 @@
include!("./lib.rs");
use tek::tui::ArrangerTui;
pub fn main () -> Usually<()> {
ArrangerCli::parse().run()
}
use tek::ArrangerTui;
pub fn main () -> Usually<()> { ArrangerCli::parse().run() }
/// Launches an interactive MIDI arranger.
#[derive(Debug, Parser)]

View file

@ -30,7 +30,7 @@ pub struct GrooveboxCli {
impl GrooveboxCli {
fn run (&self) -> Usually<()> {
Tui::run(JackClient::new("tek_groovebox")?.activate_with(|jack|{
let mut app = tek::tui::GrooveboxTui::try_from(jack)?;
let mut app = tek::GrooveboxTui::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())?;

View file

@ -9,7 +9,7 @@ pub fn main () -> Usually<()> { SamplerCli::parse().run() }
impl SamplerCli {
fn run (&self) -> Usually<()> {
Tui::run(JackClient::new("tek_sampler")?.activate_with(|x|{
let sampler = tek::tui::SamplerTui::try_from(x)?;
let sampler = tek::SamplerTui::try_from(x)?;
Ok(sampler)
})?)?;
Ok(())

View file

@ -1,4 +1,4 @@
#[allow(unused_imports)] use tek::{*, jack::*, plugin::*, audio::*};
#[allow(unused_imports)] use tek::{*, jack::*, plugin::*};
use std::sync::{Arc, RwLock};
use std::collections::BTreeMap;

View file

@ -1,4 +1,13 @@
use crate::*;
mod arranger_command; pub(crate) use self::arranger_command::*;
mod arranger_scene; pub(crate) use self::arranger_scene::*;
mod arranger_select; pub(crate) use self::arranger_select::*;
mod arranger_track; pub(crate) use self::arranger_track::*;
mod arranger_mode; pub(crate) use self::arranger_mode::*;
mod arranger_v; #[allow(unused)] pub(crate) use self::arranger_v::*;
mod arranger_h;
/// Root view for standalone `tek_arranger`
pub struct ArrangerTui {
jack: Arc<RwLock<JackClient>>,

View file

@ -1,9 +1 @@
use crate::*;
mod sampler;
pub(crate) use self::sampler::*;
pub use self::sampler::{Sampler, Sample, Voice};
mod mixer;
pub(crate) use self::mixer::*;
pub use self::mixer::{Mixer, MixerTrack, MixerTrackDevice};

View file

@ -1,37 +0,0 @@
use crate::*;
#[derive(Debug)]
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,
}
audio!(|self: Mixer, _client, _scope|Control::Continue);
pub enum MixerTrackCommand {}
/// A mixer track.
#[derive(Debug)]
pub struct MixerTrack {
pub name: String,
/// Inputs of 1st device
pub audio_ins: Vec<Port<AudioIn>>,
/// Outputs of last device
pub audio_outs: Vec<Port<AudioOut>>,
/// Device chain
pub devices: Vec<Box<dyn MixerTrackDevice>>,
}
//impl MixerTrackDevice for LV2Plugin {}
pub trait MixerTrackDevice: Debug + Send + Sync {
fn boxed (self) -> Box<dyn MixerTrackDevice> where Self: Sized + 'static {
Box::new(self)
}
}
impl MixerTrackDevice for Sampler {}
impl MixerTrackDevice for Plugin {}

View file

@ -1,152 +0,0 @@
use crate::*;
/// The sampler plugin plays sounds.
#[derive(Debug)]
pub struct Sampler {
pub jack: Arc<RwLock<JackClient>>,
pub name: String,
pub mapped: BTreeMap<u7, Arc<RwLock<Sample>>>,
pub unmapped: Vec<Arc<RwLock<Sample>>>,
pub voices: Arc<RwLock<Vec<Voice>>>,
pub midi_in: Port<MidiIn>,
pub audio_outs: Vec<Port<AudioOut>>,
pub buffer: Vec<Vec<f32>>,
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,
}
/// Load sample from WAV and assign to MIDI note.
#[macro_export] macro_rules! sample {
($note:expr, $name:expr, $src:expr) => {{
let (end, data) = read_sample_data($src)?;
(
u7::from_int_lossy($note).into(),
Sample::new($name, 0, end, data).into()
)
}};
}
impl Sampler {
/// Create [Voice]s from [Sample]s in response to MIDI input.
pub fn process_midi_in (&mut self, scope: &ProcessScope) {
let Sampler { midi_in, mapped, voices, .. } = self;
for RawMidi { time, bytes } in midi_in.iter(scope) {
if let LiveEvent::Midi {
message: MidiMessage::NoteOn { ref key, ref vel }, ..
} = LiveEvent::parse(bytes).unwrap() {
if let Some(sample) = mapped.get(key) {
voices.write().unwrap().push(Sample::play(sample, time as usize, vel));
}
}
}
}
/// Zero the output buffer.
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.
pub fn process_audio_out (&mut self, scope: &ProcessScope) {
let Sampler { ref mut buffer, voices, output_gain, .. } = self;
let channel_count = buffer.len();
voices.write().unwrap().retain_mut(|voice|{
for index in 0..scope.n_frames() as usize {
if let Some(frame) = voice.next() {
for (channel, sample) in frame.iter().enumerate() {
// Averaging mixer:
//self.buffer[channel % channel_count][index] = (
//(self.buffer[channel % channel_count][index] + sample * self.output_gain) / 2.0
//);
buffer[channel % channel_count][index] += sample * *output_gain;
}
} else {
return false
}
}
true
});
}
/// Write output buffer to output ports.
pub fn write_output_buffer (&mut self, scope: &ProcessScope) {
let Sampler { ref mut audio_outs, buffer, .. } = self;
for (i, port) in audio_outs.iter_mut().enumerate() {
let buffer = &buffer[i];
for (i, value) in port.as_mut_slice(scope).iter_mut().enumerate() {
*value = *buffer.get(i).unwrap_or(&0.0);
}
}
}
}
impl Sample {
pub fn new (name: &str, start: usize, end: usize, channels: Vec<Vec<f32>>) -> Self {
Self { name: name.to_string(), start, end, channels, rate: None }
}
pub fn play (sample: &Arc<RwLock<Self>>, after: usize, velocity: &u7) -> Voice {
Voice {
sample: sample.clone(),
after,
position: sample.read().unwrap().start,
velocity: velocity.as_int() as f32 / 127.0,
}
}
/// Read WAV from file
pub fn read_data (src: &str) -> Usually<(usize, Vec<Vec<f32>>)> {
let mut channels: Vec<wavers::Samples<f32>> = vec![];
for channel in wavers::Wav::from_path(src)?.channels() {
channels.push(channel);
}
let mut end = 0;
let mut data: Vec<Vec<f32>> = vec![];
for samples in channels.iter() {
let channel = Vec::from(samples.as_ref());
end = end.max(channel.len());
data.push(channel);
}
Ok((end, data))
}
}
impl Iterator for Voice {
type Item = [f32;2];
fn next (&mut self) -> Option<Self::Item> {
if self.after > 0 {
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 += 1;
return sample.channels[0].get(position).map(|_amplitude|[
sample.channels[0][position] * self.velocity,
sample.channels[0][position] * self.velocity,
])
}
None
}
}

View file

@ -7,15 +7,44 @@ 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 use tui::*;
pub mod tui;
pub(crate) use self::tui::*;
pub use tui::*;
pub mod jack; pub(crate) use self::jack::*; pub use self::jack::*;
pub mod jack;
pub(crate) use self::jack::*;
pub use self::jack::*;
pub mod midi; pub(crate) use self::midi::*;
pub mod midi;
pub(crate) use self::midi::*;
pub mod audio; pub(crate) use self::audio::*; pub use self::audio::*;
pub mod transport;
pub(crate) use self::transport::*;
pub use self::transport::TransportTui;
pub mod plugin; pub(crate) use self::plugin::*; pub use self::plugin::*;
pub mod sequencer;
pub(crate) use self::sequencer::*;
pub use self::sequencer::SequencerTui;
pub mod arranger;
pub(crate) use self::arranger::*;
pub use self::arranger::ArrangerTui;
mod sampler;
pub(crate) use self::sampler::*;
pub use self::sampler::{SamplerTui, Sampler, Sample, Voice};
mod mixer;
pub(crate) use self::mixer::*;
pub use self::mixer::{Mixer, MixerTrack, MixerTrackDevice};
pub mod plugin;
pub(crate) use self::plugin::*;
pub use self::plugin::*;
pub mod groovebox;
pub(crate) use self::groovebox::*;
pub use self::groovebox::GrooveboxTui;
pub use ::better_panic; pub(crate) use better_panic::{Settings, Verbosity};

View file

@ -1,14 +1,30 @@
use crate::*;
pub struct Mixer<E: Engine> {
#[derive(Debug)]
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<Track<E>>,
pub tracks: Vec<MixerTrack>,
pub selected_track: usize,
pub selected_column: usize,
}
impl<E: Engine> Mixer<E> {
/// A mixer track.
#[derive(Debug)]
pub struct MixerTrack {
pub name: String,
/// Inputs of 1st device
pub audio_ins: Vec<Port<AudioIn>>,
/// Outputs of last device
pub audio_outs: Vec<Port<AudioOut>>,
/// Device chain
pub devices: Vec<Box<dyn MixerTrackDevice>>,
}
audio!(|self: Mixer, _client, _scope|Control::Continue);
impl Mixer {
pub fn new (jack: &Arc<RwLock<JackClient>>, name: &str) -> Usually<Self> {
Ok(Self {
jack: jack.clone(),
@ -19,57 +35,42 @@ impl<E: Engine> Mixer<E> {
})
}
pub fn track_add (&mut self, name: &str, channels: usize) -> Usually<&mut Self> {
let track = Track::new(name)?;
let track = MixerTrack::new(name)?;
self.tracks.push(track);
Ok(self)
}
pub fn track (&self) -> Option<&Track<E>> {
pub fn track (&self) -> Option<&MixerTrack> {
self.tracks.get(self.selected_track)
}
}
//pub const ACTIONS: [(&'static str, &'static str);2] = [
//("+/-", "Adjust"),
//("Ins/Del", "Add/remove track"),
//];
/// A sequencer track.
#[derive(Debug)]
pub struct Track<E: Engine> {
pub name: String,
/// Inputs and outputs of 1st and last device
pub ports: JackPorts,
/// Device chain
pub devices: Vec<JackDevice<E>>,
/// Device selector
pub device: usize,
}
impl<E: Engine> Track<E> {
impl MixerTrack {
pub fn new (name: &str) -> Usually<Self> {
Ok(Self {
name: name.to_string(),
ports: JackPorts::default(),
devices: vec![],
device: 0,
name: name.to_string(),
audio_ins: vec![],
audio_outs: vec![],
devices: vec![],
//ports: JackPorts::default(),
//devices: vec![],
//device: 0,
})
}
fn get_device_mut (&self, i: usize) -> Option<RwLockWriteGuard<Box<dyn AudioComponent<E>>>> {
self.devices.get(i).map(|d|d.state.write().unwrap())
}
pub fn device_mut (&self) -> Option<RwLockWriteGuard<Box<dyn AudioComponent<E>>>> {
self.get_device_mut(self.device)
}
/// Add a device to the end of the chain.
pub fn append_device (&mut self, device: JackDevice<E>) -> Usually<&mut JackDevice<E>> {
self.devices.push(device);
let index = self.devices.len() - 1;
Ok(&mut self.devices[index])
}
pub fn add_device (&mut self, device: JackDevice<E>) {
self.devices.push(device);
}
//fn get_device_mut (&self, i: usize) -> Option<RwLockWriteGuard<Box<dyn AudioComponent<E>>>> {
//self.devices.get(i).map(|d|d.state.write().unwrap())
//}
//pub fn device_mut (&self) -> Option<RwLockWriteGuard<Box<dyn AudioComponent<E>>>> {
//self.get_device_mut(self.device)
//}
///// Add a device to the end of the chain.
//pub fn append_device (&mut self, device: JackDevice<E>) -> Usually<&mut JackDevice<E>> {
//self.devices.push(device);
//let index = self.devices.len() - 1;
//Ok(&mut self.devices[index])
//}
//pub fn add_device (&mut self, device: JackDevice<E>) {
//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])?;
@ -88,14 +89,14 @@ impl<E: Engine> Track<E> {
//}
}
pub struct TrackView<'a, E: Engine> {
pub chain: Option<&'a Track<E>>,
pub struct TrackView<'a> {
pub chain: Option<&'a MixerTrack>,
pub direction: Direction,
pub focused: bool,
pub entered: bool,
}
impl<'a> Render<Tui> for TrackView<'a, Tui> {
impl<'a> Render<Tui> for TrackView<'a> {
fn min_size (&self, area: [u16;2]) -> Perhaps<[u16;2]> {
todo!()
}
@ -129,40 +130,40 @@ impl<'a> Render<Tui> for TrackView<'a, Tui> {
}
}
impl Content<Tui> for Mixer<Tui> {
fn content (&self) -> impl Render<Tui> {
Stack::right(|add| {
for channel in self.tracks.iter() {
add(channel)?;
}
Ok(())
})
}
}
//impl Content<Tui> for Mixer<Tui> {
//fn content (&self) -> impl Render<Tui> {
//Stack::right(|add| {
//for channel in self.tracks.iter() {
//add(channel)?;
//}
//Ok(())
//})
//}
//}
impl Content<Tui> for Track<Tui> {
fn content (&self) -> impl Render<Tui> {
TrackView {
chain: Some(&self),
direction: tek_core::Direction::Right,
focused: true,
entered: true,
//pub channels: u8,
//pub input_ports: Vec<Port<AudioIn>>,
//pub pre_gain_meter: f64,
//pub gain: f64,
//pub insert_ports: Vec<Port<AudioOut>>,
//pub return_ports: Vec<Port<AudioIn>>,
//pub post_gain_meter: f64,
//pub post_insert_meter: f64,
//pub level: f64,
//pub pan: f64,
//pub output_ports: Vec<Port<AudioOut>>,
//pub post_fader_meter: f64,
//pub route: String,
}
}
}
//impl Content<Tui> for Track<Tui> {
//fn content (&self) -> impl Render<Tui> {
//TrackView {
//chain: Some(&self),
//direction: tek_core::Direction::Right,
//focused: true,
//entered: true,
////pub channels: u8,
////pub input_ports: Vec<Port<AudioIn>>,
////pub pre_gain_meter: f64,
////pub gain: f64,
////pub insert_ports: Vec<Port<AudioOut>>,
////pub return_ports: Vec<Port<AudioIn>>,
////pub post_gain_meter: f64,
////pub post_insert_meter: f64,
////pub level: f64,
////pub pan: f64,
////pub output_ports: Vec<Port<AudioOut>>,
////pub post_fader_meter: f64,
////pub route: String,
//}
//}
//}
handle!(<Tui>|self:Mixer,engine|{
if let TuiEvent::Input(crossterm::event::Event::Key(event)) = engine.event() {
@ -212,18 +213,18 @@ handle!(<Tui>|self:Mixer,engine|{
Ok(None)
});
handle!(<Tui>|self:Track<Tui>,from|{
handle!(<Tui>|self:MixerTrack,from|{
match from.event() {
//, NONE, "chain_cursor_up", "move cursor up", || {
key!(KeyCode::Up) => {
key_pat!(KeyCode::Up) => {
Ok(Some(true))
},
// , NONE, "chain_cursor_down", "move cursor down", || {
key!(KeyCode::Down) => {
key_pat!(KeyCode::Down) => {
Ok(Some(true))
},
// Left, NONE, "chain_cursor_left", "move cursor left", || {
key!(KeyCode::Left) => {
key_pat!(KeyCode::Left) => {
//if let Some(track) = app.arranger.track_mut() {
//track.device = track.device.saturating_sub(1);
//return Ok(true)
@ -231,7 +232,7 @@ handle!(<Tui>|self:Track<Tui>,from|{
Ok(Some(true))
},
// , NONE, "chain_cursor_right", "move cursor right", || {
key!(KeyCode::Right) => {
key_pat!(KeyCode::Right) => {
//if let Some(track) = app.arranger.track_mut() {
//track.device = (track.device + 1).min(track.devices.len().saturating_sub(1));
//return Ok(true)
@ -239,10 +240,24 @@ handle!(<Tui>|self:Track<Tui>,from|{
Ok(Some(true))
},
// , NONE, "chain_mode_switch", "switch the display mode", || {
key!(KeyCode::Char('`')) => {
key_pat!(KeyCode::Char('`')) => {
//app.chain_mode = !app.chain_mode;
Ok(Some(true))
},
_ => Ok(None)
}
});
pub enum MixerTrackCommand {}
//impl MixerTrackDevice for LV2Plugin {}
pub trait MixerTrackDevice: Debug + Send + Sync {
fn boxed (self) -> Box<dyn MixerTrackDevice> where Self: Sized + 'static {
Box::new(self)
}
}
impl MixerTrackDevice for Sampler {}
impl MixerTrackDevice for Plugin {}

View file

@ -127,3 +127,140 @@ audio!(|self: PluginAudio, client, scope|{
//}
//Ok(jack)
//}
impl Plugin {
/// Create a plugin host device.
pub fn new (
jack: &Arc<RwLock<JackClient>>,
name: &str,
) -> Usually<Self> {
Ok(Self {
//_engine: Default::default(),
jack: jack.clone(),
name: name.into(),
path: None,
plugin: None,
selected: 0,
mapping: false,
audio_ins: vec![],
audio_outs: vec![],
midi_ins: vec![],
midi_outs: vec![],
//ports: JackPorts::default()
})
}
}
impl Render<Tui> for Plugin {
fn min_size (&self, to: [u16;2]) -> Perhaps<[u16;2]> {
Ok(Some(to))
}
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
let area = to.area();
let [x, y, _, height] = area;
let mut width = 20u16;
match &self.plugin {
Some(PluginKind::LV2(LV2Plugin { port_list, instance, .. })) => {
let start = self.selected.saturating_sub((height as usize / 2).saturating_sub(1));
let end = start + height as usize - 2;
//draw_box(buf, Rect { x, y, width, height });
for i in start..end {
if let Some(port) = port_list.get(i) {
let value = if let Some(value) = instance.control_input(port.index) {
value
} else {
port.default_value
};
//let label = &format!("C·· M·· {:25} = {value:.03}", port.name);
let label = &format!("{:25} = {value:.03}", port.name);
width = width.max(label.len() as u16 + 4);
let style = if i == self.selected {
Some(Style::default().green())
} else {
None
} ;
to.blit(&label, x + 2, y + 1 + i as u16 - start as u16, style);
} else {
break
}
}
},
_ => {}
};
draw_header(self, to, x, y, width)?;
Ok(())
}
}
fn draw_header (state: &Plugin, to: &mut TuiOutput, x: u16, y: u16, w: u16) -> Usually<()> {
let style = Style::default().gray();
let label1 = format!(" {}", state.name);
to.blit(&label1, x + 1, y, Some(style.white().bold()));
if let Some(ref path) = state.path {
let label2 = format!("{}", &path[..((w as usize - 10).min(path.len()))]);
to.blit(&label2, x + 2 + label1.len() as u16, y, Some(style.not_dim()));
}
Ok(())
//Ok(Rect { x, y, width: w, height: 1 })
}
handle!(<Tui>|self:Plugin, from|{
match from.event() {
key_pat!(KeyCode::Up) => {
self.selected = self.selected.saturating_sub(1);
Ok(Some(true))
},
key_pat!(KeyCode::Down) => {
self.selected = (self.selected + 1).min(match &self.plugin {
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
_ => unimplemented!()
});
Ok(Some(true))
},
key_pat!(KeyCode::PageUp) => {
self.selected = self.selected.saturating_sub(8);
Ok(Some(true))
},
key_pat!(KeyCode::PageDown) => {
self.selected = (self.selected + 10).min(match &self.plugin {
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
_ => unimplemented!()
});
Ok(Some(true))
},
key_pat!(KeyCode::Char(',')) => {
match self.plugin.as_mut() {
Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
let index = port_list[self.selected].index;
if let Some(value) = instance.control_input(index) {
instance.set_control_input(index, value - 0.01);
}
},
_ => {}
}
Ok(Some(true))
},
key_pat!(KeyCode::Char('.')) => {
match self.plugin.as_mut() {
Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
let index = port_list[self.selected].index;
if let Some(value) = instance.control_input(index) {
instance.set_control_input(index, value + 0.01);
}
},
_ => {}
}
Ok(Some(true))
},
key_pat!(KeyCode::Char('g')) => {
match self.plugin {
//Some(PluginKind::LV2(ref mut plugin)) => {
//plugin.ui_thread = Some(run_lv2_ui(LV2PluginUI::new()?)?);
//},
Some(_) => unreachable!(),
None => {}
}
Ok(Some(true))
},
_ => Ok(None)
}
});

View file

@ -1,5 +1,155 @@
use crate::*;
use super::{*, piano_h::PianoHorizontalKeys};
use crate::{*, tui::piano_h::PianoHorizontalKeys};
/// The sampler plugin plays sounds.
#[derive(Debug)]
pub struct Sampler {
pub jack: Arc<RwLock<JackClient>>,
pub name: String,
pub mapped: BTreeMap<u7, Arc<RwLock<Sample>>>,
pub unmapped: Vec<Arc<RwLock<Sample>>>,
pub voices: Arc<RwLock<Vec<Voice>>>,
pub midi_in: Port<MidiIn>,
pub audio_outs: Vec<Port<AudioOut>>,
pub buffer: Vec<Vec<f32>>,
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,
}
/// Load sample from WAV and assign to MIDI note.
#[macro_export] macro_rules! sample {
($note:expr, $name:expr, $src:expr) => {{
let (end, data) = read_sample_data($src)?;
(
u7::from_int_lossy($note).into(),
Sample::new($name, 0, end, data).into()
)
}};
}
impl Sampler {
/// Create [Voice]s from [Sample]s in response to MIDI input.
pub fn process_midi_in (&mut self, scope: &ProcessScope) {
let Sampler { midi_in, mapped, voices, .. } = self;
for RawMidi { time, bytes } in midi_in.iter(scope) {
if let LiveEvent::Midi {
message: MidiMessage::NoteOn { ref key, ref vel }, ..
} = LiveEvent::parse(bytes).unwrap() {
if let Some(sample) = mapped.get(key) {
voices.write().unwrap().push(Sample::play(sample, time as usize, vel));
}
}
}
}
/// Zero the output buffer.
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.
pub fn process_audio_out (&mut self, scope: &ProcessScope) {
let Sampler { ref mut buffer, voices, output_gain, .. } = self;
let channel_count = buffer.len();
voices.write().unwrap().retain_mut(|voice|{
for index in 0..scope.n_frames() as usize {
if let Some(frame) = voice.next() {
for (channel, sample) in frame.iter().enumerate() {
// Averaging mixer:
//self.buffer[channel % channel_count][index] = (
//(self.buffer[channel % channel_count][index] + sample * self.output_gain) / 2.0
//);
buffer[channel % channel_count][index] += sample * *output_gain;
}
} else {
return false
}
}
true
});
}
/// Write output buffer to output ports.
pub fn write_output_buffer (&mut self, scope: &ProcessScope) {
let Sampler { ref mut audio_outs, buffer, .. } = self;
for (i, port) in audio_outs.iter_mut().enumerate() {
let buffer = &buffer[i];
for (i, value) in port.as_mut_slice(scope).iter_mut().enumerate() {
*value = *buffer.get(i).unwrap_or(&0.0);
}
}
}
}
impl Sample {
pub fn new (name: &str, start: usize, end: usize, channels: Vec<Vec<f32>>) -> Self {
Self { name: name.to_string(), start, end, channels, rate: None }
}
pub fn play (sample: &Arc<RwLock<Self>>, after: usize, velocity: &u7) -> Voice {
Voice {
sample: sample.clone(),
after,
position: sample.read().unwrap().start,
velocity: velocity.as_int() as f32 / 127.0,
}
}
/// Read WAV from file
pub fn read_data (src: &str) -> Usually<(usize, Vec<Vec<f32>>)> {
let mut channels: Vec<wavers::Samples<f32>> = vec![];
for channel in wavers::Wav::from_path(src)?.channels() {
channels.push(channel);
}
let mut end = 0;
let mut data: Vec<Vec<f32>> = vec![];
for samples in channels.iter() {
let channel = Vec::from(samples.as_ref());
end = end.max(channel.len());
data.push(channel);
}
Ok((end, data))
}
}
impl Iterator for Voice {
type Item = [f32;2];
fn next (&mut self) -> Option<Self::Item> {
if self.after > 0 {
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 += 1;
return sample.channels[0].get(position).map(|_amplitude|[
sample.channels[0][position] * self.velocity,
sample.channels[0][position] * self.velocity,
])
}
None
}
}
use KeyCode::Char;
use std::fs::File;

View file

@ -12,40 +12,15 @@ mod tui_style;
mod tui_theme; pub(crate) use self::tui_theme::*;
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;
///////////////////////////////////////////////////////
mod arranger_command; pub(crate) use self::arranger_command::*;
mod arranger_scene; pub(crate) use self::arranger_scene::*;
mod arranger_select; pub(crate) use self::arranger_select::*;
mod arranger_track; pub(crate) use self::arranger_track::*;
mod arranger_mode; pub(crate) use self::arranger_mode::*;
mod arranger_v; #[allow(unused)] pub(crate) use self::arranger_v::*;
mod arranger_h;
////////////////////////////////////////////////////////
mod pool; pub(crate) use self::pool::*;
mod phrase_editor; pub(crate) use self::phrase_editor::*;
mod status; pub(crate) use self::status::*;
mod file_browser; pub(crate) use self::file_browser::*;
mod piano_h; pub(crate) use self::piano_h::*;
pub mod pool; pub(crate) use self::pool::*;
pub mod phrase_editor; pub(crate) use self::phrase_editor::*;
pub mod status; pub(crate) use self::status::*;
pub mod file_browser; pub(crate) use self::file_browser::*;
pub mod piano_h; pub(crate) use self::piano_h::*;
////////////////////////////////////////////////////////

View file

@ -1,147 +0,0 @@
use crate::*;
/// A plugin device.
pub struct Plugin<E> {
_engine: PhantomData<E>,
/// 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,
}
impl<E> Plugin<E> {
/// Create a plugin host device.
pub fn new (
jack: &Arc<RwLock<JackClient>>,
name: &str,
) -> Usually<Self> {
Ok(Self {
_engine: Default::default(),
jack: jack.clone(),
name: name.into(),
path: None,
plugin: None,
selected: 0,
mapping: false,
ports: JackPorts::default()
})
}
}
impl Render<Tui> for Plugin<Tui> {
fn min_size (&self, to: [u16;2]) -> Perhaps<[u16;2]> {
Ok(Some(to))
}
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
let area = to.area();
let [x, y, _, height] = area;
let mut width = 20u16;
match &self.plugin {
Some(PluginKind::LV2(LV2Plugin { port_list, instance, .. })) => {
let start = self.selected.saturating_sub((height as usize / 2).saturating_sub(1));
let end = start + height as usize - 2;
//draw_box(buf, Rect { x, y, width, height });
for i in start..end {
if let Some(port) = port_list.get(i) {
let value = if let Some(value) = instance.control_input(port.index) {
value
} else {
port.default_value
};
//let label = &format!("C·· M·· {:25} = {value:.03}", port.name);
let label = &format!("{:25} = {value:.03}", port.name);
width = width.max(label.len() as u16 + 4);
let style = if i == self.selected {
Some(Style::default().green())
} else {
None
} ;
to.blit(&label, x + 2, y + 1 + i as u16 - start as u16, style);
} else {
break
}
}
},
_ => {}
};
draw_header(self, to, x, y, width)?;
Ok(())
}
}
fn draw_header <E> (state: &Plugin<E>, to: &mut TuiOutput, x: u16, y: u16, w: u16) -> Usually<Rect> {
let style = Style::default().gray();
let label1 = format!(" {}", state.name);
to.blit(&label1, x + 1, y, Some(style.white().bold()));
if let Some(ref path) = state.path {
let label2 = format!("{}", &path[..((w as usize - 10).min(path.len()))]);
to.blit(&label2, x + 2 + label1.len() as u16, y, Some(style.not_dim()));
}
Ok(Rect { x, y, width: w, height: 1 })
}
handle!(<Tui>|self:Plugin<tui>,from|{
match from.event() {
key!(KeyCode::Up) => {
self.selected = self.selected.saturating_sub(1);
Ok(Some(true))
},
key!(KeyCode::Down) => {
self.selected = (self.selected + 1).min(match &self.plugin {
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
_ => unimplemented!()
});
Ok(Some(true))
},
key!(KeyCode::PageUp) => {
self.selected = self.selected.saturating_sub(8);
Ok(Some(true))
},
key!(KeyCode::PageDown) => {
self.selected = (self.selected + 10).min(match &self.plugin {
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
_ => unimplemented!()
});
Ok(Some(true))
},
key!(KeyCode::Char(',')) => {
match self.plugin.as_mut() {
Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
let index = port_list[self.selected].index;
if let Some(value) = instance.control_input(index) {
instance.set_control_input(index, value - 0.01);
}
},
_ => {}
}
Ok(Some(true))
},
key!(KeyCode::Char('.')) => {
match self.plugin.as_mut() {
Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
let index = port_list[self.selected].index;
if let Some(value) = instance.control_input(index) {
instance.set_control_input(index, value + 0.01);
}
},
_ => {}
}
Ok(Some(true))
},
key!(KeyCode::Char('g')) => {
match self.plugin {
Some(PluginKind::LV2(ref mut plugin)) => {
plugin.ui_thread = Some(run_lv2_ui(LV2PluginUI::new()?)?);
},
Some(_) => unreachable!(),
None => {}
}
Ok(Some(true))
},
_ => Ok(None)
}
});
}