control sample start/end with cc20/21

This commit is contained in:
🪞👃🪞 2024-12-28 20:00:58 +01:00
parent 1859f378ea
commit b992843e1c
5 changed files with 93 additions and 15 deletions

View file

@ -32,17 +32,18 @@ impl GrooveboxCli {
fn run (&self) -> Usually<()> {
Tui::run(JackClient::new("tek_groovebox")?.activate_with(|jack|{
let app = tek::GrooveboxTui::try_from(jack)?;
let jack = jack.read().unwrap();
jack.read().unwrap().client().connect_ports(&app.player.midi_outs[0], &app.sampler.midi_in)?;
jack.client().connect_ports(&app.player.midi_outs[0], &app.sampler.midi_in)?;
jack.connect_midi_from(&app.player.midi_ins[0], &self.midi_from)?;
jack.connect_midi_from(&app.sampler.midi_in, &self.midi_from)?;
connect_from(&jack, &app.player.midi_ins[0], &self.midi_from)?;
connect_to(&jack, &app.player.midi_outs[0], &self.midi_to)?;
jack.connect_midi_to(&app.player.midi_outs[0], &self.midi_to)?;
connect_audio_from(&jack, &app.sampler.audio_ins[0], &self.l_from)?;
connect_audio_from(&jack, &app.sampler.audio_ins[1], &self.r_from)?;
connect_audio_to(&jack, &app.sampler.audio_outs[0], &self.l_to)?;
connect_audio_to(&jack, &app.sampler.audio_outs[1], &self.r_to)?;
jack.connect_audio_from(&app.sampler.audio_ins[0], &self.l_from)?;
jack.connect_audio_from(&app.sampler.audio_ins[1], &self.r_from)?;
jack.connect_audio_to(&app.sampler.audio_outs[0], &self.l_to)?;
jack.connect_audio_to(&app.sampler.audio_outs[1], &self.r_to)?;
Ok(app)
})?)?;

View file

@ -52,6 +52,30 @@ audio!(|self: GrooveboxTui, client, scope|{
if Control::Quit == SamplerAudio(&mut self.sampler).process(client, scope) {
return Control::Quit
}
for RawMidi { time, bytes } in self.sampler.midi_in.iter(scope) {
if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() {
match message {
MidiMessage::Controller { controller, value } => {
if controller == u7::from(20) {
if let Some(sample) = &self.sampler.mapped[self.editor.note_point()] {
let mut sample = sample.write().unwrap();
let percentage = value.as_int() as f64 / 127.;
sample.start = (percentage * sample.end as f64) as usize;
}
} else if controller == u7::from(21) {
if let Some(sample) = &self.sampler.mapped[self.editor.note_point()] {
let mut sample = sample.write().unwrap();
let percentage = value.as_int() as f64 / 127.;
let length = sample.channels[0].len();
sample.end = sample.start + (percentage * (length as f64 - sample.start as f64)) as usize;
sample.end = sample.end.min(length);
}
}
}
_ => {}
}
}
}
self.perf.update(t0, scope);
Control::Continue
});

View file

@ -22,6 +22,7 @@ pub(crate) use self::jack_event::*;
pub mod ports;
pub(crate) use self::ports::*;
pub use self::ports::RegisterPort;
/// Implement [TryFrom<&Arc<RwLock<JackClient>>>]: create app state from wrapped JACK handle.
#[macro_export] macro_rules! from_jack {

View file

@ -5,9 +5,13 @@ pub trait RegisterPort {
fn midi_out (&self, name: &str) -> Usually<Port<MidiOut>>;
fn audio_in (&self, name: &str) -> Usually<Port<AudioIn>>;
fn audio_out (&self, name: &str) -> Usually<Port<AudioOut>>;
fn connect_midi_from (&self, my_input: &Port<MidiIn>, ports: &[String]) -> Usually<()>;
fn connect_midi_to (&self, my_output: &Port<MidiOut>, ports: &[String]) -> Usually<()>;
fn connect_audio_from (&self, my_input: &Port<AudioIn>, ports: &[String]) -> Usually<()>;
fn connect_audio_to (&self, my_output: &Port<AudioOut>, ports: &[String]) -> Usually<()>;
}
impl RegisterPort for &Arc<RwLock<JackClient>> {
impl RegisterPort for Arc<RwLock<JackClient>> {
fn midi_in (&self, name: &str) -> Usually<Port<MidiIn>> {
Ok(self.read().unwrap().client().register_port(name, MidiIn::default())?)
}
@ -20,6 +24,50 @@ impl RegisterPort for &Arc<RwLock<JackClient>> {
fn audio_in (&self, name: &str) -> Usually<Port<AudioIn>> {
Ok(self.read().unwrap().client().register_port(name, AudioIn::default())?)
}
fn connect_midi_from (&self, input: &Port<MidiIn>, ports: &[String]) -> Usually<()> {
let jack = self.read().unwrap();
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_midi_to (&self, output: &Port<MidiOut>, ports: &[String]) -> Usually<()> {
let jack = self.read().unwrap();
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(())
}
fn connect_audio_from (&self, input: &Port<AudioIn>, ports: &[String]) -> Usually<()> {
let jack = self.read().unwrap();
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_audio_to (&self, output: &Port<AudioOut>, ports: &[String]) -> Usually<()> {
let jack = self.read().unwrap();
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(())
}
}
/// Trait for things that may expose JACK ports.

View file

@ -67,7 +67,7 @@ impl Sampler {
unmapped: vec![],
voices: Arc::new(RwLock::new(vec![])),
buffer: vec![vec![0.0;16384];2],
output_gain: 0.5,
output_gain: 1.,
recording: None,
})
}
@ -97,6 +97,7 @@ pub enum SamplerCommand {
RecordCancel,
RecordFinish,
SetSample(u7, Option<Arc<RwLock<Sample>>>),
SetStart(u7, usize),
SetGain(f32),
NoteOn(u7, u7),
NoteOff(u7),
@ -176,11 +177,14 @@ impl Sampler {
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(ref sample) = mapped[key.as_int() as usize] {
voices.write().unwrap().push(Sample::play(sample, time as usize, vel));
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));
}
},
_ => {}
}
}
}