use bevy::{platform::collections::HashMap, prelude::*};
use midix::prelude::*;
mod beat;
pub use beat::*;
mod channel_settings;
pub use channel_settings::*;
mod section;
pub use section::*;
use super::MidiSong;
#[derive(Copy, Clone, Debug)]
pub struct ChannelSettings {
pub program: Program,
pub velocity: Velocity,
}
impl Default for ChannelSettings {
fn default() -> Self {
Self {
program: Program::new(1).unwrap(),
velocity: Velocity::MAX,
}
}
}
pub struct SimpleMidiSong {
beats_per_minute: f64,
pub(crate) channel_presets: HashMap<Channel, ChannelSettings>,
beats: HashMap<u64, Vec<ChannelVoiceMessage>>,
last_beat: u64,
}
impl SimpleMidiSong {
pub fn new(beats_per_minute: f64) -> Self {
Self {
beats_per_minute,
channel_presets: Default::default(),
beats: Default::default(),
last_beat: 0,
}
}
pub fn channel(&mut self, channel: Channel) -> ChannelModifier<'_> {
ChannelModifier {
song: self,
channel,
}
}
pub fn beat(&mut self, beat_no: u64) -> Beat<'_> {
Beat {
song: self,
beat_no,
}
}
pub fn add_event(&mut self, beat_no: u64, event: ChannelVoiceMessage) {
if beat_no > self.last_beat {
self.last_beat = beat_no
}
let current_beat = self.beats.entry(beat_no).or_default();
current_beat.push(event);
}
pub fn add_events<Msgs>(&mut self, beat_no: u64, events: Msgs)
where
Msgs: IntoIterator<Item = ChannelVoiceMessage>,
{
if beat_no > self.last_beat {
self.last_beat = beat_no
}
let current_beat = self.beats.entry(beat_no).or_default();
current_beat.extend(events);
}
}
impl SimpleMidiSong {
pub fn into_song(self) -> MidiSong {
let micros_per_beat = 60_000_000. / self.beats_per_minute;
let mut next_beat_additions = Vec::new();
if !self.channel_presets.is_empty() {
for (channel, settings) in self.channel_presets.iter() {
next_beat_additions.push(ChannelVoiceMessage::new(
*channel,
VoiceEvent::program_change(settings.program),
));
}
}
let mut commands = Vec::with_capacity(self.beats.len() * 6);
for i in 0..=self.last_beat {
let beat_no = i + 1;
let timestamp = i * micros_per_beat as u64;
let Some(events) = self.beats.get(&beat_no) else {
let iter = next_beat_additions
.iter()
.copied()
.map(|nb| Timed::new(timestamp, nb))
.collect::<Vec<_>>();
next_beat_additions.clear();
commands.extend(iter);
continue;
};
let additions_for_this_beat = next_beat_additions.clone();
next_beat_additions.clear();
for event in events.iter() {
if let Some(key) = event.is_note_on() {
let channel = event.channel();
next_beat_additions.push(ChannelVoiceMessage::new(
channel,
VoiceEvent::note_off(key, Velocity::MAX),
));
}
}
commands.extend(
additions_for_this_beat
.into_iter()
.chain(events.iter().copied())
.map(|msg| Timed::new(timestamp, msg)),
);
}
MidiSong::new(commands)
}
}