Skip to content

Commit

Permalink
Make midi types cheap to clone & make use of that for player and wate…
Browse files Browse the repository at this point in the history
…rfall (#84)
  • Loading branch information
PolyMeilex authored Sep 24, 2023
1 parent 34a2424 commit 28a437f
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 55 deletions.
2 changes: 1 addition & 1 deletion midi-file/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ mod tests {
fn load() {
let midi = MidiFile::new("../test.mid").unwrap();

for (_id, _note) in midi.merged_track.notes.iter().enumerate() {
for (_id, _note) in midi.merged_tracks.notes.iter().enumerate() {
// println!("{id}: {}", note.start.as_micros(),);
}
}
Expand Down
42 changes: 29 additions & 13 deletions midi-file/src/midi.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
use crate::{program_track::ProgramTrack, tempo_track::TempoTrack, MidiTrack};
use crate::{program_track::ProgramTrack, tempo_track::TempoTrack, MidiEvent, MidiNote, MidiTrack};
use midly::{Format, Smf, Timing};
use std::{fs, path::Path};
use std::{fs, path::Path, sync::Arc};

#[derive(Debug, Clone)]
pub struct MergedTracks {
pub notes: Arc<[MidiNote]>,
pub events: Arc<[MidiEvent]>,
}

#[derive(Debug, Clone)]
pub struct MidiFile {
pub format: Format,
pub tracks: Vec<MidiTrack>,
pub merged_track: MidiTrack,
pub tracks: Arc<[MidiTrack]>,
pub merged_tracks: MergedTracks,
pub program_map: ProgramTrack,
}

Expand Down Expand Up @@ -51,26 +57,36 @@ impl MidiFile {
})
.collect();

let mut merged_track: MidiTrack = tracks[0].clone();
let (notes_count, events_count) = tracks.iter().fold((0, 0), |(notes, events), track| {
(notes + track.notes.len(), events + track.events.len())
});

for track in tracks.iter().skip(1) {
let mut notes = Vec::with_capacity(notes_count);
let mut events = Vec::with_capacity(events_count);

for track in tracks.iter() {
for n in track.notes.iter().cloned() {
merged_track.notes.push(n);
notes.push(n);
}
for e in track.events.iter().cloned() {
merged_track.events.push(e);
events.push(e);
}
}

merged_track.notes.sort_by_key(|n| n.start);
merged_track.events.sort_by_key(|n| n.timestamp);
notes.sort_by_key(|n| n.start);
events.sort_by_key(|n| n.timestamp);

let program_map = ProgramTrack::new(&events);

let program_map = ProgramTrack::new(&merged_track.events);
let merged_track = MergedTracks {
notes: notes.into(),
events: events.into(),
};

Ok(Self {
format: smf.header.format,
tracks,
merged_track,
tracks: tracks.into(),
merged_tracks: merged_track,
program_map,
})
}
Expand Down
14 changes: 8 additions & 6 deletions midi-file/src/playback.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use std::time::Duration;

use crate::{MidiEvent, MidiTrack};
use crate::{MergedTracks, MidiEvent};

#[derive(Debug, Clone)]
pub struct PlaybackState {
tracks: MergedTracks,
is_paused: bool,
running: Duration,
leed_in: Duration,
Expand All @@ -14,19 +15,20 @@ pub struct PlaybackState {
}

impl PlaybackState {
pub fn new(leed_in: Duration, track: &MidiTrack) -> Self {
let first_note_start = if let Some(note) = track.notes.first() {
pub fn new(leed_in: Duration, tracks: MergedTracks) -> Self {
let first_note_start = if let Some(note) = tracks.notes.first() {
note.start
} else {
Duration::ZERO
};
let last_note_end = if let Some(note) = track.notes.last() {
let last_note_end = if let Some(note) = tracks.notes.last() {
note.start + note.duration
} else {
Duration::ZERO
};

Self {
tracks,
is_paused: false,
running: Duration::ZERO,
leed_in,
Expand All @@ -37,12 +39,12 @@ impl PlaybackState {
}
}

pub fn update<'a>(&mut self, track: &'a MidiTrack, delta: Duration) -> Vec<&'a MidiEvent> {
pub fn update(&mut self, delta: Duration) -> Vec<&MidiEvent> {
if !self.is_paused {
self.running += delta;
}

let events: Vec<_> = track.events[self.seen_events..]
let events: Vec<_> = self.tracks.events[self.seen_events..]
.iter()
.take_while(|event| event.timestamp + self.leed_in <= self.running)
.collect();
Expand Down
12 changes: 9 additions & 3 deletions midi-file/src/program_track.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use crate::MidiEvent;
use std::{collections::HashMap, sync::OnceLock, time::Duration};
use std::{
collections::HashMap,
sync::{Arc, OnceLock},
time::Duration,
};

/// HashMap<Channel, Program>
fn default_programs() -> &'static HashMap<u8, u8> {
Expand All @@ -15,7 +19,7 @@ struct Bucket {

#[derive(Debug, Clone)]
pub struct ProgramTrack {
timestamps: Vec<Bucket>,
timestamps: Arc<[Bucket]>,
}

impl ProgramTrack {
Expand All @@ -35,7 +39,9 @@ impl ProgramTrack {
}
}

Self { timestamps }
Self {
timestamps: timestamps.into(),
}
}

/// Search for program at certain timestamp
Expand Down
15 changes: 7 additions & 8 deletions midi-file/src/track.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use midly::{MidiMessage, TrackEvent, TrackEventKind};
use std::{collections::HashMap, time::Duration};
use std::{collections::HashMap, sync::Arc, time::Duration};

use crate::tempo_track::TempoTrack;

Expand Down Expand Up @@ -34,14 +34,14 @@ pub struct MidiNote {
#[derive(Debug, Clone)]
pub struct MidiTrack {
// Translated notes with calculated timings
pub notes: Vec<MidiNote>,
pub notes: Arc<[MidiNote]>,

pub events: Vec<MidiEvent>,
pub events: Arc<[MidiEvent]>,

pub track_id: usize,
pub track_color_id: usize,

pub programs: Vec<ProgramEvent>,
pub programs: Arc<[ProgramEvent]>,
pub has_drums: bool,
pub has_other_than_drums: bool,
}
Expand All @@ -66,10 +66,9 @@ impl MidiTrack {
Self {
track_id,
track_color_id,
notes,
events,

programs,
notes: notes.into(),
events: events.into(),
programs: programs.into(),
has_drums,
has_other_than_drums,
}
Expand Down
16 changes: 10 additions & 6 deletions neothesia-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ struct Recorder {
transform_uniform: Uniform<TransformUniform>,

playback: midi_file::PlaybackState,
midi: midi_file::MidiFile,

keyboard: KeyboardRenderer,
waterfall: WaterfallRenderer,
Expand Down Expand Up @@ -77,10 +76,16 @@ impl Recorder {

keyboard.position_on_bottom_of_parent(height as f32);

let mut waterfall =
WaterfallRenderer::new(&gpu, &midi, &config, &transform_uniform, keyboard_layout);
let mut waterfall = WaterfallRenderer::new(
&gpu,
midi.merged_tracks.clone(),
&config,
&transform_uniform,
keyboard_layout,
);

let playback = midi_file::PlaybackState::new(Duration::from_secs(3), &midi.merged_track);
let playback =
midi_file::PlaybackState::new(Duration::from_secs(3), midi.merged_tracks.clone());

waterfall.update(&gpu.queue, time_without_lead_in(&playback));

Expand All @@ -91,7 +96,6 @@ impl Recorder {
transform_uniform,

playback,
midi,

keyboard,
waterfall,
Expand All @@ -104,7 +108,7 @@ impl Recorder {
}

fn update(&mut self, delta: Duration) {
let events = self.playback.update(&self.midi.merged_track, delta);
let events = self.playback.update(delta);
file_midi_events(&mut self.keyboard, &self.config, &events);

self.waterfall
Expand Down
17 changes: 10 additions & 7 deletions neothesia-core/src/render/waterfall/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::config::Config;
use crate::TransformUniform;
use crate::Uniform;
use midi_file::MidiFile;
use midi_file::MergedTracks;
use wgpu_jumpstart::Color;
use wgpu_jumpstart::Gpu;

Expand All @@ -10,23 +10,27 @@ use pipeline::{NoteInstance, WaterfallPipeline};

pub struct WaterfallRenderer {
notes_pipeline: WaterfallPipeline,
merged_tracks: MergedTracks,
}

impl WaterfallRenderer {
pub fn new(
gpu: &Gpu,
midi: &MidiFile,
merged_tracks: MergedTracks,
config: &Config,
transform_uniform: &Uniform<TransformUniform>,
layout: piano_math::KeyboardLayout,
) -> Self {
let notes_pipeline =
WaterfallPipeline::new(gpu, transform_uniform, midi.merged_track.notes.len());
let mut notes = Self { notes_pipeline };
WaterfallPipeline::new(gpu, transform_uniform, merged_tracks.notes.len());
let mut notes = Self {
notes_pipeline,
merged_tracks,
};
notes
.notes_pipeline
.set_speed(&gpu.queue, config.animation_speed);
notes.resize(&gpu.queue, midi, config, layout);
notes.resize(&gpu.queue, config, layout);
notes
}

Expand All @@ -37,7 +41,6 @@ impl WaterfallRenderer {
pub fn resize(
&mut self,
queue: &wgpu::Queue,
midi: &MidiFile,
config: &Config,
layout: piano_math::KeyboardLayout,
) {
Expand All @@ -46,7 +49,7 @@ impl WaterfallRenderer {
let mut instances = Vec::new();

let mut longer_than_range = false;
for note in midi.merged_track.notes.iter() {
for note in self.merged_tracks.notes.iter() {
if layout.range.contains(note.note) && note.channel != 9 {
let key = &layout.keys[note.note as usize - range_start];

Expand Down
12 changes: 3 additions & 9 deletions neothesia/src/scene/playing_scene/midi_player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl MidiPlayer {
let mut player = Self {
playback: midi_file::PlaybackState::new(
Duration::from_secs(3),
&midi_file.merged_track,
midi_file.merged_tracks.clone(),
),
output_manager: target.output_manager.clone(),
midi_file,
Expand All @@ -42,10 +42,6 @@ impl MidiPlayer {
player
}

pub fn midi_file(&self) -> &MidiFile {
&self.midi_file
}

/// When playing: returns midi events
///
/// When paused: returns None
Expand All @@ -54,7 +50,7 @@ impl MidiPlayer {

let elapsed = (delta / 10) * (target.config.speed_multiplier * 10.0) as u32;

let events = self.playback.update(&self.midi_file.merged_track, elapsed);
let events = self.playback.update(elapsed);

events.iter().for_each(|event| {
self.output_manager
Expand Down Expand Up @@ -116,9 +112,7 @@ impl MidiPlayer {
self.playback.set_time(time);

// Discard all of the events till that point
let events = self
.playback
.update(&self.midi_file.merged_track, Duration::ZERO);
let events = self.playback.update(Duration::ZERO);
std::mem::drop(events);

self.clear();
Expand Down
3 changes: 1 addition & 2 deletions neothesia/src/scene/playing_scene/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl PlayingScene {

let mut notes = WaterfallRenderer::new(
&target.gpu,
&midi_file,
midi_file.merged_tracks.clone(),
&target.config,
&target.transform_uniform,
keyboard_layout.clone(),
Expand Down Expand Up @@ -77,7 +77,6 @@ impl Scene for PlayingScene {
self.keyboard.resize(target);
self.notes.resize(
&target.gpu.queue,
self.player.midi_file(),
&target.config,
self.keyboard.layout().clone(),
);
Expand Down

0 comments on commit 28a437f

Please sign in to comment.