fix some lints, add FromEdn trait

This commit is contained in:
🪞👃🪞 2024-12-27 13:18:00 +01:00
parent 7962bdf86b
commit e96faeb6d3
14 changed files with 126 additions and 124 deletions

View file

@ -50,11 +50,11 @@ 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, .. } = LiveEvent::parse(bytes).unwrap() {
if let MidiMessage::NoteOn { ref key, ref vel } = message {
if let Some(sample) = mapped.get(key) {
voices.write().unwrap().push(Sample::play(sample, time as usize, vel));
}
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));
}
}
}
@ -85,7 +85,7 @@ impl Sampler {
return false
}
}
return true
true
});
}
@ -135,13 +135,13 @@ impl Iterator for Voice {
type Item = [f32;2];
fn next (&mut self) -> Option<Self::Item> {
if self.after > 0 {
self.after = self.after - 1;
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;
self.position += 1;
return sample.channels[0].get(position).map(|_amplitude|[
sample.channels[0][position] * self.velocity,
sample.channels[0][position] * self.velocity,

View file

@ -46,7 +46,7 @@ impl ArrangerCli {
let mut app = ArrangerTui::try_from(jack)?;
let jack = jack.read().unwrap();
app.color = ItemPalette::random();
add_tracks(&jack, &mut app, &self)?;
add_tracks(&jack, &mut app, self)?;
add_scenes(&mut app, self.scenes)?;
Ok(app)
})?)?;
@ -82,9 +82,8 @@ fn add_tracks (jack: &JackClient, app: &mut ArrangerTui, cli: &ArrangerCli) -> U
panic!("Tried to connect track {track} or {n}. Pass -t {track} to increase number of tracks.")
}
if let Some(port) = split.next() {
if let Some(port) = jack.port_by_name(&port) {
jack.client().connect_ports(&port, &app.tracks[track-1].player.midi_ins[0])?;
//jack.client().connect_ports(&port, &app.tracks[track].player.midi_ins[0])?;
if let Some(port) = jack.port_by_name(port).as_ref() {
jack.client().connect_ports(port, &app.tracks[track-1].player.midi_ins[0])?;
} else {
panic!("Missing MIDI output: {port}. Use jack_lsp to list all port names.");
}
@ -106,8 +105,8 @@ fn add_tracks (jack: &JackClient, app: &mut ArrangerTui, cli: &ArrangerCli) -> U
panic!("Tried to connect track {track} or {n}. Pass -t {track} to increase number of tracks.")
}
if let Some(port) = split.next() {
if let Some(port) = jack.port_by_name(&port) {
jack.client().connect_ports(&app.tracks[track-1].player.midi_outs[0], &port)?;
if let Some(port) = jack.port_by_name(port).as_ref() {
jack.client().connect_ports(&app.tracks[track-1].player.midi_outs[0], port)?;
} else {
panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names.");
}

View file

@ -24,15 +24,11 @@ pub struct SequencerCli {
/// MIDI ins to connect to (multiple instances accepted)
#[arg(short='o', long)]
midi_to: Vec<String>,
/// Default phrase duration (in pulses; default: 4 * PPQ = 1 bar)
#[arg(short, long)]
length: Option<usize>,
}
impl SequencerCli {
fn run (&self) -> Usually<()> {
let name = self.name.as_ref().map(|n|n.as_str()).unwrap_or("tek_sequencer");
let name = self.name.as_deref().unwrap_or("tek_sequencer");
Tui::run(JackClient::new(name)?.activate_with(|jack|{
let mut app = SequencerTui::try_from(jack)?;
let jack = jack.read().unwrap();
@ -42,11 +38,6 @@ impl SequencerCli {
connect_to(&jack, &midi_out, &self.midi_to)?;
app.player.midi_ins.push(midi_in);
app.player.midi_outs.push(midi_out);
if let Some(_) = self.length {
// TODO: if let Some(phrase) = sequencer.phrase.as_mut() {
//phrase.write().unwrap().length = length;
//}
}
Ok(app)
})?)?;
Ok(())
@ -55,8 +46,8 @@ impl SequencerCli {
fn connect_from (jack: &JackClient, input: &Port<MidiIn>, ports: &[String]) -> Usually<()> {
for port in ports.iter() {
if let Some(port) = jack.port_by_name(&port) {
jack.client().connect_ports(&port, &input)?;
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.");
}
@ -66,8 +57,8 @@ fn connect_from (jack: &JackClient, input: &Port<MidiIn>, ports: &[String]) -> U
fn connect_to (jack: &JackClient, output: &Port<MidiOut>, ports: &[String]) -> Usually<()> {
for port in ports.iter() {
if let Some(port) = jack.port_by_name(&port) {
jack.client().connect_ports(&output, &port)?;
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.");
}

View file

@ -4,6 +4,23 @@ use crate::*;
pub use clojure_reader::edn::Edn;
//pub use clojure_reader::{edn::{read, Edn}, error::Error as EdnError};
pub trait FromEdn<C>: Sized {
const ID: &'static str;
fn from_edn <'e> (context: C, expr: &[Edn<'e>]) -> Usually<Self>;
}
/// Implements the [FromEdn] trait.
#[macro_export] macro_rules! from_edn {
(|$context:pat = $Context:ty, $id:expr, $args:ident| -> $T:ty $body:block) => {
impl FromEdn<$Context> for $T {
const ID: &'static str = $id;
fn from_edn <'e> ($context: $Context, $args: &[Edn<'e>]) -> Usually<Self> {
$body
}
}
}
}
/// EDN parsing helper.
#[macro_export] macro_rules! edn {
($edn:ident { $($pat:pat => $expr:expr),* $(,)? }) => {
@ -16,82 +33,79 @@ pub use clojure_reader::edn::Edn;
};
}
impl crate::Sampler {
pub fn from_edn <'e> (jack: &Arc<RwLock<JackClient>>, args: &[Edn<'e>]) -> Usually<Self> {
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);
from_edn!(|jack = &Arc<RwLock<JackClient>>, "sampler", args| -> crate::Sampler {
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) = MidiSample::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);
}
},
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:?}")
});
let midi_in = jack.read().unwrap().client().register_port("in", MidiIn::default())?;
Ok(Sampler {
jack: jack.clone(),
name: name.into(),
mapped: samples,
unmapped: Default::default(),
voices: Default::default(),
buffer: Default::default(),
audio_outs: vec![],
output_gain: 0.,
midi_in,
})
}
}
_ => panic!("unexpected in sampler {name}: {args:?}")
},
_ => panic!("unexpected in sampler {name}: {edn:?}")
});
let midi_in = jack.read().unwrap().client().register_port("in", MidiIn::default())?;
Ok(Self {
jack: jack.clone(),
name: name.into(),
mapped: samples,
unmapped: Default::default(),
voices: Default::default(),
buffer: Default::default(),
audio_outs: vec![],
output_gain: 0.,
midi_in,
})
});
impl crate::Sample {
pub fn from_edn <'e> (jack: &Arc<RwLock<JackClient>>, dir: &str, args: &[Edn<'e>]) -> Usually<(Option<u7>, Arc<RwLock<Self>>)> {
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) = Sample::read_data(&format!("{dir}/{file}"))?;
Ok((midi, Arc::new(RwLock::new(Self {
name: name.into(),
start,
end,
channels: data,
rate: None
}))))
}
}
type MidiSample = (Option<u7>, Arc<RwLock<crate::Sample>>);
from_edn!(|(jack, dir) = (&Arc<RwLock<JackClient>>, &str), "sample", args| -> MidiSample {
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) = Sample::read_data(&format!("{dir}/{file}"))?;
Ok((midi, Arc::new(RwLock::new(crate::Sample {
name,
start,
end,
channels: data,
rate: None
}))))
});
//impl ArrangerScene {

View file

@ -165,7 +165,7 @@ pub struct PlayerAudio<'a, T: MidiPlayerApi>(
);
/// JACK process callback for a sequencer's phrase player/recorder.
impl<'a, T: MidiPlayerApi> Audio for PlayerAudio<'a, T> {
impl<T: MidiPlayerApi> Audio for PlayerAudio<'_, T> {
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
let model = &mut self.0;
let note_buf = &mut self.1;
@ -217,7 +217,7 @@ impl MidiRecordApi for MidiPlayer {
impl MidiPlaybackApi for MidiPlayer {
fn notes_out (&self) -> &Arc<RwLock<[bool; 128]>> {
&self.notes_in
&self.notes_out
}
}

View file

@ -76,13 +76,12 @@ impl MidiClip {
}
/// Check if a range `start..end` contains MIDI Note On `k`
pub fn contains_note_on (&self, k: u7, start: usize, end: usize) -> bool {
//panic!("{:?} {start} {end}", &self);
for events in self.notes[start.max(0)..end.min(self.notes.len())].iter() {
for event in events.iter() {
if let MidiMessage::NoteOn {key,..} = event { if *key == k { return true } }
}
}
return false
false
}
}

View file

@ -5,6 +5,6 @@ pub trait HasMidiIns {
fn midi_ins (&self) -> &Vec<Port<MidiIn>>;
fn midi_ins_mut (&mut self) -> &mut Vec<Port<MidiIn>>;
fn has_midi_ins (&self) -> bool {
self.midi_ins().len() > 0
!self.midi_ins().is_empty()
}
}

View file

@ -27,9 +27,8 @@ pub trait HasPlayPhrase: HasClock {
}
fn enqueue_next (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
let start = self.clock().next_launch_pulse() as f64;
let instant = Moment::from_pulse(&self.clock().timebase(), start);
let phrase = phrase.map(|p|p.clone());
*self.next_phrase_mut() = Some((instant, phrase));
let instant = Moment::from_pulse(self.clock().timebase(), start);
*self.next_phrase_mut() = Some((instant, phrase.cloned()));
*self.reset_mut() = true;
}
}

View file

@ -5,7 +5,7 @@ pub trait HasMidiOuts {
fn midi_outs (&self) -> &Vec<Port<MidiOut>>;
fn midi_outs_mut (&mut self) -> &mut Vec<Port<MidiOut>>;
fn has_midi_outs (&self) -> bool {
self.midi_outs().len() > 0
!self.midi_outs().is_empty()
}
/// Buffer for serializing a MIDI event. FIXME rename
fn midi_note (&mut self) -> &mut Vec<u8>;

View file

@ -49,7 +49,7 @@ pub trait MidiPlaybackApi: HasPlayPhrase + HasClock + HasMidiOuts {
// Samples elapsed since phrase was supposed to start
let skipped = sample0 - start;
// Switch over to enqueued phrase
let started = Moment::from_sample(&self.clock().timebase(), start as f64);
let started = Moment::from_sample(self.clock().timebase(), start as f64);
// Launch enqueued phrase
*self.play_phrase_mut() = Some((started, phrase.clone()));
// Unset enqueuement (TODO: where to implement looping?)
@ -123,7 +123,7 @@ pub trait MidiPlaybackApi: HasPlayPhrase + HasClock + HasMidiOuts {
// Append serialized message to output buffer.
out[sample].push(note_buf.clone());
// Update the list of currently held notes.
update_keys(&mut*notes, &message);
update_keys(&mut*notes, message);
}
}
}
@ -138,10 +138,11 @@ pub trait MidiPlaybackApi: HasPlayPhrase + HasClock + HasMidiOuts {
/// Write a chunk of MIDI data from the output buffer to an output port.
fn write_port (writer: &mut MidiWriter, samples: usize, out: &[Vec<Vec<u8>>]) {
for time in 0..samples {
for event in out[time].iter() {
writer.write(&RawMidi { time: time as u32, bytes: &event })
.expect(&format!("{event:?}"));
for (time, events) in out.iter().enumerate().take(samples) {
for bytes in events.iter() {
writer.write(&RawMidi { time: time as u32, bytes }).unwrap_or_else(|_|{
panic!("Failed to write MIDI data: {bytes:?}");
});
}
}
}

View file

@ -33,12 +33,11 @@ pub trait MidiRecordApi: HasClock + HasPlayPhrase + HasMidiIns {
let sample = (sample0 + sample - start) as f64;
let pulse = timebase.samples_to_pulse(sample);
let quantized = (pulse / quant).round() * quant;
let looped = quantized as usize % length;
looped
quantized as usize % length
}, message);
}
}
update_keys(&mut*notes_in.write().unwrap(), &message);
update_keys(&mut notes_in.write().unwrap(), &message);
}
}
}
@ -61,7 +60,7 @@ pub trait MidiRecordApi: HasClock + HasPlayPhrase + HasMidiIns {
for (sample, event, bytes) in parse_midi_input(input.iter(scope)) {
if let LiveEvent::Midi { message, .. } = event {
midi_buf[sample].push(bytes.to_vec());
update_keys(&mut*notes_in.write().unwrap(), &message);
update_keys(&mut notes_in.write().unwrap(), &message);
}
}
}

View file

@ -19,7 +19,7 @@ pub trait Coordinate: Send + Sync + Copy
0.into()
}
}
fn ZERO () -> Self {
fn zero () -> Self {
0.into()
}
}

View file

@ -80,7 +80,7 @@ where
let mut h: E::Unit = 0.into();
(self.0)(&mut |component: &dyn Render<E>| {
let max = to.h().minus(h);
if max > E::Unit::ZERO() {
if max > E::Unit::zero() {
let item = E::max_y(max, E::push_y(h, component));
let size = item.min_size(to)?.map(|size|size.wh());
if let Some([width, height]) = size {
@ -98,7 +98,7 @@ where
let mut h: E::Unit = 0.into();
(self.0)(&mut |component: &dyn Render<E>| {
let max = to.w().minus(w);
if max > E::Unit::ZERO() {
if max > E::Unit::zero() {
let item = E::max_x(max, E::push_x(h, component));
let size = item.min_size(to)?.map(|size|size.wh());
if let Some([width, height]) = size {
@ -116,7 +116,7 @@ where
let mut h: E::Unit = 0.into();
(self.0)(&mut |component: &dyn Render<E>| {
let max = to.h().minus(h);
if max > E::Unit::ZERO() {
if max > E::Unit::zero() {
let item = E::max_y(to.h() - h, component);
let size = item.min_size(to)?.map(|size|size.wh());
if let Some([width, height]) = size {

View file

@ -37,7 +37,7 @@ render!(<Tui>|self: ArrangerVHead<'a>|Tui::push_x(self.scenes_w, row!(
impl<'a> ArrangerVHead<'a> {
/// name and width of track
fn format_name (track: &ArrangerTrack, w: usize) -> impl Render<Tui> {
fn format_name (track: &ArrangerTrack, _w: usize) -> impl Render<Tui> {
let name = track.name().read().unwrap().clone();
Tui::bold(true, Tui::fg(track.color.lightest.rgb, name))
}