From 4a8f5b267f01664677d23cf0b6e2806f3e6265db Mon Sep 17 00:00:00 2001 From: unspeaker Date: Thu, 11 Jul 2024 16:53:15 +0300 Subject: [PATCH] perf: use Vec instead of BTreeMap in Phrase --- src/control.rs | 8 +- src/core/midi.rs | 3 - src/edn.rs | 20 ++-- src/model.rs | 2 +- src/model/phrase.rs | 44 ++++---- src/view/border.rs | 42 +++---- src/view/sequencer.rs | 252 +++++++++++++++--------------------------- 7 files changed, 150 insertions(+), 221 deletions(-) diff --git a/src/control.rs b/src/control.rs index c1aeb484..632aec14 100644 --- a/src/control.rs +++ b/src/control.rs @@ -106,11 +106,9 @@ const KEYMAP: &'static [KeyBinding] = keymap!(App { [Char('x'), NONE, "extend", "double the current clip", |app: &mut App| { if let Some(phrase) = app.phrase_mut() { - let mut notes = BTreeMap::new(); - for (time, events) in phrase.notes.iter() { - notes.insert(time + phrase.length, events.clone()); - } - phrase.notes.append(&mut notes); + let mut notes = phrase.notes.clone(); + notes.extend_from_slice(&mut phrase.notes); + phrase.notes = notes; phrase.length = phrase.length * 2; } Ok(true) diff --git a/src/core/midi.rs b/src/core/midi.rs index d523bdc2..1b393112 100644 --- a/src/core/midi.rs +++ b/src/core/midi.rs @@ -1,8 +1,5 @@ use crate::core::*; -pub type PhraseData = - BTreeMap>; - pub type MIDIMessage = Vec; diff --git a/src/edn.rs b/src/edn.rs index 7e56a3db..58f54f11 100644 --- a/src/edn.rs +++ b/src/edn.rs @@ -137,10 +137,10 @@ impl Track { impl Phrase { fn load_edn <'e> (ppq: usize, args: &[Edn<'e>]) -> Usually { - let mut name = String::new(); - let mut beats = 0usize; - let mut steps = 0usize; - let mut data = BTreeMap::new(); + let mut phrase = Self::default(); + let mut name = String::new(); + let mut beats = 0usize; + let mut steps = 0usize; edn!(edn in args { Edn::Map(map) => { if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) { @@ -148,6 +148,10 @@ impl Phrase { } if let Some(Edn::Int(b)) = map.get(&Edn::Key(":beats")) { beats = *b as usize; + phrase.length = ppq * beats; + for _ in phrase.notes.len()..phrase.length { + phrase.notes.push(Vec::with_capacity(16)) + } } if let Some(Edn::Int(s)) = map.get(&Edn::Key(":steps")) { steps = *s as usize; @@ -169,15 +173,11 @@ impl Phrase { args.get(0), args.get(1), ) { - if !data.contains_key(&time) { - data.insert(time, vec![]); - } let (key, vel) = ( u7::from((*key as u8).min(127)), u7::from((*vel as u8).min(127)) ); - data.get_mut(&time).unwrap() - .push(MidiMessage::NoteOn { key, vel }) + phrase.notes[time].push(MidiMessage::NoteOn { key, vel }) } else { panic!("unexpected list in phrase '{name}'") }, @@ -187,7 +187,7 @@ impl Phrase { }, _ => panic!("unexpected in phrase '{name}': {edn:?}"), }); - Ok(Self::new(&name, beats * ppq, Some(data))) + Ok(phrase) } } diff --git a/src/model.rs b/src/model.rs index b8cff6a0..9bf14953 100644 --- a/src/model.rs +++ b/src/model.rs @@ -6,7 +6,7 @@ pub mod sampler; pub mod scene; pub mod track; -pub use self::phrase::Phrase; +pub use self::phrase::{Phrase, PhraseData}; pub use self::scene::Scene; pub use self::track::Track; pub use self::sampler::{Sampler, Sample, read_sample_data}; diff --git a/src/model/phrase.rs b/src/model/phrase.rs index b75679c5..3480b66a 100644 --- a/src/model/phrase.rs +++ b/src/model/phrase.rs @@ -11,6 +11,8 @@ use crate::{core::*, model::App}; }} } +pub type PhraseData = Vec>; + #[derive(Debug)] pub struct Phrase { pub name: String, @@ -30,13 +32,20 @@ impl Phrase { Self { name: name.to_string(), length, - notes: notes.unwrap_or(BTreeMap::new()), + notes: notes.unwrap_or(vec![Vec::with_capacity(16);length]), looped: Some((0, length)) } } + pub fn record_event (&mut self, pulse: usize, message: MidiMessage) { + if pulse >= self.length { + panic!("extend phrase first") + } + self.notes[pulse].push(message); + } /// Check if a range `start..end` contains MIDI Note On `k` pub fn contains_note_on (&self, k: u7, start: usize, end: usize) -> bool { - for (_, (_, events)) in self.notes.range(start..end).enumerate() { + //panic!("{:?} {start} {end}", &self); + for events in self.notes[start.max(0)..end.min(self.notes.len())].iter() { for event in events.iter() { match event { MidiMessage::NoteOn {key,..} => { @@ -63,31 +72,20 @@ impl Phrase { frame0, frame0 + frames ) { let tick = tick % self.length; - if let Some(events) = self.notes.get(&(tick as usize)) { - for message in events.iter() { - buf.clear(); - let channel = 0.into(); - let message = *message; - LiveEvent::Midi { channel, message }.write(&mut buf).unwrap(); - output[time as usize].push(buf.clone()); - match message { - MidiMessage::NoteOn { key, .. } => notes_on[key.as_int() as usize] = true, - MidiMessage::NoteOff { key, .. } => notes_on[key.as_int() as usize] = false, - _ => {} - } + for message in self.notes[tick].iter() { + buf.clear(); + let channel = 0.into(); + let message = *message; + LiveEvent::Midi { channel, message }.write(&mut buf).unwrap(); + output[time as usize].push(buf.clone()); + match message { + MidiMessage::NoteOn { key, .. } => notes_on[key.as_int() as usize] = true, + MidiMessage::NoteOff { key, .. } => notes_on[key.as_int() as usize] = false, + _ => {} } } } } - - pub fn record_event (&mut self, pulse: usize, message: MidiMessage) { - let contains = self.notes.contains_key(&pulse); - if contains { - self.notes.get_mut(&pulse).unwrap().push(message.clone()); - } else { - self.notes.insert(pulse, vec![message.clone()]); - } - } } impl App { diff --git a/src/view/border.rs b/src/view/border.rs index a352e3ab..670337ae 100644 --- a/src/view/border.rs +++ b/src/view/border.rs @@ -11,45 +11,49 @@ pub trait BorderStyle { const W: &'static str = ""; #[inline] - fn draw (&self, buf: &mut Buffer, area: Rect) { - self.draw_horizontal(buf, area, None); - self.draw_vertical(buf, area, None); - self.draw_corners(buf, area, None); + fn draw (&self, buf: &mut Buffer, area: Rect) -> Usually { + self.draw_horizontal(buf, area, None)?; + self.draw_vertical(buf, area, None)?; + self.draw_corners(buf, area, None)?; + Ok(area) } #[inline] - fn draw_horizontal (&self, buf: &mut Buffer, area: Rect, style: Option