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