perf: use Vec instead of BTreeMap in Phrase

This commit is contained in:
🪞👃🪞 2024-07-11 16:53:15 +03:00
parent db25099268
commit 4a8f5b267f
7 changed files with 150 additions and 221 deletions

View file

@ -106,11 +106,9 @@ const KEYMAP: &'static [KeyBinding<App>] = 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)

View file

@ -1,8 +1,5 @@
use crate::core::*;
pub type PhraseData =
BTreeMap<usize, Vec<MidiMessage>>;
pub type MIDIMessage =
Vec<u8>;

View file

@ -137,10 +137,10 @@ impl Track {
impl Phrase {
fn load_edn <'e> (ppq: usize, args: &[Edn<'e>]) -> Usually<Self> {
let mut phrase = Self::default();
let mut name = String::new();
let mut beats = 0usize;
let mut steps = 0usize;
let mut data = BTreeMap::new();
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)
}
}

View file

@ -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};

View file

@ -11,6 +11,8 @@ use crate::{core::*, model::App};
}}
}
pub type PhraseData = Vec<Vec<MidiMessage>>;
#[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,8 +72,7 @@ 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() {
for message in self.notes[tick].iter() {
buf.clear();
let channel = 0.into();
let message = *message;
@ -80,16 +88,6 @@ impl Phrase {
}
}
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 {
pub fn phrase (&self) -> Option<&Phrase> {
let (track_id, track) = self.track()?;

View file

@ -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<Rect> {
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<Style>) {
fn draw_horizontal (&self, buf: &mut Buffer, area: Rect, style: Option<Style>) -> Usually<Rect> {
let style = style.or_else(||self.style_horizontal());
for x in area.x..(area.x+area.width).saturating_sub(1) {
self.draw_north(buf, x, area.y, style);
self.draw_south(buf, x, (area.y + area.height).saturating_sub(1), style);
self.draw_north(buf, x, area.y, style)?;
self.draw_south(buf, x, (area.y + area.height).saturating_sub(1), style)?;
}
Ok(area)
}
#[inline]
fn draw_north (&self, buf: &mut Buffer, x: u16, y: u16, style: Option<Style>) {
Self::N.blit(buf, x, y, style);
fn draw_north (&self, buf: &mut Buffer, x: u16, y: u16, style: Option<Style>) -> Usually<Rect> {
Self::N.blit(buf, x, y, style)
}
#[inline]
fn draw_south (&self, buf: &mut Buffer, x: u16, y: u16, style: Option<Style>) {
Self::S.blit(buf, x, y, style);
fn draw_south (&self, buf: &mut Buffer, x: u16, y: u16, style: Option<Style>) -> Usually<Rect> {
Self::S.blit(buf, x, y, style)
}
#[inline]
fn draw_vertical (&self, buf: &mut Buffer, area: Rect, style: Option<Style>) {
fn draw_vertical (&self, buf: &mut Buffer, area: Rect, style: Option<Style>) -> Usually<Rect> {
let style = style.or_else(||self.style_vertical());
for y in area.y..(area.y+area.height).saturating_sub(1) {
Self::W.blit(buf, area.x, y, style);
Self::E.blit(buf, area.x + area.width - 1, y, style);
Self::W.blit(buf, area.x, y, style)?;
Self::E.blit(buf, area.x + area.width - 1, y, style)?;
}
Ok(area)
}
#[inline]
fn draw_corners (&self, buf: &mut Buffer, area: Rect, style: Option<Style>) {
fn draw_corners (&self, buf: &mut Buffer, area: Rect, style: Option<Style>) -> Usually<Rect> {
let style = style.or_else(||self.style_corners());
Self::NW.blit(buf, area.x, area.y, style);
Self::NE.blit(buf, area.x + area.width - 1, area.y, style);
Self::SW.blit(buf, area.x, area.y + area.height - 1, style);
Self::SE.blit(buf, area.x + area.width - 1, area.y + area.height - 1, style);
Self::NW.blit(buf, area.x, area.y, style)?;
Self::NE.blit(buf, area.x + area.width - 1, area.y, style)?;
Self::SW.blit(buf, area.x, area.y + area.height - 1, style)?;
Self::SE.blit(buf, area.x + area.width - 1, area.y + area.height - 1, style)?;
Ok(area)
}
#[inline]

View file

@ -1,31 +1,28 @@
use crate::{core::*,model::*};
#[derive(Debug, Clone)]
pub enum SequencerMode { Tiny, Compact, Horizontal, Vertical, }
pub struct SequencerView<'a> {
pub focused: bool,
focused: bool,
/// Displayed phrase
pub phrase: Option<&'a Phrase>,
phrase: Option<&'a Phrase>,
/// Resolution of MIDI sequence
pub ppq: usize,
ppq: usize,
/// Range of notes to display
pub note_start: usize,
note_start: usize,
/// Position of cursor within note range
pub note_cursor: usize,
note_cursor: usize,
/// PPQ per display unit
pub time_zoom: usize,
time_zoom: usize,
/// Range of time steps to display
pub time_start: usize,
time_start: usize,
/// Position of cursor within time range
pub time_cursor: usize,
time_cursor: usize,
/// Current time
pub now: usize,
now: usize,
/// Highlight input keys
pub notes_in: &'a [bool; 128],
notes_in: &'a [bool; 128],
/// Highlight output keys
pub notes_out: &'a [bool; 128],
notes_out: &'a [bool; 128],
}
impl<'a> SequencerView<'a> {
@ -34,9 +31,8 @@ impl<'a> SequencerView<'a> {
0 => None,
_ => app.tracks.get(app.track_cursor - 1)
};
let phrase = app.phrase();
Self {
phrase,
phrase: app.phrase(),
focused: app.section == AppSection::Sequencer,
ppq: app.timebase.ppq() as usize,
now: app.timebase.frame_to_pulse(app.playhead as f64) as usize,
@ -75,38 +71,46 @@ impl<'a> SequencerView<'a> {
Style::default()
}
}
fn index_to_color (&self, index: usize, default: Color) -> Color {
if self.notes_in[index] && self.notes_out[index] {
Color::Yellow
} else if self.notes_in[index] {
Color::Red
} else if self.notes_out[index] {
Color::Green
} else {
default
}
}
}
impl<'a> SequencerView<'a> {
fn horizontal_draw (&self, buf: &mut Buffer, area: Rect) -> Usually<()> {
self.horizontal_keys(buf, area)?;
self.horizontal_quant(buf, area);
self.horizontal_timer(buf, area, self.phrase);
self.horizontal_lanes(buf, area, self.phrase);
self.horizontal_cursor(buf, area);
self.horizontal_quant(buf, area)?;
self.horizontal_timer(buf, area, self.phrase)?;
self.horizontal_lanes(buf, area, self.phrase)?;
self.horizontal_cursor(buf, area)?;
Ok(())
}
fn horizontal_quant (&self, buf: &mut Buffer, area: Rect) {
fn horizontal_quant (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let quant = ppq_to_name(self.time_zoom);
let quant_x = area.x + area.width - 1 - quant.len() as u16;
let quant_y = area.y + area.height - 2;
quant.blit(buf, quant_x, quant_y, self.style_focus());
quant.blit(buf, quant_x, quant_y, self.style_focus())
}
fn horizontal_cursor (&self, buf: &mut Buffer, area: Rect) {
fn horizontal_cursor (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let (time, note) = (self.time_cursor, self.note_cursor);
let x = area.x + 5 + time as u16;
let y = area.y + 1 + note as u16 / 2;
let c = if note % 2 == 0 { "" } else { "" };
c.blit(buf, x, y, self.style_focus());
c.blit(buf, x, y, self.style_focus())
}
fn horizontal_timer (&self, buf: &mut Buffer, area: Rect, phrase: Option<&Phrase>) {
if phrase.is_none() {
return
}
let phrase = phrase.unwrap();
fn horizontal_timer (&self, buf: &mut Buffer, area: Rect, phrase: Option<&Phrase>) -> Usually<Rect> {
if let Some(phrase) = phrase {
let (time0, time_z, now) = (self.time_start, self.time_zoom, self.now % phrase.length);
let Rect { x, width, .. } = area;
let offset = 5;
@ -114,135 +118,69 @@ impl<'a> SequencerView<'a> {
let step = (time0 + (x-offset) as usize) * time_z;
let next_step = (time0 + (x-offset) as usize + 1) * time_z;
let style = Self::style_timer_step(now, step, next_step);
"-".blit(buf, x, area.y, Some(style));
"-".blit(buf, x, area.y, Some(style))?;
}
}
return Ok(Rect { x: area.x, y: area.y, width: area.width, height: 1 })
}
fn horizontal_keys (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let (note0, notes_in, notes_out) = (self.note_start, self.notes_in, self.notes_out);
let mut cell_bg = Cell::default();
cell_bg.set_char('░');
cell_bg.set_style(Style::default().not_dim().black());
let mut cell_full = Cell::default();
cell_full.set_char('█');
cell_full.set_style(Style::default().not_dim());
let mut cell_hi = Cell::default();
cell_hi.set_char('▀');
cell_hi.set_style(Style::default().not_dim());
let mut cell_lo = Cell::default();
cell_lo.set_char('▄');
cell_lo.set_style(Style::default().not_dim());
let note0 = self.note_start;
let not_dim = Style::default().not_dim();
let black = not_dim.black();
let Rect { x, y, width, height } = area;
let height = height.min(128);
let h = height.saturating_sub(3);
let index_to_color = |index: usize, default: Color|
if notes_in[index] && notes_out[index] {
Color::Yellow
} else if notes_in[index] {
Color::Red
} else if notes_out[index] {
Color::Green
} else {
default
};
for index in 0..h {
let y = y + h - index;
match index % 6 {
0 => {
let key1 = buf.get_mut(x + 1, y);
*key1 = cell_lo.clone();
key1.set_fg(index_to_color(index as usize * 2, Color::White));
key1.set_bg(index_to_color(index as usize * 2 + 1, Color::Black));
let key2 = buf.get_mut(x + 2, y);
*key2 = cell_lo.clone();
key2.set_fg(index_to_color(index as usize * 2, Color::White));
key2.set_bg(index_to_color(index as usize * 2 + 1, Color::White));
},
1 => {
let key1 = buf.get_mut(x + 1, y);
*key1 = cell_lo.clone();
key1.set_fg(index_to_color(index as usize * 2, Color::White));
key1.set_bg(index_to_color(index as usize * 2 + 1, Color::Black));
let key2 = buf.get_mut(x + 2, y);
*key2 = cell_lo.clone();
key2.set_fg(index_to_color(index as usize * 2, Color::White));
key2.set_bg(index_to_color(index as usize * 2 + 1, Color::White));
},
2 => {
let key1 = buf.get_mut(x + 1, y);
*key1 = cell_lo.clone();
key1.set_fg(index_to_color(index as usize * 2, Color::White));
key1.set_bg(index_to_color(index as usize * 2 + 1, Color::White));
let key2 = buf.get_mut(x + 2, y);
*key2 = cell_lo.clone();
key2.set_fg(index_to_color(index as usize * 2, Color::White));
key2.set_bg(index_to_color(index as usize * 2 + 1, Color::White));
},
3 => {
let key1 = buf.get_mut(x + 1, y);
*key1 = cell_lo.clone();
key1.set_fg(index_to_color(index as usize * 2, Color::Black));
key1.set_bg(index_to_color(index as usize * 2 + 1, Color::White));
let key2 = buf.get_mut(x + 2, y);
*key2 = cell_lo.clone();
key2.set_fg(index_to_color(index as usize * 2, Color::White));
key2.set_bg(index_to_color(index as usize * 2 + 1, Color::White));
},
4 => {
let key1 = buf.get_mut(x + 1, y);
*key1 = cell_lo.clone();
key1.set_fg(index_to_color(index as usize * 2, Color::Black));
key1.set_bg(index_to_color(index as usize * 2 + 1, Color::White));
let key2 = buf.get_mut(x + 2, y);
*key2 = cell_lo.clone();
key2.set_fg(index_to_color(index as usize * 2, Color::White));
key2.set_bg(index_to_color(index as usize * 2 + 1, Color::White));
},
5 => {
let key1 = buf.get_mut(x + 1, y);
*key1 = cell_lo.clone();
key1.set_fg(index_to_color(index as usize * 2, Color::Black));
key1.set_bg(index_to_color(index as usize * 2 + 1, Color::White));
let key2 = buf.get_mut(x + 2, y);
*key2 = cell_lo.clone();
key2.set_fg(index_to_color(index as usize * 2, Color::White));
key2.set_bg(index_to_color(index as usize * 2 + 1, Color::White));
},
key1.set_char('▄');
key1.set_style(not_dim);
let (fg, bg) = match index % 6 {
0 => (Color::White, Color::Black),
1 => (Color::White, Color::Black),
2 => (Color::White, Color::White),
3 => (Color::Black, Color::White),
4 => (Color::Black, Color::White),
5 => (Color::Black, Color::White),
_ => { unreachable!(); }
}
};
key1.set_fg(self.index_to_color(index as usize * 2, fg));
key1.set_bg(self.index_to_color(index as usize * 2 + 1, bg));
let key2 = buf.get_mut(x + 2, y);
key2.set_char('▄');
key2.set_style(not_dim);
key2.set_fg(self.index_to_color(index as usize * 2, Color::White));
key2.set_bg(self.index_to_color(index as usize * 2 + 1, Color::White));
for x in x+5..x+width-1 {
*buf.get_mut(x, y) = cell_bg.clone();
let cell = buf.get_mut(x, y);
cell.set_char('░');
cell.set_style(black);
}
let note_a = note0 + (index * 2) as usize;
if note_a % 12 == 0 {
let octave = format!("C{}", (note_a / 12) as i8 - 2);
octave.blit(buf, x + 3, y, None);
octave.blit(buf, x + 3, y, None)?;
continue
}
let note_b = note0 + (index * 2) as usize;
if note_b % 12 == 0 {
let octave = format!("C{}", (note_b / 12) as i8 - 2);
octave.blit(buf, x + 3, y, None);
octave.blit(buf, x + 3, y, None)?;
continue
}
}
Ok(area)
}
fn horizontal_lanes (&self, buf: &mut Buffer, area: Rect, phrase: Option<&Phrase>) {
fn horizontal_lanes (&self, buf: &mut Buffer, area: Rect, phrase: Option<&Phrase>) -> Usually<Rect> {
if phrase.is_none() {
return
return Ok(Rect { x: area.x, y: area.x, width: 0, height: 0 })
}
let phrase = phrase.unwrap();
let (ppq, time_z, time0, note0) =
(self.ppq, self.time_zoom, self.time_start, self.note_start);
let bg = Style::default();
let (bw, wh) = (bg.dim(), bg.white().not_dim());
let dim = Style::default().dim();
let Rect { x, y, width, height } = area;
@ -251,18 +189,6 @@ impl<'a> SequencerView<'a> {
x: x + offset, y, width: width - offset, height: height - 2
};
let mut cell_a = Cell::default();
cell_a.set_char('▄');
cell_a.set_style(wh);
let mut cell_b = Cell::default();
cell_b.set_char('▀');
cell_b.set_style(wh);
let mut cell_ab = Cell::default();
cell_ab.set_char('█');
cell_ab.set_style(wh);
let mut steps = vec![];
for x in phrase_area.x .. phrase_area.x + phrase_area.width {
let x0 = x.saturating_sub(phrase_area.x) as usize;
@ -275,20 +201,21 @@ impl<'a> SequencerView<'a> {
for y in phrase_area.y .. phrase_area.y + phrase_area.height {
if y == phrase_area.y {
if step % (4 * ppq) == 0 {
format!("{}", 1 + step / (4 * ppq)).blit(buf, x, y, None);
format!("{}", 1 + step / (4 * ppq)).blit(buf, x, y, None)?;
} else if step % ppq == 0 {
let cell = buf.get_mut(x, y);
cell.set_char(if step % ppq == 0 { '|' } else { '·' });
cell.set_style(bw);
cell.set_style(dim);
}
} else {
let cell = buf.get_mut(x, y);
cell.set_char(if step % ppq == 0 { '|' } else { '·' });
cell.set_style(bw);
cell.set_style(dim);
}
}
}
let wh = Style::default().white().not_dim();
for index in 0..height-2 {
let note_a = note0 + index as usize * 2;
let note_b = note0 + index as usize * 2 + 1;
@ -297,20 +224,25 @@ impl<'a> SequencerView<'a> {
phrase.contains_note_on(u7::from_int_lossy(note_a as u8), *step, *next_step),
phrase.contains_note_on(u7::from_int_lossy(note_b as u8), *step, *next_step),
);
let cell = if a && b {
&cell_ab
} else if a {
&cell_a
} else if b {
&cell_b
} else {
continue
};
if let Some(block) = half_block(a, b) {
let y = y + height.saturating_sub(index+2) as u16;
*buf.get_mut(*x, y) = cell.clone()
let cell = buf.get_mut(*x, y);
cell.set_char(block);
cell.set_style(wh);
}
}
}
Ok(area)
}
}
fn half_block (lower: bool, upper: bool) -> Option<char> {
match (lower, upper) {
(true, true) => Some('█'),
(true, false) => Some('▄'),
(false, true) => Some('▀'),
_ => None
}
}
//pub fn footer (