mirror of
https://github.com/dredozubov/polyrhythmix.git
synced 2024-11-25 21:37:43 +00:00
Midi meta events added: time signatures
This commit is contained in:
parent
c52f1e3ff4
commit
fd78edb4f6
3 changed files with 163 additions and 38 deletions
|
@ -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),
|
||||||
|
|
172
src/midi/core.rs
172
src/midi/core.rs
|
@ -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]
|
||||||
}
|
}
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue