wip: enabling autoconnecting ports

This commit is contained in:
🪞👃🪞 2025-01-09 20:40:26 +01:00
parent c23f52c87b
commit fe70b57dc1
10 changed files with 375 additions and 424 deletions

View file

@ -37,13 +37,9 @@ impl Arranger {
let color = track_color_1.mix(track_color_2, i as f32 / count as f32).into();
let mut track = self.track_add(None, Some(color))?;
track.width = width;
let name = &format!("{}I", &track.name);
let port = jack.read().unwrap().client().register_port(&name, MidiIn::default())?;
let port = JackPort::<MidiIn>::new(&jack, &format!("{}I", &track.name), &[])?;
track.player.midi_ins.push(port);
let name = &format!("{}O", &track.name);
let port = jack.read().unwrap().client().register_port(&name, MidiOut::default())?;
let port = JackPort::<MidiOut>::new(&jack, &format!("{}O", &track.name), &[])?;
track.player.midi_outs.push(port);
}
for connection in midi_from.iter() {
@ -51,14 +47,14 @@ impl Arranger {
let number = split.next().unwrap().trim();
if let Ok(track) = number.parse::<usize>() {
if track < 1 {
panic!("Tracks are zero-indexed")
panic!("Tracks start from 1")
}
if track > count {
panic!("Tried to connect track {track} or {count}. Pass -t {track} to increase number of tracks.")
}
if let Some(port) = split.next() {
if let Some(port) = jack.read().unwrap().client().port_by_name(port).as_ref() {
jack.read().unwrap().client().connect_ports(port, &self.tracks[track-1].player.midi_ins[0])?;
//jack.read().unwrap().client().connect_ports(port, &self.tracks[track-1].player.midi_ins[0])?;
} else {
panic!("Missing MIDI output: {port}. Use jack_lsp to list all port names.");
}
@ -74,14 +70,14 @@ impl Arranger {
let number = split.next().unwrap().trim();
if let Ok(track) = number.parse::<usize>() {
if track < 1 {
panic!("Tracks are zero-indexed")
panic!("Tracks start from 1")
}
if track > count {
panic!("Tried to connect track {track} or {count}. Pass -t {track} to increase number of tracks.")
}
if let Some(port) = split.next() {
if let Some(port) = jack.read().unwrap().client().port_by_name(port).as_ref() {
jack.read().unwrap().client().connect_ports(&self.tracks[track-1].player.midi_outs[0], port)?;
//jack.read().unwrap().client().connect_ports(&self.tracks[track-1].player.midi_outs[0], port)?;
} else {
panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names.");
}

View file

@ -38,7 +38,7 @@ audio!(|self: Groovebox, client, scope|{
return Control::Quit
}
// TODO move these to editor and sampler:
for RawMidi { time, bytes } in self.player.midi_ins[0].iter(scope) {
for RawMidi { time, bytes } in self.player.midi_ins[0].port.iter(scope) {
if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() {
match message {
MidiMessage::NoteOn { ref key, .. } => {

View file

@ -2,12 +2,10 @@ mod sample; pub use self::sample::*;
mod sample_import; pub use self::sample_import::*;
mod sample_list; pub use self::sample_list::*;
mod sample_viewer; pub use self::sample_viewer::*;
mod sampler_audio; pub use self::sampler_audio::*;
mod sampler_command; pub use self::sampler_command::*;
mod sampler_status; pub use self::sampler_status::*;
mod sampler_tui; pub use self::sampler_tui::*;
mod voice; pub use self::voice::*;
use crate::*;
use KeyCode::Char;
use std::fs::File;
@ -23,7 +21,6 @@ use symphonia::{
default::get_codecs,
};
/// The sampler plugin plays sounds.
#[derive(Debug)]
pub struct Sampler {
@ -33,10 +30,10 @@ pub struct Sampler {
pub recording: Option<(usize, Arc<RwLock<Sample>>)>,
pub unmapped: Vec<Arc<RwLock<Sample>>>,
pub voices: Arc<RwLock<Vec<Voice>>>,
pub midi_in: Port<MidiIn>,
pub audio_ins: Vec<Port<AudioIn>>,
pub midi_in: JackPort<MidiIn>,
pub audio_ins: Vec<JackPort<AudioIn>>,
pub input_meter: Vec<f32>,
pub audio_outs: Vec<Port<AudioOut>>,
pub audio_outs: Vec<JackPort<AudioOut>>,
pub buffer: Vec<Vec<f32>>,
pub output_gain: f32
}
@ -44,21 +41,21 @@ impl Sampler {
pub fn new (
jack: &Arc<RwLock<JackConnection>>,
name: impl AsRef<str>,
midi_from: &[impl AsRef<str>],
audio_from: &[&[impl AsRef<str>];2],
audio_to: &[&[impl AsRef<str>];2],
midi_from: &[PortConnection],
audio_from: &[&[PortConnection];2],
audio_to: &[&[PortConnection];2],
) -> Usually<Self> {
let name = name.as_ref();
Ok(Self {
midi_in: jack.midi_in(&format!("M/{name}"), midi_from)?,
midi_in: JackPort::<MidiIn>::new(jack, format!("M/{name}"), midi_from)?,
audio_ins: vec![
jack.audio_in(&format!("L/{name}"), audio_from[0])?,
jack.audio_in(&format!("R/{name}"), audio_from[1])?
JackPort::<AudioIn>::new(jack, &format!("L/{name}"), audio_from[0])?,
JackPort::<AudioIn>::new(jack, &format!("R/{name}"), audio_from[1])?,
],
input_meter: vec![0.0;2],
audio_outs: vec![
jack.audio_out(&format!("{name}/L"), audio_to[0])?,
jack.audio_out(&format!("{name}/R"), audio_to[1])?,
JackPort::<AudioOut>::new(jack, &format!("{name}/L"), audio_to[0])?,
JackPort::<AudioOut>::new(jack, &format!("{name}/R"), audio_to[1])?,
],
jack: jack.clone(),
name: name.into(),
@ -91,6 +88,120 @@ impl Sampler {
}
}
audio!(|self: SamplerTui, client, scope|{
SamplerAudio(&mut self.state).process(client, scope)
});
pub struct SamplerAudio<'a>(pub &'a mut Sampler);
audio!(|self: SamplerAudio<'a>, _client, scope|{
self.0.process_midi_in(scope);
self.0.clear_output_buffer();
self.0.process_audio_out(scope);
self.0.write_output_buffer(scope);
self.0.process_audio_in(scope);
Control::Continue
});
impl Sampler {
pub fn process_audio_in (&mut self, scope: &ProcessScope) {
let Sampler { audio_ins, input_meter, recording, .. } = self;
if audio_ins.len() != input_meter.len() {
*input_meter = vec![0.0;audio_ins.len()];
}
if let Some((_, sample)) = recording {
let mut sample = sample.write().unwrap();
if sample.channels.len() != audio_ins.len() {
panic!("channel count mismatch");
}
let iterator = audio_ins.iter().zip(input_meter).zip(sample.channels.iter_mut());
let mut length = 0;
for ((input, meter), channel) in iterator {
let slice = input.port.as_slice(scope);
length = length.max(slice.len());
let total: f32 = slice.iter().map(|x|x.abs()).sum();
let count = slice.len() as f32;
*meter = 10. * (total / count).log10();
channel.extend_from_slice(slice);
}
sample.end += length;
} else {
for (input, meter) in audio_ins.iter().zip(input_meter) {
let slice = input.port.as_slice(scope);
let total: f32 = slice.iter().map(|x|x.abs()).sum();
let count = slice.len() as f32;
*meter = 10. * (total / count).log10();
}
}
}
/// 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.port.iter(scope) {
if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() {
match message {
MidiMessage::NoteOn { ref key, ref vel } => {
if let Some(ref sample) = mapped[key.as_int() as usize] {
voices.write().unwrap().push(Sample::play(sample, time as usize, vel));
}
},
MidiMessage::Controller { controller, value } => {
// TODO
}
_ => {}
}
}
}
}
/// 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.port.as_mut_slice(scope).iter_mut().enumerate() {
*value = *buffer.get(i).unwrap_or(&0.0);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
type MidiSample = (Option<u7>, Arc<RwLock<crate::Sample>>);
from_edn!("sampler" => |jack: &Arc<RwLock<JackConnection>>, args| -> crate::Sampler {
let mut name = String::new();
let mut dir = String::new();
@ -120,8 +231,6 @@ from_edn!("sampler" => |jack: &Arc<RwLock<JackConnection>>, args| -> crate::Samp
Self::new(jack, &name)
});
type MidiSample = (Option<u7>, Arc<RwLock<crate::Sample>>);
from_edn!("sample" => |(_jack, dir): (&Arc<RwLock<JackConnection>>, &str), args| -> MidiSample {
let mut name = String::new();
let mut file = String::new();

View file

@ -1,111 +0,0 @@
use crate::*;
audio!(|self: SamplerTui, client, scope|{
SamplerAudio(&mut self.state).process(client, scope)
});
pub struct SamplerAudio<'a>(pub &'a mut Sampler);
audio!(|self: SamplerAudio<'a>, _client, scope|{
self.0.process_midi_in(scope);
self.0.clear_output_buffer();
self.0.process_audio_out(scope);
self.0.write_output_buffer(scope);
self.0.process_audio_in(scope);
Control::Continue
});
impl Sampler {
pub fn process_audio_in (&mut self, scope: &ProcessScope) {
let Sampler { audio_ins, input_meter, recording, .. } = self;
if audio_ins.len() != input_meter.len() {
*input_meter = vec![0.0;audio_ins.len()];
}
if let Some((_, sample)) = recording {
let mut sample = sample.write().unwrap();
if sample.channels.len() != audio_ins.len() {
panic!("channel count mismatch");
}
let iterator = audio_ins.iter().zip(input_meter).zip(sample.channels.iter_mut());
let mut length = 0;
for ((input, meter), channel) in iterator {
let slice = input.as_slice(scope);
length = length.max(slice.len());
let total: f32 = slice.iter().map(|x|x.abs()).sum();
let count = slice.len() as f32;
*meter = 10. * (total / count).log10();
channel.extend_from_slice(slice);
}
sample.end += length;
} else {
for (input, meter) in audio_ins.iter().zip(input_meter) {
let slice = input.as_slice(scope);
let total: f32 = slice.iter().map(|x|x.abs()).sum();
let count = slice.len() as f32;
*meter = 10. * (total / count).log10();
}
}
}
/// 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, .. } = LiveEvent::parse(bytes).unwrap() {
match message {
MidiMessage::NoteOn { ref key, ref vel } => {
if let Some(ref sample) = mapped[key.as_int() as usize] {
voices.write().unwrap().push(Sample::play(sample, time as usize, vel));
}
},
MidiMessage::Controller { controller, value } => {
// TODO
}
_ => {}
}
}
}
}
/// 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);
}
}
}
}