basic midi import example

This commit is contained in:
🪞👃🪞 2024-11-24 03:47:03 +01:00
parent 35944cf684
commit f5b1f495ad
8 changed files with 114 additions and 66 deletions

View file

@ -0,0 +1,18 @@
use tek_api::*;
struct ExamplePhrases(Vec<Arc<RwLock<Phrase>>>);
impl HasPhrases for ExamplePhrases {
fn phrases (&self) -> &Vec<Arc<RwLock<Phrase>>> {
&self.0
}
fn phrases_mut (&mut self) -> &mut Vec<Arc<RwLock<Phrase>>> {
&mut self.0
}
}
fn main () -> Usually<()> {
let mut phrases = ExamplePhrases(vec![]);
PhrasePoolCommand::Import(0, String::from("./example.mid")).execute(&mut phrases)?;
Ok(())
}

View file

@ -1,4 +1,5 @@
use crate::*;
use tek_core::midly::Smf;
pub trait HasPhrases {
fn phrases (&self) -> &Vec<Arc<RwLock<Phrase>>>;
@ -10,11 +11,11 @@ pub enum PhrasePoolCommand {
Add(usize, Phrase),
Delete(usize),
Swap(usize, usize),
Color(usize, ItemColor),
Import(usize, String),
Export(usize, String),
SetName(usize, String),
SetLength(usize, usize),
SetColor(usize, ItemColor),
}
impl<T: HasPhrases> Command<T> for PhrasePoolCommand {
@ -39,12 +40,26 @@ impl<T: HasPhrases> Command<T> for PhrasePoolCommand {
model.phrases_mut().swap(index, other);
return Ok(Some(Self::Swap(index, other)))
},
Self::Color(index, color) => {
let mut color = ItemColorTriplet::from(color);
std::mem::swap(&mut color, &mut model.phrases()[index].write().unwrap().color);
return Ok(Some(Self::Color(index, color.base)))
},
Self::Import(index, path) => {
let bytes = std::fs::read(&path)?;
let smf = Smf::parse(bytes.as_slice())?;
println!("{:?}", &smf.header);
let mut t = 0u32;
let mut events = vec![];
for (i, track) in smf.tracks.iter().enumerate() {
for (j, event) in track.iter().enumerate() {
t += event.delta.as_int();
if let TrackEventKind::Midi { channel, message } = event.kind {
events.push((t, channel.as_int(), message));
}
}
}
let mut phrase = Phrase::new(&path, true, t as usize + 1, None, None);
for event in events.iter() {
println!("{event:?}");
phrase.notes[event.0 as usize].push(event.2);
}
return Self::Add(index, phrase).execute(model)
},
Self::Export(index, path) => {
},
@ -52,6 +67,11 @@ impl<T: HasPhrases> Command<T> for PhrasePoolCommand {
},
Self::SetLength(index, length) => {
},
Self::SetColor(index, color) => {
let mut color = ItemColorTriplet::from(color);
std::mem::swap(&mut color, &mut model.phrases()[index].write().unwrap().color);
return Ok(Some(Self::SetColor(index, color.base)))
},
}
Ok(None)
}

View file

@ -11,9 +11,9 @@ pub trait HasPhrase: ClockApi {
fn reset (&self) -> bool;
fn reset_mut (&mut self) -> &mut bool;
fn phrase (&self)
fn play_phrase (&self)
-> &Option<(Instant, Option<Arc<RwLock<Phrase>>>)>;
fn phrase_mut (&mut self)
fn play_phrase_mut (&mut self)
-> &mut Option<(Instant, Option<Arc<RwLock<Phrase>>>)>;
fn next_phrase (&self)
@ -29,7 +29,7 @@ pub trait HasPhrase: ClockApi {
*self.reset_mut() = true;
}
fn pulses_since_start (&self) -> Option<f64> {
if let Some((started, Some(_))) = self.phrase().as_ref() {
if let Some((started, Some(_))) = self.play_phrase().as_ref() {
Some(self.current().pulse.get() - started.pulse.get())
} else {
None
@ -71,7 +71,7 @@ pub trait MidiInputApi: ClockApi + HasPhrase {
let sample0 = scope.last_frame_time() as usize;
// For highlighting keys and note repeat
let notes_in = self.notes_in().clone();
if let (true, Some((started, ref phrase))) = (self.is_rolling(), self.phrase().clone()) {
if let (true, Some((started, ref phrase))) = (self.is_rolling(), self.play_phrase().clone()) {
let start = started.sample.get() as usize;
let quant = self.quant().get();
let timebase = self.timebase().clone();
@ -166,8 +166,8 @@ pub trait MidiOutputApi: ClockApi + HasPhrase {
let sample0 = scope.last_frame_time() as usize;
let samples = scope.n_frames() as usize;
// If no phrase is playing, prepare for switchover immediately
next = self.phrase().is_none();
let phrase = self.phrase();
next = self.play_phrase().is_none();
let phrase = self.play_phrase();
let started0 = self.transport_offset();
let timebase = self.timebase();
let notes_out = self.notes_out();
@ -241,7 +241,7 @@ pub trait MidiOutputApi: ClockApi + HasPhrase {
let skipped = sample0 - start;
// Switch over to enqueued phrase
let started = Instant::from_sample(&self.timebase(), start as f64);
*self.phrase_mut() = Some((started, phrase.clone()));
*self.play_phrase_mut() = Some((started, phrase.clone()));
// Unset enqueuement (TODO: where to implement looping?)
*self.next_phrase_mut() = None
}

View file

@ -78,7 +78,7 @@ pub trait ArrangerSceneApi: Sized {
Some(clip) => tracks
.get(track_index)
.map(|track|{
if let Some((_, Some(phrase))) = track.phrase() {
if let Some((_, Some(phrase))) = track.play_phrase() {
*phrase.read().unwrap() == *clip.read().unwrap()
} else {
false