mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 04:06:45 +01:00
fix some lints, add FromEdn trait
This commit is contained in:
parent
7962bdf86b
commit
e96faeb6d3
14 changed files with 126 additions and 124 deletions
|
|
@ -50,11 +50,11 @@ impl Sampler {
|
||||||
pub fn process_midi_in (&mut self, scope: &ProcessScope) {
|
pub fn process_midi_in (&mut self, scope: &ProcessScope) {
|
||||||
let Sampler { midi_in, mapped, voices, .. } = self;
|
let Sampler { midi_in, mapped, voices, .. } = self;
|
||||||
for RawMidi { time, bytes } in midi_in.iter(scope) {
|
for RawMidi { time, bytes } in midi_in.iter(scope) {
|
||||||
if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() {
|
if let LiveEvent::Midi {
|
||||||
if let MidiMessage::NoteOn { ref key, ref vel } = message {
|
message: MidiMessage::NoteOn { ref key, ref vel }, ..
|
||||||
if let Some(sample) = mapped.get(key) {
|
} = LiveEvent::parse(bytes).unwrap() {
|
||||||
voices.write().unwrap().push(Sample::play(sample, time as usize, vel));
|
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 false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,13 +135,13 @@ impl Iterator for Voice {
|
||||||
type Item = [f32;2];
|
type Item = [f32;2];
|
||||||
fn next (&mut self) -> Option<Self::Item> {
|
fn next (&mut self) -> Option<Self::Item> {
|
||||||
if self.after > 0 {
|
if self.after > 0 {
|
||||||
self.after = self.after - 1;
|
self.after -= 1;
|
||||||
return Some([0.0, 0.0])
|
return Some([0.0, 0.0])
|
||||||
}
|
}
|
||||||
let sample = self.sample.read().unwrap();
|
let sample = self.sample.read().unwrap();
|
||||||
if self.position < sample.end {
|
if self.position < sample.end {
|
||||||
let position = self.position;
|
let position = self.position;
|
||||||
self.position = self.position + 1;
|
self.position += 1;
|
||||||
return sample.channels[0].get(position).map(|_amplitude|[
|
return sample.channels[0].get(position).map(|_amplitude|[
|
||||||
sample.channels[0][position] * self.velocity,
|
sample.channels[0][position] * self.velocity,
|
||||||
sample.channels[0][position] * self.velocity,
|
sample.channels[0][position] * self.velocity,
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ impl ArrangerCli {
|
||||||
let mut app = ArrangerTui::try_from(jack)?;
|
let mut app = ArrangerTui::try_from(jack)?;
|
||||||
let jack = jack.read().unwrap();
|
let jack = jack.read().unwrap();
|
||||||
app.color = ItemPalette::random();
|
app.color = ItemPalette::random();
|
||||||
add_tracks(&jack, &mut app, &self)?;
|
add_tracks(&jack, &mut app, self)?;
|
||||||
add_scenes(&mut app, self.scenes)?;
|
add_scenes(&mut app, self.scenes)?;
|
||||||
Ok(app)
|
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.")
|
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) = split.next() {
|
||||||
if let Some(port) = jack.port_by_name(&port) {
|
if let Some(port) = jack.port_by_name(port).as_ref() {
|
||||||
jack.client().connect_ports(&port, &app.tracks[track-1].player.midi_ins[0])?;
|
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])?;
|
|
||||||
} else {
|
} else {
|
||||||
panic!("Missing MIDI output: {port}. Use jack_lsp to list all port names.");
|
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.")
|
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) = split.next() {
|
||||||
if let Some(port) = jack.port_by_name(&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)?;
|
jack.client().connect_ports(&app.tracks[track-1].player.midi_outs[0], port)?;
|
||||||
} else {
|
} else {
|
||||||
panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names.");
|
panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names.");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,15 +24,11 @@ pub struct SequencerCli {
|
||||||
/// MIDI ins to connect to (multiple instances accepted)
|
/// MIDI ins to connect to (multiple instances accepted)
|
||||||
#[arg(short='o', long)]
|
#[arg(short='o', long)]
|
||||||
midi_to: Vec<String>,
|
midi_to: Vec<String>,
|
||||||
|
|
||||||
/// Default phrase duration (in pulses; default: 4 * PPQ = 1 bar)
|
|
||||||
#[arg(short, long)]
|
|
||||||
length: Option<usize>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SequencerCli {
|
impl SequencerCli {
|
||||||
fn run (&self) -> Usually<()> {
|
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|{
|
Tui::run(JackClient::new(name)?.activate_with(|jack|{
|
||||||
let mut app = SequencerTui::try_from(jack)?;
|
let mut app = SequencerTui::try_from(jack)?;
|
||||||
let jack = jack.read().unwrap();
|
let jack = jack.read().unwrap();
|
||||||
|
|
@ -42,11 +38,6 @@ impl SequencerCli {
|
||||||
connect_to(&jack, &midi_out, &self.midi_to)?;
|
connect_to(&jack, &midi_out, &self.midi_to)?;
|
||||||
app.player.midi_ins.push(midi_in);
|
app.player.midi_ins.push(midi_in);
|
||||||
app.player.midi_outs.push(midi_out);
|
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(app)
|
||||||
})?)?;
|
})?)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -55,8 +46,8 @@ impl SequencerCli {
|
||||||
|
|
||||||
fn connect_from (jack: &JackClient, input: &Port<MidiIn>, ports: &[String]) -> Usually<()> {
|
fn connect_from (jack: &JackClient, input: &Port<MidiIn>, ports: &[String]) -> Usually<()> {
|
||||||
for port in ports.iter() {
|
for port in ports.iter() {
|
||||||
if let Some(port) = jack.port_by_name(&port) {
|
if let Some(port) = jack.port_by_name(port).as_ref() {
|
||||||
jack.client().connect_ports(&port, &input)?;
|
jack.client().connect_ports(port, input)?;
|
||||||
} else {
|
} else {
|
||||||
panic!("Missing MIDI output: {port}. Use jack_lsp to list all port names.");
|
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<()> {
|
fn connect_to (jack: &JackClient, output: &Port<MidiOut>, ports: &[String]) -> Usually<()> {
|
||||||
for port in ports.iter() {
|
for port in ports.iter() {
|
||||||
if let Some(port) = jack.port_by_name(&port) {
|
if let Some(port) = jack.port_by_name(port).as_ref() {
|
||||||
jack.client().connect_ports(&output, &port)?;
|
jack.client().connect_ports(output, port)?;
|
||||||
} else {
|
} else {
|
||||||
panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names.");
|
panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names.");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,23 @@ use crate::*;
|
||||||
pub use clojure_reader::edn::Edn;
|
pub use clojure_reader::edn::Edn;
|
||||||
//pub use clojure_reader::{edn::{read, Edn}, error::Error as EdnError};
|
//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.
|
/// EDN parsing helper.
|
||||||
#[macro_export] macro_rules! edn {
|
#[macro_export] macro_rules! edn {
|
||||||
($edn:ident { $($pat:pat => $expr:expr),* $(,)? }) => {
|
($edn:ident { $($pat:pat => $expr:expr),* $(,)? }) => {
|
||||||
|
|
@ -16,82 +33,79 @@ pub use clojure_reader::edn::Edn;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::Sampler {
|
from_edn!(|jack = &Arc<RwLock<JackClient>>, "sampler", args| -> crate::Sampler {
|
||||||
pub fn from_edn <'e> (jack: &Arc<RwLock<JackClient>>, args: &[Edn<'e>]) -> Usually<Self> {
|
let mut name = String::new();
|
||||||
let mut name = String::new();
|
let mut dir = String::new();
|
||||||
let mut dir = String::new();
|
let mut samples = BTreeMap::new();
|
||||||
let mut samples = BTreeMap::new();
|
edn!(edn in args {
|
||||||
edn!(edn in args {
|
Edn::Map(map) => {
|
||||||
Edn::Map(map) => {
|
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
|
||||||
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
|
name = String::from(*n);
|
||||||
name = String::from(*n);
|
}
|
||||||
}
|
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":dir")) {
|
||||||
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":dir")) {
|
dir = String::from(*n);
|
||||||
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) {
|
_ => panic!("unexpected in sampler {name}: {args:?}")
|
||||||
Some(Edn::Symbol("sample")) => {
|
},
|
||||||
let (midi, sample) = Sample::from_edn(jack, &dir, &args[1..])?;
|
_ => panic!("unexpected in sampler {name}: {edn:?}")
|
||||||
if let Some(midi) = midi {
|
});
|
||||||
samples.insert(midi, sample);
|
let midi_in = jack.read().unwrap().client().register_port("in", MidiIn::default())?;
|
||||||
} else {
|
Ok(Self {
|
||||||
panic!("sample without midi binding: {}", sample.read().unwrap().name);
|
jack: jack.clone(),
|
||||||
}
|
name: name.into(),
|
||||||
},
|
mapped: samples,
|
||||||
_ => panic!("unexpected in sampler {name}: {args:?}")
|
unmapped: Default::default(),
|
||||||
},
|
voices: Default::default(),
|
||||||
_ => panic!("unexpected in sampler {name}: {edn:?}")
|
buffer: Default::default(),
|
||||||
});
|
audio_outs: vec![],
|
||||||
let midi_in = jack.read().unwrap().client().register_port("in", MidiIn::default())?;
|
output_gain: 0.,
|
||||||
Ok(Sampler {
|
midi_in,
|
||||||
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 {
|
type MidiSample = (Option<u7>, Arc<RwLock<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
|
|
||||||
}))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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 {
|
//impl ArrangerScene {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ pub struct PlayerAudio<'a, T: MidiPlayerApi>(
|
||||||
);
|
);
|
||||||
|
|
||||||
/// JACK process callback for a sequencer's phrase player/recorder.
|
/// 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 {
|
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||||
let model = &mut self.0;
|
let model = &mut self.0;
|
||||||
let note_buf = &mut self.1;
|
let note_buf = &mut self.1;
|
||||||
|
|
@ -217,7 +217,7 @@ impl MidiRecordApi for MidiPlayer {
|
||||||
|
|
||||||
impl MidiPlaybackApi for MidiPlayer {
|
impl MidiPlaybackApi for MidiPlayer {
|
||||||
fn notes_out (&self) -> &Arc<RwLock<[bool; 128]>> {
|
fn notes_out (&self) -> &Arc<RwLock<[bool; 128]>> {
|
||||||
&self.notes_in
|
&self.notes_out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,13 +76,12 @@ impl MidiClip {
|
||||||
}
|
}
|
||||||
/// Check if a range `start..end` contains MIDI Note On `k`
|
/// Check if a range `start..end` contains MIDI Note On `k`
|
||||||
pub fn contains_note_on (&self, k: u7, start: usize, end: usize) -> bool {
|
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 events in self.notes[start.max(0)..end.min(self.notes.len())].iter() {
|
||||||
for event in events.iter() {
|
for event in events.iter() {
|
||||||
if let MidiMessage::NoteOn {key,..} = event { if *key == k { return true } }
|
if let MidiMessage::NoteOn {key,..} = event { if *key == k { return true } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,6 @@ pub trait HasMidiIns {
|
||||||
fn midi_ins (&self) -> &Vec<Port<MidiIn>>;
|
fn midi_ins (&self) -> &Vec<Port<MidiIn>>;
|
||||||
fn midi_ins_mut (&mut self) -> &mut Vec<Port<MidiIn>>;
|
fn midi_ins_mut (&mut self) -> &mut Vec<Port<MidiIn>>;
|
||||||
fn has_midi_ins (&self) -> bool {
|
fn has_midi_ins (&self) -> bool {
|
||||||
self.midi_ins().len() > 0
|
!self.midi_ins().is_empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,8 @@ pub trait HasPlayPhrase: HasClock {
|
||||||
}
|
}
|
||||||
fn enqueue_next (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
fn enqueue_next (&mut self, phrase: Option<&Arc<RwLock<MidiClip>>>) {
|
||||||
let start = self.clock().next_launch_pulse() as f64;
|
let start = self.clock().next_launch_pulse() as f64;
|
||||||
let instant = Moment::from_pulse(&self.clock().timebase(), start);
|
let instant = Moment::from_pulse(self.clock().timebase(), start);
|
||||||
let phrase = phrase.map(|p|p.clone());
|
*self.next_phrase_mut() = Some((instant, phrase.cloned()));
|
||||||
*self.next_phrase_mut() = Some((instant, phrase));
|
|
||||||
*self.reset_mut() = true;
|
*self.reset_mut() = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ pub trait HasMidiOuts {
|
||||||
fn midi_outs (&self) -> &Vec<Port<MidiOut>>;
|
fn midi_outs (&self) -> &Vec<Port<MidiOut>>;
|
||||||
fn midi_outs_mut (&mut self) -> &mut Vec<Port<MidiOut>>;
|
fn midi_outs_mut (&mut self) -> &mut Vec<Port<MidiOut>>;
|
||||||
fn has_midi_outs (&self) -> bool {
|
fn has_midi_outs (&self) -> bool {
|
||||||
self.midi_outs().len() > 0
|
!self.midi_outs().is_empty()
|
||||||
}
|
}
|
||||||
/// Buffer for serializing a MIDI event. FIXME rename
|
/// Buffer for serializing a MIDI event. FIXME rename
|
||||||
fn midi_note (&mut self) -> &mut Vec<u8>;
|
fn midi_note (&mut self) -> &mut Vec<u8>;
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ pub trait MidiPlaybackApi: HasPlayPhrase + HasClock + HasMidiOuts {
|
||||||
// Samples elapsed since phrase was supposed to start
|
// Samples elapsed since phrase was supposed to start
|
||||||
let skipped = sample0 - start;
|
let skipped = sample0 - start;
|
||||||
// Switch over to enqueued phrase
|
// 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
|
// Launch enqueued phrase
|
||||||
*self.play_phrase_mut() = Some((started, phrase.clone()));
|
*self.play_phrase_mut() = Some((started, phrase.clone()));
|
||||||
// Unset enqueuement (TODO: where to implement looping?)
|
// Unset enqueuement (TODO: where to implement looping?)
|
||||||
|
|
@ -123,7 +123,7 @@ pub trait MidiPlaybackApi: HasPlayPhrase + HasClock + HasMidiOuts {
|
||||||
// Append serialized message to output buffer.
|
// Append serialized message to output buffer.
|
||||||
out[sample].push(note_buf.clone());
|
out[sample].push(note_buf.clone());
|
||||||
// Update the list of currently held notes.
|
// 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.
|
/// 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>>]) {
|
fn write_port (writer: &mut MidiWriter, samples: usize, out: &[Vec<Vec<u8>>]) {
|
||||||
for time in 0..samples {
|
for (time, events) in out.iter().enumerate().take(samples) {
|
||||||
for event in out[time].iter() {
|
for bytes in events.iter() {
|
||||||
writer.write(&RawMidi { time: time as u32, bytes: &event })
|
writer.write(&RawMidi { time: time as u32, bytes }).unwrap_or_else(|_|{
|
||||||
.expect(&format!("{event:?}"));
|
panic!("Failed to write MIDI data: {bytes:?}");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,11 @@ pub trait MidiRecordApi: HasClock + HasPlayPhrase + HasMidiIns {
|
||||||
let sample = (sample0 + sample - start) as f64;
|
let sample = (sample0 + sample - start) as f64;
|
||||||
let pulse = timebase.samples_to_pulse(sample);
|
let pulse = timebase.samples_to_pulse(sample);
|
||||||
let quantized = (pulse / quant).round() * quant;
|
let quantized = (pulse / quant).round() * quant;
|
||||||
let looped = quantized as usize % length;
|
quantized as usize % length
|
||||||
looped
|
|
||||||
}, message);
|
}, 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)) {
|
for (sample, event, bytes) in parse_midi_input(input.iter(scope)) {
|
||||||
if let LiveEvent::Midi { message, .. } = event {
|
if let LiveEvent::Midi { message, .. } = event {
|
||||||
midi_buf[sample].push(bytes.to_vec());
|
midi_buf[sample].push(bytes.to_vec());
|
||||||
update_keys(&mut*notes_in.write().unwrap(), &message);
|
update_keys(&mut notes_in.write().unwrap(), &message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ pub trait Coordinate: Send + Sync + Copy
|
||||||
0.into()
|
0.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn ZERO () -> Self {
|
fn zero () -> Self {
|
||||||
0.into()
|
0.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ where
|
||||||
let mut h: E::Unit = 0.into();
|
let mut h: E::Unit = 0.into();
|
||||||
(self.0)(&mut |component: &dyn Render<E>| {
|
(self.0)(&mut |component: &dyn Render<E>| {
|
||||||
let max = to.h().minus(h);
|
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 item = E::max_y(max, E::push_y(h, component));
|
||||||
let size = item.min_size(to)?.map(|size|size.wh());
|
let size = item.min_size(to)?.map(|size|size.wh());
|
||||||
if let Some([width, height]) = size {
|
if let Some([width, height]) = size {
|
||||||
|
|
@ -98,7 +98,7 @@ where
|
||||||
let mut h: E::Unit = 0.into();
|
let mut h: E::Unit = 0.into();
|
||||||
(self.0)(&mut |component: &dyn Render<E>| {
|
(self.0)(&mut |component: &dyn Render<E>| {
|
||||||
let max = to.w().minus(w);
|
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 item = E::max_x(max, E::push_x(h, component));
|
||||||
let size = item.min_size(to)?.map(|size|size.wh());
|
let size = item.min_size(to)?.map(|size|size.wh());
|
||||||
if let Some([width, height]) = size {
|
if let Some([width, height]) = size {
|
||||||
|
|
@ -116,7 +116,7 @@ where
|
||||||
let mut h: E::Unit = 0.into();
|
let mut h: E::Unit = 0.into();
|
||||||
(self.0)(&mut |component: &dyn Render<E>| {
|
(self.0)(&mut |component: &dyn Render<E>| {
|
||||||
let max = to.h().minus(h);
|
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 item = E::max_y(to.h() - h, component);
|
||||||
let size = item.min_size(to)?.map(|size|size.wh());
|
let size = item.min_size(to)?.map(|size|size.wh());
|
||||||
if let Some([width, height]) = size {
|
if let Some([width, height]) = size {
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ render!(<Tui>|self: ArrangerVHead<'a>|Tui::push_x(self.scenes_w, row!(
|
||||||
|
|
||||||
impl<'a> ArrangerVHead<'a> {
|
impl<'a> ArrangerVHead<'a> {
|
||||||
/// name and width of track
|
/// 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();
|
let name = track.name().read().unwrap().clone();
|
||||||
Tui::bold(true, Tui::fg(track.color.lightest.rgb, name))
|
Tui::bold(true, Tui::fg(track.color.lightest.rgb, name))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue