1use crate::defaults::{FloatType, IntegerType};
5use crate::error::{Error, Result};
6use crate::instrument::Instrument;
7use crate::notation::Lyric;
8use crate::note::Note;
9use crate::stream::{Stream, StreamElement, StreamEvent};
10use crate::volume::Volume;
11
12const TICKS_PER_QUARTER: i64 = 10080;
14const TICKS_AT_START: i64 = TICKS_PER_QUARTER;
17
18const NOTE_OFF: u8 = 0x80;
19const NOTE_ON: u8 = 0x90;
20const PROGRAM_CHANGE: u8 = 0xC0;
21const PITCH_BEND: u8 = 0xE0;
22const SEQUENCE_TRACK_NAME: u8 = 0x03;
23const LYRIC: u8 = 0x05;
24const END_OF_TRACK: u8 = 0x2F;
25const SET_TEMPO: u8 = 0x51;
26const TIME_SIGNATURE: u8 = 0x58;
27const KEY_SIGNATURE: u8 = 0x59;
28
29#[derive(Clone, Debug, PartialEq, Eq)]
31#[non_exhaustive]
32pub struct ExportOptions {
33 pub add_start_delay: bool,
36 pub add_end_delay: bool,
39 pub acceptable_channels: Option<Vec<u8>>,
43}
44
45impl Default for ExportOptions {
46 fn default() -> Self {
47 Self {
48 add_start_delay: false,
49 add_end_delay: true,
50 acceptable_channels: None,
51 }
52 }
53}
54
55pub fn to_midi(stream: &Stream, options: &ExportOptions) -> Result<Vec<u8>> {
91 let tracks = midi_tracks(stream, options)?;
92 let mut bytes = Vec::new();
93 bytes.extend_from_slice(b"MThd");
94 put_number(&mut bytes, 6, 4);
95 put_number(&mut bytes, 1, 2);
96 put_number(&mut bytes, tracks.len() as i64, 2);
97 put_number(&mut bytes, TICKS_PER_QUARTER, 2);
98 for track in tracks {
99 bytes.extend_from_slice(b"MTrk");
100 put_number(&mut bytes, track.len() as i64, 4);
101 bytes.extend_from_slice(&track);
102 }
103 Ok(bytes)
104}
105
106#[derive(Clone, Debug)]
109enum Kind {
110 NoteOn,
111 NoteOff,
112 PitchBend,
113 ProgramChange,
114 Meta(u8),
115}
116
117#[derive(Clone, Debug)]
118struct Event {
119 kind: Kind,
120 channel: Option<u8>,
121 first: u8,
123 second: u8,
125 data: Vec<u8>,
127 cent_shift: Option<IntegerType>,
128 corresponding: Option<usize>,
130}
131
132impl Event {
133 fn new(kind: Kind) -> Self {
134 Self {
135 kind,
136 channel: Some(1),
137 first: 0,
138 second: 0,
139 data: Vec::new(),
140 cent_shift: None,
141 corresponding: None,
142 }
143 }
144
145 fn meta(kind: u8, data: Vec<u8>) -> Self {
146 Self {
147 data,
148 ..Self::new(Kind::Meta(kind))
149 }
150 }
151
152 fn sort_order(&self) -> i32 {
155 match self.kind {
156 Kind::NoteOff => -20,
157 Kind::PitchBend => -10,
158 _ => 0,
159 }
160 }
161
162 fn bend(cents: FloatType) -> Self {
164 let range = 200.0;
165 let cents = cents.clamp(-range, range);
166 let center = 0x2000;
167 let shift = if cents > 0.0 {
168 (cents / range * FloatType::from(0x4000 - 1 - center)).round_ties_even()
169 } else if cents < 0.0 {
170 (cents / range * FloatType::from(center)).round_ties_even()
171 } else {
172 0.0
173 };
174 let target = center + shift as i32;
175 Self {
176 first: (target & 0x7F) as u8,
177 second: ((target >> 7) & 0x7F) as u8,
178 ..Self::new(Kind::PitchBend)
179 }
180 }
181
182 fn write(&self, out: &mut Vec<u8>) {
184 let channel = self.channel.unwrap_or(1).saturating_sub(1);
185 match self.kind {
186 Kind::NoteOn | Kind::NoteOff | Kind::PitchBend => {
187 let status = match self.kind {
188 Kind::NoteOn => NOTE_ON,
189 Kind::NoteOff => NOTE_OFF,
190 _ => PITCH_BEND,
191 };
192 out.extend_from_slice(&[status + channel, self.first, self.second]);
193 }
194 Kind::ProgramChange => out.extend_from_slice(&[PROGRAM_CHANGE + channel, self.first]),
195 Kind::Meta(kind) => {
196 out.extend_from_slice(&[0xFF, kind]);
197 put_variable_length(out, self.data.len() as i64);
198 out.extend_from_slice(&self.data);
199 }
200 }
201 }
202}
203
204fn put_number(out: &mut Vec<u8>, number: i64, length: usize) {
206 for index in 0..length {
207 out.push(((number >> (8 * (length - 1 - index))) & 0xFF) as u8);
208 }
209}
210
211fn put_variable_length(out: &mut Vec<u8>, mut number: i64) {
213 let mut bytes = vec![(number & 0x7F) as u8];
214 number >>= 7;
215 while number > 0 {
216 bytes.push((number & 0x7F) as u8 | 0x80);
217 number >>= 7;
218 }
219 bytes.reverse();
220 out.extend_from_slice(&bytes);
221}
222
223fn ticks(quarter_length: FloatType) -> i64 {
226 (quarter_length * TICKS_PER_QUARTER as FloatType).round_ties_even() as i64
227}
228
229struct Track {
233 elements: Vec<(FloatType, StreamElement)>,
234 conductor: bool,
235 id: Option<String>,
238}
239
240impl Track {
241 fn split_from(&self) -> Option<&str> {
244 self.id
245 .as_deref()
246 .and_then(|id| id.rsplit_once("-Staff").map(|(part, _)| part))
247 }
248
249 fn dynamic_at_or_before(&self, offset: FloatType) -> Option<(FloatType, FloatType)> {
251 self.elements
252 .iter()
253 .rev()
254 .find(|(at, element)| *at <= offset && matches!(element, StreamElement::Dynamic(_)))
255 .and_then(|(at, element)| match element {
256 StreamElement::Dynamic(dynamic) => Some((*at, dynamic.volume_scalar())),
257 _ => None,
258 })
259 }
260}
261
262fn tracks_of(stream: &Stream) -> Result<Vec<Track>> {
266 let holds_measures = stream.recurse().iter().any(|(_, element)| {
268 matches!(element, StreamElement::Stream(inner) if inner.kind() == crate::stream::StreamKind::Measure)
269 });
270 let mut stream = if holds_measures {
271 stream.expand_repeats()?
272 } else {
273 stream.clone()
274 };
275 let conductor = conductor_of(&mut stream);
276 let mut parts: Vec<Stream> = if stream.has_part_like_streams() {
277 stream
278 .events()
279 .iter()
280 .filter_map(|event| event.element().as_stream().cloned())
281 .collect()
282 } else {
283 vec![stream]
284 };
285 let mut tracks = vec![Track {
286 elements: conductor,
287 conductor: true,
288 id: None,
289 }];
290 for part in &mut parts {
291 part.strip_ties(true)?;
292 tracks.push(Track {
293 elements: flattened(part),
294 conductor: false,
295 id: part.id().map(str::to_string),
296 });
297 }
298 Ok(tracks)
299}
300
301fn conductor_of(stream: &mut Stream) -> Vec<(FloatType, StreamElement)> {
305 let kinds: [fn(&StreamElement) -> bool; 3] = [
306 |element| matches!(element, StreamElement::MetronomeMark(_)),
307 |element| matches!(element, StreamElement::TimeSignature(_)),
308 |element| {
309 matches!(
310 element,
311 StreamElement::KeySignature(_) | StreamElement::Key(_)
312 )
313 },
314 ];
315 let mut conductor: Vec<StreamEvent> = Vec::new();
316 for kind in kinds {
317 let mut last = -1.0;
318 for (offset, element) in stream.recurse() {
319 if !kind(element) {
320 continue;
321 }
322 if offset > last {
323 conductor.push(StreamEvent::new(offset, element.clone()));
324 }
325 last = offset;
326 }
327 }
328 stream.retain_leaves(&mut |_, element| !kinds.iter().any(|kind| kind(element)));
329 if !conductor
330 .iter()
331 .any(|event| matches!(event.element(), StreamElement::MetronomeMark(_)))
332 {
333 conductor.push(StreamEvent::new(
334 0.0,
335 StreamElement::MetronomeMark(crate::tempo::MetronomeMark::new(120.0)),
336 ));
337 }
338 if !conductor
339 .iter()
340 .any(|event| matches!(event.element(), StreamElement::TimeSignature(_)))
341 {
342 conductor.push(StreamEvent::new(
343 0.0,
344 StreamElement::TimeSignature(
345 crate::meter::TimeSignature::new(4, 4).expect("four-four is a meter"),
346 ),
347 ));
348 }
349 conductor.sort_by(crate::makenotation::event_order);
350 conductor
351 .into_iter()
352 .map(|event| (event.offset(), event.element().clone()))
353 .collect()
354}
355
356fn flattened(stream: &Stream) -> Vec<(FloatType, StreamElement)> {
360 let mut events: Vec<StreamEvent> = stream
361 .leaves()
362 .into_iter()
363 .map(|(offset, element)| {
364 StreamEvent::new(crate::makenotation::op_frac(offset), element.clone())
365 })
366 .collect();
367 events.sort_by(crate::makenotation::event_order);
368 events
369 .into_iter()
370 .map(|event| (event.offset(), event.element().clone()))
371 .collect()
372}
373
374#[derive(Clone, Debug)]
377struct Packet {
378 track: usize,
379 offset: i64,
380 event: usize,
381 cent_shift: Option<IntegerType>,
382 duration: i64,
383 last_instrument: Option<Instrument>,
384 initial_channel: Option<u8>,
385}
386
387#[derive(Clone, Debug)]
391enum Initial {
392 None,
393 Conductor,
394 Percussion,
395 Instrument(Box<Instrument>),
396}
397
398impl Initial {
399 fn program(&self) -> Option<u8> {
400 match self {
401 Self::Instrument(instrument) => instrument.midi_program(),
402 _ => None,
403 }
404 }
405}
406
407fn midi_tracks(stream: &Stream, options: &ExportOptions) -> Result<Vec<Vec<u8>>> {
408 let tracks = tracks_of(stream)?;
409 let (by_program, dynamic) = channel_instrument_data(&tracks, options);
410
411 let mut events: Vec<Event> = Vec::new();
412 let mut packets: Vec<Packet> = Vec::new();
413 let mut initials: Vec<(Initial, Option<u8>)> = Vec::new();
414 for (id, track) in tracks.iter().enumerate() {
415 let first = track
416 .elements
417 .iter()
418 .find_map(|(offset, element)| match element {
419 StreamElement::Instrument(instrument) => {
420 Some((*offset, instrument.as_ref().clone()))
421 }
422 _ => None,
423 });
424 let initial = match first {
425 Some((offset, instrument)) if offset == 0.0 && instrument.midi_program().is_some() => {
426 Initial::Instrument(Box::new(instrument))
427 }
428 first => {
429 let strokes = track.elements.iter().any(|(_, element)| {
430 matches!(
431 element,
432 StreamElement::Unpitched(_) | StreamElement::PercussionChord(_)
433 )
434 });
435 let sounds = track
436 .elements
437 .iter()
438 .any(|(_, element)| crate::makenotation::is_general_note(element));
439 if strokes {
440 Initial::Percussion
441 } else if id == 0 && !sounds {
442 Initial::Conductor
443 } else {
444 first.map_or(Initial::None, |(_, instrument)| {
445 Initial::Instrument(Box::new(instrument))
446 })
447 }
448 }
449 };
450 let channel = match &initial {
451 Initial::None => Some(by_program.get(&None).copied().unwrap_or(1)),
452 Initial::Percussion => Some(10),
453 Initial::Conductor => None,
454 Initial::Instrument(instrument) => Some(
455 by_program
456 .get(&instrument.midi_program())
457 .copied()
458 .unwrap_or(1),
459 ),
460 };
461 let mut track_packets = track_packets(id, &tracks, options, &mut events)?;
462 for packet in &mut track_packets {
463 packet.initial_channel = channel;
464 }
465 packets.extend(track_packets);
466 initials.push((initial, channel));
467 }
468
469 let packets = assign_channels(packets, &mut events, &dynamic, &initials)?;
470
471 let mut written = Vec::new();
472 for (id, (initial, channel)) in initials.iter().enumerate() {
473 let mut out: Vec<u8> = Vec::new();
474 start_events(&mut out, initial, *channel);
475 let mut last = 0;
476 for packet in packets.iter().filter(|packet| packet.track == id) {
477 let delta = packet.offset - last;
478 if delta < 0 {
479 return Err(Error::midi("got a negative delta time".to_string()));
480 }
481 put_variable_length(&mut out, delta);
482 events[packet.event].write(&mut out);
483 last = packet.offset;
484 }
485 put_variable_length(
486 &mut out,
487 if options.add_end_delay {
488 TICKS_AT_START
489 } else {
490 0
491 },
492 );
493 Event::meta(END_OF_TRACK, Vec::new()).write(&mut out);
494 written.push(out);
495 }
496 Ok(written)
497}
498
499fn start_events(out: &mut Vec<u8>, initial: &Initial, channel: Option<u8>) {
502 let name = match initial {
503 Initial::Conductor => return,
504 Initial::Instrument(instrument) => instrument.best_name().unwrap_or("").to_string(),
505 Initial::Percussion => percussion().best_name().unwrap_or("").to_string(),
506 Initial::None => String::new(),
507 };
508 put_variable_length(out, 0);
509 Event::meta(SEQUENCE_TRACK_NAME, name.into_bytes()).write(out);
510 if let Some(program) = initial.program() {
511 put_variable_length(out, 0);
512 Event {
513 first: program,
514 channel,
515 ..Event::new(Kind::ProgramChange)
516 }
517 .write(out);
518 }
519}
520
521fn percussion() -> Instrument {
524 Instrument::of_kind("UnpitchedPercussion").unwrap_or_default()
525}
526
527fn channel_instrument_data(
531 tracks: &[Track],
532 options: &ExportOptions,
533) -> (Vec<(Option<u8>, u8)>, Vec<u8>) {
534 let mut acceptable: Vec<u8> = options
535 .acceptable_channels
536 .clone()
537 .unwrap_or_else(|| (1..=9).chain(11..=16).collect());
538 let mut programs: Vec<Option<u8>> = Vec::new();
539 let mut by_program: Vec<(Option<u8>, u8)> = Vec::new();
540 let mut assigned: Vec<u8> = Vec::new();
541 for track in tracks.iter().filter(|track| !track.conductor) {
542 let mut any = false;
543 for (_, element) in &track.elements {
544 let StreamElement::Instrument(instrument) = element else {
545 continue;
546 };
547 let program = instrument.midi_program();
548 if let Some(channel) = instrument.midi_channel()
549 && !by_program.has(&program)
550 {
551 let mut channel = channel + 1;
552 if let Some(index) = acceptable.iter().position(|ok| *ok == channel) {
553 acceptable.remove(index);
554 } else if channel != 10 {
555 channel = 1;
556 }
557 if !assigned.contains(&channel) {
558 assigned.push(channel);
559 }
560 by_program.push((program, channel));
561 }
562 if !programs.contains(&program) {
563 programs.push(program);
564 }
565 any = true;
566 }
567 if !any && !programs.contains(&None) {
568 programs.push(None);
569 }
570 }
571 let needed: Vec<Option<u8>> = programs
572 .into_iter()
573 .filter(|program| !by_program.has(program))
574 .collect();
575 for (index, program) in needed.into_iter().enumerate() {
576 let channel = if index + 1 < acceptable.len() {
577 acceptable[index]
578 } else {
579 acceptable.first().copied().unwrap_or(1)
580 };
581 if !assigned.contains(&channel) {
582 assigned.push(channel);
583 }
584 by_program.push((program, channel));
585 }
586 let dynamic = acceptable
587 .into_iter()
588 .filter(|channel| !assigned.contains(channel))
589 .collect();
590 (by_program, dynamic)
591}
592
593trait Keyed<K, V> {
595 fn get(&self, key: &K) -> Option<&V>;
596 fn has(&self, key: &K) -> bool {
597 self.get(key).is_some()
598 }
599}
600
601impl<K: PartialEq, V> Keyed<K, V> for Vec<(K, V)> {
602 fn get(&self, key: &K) -> Option<&V> {
603 self.iter()
604 .find(|(held, _)| held == key)
605 .map(|(_, value)| value)
606 }
607}
608
609fn track_packets(
611 id: usize,
612 tracks: &[Track],
613 options: &ExportOptions,
614 events: &mut Vec<Event>,
615) -> Result<Vec<Packet>> {
616 let track = &tracks[id];
617 let context_dynamic = |offset: FloatType| -> Option<FloatType> {
623 track
624 .dynamic_at_or_before(offset)
625 .map(|(_, scalar)| scalar)
626 .or_else(|| {
627 let part = track.split_from()?;
628 tracks
629 .iter()
630 .filter(|other| other.split_from() == Some(part))
631 .filter_map(|other| other.dynamic_at_or_before(offset))
632 .fold(
633 None,
634 |best: Option<(FloatType, FloatType)>, found| match best {
635 Some(best) if best.0 > found.0 => Some(best),
636 _ => Some(found),
637 },
638 )
639 .map(|(_, scalar)| scalar)
640 })
641 };
642 let as_stream = Stream::from_events(
643 track
644 .elements
645 .iter()
646 .map(|(offset, element)| StreamEvent::new(*offset, element.clone()))
647 .collect::<Vec<_>>(),
648 );
649 let in_force = crate::volume::dynamics_in_force(&as_stream);
650 let leaves = as_stream.leaves();
651 let dynamic_at = |index: usize| -> Option<FloatType> {
652 in_force[index].and_then(|position| match leaves[position].1 {
653 StreamElement::Dynamic(dynamic) => Some(dynamic.volume_scalar()),
654 _ => None,
655 })
656 };
657
658 let mut packets = Vec::new();
659 let mut last_instrument: Option<Instrument> = None;
660 let mut percussion_in_force: Option<u8> = None;
661 for (index, (offset, element)) in track.elements.iter().enumerate() {
662 if let StreamElement::Instrument(instrument) = element {
663 last_instrument = Some(instrument.as_ref().clone());
664 if instrument.is_a("MidiPercussionMapMixin")
667 && let Some(pitch) = instrument.percussion_pitch()
668 {
669 percussion_in_force = Some(pitch);
670 }
671 }
672 let list = element_events(
673 element,
674 dynamic_at(index),
675 context_dynamic(*offset),
676 percussion_in_force,
677 )?;
678 let Some(list) = list else {
679 continue;
680 };
681 let duration = ticks(element.quarter_length());
682 let mut note_played = false;
683 let mut waiting: std::collections::VecDeque<usize> = std::collections::VecDeque::new();
685 for mut event in list {
686 if matches!(event.kind, Kind::NoteOn) {
687 note_played = true;
688 }
689 let mut at = ticks(*offset);
690 if note_played && options.add_start_delay {
691 at += TICKS_AT_START;
692 }
693 let is_off = matches!(event.kind, Kind::NoteOff);
694 if is_off {
695 at += duration;
696 }
697 let index = events.len();
698 if matches!(event.kind, Kind::NoteOn) {
699 waiting.push_back(index);
700 } else if is_off {
701 let on = waiting.pop_front().expect("a note-off follows its note-on");
702 events[on].corresponding = Some(index);
703 event.corresponding = Some(on);
704 }
705 packets.push(Packet {
706 track: id,
707 offset: at,
708 event: index,
709 cent_shift: event.cent_shift,
710 duration: if is_off { 0 } else { duration },
711 last_instrument: last_instrument.clone(),
712 initial_channel: None,
713 });
714 events.push(event);
715 }
716 }
717 packets.sort_by_key(|packet| (packet.offset, events[packet.event].sort_order()));
718 Ok(packets)
719}
720
721fn element_events(
724 element: &StreamElement,
725 dynamic: Option<FloatType>,
726 context_dynamic: Option<FloatType>,
727 percussion_in_force: Option<u8>,
728) -> Result<Option<Vec<Event>>> {
729 let shift = |articulations: &[crate::articulations::Articulation]| -> FloatType {
730 articulations
731 .iter()
732 .map(crate::articulations::Articulation::volume_shift)
733 .sum()
734 };
735 let velocity = |volume: &Volume, dynamic: Option<FloatType>, shift: FloatType| -> u8 {
736 (volume.realized_with(dynamic, shift, 0.5, true) * 127.0).round_ties_even() as u8
737 };
738 let stroke_pitch = |stored: Option<&Instrument>| -> u8 {
739 stored
740 .filter(|instrument| instrument.is_a("MidiPercussionMapMixin"))
741 .and_then(Instrument::percussion_pitch)
742 .or(percussion_in_force)
743 .unwrap_or(60)
744 };
745 Ok(Some(match element {
746 StreamElement::Note(note) => single(
747 note,
748 note.pitch().midi(),
749 pitch_shift(note.pitch()),
750 velocity(¬e.volume(), dynamic, shift(note.articulations())),
751 1,
752 ),
753 StreamElement::Unpitched(stroke) => {
754 let note = stroke.written();
755 single(
756 note,
757 IntegerType::from(stroke_pitch(stroke.stored_instrument())),
758 None,
759 velocity(¬e.volume(), dynamic, shift(note.articulations())),
760 10,
761 )
762 }
763 StreamElement::Chord(chord) => {
764 let members: Vec<(IntegerType, Option<IntegerType>, u8)> =
765 if chord.has_component_volumes() {
766 chord
767 .notes()
768 .iter()
769 .map(|note| {
770 (
771 note.pitch().midi(),
772 pitch_shift(note.pitch()),
773 velocity(
774 ¬e.volume(),
775 context_dynamic,
776 shift(chord.articulations()),
777 ),
778 )
779 })
780 .collect()
781 } else {
782 let loud = velocity(&chord.volume(), dynamic, shift(chord.articulations()));
783 chord
784 .notes()
785 .iter()
786 .map(|note| (note.pitch().midi(), pitch_shift(note.pitch()), loud))
787 .collect()
788 };
789 together(&lyric_text(chord.lyrics()), &members, 1)
790 }
791 StreamElement::ChordSymbol(symbol) => {
792 let loud = velocity(&Volume::default(), dynamic, 0.0);
793 let members: Vec<_> = symbol
794 .pitches()
795 .unwrap_or_default()
796 .iter()
797 .map(|pitch| (pitch.midi(), pitch_shift(pitch), loud))
798 .collect();
799 together("", &members, 1)
800 }
801 StreamElement::PercussionChord(chord) => {
802 let written = chord.written();
803 let component = written.has_component_volumes();
804 let loud = velocity(&written.volume(), dynamic, shift(written.articulations()));
805 let members: Vec<_> = written
806 .notes()
807 .iter()
808 .enumerate()
809 .map(|(index, note)| {
810 let velocity = if component {
811 velocity(
812 ¬e.volume(),
813 context_dynamic,
814 shift(written.articulations()),
815 )
816 } else {
817 loud
818 };
819 if chord.is_unpitched(index) {
820 (
821 IntegerType::from(stroke_pitch(written.stored_instrument())),
822 None,
823 velocity,
824 )
825 } else {
826 (note.pitch().midi(), pitch_shift(note.pitch()), velocity)
827 }
828 })
829 .collect();
830 together(&lyric_text(written.lyrics()), &members, 10)
831 }
832 StreamElement::TimeSignature(meter) => {
833 let numerator = meter.numerator();
834 if numerator > 255 {
835 return Ok(Some(Vec::new()));
836 }
837 let denominator = FloatType::from(meter.denominator()).log2() as u8;
838 vec![Event::meta(
839 TIME_SIGNATURE,
840 vec![numerator as u8, denominator, 24, 8],
841 )]
842 }
843 StreamElement::KeySignature(signature) => key_events(signature.sharps(), false),
844 StreamElement::Key(key) => key_events(key.key_signature().sharps(), key.mode() == "minor"),
845 StreamElement::MetronomeMark(mark) => {
846 if mark.number().is_none() && mark.number_sounding().is_none() {
847 return Ok(None);
848 }
849 let Some(bpm) = mark.sounding_quarter_bpm() else {
850 return Ok(None);
851 };
852 let mut data = Vec::new();
853 put_number(&mut data, (60_000_000.0 / bpm).round_ties_even() as i64, 3);
854 vec![Event::meta(SET_TEMPO, data)]
855 }
856 StreamElement::Instrument(instrument) => vec![Event {
857 first: instrument.midi_program().unwrap_or(0),
858 ..Event::new(Kind::ProgramChange)
859 }],
860 _ => return Ok(None),
861 }))
862}
863
864fn key_events(sharps: IntegerType, minor: bool) -> Vec<Event> {
865 vec![Event::meta(
866 KEY_SIGNATURE,
867 vec![sharps.rem_euclid(256) as u8, u8::from(minor)],
868 )]
869}
870
871fn pitch_shift(pitch: &crate::pitch::Pitch) -> Option<IntegerType> {
874 (!pitch.is_twelve_tone()).then(|| pitch.cent_shift_from_midi())
875}
876
877fn lyric_text(lyrics: &[Lyric]) -> String {
879 lyrics
880 .iter()
881 .filter_map(Lyric::explicit_text)
882 .collect::<Vec<_>>()
883 .join("\n")
884}
885
886fn single(
888 note: &Note,
889 key: IntegerType,
890 cent_shift: Option<IntegerType>,
891 velocity: u8,
892 channel: u8,
893) -> Vec<Event> {
894 let mut events = Vec::new();
895 let lyric = lyric_text(note.lyrics());
896 if !lyric.is_empty() {
897 events.push(Event::meta(LYRIC, lyric.into_bytes()));
898 }
899 let on = Event {
900 first: key as u8,
901 second: velocity,
902 channel: Some(channel),
903 cent_shift,
904 ..Event::new(Kind::NoteOn)
905 };
906 let off = Event {
907 kind: Kind::NoteOff,
908 second: 0,
909 ..on.clone()
910 };
911 events.push(on);
912 events.push(off);
913 events
914}
915
916fn together(
918 lyric: &str,
919 members: &[(IntegerType, Option<IntegerType>, u8)],
920 channel: u8,
921) -> Vec<Event> {
922 let mut events = Vec::new();
923 if !lyric.is_empty() {
924 events.push(Event::meta(LYRIC, lyric.as_bytes().to_vec()));
925 }
926 for (key, cent_shift, velocity) in members {
927 events.push(Event {
928 first: *key as u8,
929 second: *velocity,
930 cent_shift: *cent_shift,
931 ..Event::new(Kind::NoteOn)
932 });
933 }
934 for (key, cent_shift, _) in members {
935 events.push(Event {
936 first: *key as u8,
937 second: 0,
938 channel: Some(channel),
939 cent_shift: *cent_shift,
940 ..Event::new(Kind::NoteOff)
941 });
942 }
943 events
944}
945
946fn assign_channels(
950 packets: Vec<Packet>,
951 events: &mut Vec<Event>,
952 dynamic: &[u8],
953 initials: &[(Initial, Option<u8>)],
954) -> Result<Vec<Packet>> {
955 let mut unique: Vec<((i64, i64, u8), Vec<IntegerType>)> = Vec::new();
956 let mut post: Vec<Packet> = Vec::new();
957 let mut used_tracks: Vec<usize> = Vec::new();
958 let bend = |events: &mut Vec<Event>, cents: FloatType, channel: Option<u8>| {
959 events.push(Event {
960 channel,
961 ..Event::bend(cents)
962 });
963 events.len() - 1
964 };
965 let plain = |track: usize, offset: i64, event: usize| Packet {
966 track,
967 offset,
968 event,
969 cent_shift: None,
970 duration: 0,
971 last_instrument: None,
972 initial_channel: None,
973 };
974 for packet in packets {
975 if !used_tracks.contains(&packet.track) {
976 used_tracks.push(packet.track);
977 }
978 let kind = events[packet.event].kind.clone();
979 if !matches!(kind, Kind::NoteOn) {
980 if !matches!(kind, Kind::NoteOff) {
981 events[packet.event].channel = packet.initial_channel;
982 }
983 let (track, offset, shift, channel) = (
984 packet.track,
985 packet.offset,
986 packet.cent_shift,
987 events[packet.event].channel,
988 );
989 post.push(packet);
990 if matches!(kind, Kind::NoteOff) && shift.is_some_and(|shift| shift != 0) {
991 let event = bend(events, 0.0, channel);
992 post.push(plain(track, offset, event));
993 }
994 continue;
995 }
996 events[packet.event].channel = packet.initial_channel;
997 let start = packet.offset;
998 let end = packet.offset + packet.duration;
999 let shift = packet.cent_shift.filter(|shift| *shift != 0);
1000 let mut exclude: Vec<u8> = Vec::new();
1001 for ((used_start, used_stop, used_channel), shifts) in &unique {
1002 let (used_start, used_stop) = (*used_start, *used_stop);
1003 let overlaps = (start <= used_start && used_start < end)
1004 || (start < used_stop && used_stop < end)
1005 || (used_start <= start && start < used_stop)
1006 || (used_start < end && end < used_stop);
1007 if overlaps
1008 && (!shifts.is_empty() || shift.is_some())
1009 && !exclude.contains(used_channel)
1010 {
1011 exclude.push(*used_channel);
1012 }
1013 }
1014 let corresponding = events[packet.event]
1015 .corresponding
1016 .expect("a note-on has its note-off");
1017 let channel = if exclude.is_empty() {
1018 let channel = events[packet.event].channel;
1019 events[corresponding].channel = channel;
1020 channel
1021 } else {
1022 let channel = dynamic
1023 .iter()
1024 .copied()
1025 .find(|channel| !exclude.contains(channel))
1026 .ok_or_else(|| {
1027 Error::midi(
1028 "no unused channels available for microtone/instrument assignment"
1029 .to_string(),
1030 )
1031 })?;
1032 events[packet.event].channel = Some(channel);
1033 events[corresponding].channel = Some(channel);
1034 if let Some(instrument) = &packet.last_instrument {
1035 events.push(Event {
1036 first: instrument.midi_program().unwrap_or(0),
1037 channel: Some(channel),
1038 ..Event::new(Kind::ProgramChange)
1039 });
1040 post.push(plain(packet.track, start, events.len() - 1));
1041 }
1042 Some(channel)
1043 };
1044 if let Some(shift) = shift {
1045 let event = bend(events, FloatType::from(shift), channel);
1046 post.push(plain(packet.track, start, event));
1047 }
1048 let key = (
1049 packet.offset,
1050 packet.offset + packet.duration,
1051 channel.unwrap_or(1),
1052 );
1053 let held = match unique.iter().position(|(held, _)| *held == key) {
1054 Some(index) => index,
1055 None => {
1056 unique.push((key, Vec::new()));
1057 unique.len() - 1
1058 }
1059 };
1060 if let Some(shift) = shift {
1061 unique[held].1.push(shift);
1062 }
1063 post.push(packet);
1064 }
1065 for track in used_tracks {
1066 if track == 0 {
1067 continue;
1068 }
1069 let Some(channel) = initials.get(track).and_then(|(_, channel)| *channel) else {
1070 continue;
1071 };
1072 let event = bend(events, 0.0, Some(channel));
1073 post.push(plain(track, 0, event));
1074 }
1075 post.sort_by_key(|packet| (packet.offset, events[packet.event].sort_order()));
1076 Ok(post)
1077}
1078
1079#[cfg(test)]
1080mod tests {
1081 use super::*;
1082 use crate::notation::{Tie, TieType};
1083 use crate::{Duration, Note, StreamKind};
1084
1085 fn variable_length(number: i64) -> Vec<u8> {
1086 let mut out = Vec::new();
1087 put_variable_length(&mut out, number);
1088 out
1089 }
1090
1091 #[test]
1094 fn a_whistle_stroke_plays_its_percussion_key() {
1095 let key_of = |stored: Option<Instrument>, in_force: Option<u8>| {
1096 let mut stroke = crate::percussion::Unpitched::new();
1097 stroke.written_mut().set_stored_instrument(stored);
1098 let events = element_events(&StreamElement::Unpitched(stroke), None, None, in_force)
1099 .unwrap()
1100 .unwrap();
1101 events
1102 .iter()
1103 .find(|event| matches!(event.kind, Kind::NoteOn))
1104 .map(|event| event.first)
1105 };
1106 let mut long = Instrument::of_kind("Whistle").unwrap();
1107 long.set_modifier("long");
1108 assert_eq!(key_of(Some(long), None), Some(72));
1109 assert_eq!(
1110 key_of(Some(Instrument::of_kind("Whistle").unwrap()), None),
1111 Some(71)
1112 );
1113 assert_eq!(
1114 key_of(Some(Instrument::of_kind("Flute").unwrap()), None),
1115 Some(60)
1116 );
1117 }
1118
1119 #[test]
1120 fn numbers_are_written_seven_bits_a_byte() {
1121 assert_eq!(variable_length(0), [0x00]);
1122 assert_eq!(variable_length(0x7F), [0x7F]);
1123 assert_eq!(variable_length(0x80), [0x81, 0x00]);
1124 assert_eq!(variable_length(10080), [0xCE, 0x60]);
1125 }
1126
1127 #[test]
1128 fn a_bend_spans_two_semitones_either_way() {
1129 let middle = Event::bend(0.0);
1130 assert_eq!((middle.first, middle.second), (0x00, 0x40));
1131 let up = Event::bend(200.0);
1132 assert_eq!((up.first, up.second), (0x7F, 0x7F));
1133 let down = Event::bend(-300.0);
1134 assert_eq!((down.first, down.second), (0x00, 0x00));
1135 }
1136
1137 #[test]
1138 fn tied_notes_sound_once_after_a_conductor_saying_120_and_four_four() {
1139 let mut part = Stream::with_kind(StreamKind::Part);
1140 let mut first = Note::from_name("C4").unwrap();
1141 first.set_tie(Some(Tie::new(TieType::Start)));
1142 let mut second = Note::from_name("C4")
1143 .unwrap()
1144 .with_duration(Duration::half());
1145 second.set_tie(Some(Tie::new(TieType::Stop)));
1146 part.push(first);
1147 part.push(second);
1148 let bytes = to_midi(&part, &ExportOptions::default()).unwrap();
1149 assert_eq!(&bytes[10..12], [0, 2]);
1151 assert!(
1153 bytes
1154 .windows(6)
1155 .any(|window| window == [0xFF, 0x51, 0x03, 0x07, 0xA1, 0x20])
1156 );
1157 assert!(
1158 bytes
1159 .windows(7)
1160 .any(|window| window == [0xFF, 0x58, 0x04, 0x04, 0x02, 0x18, 0x08])
1161 );
1162 let note_ons = bytes
1163 .windows(2)
1164 .filter(|window| window == &[0x90, 60])
1165 .count();
1166 assert_eq!(note_ons, 1);
1167 assert!(
1169 bytes
1170 .windows(4)
1171 .any(|window| window == [0x81, 0xEC, 0x20, 0x80])
1172 );
1173 }
1174}