Midi meta events added: time signatures

This commit is contained in:
Denis Redozubov 2023-06-09 17:18:31 +04:00
parent c52f1e3ff4
commit fd78edb4f6
3 changed files with 163 additions and 38 deletions

View file

@ -55,6 +55,18 @@ impl KnownLength for BasicLength {
} }
impl BasicLength { impl BasicLength {
pub(crate) fn to_power_of_2(&self) -> u8 {
match self {
BasicLength::Whole => todo!(),
BasicLength::Half => todo!(),
BasicLength::Fourth => todo!(),
BasicLength::Eighth => todo!(),
BasicLength::Sixteenth => todo!(),
BasicLength::ThirtySecond => todo!(),
BasicLength::SixtyFourth => todo!(),
}
}
pub fn from_num(n: u16) -> Result<Self, String> { pub fn from_num(n: u16) -> Result<Self, String> {
match n { match n {
64 => Ok(BasicLength::SixtyFourth), 64 => Ok(BasicLength::SixtyFourth),

View file

@ -18,9 +18,9 @@ use crate::dsl::dsl::{
group_or_delimited_group, groups, BasicLength, Group, GroupOrNote, Groups, KnownLength, Length, group_or_delimited_group, groups, BasicLength, Group, GroupOrNote, Groups, KnownLength, Length,
ModdedLength, Note, Times, ModdedLength, Note, Times,
}; };
use crate::midi::time::TimeSignature;
use GroupOrNote::*; use GroupOrNote::*;
use Note::*; use Note::*;
use crate::midi::time::TimeSignature;
#[allow(dead_code)] #[allow(dead_code)]
static BAR_LIMIT: u32 = 1000; static BAR_LIMIT: u32 = 1000;
@ -128,10 +128,7 @@ where
} }
fn le(&self, other: &Self) -> bool { fn le(&self, other: &Self) -> bool {
matches!( matches!(self.partial_cmp(other), Some(Less | Equal))
self.partial_cmp(other),
Some(Less | Equal)
)
} }
fn gt(&self, other: &Self) -> bool { fn gt(&self, other: &Self) -> bool {
@ -139,10 +136,7 @@ where
} }
fn ge(&self, other: &Self) -> bool { fn ge(&self, other: &Self) -> bool {
matches!( matches!(self.partial_cmp(other), Some(Greater | Equal))
self.partial_cmp(other),
Some(Greater | Equal)
)
} }
} }
@ -429,7 +423,7 @@ impl Length {
} }
#[allow(dead_code)] #[allow(dead_code)]
static MICROSECONDS_PER_BPM: u128 = 500000 as u128 / TICKS_PER_QUARTER_NOTE as u128; static MICROSECONDS_PER_BPM: u128 = 50000 as u128 / TICKS_PER_QUARTER_NOTE as u128;
#[allow(dead_code)] #[allow(dead_code)]
static MIDI_CLOCKS_PER_CLICK: u8 = 24; static MIDI_CLOCKS_PER_CLICK: u8 = 24;
@ -450,12 +444,12 @@ static MIDI_CLOCKS_PER_CLICK: u8 = 24;
)] )]
pub struct MidiTempo(u24); pub struct MidiTempo(u24);
// impl MidiTempo { impl MidiTempo {
// fn from_tempo(Tempo(t): Tempo) -> Self { fn from_tempo(tempo: u16) -> Self {
// let mt = t as u32 * MICROSECONDS_PER_BPM as u32; let mt = tempo as u32 * MICROSECONDS_PER_BPM as u32;
// Self(u24::from(mt)) Self(mt.into())
// } }
// } }
/// Returns an EventGrid and a total length. Length is needed as a group can end with rests that are not in the grid, /// Returns an EventGrid and a total length. Length is needed as a group can end with rests that are not in the grid,
/// and we need it to cycle the group. /// and we need it to cycle the group.
@ -742,7 +736,11 @@ fn flatten_and_merge(
.unwrap_or(BAR_LIMIT.clone()); .unwrap_or(BAR_LIMIT.clone());
println!("Converges over {} bars", converges_over_bars); println!("Converges over {} bars", converges_over_bars);
let length_limit = converges_over_bars * time_signature.to_128th(); let length_limit = converges_over_bars * time_signature.to_128th();
println!("TimeSignature {:?} in 128th: {}", time_signature, time_signature.to_128th()); println!(
"TimeSignature {:?} in 128th: {}",
time_signature,
time_signature.to_128th()
);
println!("length limit in 128th notes: {}", length_limit); println!("length limit in 128th notes: {}", length_limit);
let (kick_grid, kick_repeats) = match groups.get(&KickDrum) { let (kick_grid, kick_repeats) = match groups.get(&KickDrum) {
Some(groups) => { Some(groups) => {
@ -805,30 +803,96 @@ fn flatten_and_merge(
#[test] #[test]
fn test_flatten_and_merge() { fn test_flatten_and_merge() {
let kick_events = vec![ Event { tick: Tick(0), event_type: NoteOn(KickDrum), }, Event { tick: Tick(12), event_type: NoteOff(KickDrum), }, Event { tick: Tick(12), event_type: NoteOn(KickDrum), }, Event { tick: Tick(24), event_type: NoteOff(KickDrum), }, Event { tick: Tick(36), event_type: NoteOn(KickDrum), }, Event { tick: Tick(48), event_type: NoteOff(KickDrum), }, Event { tick: Tick(60), event_type: NoteOn(KickDrum), }, Event { tick: Tick(72), event_type: NoteOff(KickDrum), }, Event { tick: Tick(72), event_type: NoteOn(KickDrum), }, Event { tick: Tick(84), event_type: NoteOff(KickDrum), }, ]; let kick_events = vec![
let snare_events = [Event { tick: Tick(24), event_type: NoteOn(SnareDrum) }, Event { tick: Tick(48), event_type: NoteOff(SnareDrum) }, Event { tick: Tick(96), event_type: NoteOn(SnareDrum) }, Event { tick: Tick(120), event_type: NoteOff(SnareDrum) }]; Event {
tick: Tick(0),
event_type: NoteOn(KickDrum),
},
Event {
tick: Tick(12),
event_type: NoteOff(KickDrum),
},
Event {
tick: Tick(12),
event_type: NoteOn(KickDrum),
},
Event {
tick: Tick(24),
event_type: NoteOff(KickDrum),
},
Event {
tick: Tick(36),
event_type: NoteOn(KickDrum),
},
Event {
tick: Tick(48),
event_type: NoteOff(KickDrum),
},
Event {
tick: Tick(60),
event_type: NoteOn(KickDrum),
},
Event {
tick: Tick(72),
event_type: NoteOff(KickDrum),
},
Event {
tick: Tick(72),
event_type: NoteOn(KickDrum),
},
Event {
tick: Tick(84),
event_type: NoteOff(KickDrum),
},
];
let snare_events = [
Event {
tick: Tick(24),
event_type: NoteOn(SnareDrum),
},
Event {
tick: Tick(48),
event_type: NoteOff(SnareDrum),
},
Event {
tick: Tick(96),
event_type: NoteOn(SnareDrum),
},
Event {
tick: Tick(120),
event_type: NoteOff(SnareDrum),
},
];
let four_fourth = TimeSignature::from_str("4/4").unwrap(); let four_fourth = TimeSignature::from_str("4/4").unwrap();
// let kick_event_grid = EventGrid { events, length: Tick(48 * 4) }; // let kick_event_grid = EventGrid { events, length: Tick(48 * 4) };
let flattened_kick = flatten_and_merge( let flattened_kick = flatten_and_merge(
HashMap::from_iter([(KickDrum, groups("16xx-x-xx-").unwrap().1)]), HashMap::from_iter([(KickDrum, groups("16xx-x-xx-").unwrap().1)]),
four_fourth four_fourth,
).collect::<Vec<Event<Tick>>>(); )
.collect::<Vec<Event<Tick>>>();
let flattened_snare = flatten_and_merge( let flattened_snare = flatten_and_merge(
HashMap::from_iter([(SnareDrum, groups("8-x--x-").unwrap().1)]), HashMap::from_iter([(SnareDrum, groups("8-x--x-").unwrap().1)]),
four_fourth four_fourth,
).collect::<Vec<Event<Tick>>>(); )
.collect::<Vec<Event<Tick>>>();
let flattened_kick_and_snare = flatten_and_merge( let flattened_kick_and_snare = flatten_and_merge(
HashMap::from_iter([ HashMap::from_iter([
(KickDrum, groups("16xx-x-xx-").unwrap().1), (KickDrum, groups("16xx-x-xx-").unwrap().1),
(SnareDrum, groups("8-x--x-").unwrap().1) (SnareDrum, groups("8-x--x-").unwrap().1),
]), ]),
four_fourth four_fourth,
).collect::<Vec<Event<Tick>>>(); )
.collect::<Vec<Event<Tick>>>();
assert_eq!(flattened_kick, kick_events); assert_eq!(flattened_kick, kick_events);
assert_eq!(flattened_snare, snare_events); assert_eq!(flattened_snare, snare_events);
assert_eq!(flattened_kick.iter().all(|x| flattened_kick_and_snare.contains(x)), true); assert_eq!(
flattened_kick
.iter()
.all(|x| flattened_kick_and_snare.contains(x)),
true
);
} }
// The length of a beat is not standard, so in order to fully describe the length of a MIDI tick the MetaMessage::Tempo event should be present. // The length of a beat is not standard, so in order to fully describe the length of a MIDI tick the MetaMessage::Tempo event should be present.
@ -870,15 +934,48 @@ fn create_tracks<'a>(
}; };
let event_grid = event_grid_tick.to_delta(); let event_grid = event_grid_tick.to_delta();
let mut drums = Vec::new(); let mut drums = Vec::new();
// let midi_tempo = MidiTempo::from_tempo(Tempo(130)).0;
drums.push(TrackEvent { delta: 0.into(), kind: TrackEventKind::Midi { channel: 9.into(), message: MidiMessage::ProgramChange { program: 127.into() } } } ); // This is likely to be specific to Guitar Pro. Tested with Guitar Pro 7.
// drums.push(TrackEvent { delta: 0.into(), kind: TrackEventKind::Meta(MetaMessage::TrackNumber(1.into())) }); drums.push(TrackEvent {
drums.push(TrackEvent { delta: 0.into(), kind: TrackEventKind::Meta(MetaMessage::TrackName(b"Drumkit")) }); delta: 0.into(),
// drums.push(TrackEvent { delta: 0.into(), kind: TrackEventKind::Meta(MetaMessage::InstrumentName(b"Drum kit")) }); kind: TrackEventKind::Midi {
drums.push(TrackEvent { delta: 0.into(), kind: TrackEventKind::Meta(MetaMessage::MidiChannel(10.into())) }); channel: 9.into(),
drums.push(TrackEvent { delta: 0.into(), kind: TrackEventKind::Meta(MetaMessage::MidiPort(10.into())) }); message: MidiMessage::ProgramChange { program: 0.into() },
},
});
drums.push(TrackEvent {
delta: 0.into(),
kind: TrackEventKind::Meta(MetaMessage::TrackName(b"Drumkit")),
});
drums.push(TrackEvent {
delta: 0.into(),
kind: TrackEventKind::Meta(MetaMessage::InstrumentName(b"Drumkit")),
});
drums.push(TrackEvent {
delta: 0.into(),
kind: TrackEventKind::Meta(MetaMessage::MidiChannel(10.into())),
});
drums.push(TrackEvent {
delta: 0.into(),
kind: TrackEventKind::Meta(MetaMessage::MidiPort(10.into())),
});
let midi_tempo = MidiTempo::from_tempo(130).0;
// drums.push(TrackEvent { delta: 0.into(), kind: TrackEventKind::Meta(MetaMessage::Tempo(midi_tempo)) }); // drums.push(TrackEvent { delta: 0.into(), kind: TrackEventKind::Meta(MetaMessage::Tempo(midi_tempo)) });
// drums.push(TrackEvent { delta: 0.into(), kind: TrackEventKind::Meta(MetaMessage::TimeSignature(4, 4, MIDI_CLOCKS_PER_CLICK.clone(), 8))});
let (midi_time_signature_numerator, midi_time_signature_denominator) =
time_signature.to_midi();
println!("Midi time signature: {}, {}", midi_time_signature_numerator, midi_time_signature_denominator);
drums.push(TrackEvent {
delta: 0.into(),
kind: TrackEventKind::Meta(MetaMessage::TimeSignature(
midi_time_signature_numerator,
midi_time_signature_denominator,
MIDI_CLOCKS_PER_CLICK.clone(),
8,
)),
});
for event in event_grid.events { for event in event_grid.events {
let midi_message = match event.event_type { let midi_message = match event.event_type {
NoteOn(part) => MidiMessage::NoteOn { NoteOn(part) => MidiMessage::NoteOn {
@ -898,7 +995,10 @@ fn create_tracks<'a>(
}, },
}) })
} }
drums.push(TrackEvent { delta: drums.last().unwrap().delta, kind: TrackEventKind::Meta(MetaMessage::EndOfTrack) }); drums.push(TrackEvent {
delta: drums.last().unwrap().delta,
kind: TrackEventKind::Meta(MetaMessage::EndOfTrack),
});
vec![drums] vec![drums]
} }

View file

@ -14,12 +14,25 @@ pub struct TimeSignature {
} }
impl TimeSignature { impl TimeSignature {
pub fn new(numerator: u8, denominator: BasicLength) -> Self { pub(crate) fn new(numerator: u8, denominator: BasicLength) -> Self {
Self { Self {
numerator, numerator,
denominator, denominator,
} }
} }
pub(crate) fn to_midi(&self) -> (u8, u8) {
let denominator = match self.denominator {
Whole => 0, // FIXME: should it be an error?
Half => 1,
Fourth => 2,
Eighth => 3,
Sixteenth => 4,
ThirtySecond => 5,
SixtyFourth => 6,
};
(self.numerator, denominator)
}
} }
impl FromStr for TimeSignature { impl FromStr for TimeSignature {