Skip to main content

music21/midi/
write.rs

1//! A score written as a standard MIDI file: music21's
2//! `midi.translate.streamToMidiFile` and the byte writing of `midi.base`.
3
4use 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
12/// music21's `defaults.ticksPerQuarter`, its MusicXML divisions.
13const TICKS_PER_QUARTER: i64 = 10080;
14/// music21's `defaults.ticksAtStart`: the delay before the first note and
15/// after the last, a quarter.
16const 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/// How [`to_midi`] writes a score: music21's `streamToMidiFile` keywords.
30#[derive(Clone, Debug, PartialEq, Eq)]
31#[non_exhaustive]
32pub struct ExportOptions {
33    /// Whether the notes start a quarter late, leaving a beat of silence
34    /// before the first: music21's `addStartDelay`, off by default.
35    pub add_start_delay: bool,
36    /// Whether each track ends a quarter after its last event: music21's
37    /// `addEndDelay`, on by default.
38    pub add_end_delay: bool,
39    /// The channels, counted from one, a part may be given:
40    /// music21's `acceptableChannelList`. `None` is every channel but the
41    /// tenth, which is the drums'.
42    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
55/// Writes a stream as a standard MIDI file, as music21's
56/// `streamToMidiFile(...).writestr()` writes it.
57///
58/// A score is a track for each part after a conductor track holding every
59/// tempo, meter and key signature, the first of each at any one time; any
60/// other stream is one part. The conductor says a tempo of 120 and a meter
61/// of `4/4` where the score says neither. Each part names itself after its
62/// first instrument, takes a channel for each instrument's program, and
63/// plays each note at the velocity its volume comes to under the dynamic
64/// in force where it stands and its articulations; tied notes sound as one.
65/// A pitch between the keys is played on a channel of its own, bent from
66/// the key below. Unpitched strokes sound on the tenth channel, as the drum
67/// their instrument's percussion map names. Chord symbols sound as the
68/// chords they are, for as long as they last.
69///
70/// ```
71/// use music21::midi::{from_midi, to_midi, ExportOptions};
72/// use music21::{Duration, Note, Stream, StreamKind};
73///
74/// let mut part = Stream::with_kind(StreamKind::Part);
75/// part.push(Note::from_name("C4")?.with_duration(Duration::half()));
76/// part.push(Note::from_name("E4")?.with_duration(Duration::half()));
77/// let bytes = to_midi(&part, &ExportOptions::default())?;
78/// assert_eq!(&bytes[..4], b"MThd");
79/// assert_eq!(from_midi(&bytes)?.notes().len(), 2);
80/// # Ok::<(), music21::Error>(())
81/// ```
82///
83/// Repeats are played out first, as [`Stream::expand_repeats`] plays them.
84///
85/// # Errors
86///
87/// Repeats music21 cannot make sense of; more pitches between the keys
88/// sounding at once than there are channels to bend; and a run of tied
89/// notes after the first lasting no time.
90pub 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// ------------------------------------------------------------------ events
107
108#[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    /// A note's key or a bend's low seven bits; a program's number.
122    first: u8,
123    /// A note's velocity or a bend's high seven bits.
124    second: u8,
125    /// A meta event's data.
126    data: Vec<u8>,
127    cent_shift: Option<IntegerType>,
128    /// The note-off of a note-on, and the other way round.
129    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    /// music21's `MidiEvent.sortOrder`: a note-off before a bend, a bend
153    /// before anything else at one time.
154    fn sort_order(&self) -> i32 {
155        match self.kind {
156            Kind::NoteOff => -20,
157            Kind::PitchBend => -10,
158            _ => 0,
159        }
160    }
161
162    /// music21's `MidiEvent.setPitchBend`, over two semitones either way.
163    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    /// music21's `MidiEvent.getBytes`.
183    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
204/// music21's `putNumber`: `length` bytes, the most significant first.
205fn 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
211/// music21's `putVariableLengthNumber`.
212fn 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
223/// music21's `offsetToMidiTicks` and `durationToMidiTicks`, rounding as
224/// Python rounds.
225fn ticks(quarter_length: FloatType) -> i64 {
226    (quarter_length * TICKS_PER_QUARTER as FloatType).round_ties_even() as i64
227}
228
229// ------------------------------------------------------------------ prepare
230
231/// One track's elements, flattened, with the instrument it starts with.
232struct Track {
233    elements: Vec<(FloatType, StreamElement)>,
234    conductor: bool,
235    /// The part's id, by which the staves read from one part on several
236    /// know one another: `P1-Staff1`, `P1-Staff2`.
237    id: Option<String>,
238}
239
240impl Track {
241    /// The staff a part on several staves was split into, as the part it
242    /// was split from.
243    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    /// The scalar of the last dynamic at or before an offset.
250    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
262/// music21's `prepareStreamForMidi` and the walk of
263/// `streamHierarchyToMidiTracks` up to its packets: the conductor first,
264/// then each part, ties stripped and flattened.
265fn tracks_of(stream: &Stream) -> Result<Vec<Track>> {
266    // music21 plays the repeats out of anything holding measures.
267    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
301/// music21's `conductorStream`: every tempo, meter and key signature taken
302/// out of the stream, keeping for each kind the first at each later time,
303/// with a tempo of 120 and a meter of `4/4` where there are none.
304fn 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
356/// A stream's leaves in the order music21's `flatten` gives them, each at
357/// its offset as music21's fractions hold it, so that a note and a dynamic
358/// reached by different sums stand at one time.
359fn 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// ------------------------------------------------------------------ packets
375
376#[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/// The first instrument of a track as music21's `packetStorage` keeps it:
388/// one standing at the start with a program, unpitched percussion for a
389/// track of strokes, the conductor, or whatever came first.
390#[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
499/// music21's `getStartEvents`: the track's name, and the first
500/// instrument's program where it has one.
501fn 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
521/// music21's `UnpitchedPercussion()`, which a track of strokes with no
522/// instrument of its own is named after.
523fn percussion() -> Instrument {
524    Instrument::of_kind("UnpitchedPercussion").unwrap_or_default()
525}
526
527/// music21's `channelInstrumentData`: a channel for each program the parts
528/// play, an instrument's own channel where it names one, and the channels
529/// left over to bend microtones on.
530fn 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
593/// A list of pairs read as a map, in the order its keys were added.
594trait 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
609/// music21's `streamToPackets` over one flattened track.
610fn 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    // music21 realizes the notes of a chord that each carry a volume when
618    // it writes them, each volume answering to the chord: against the
619    // dynamic the chord's context search finds -- the last at or before it
620    // in its own part, or else in the part it was split from, which holds
621    // every staff of it -- and the chord's articulations.
622    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            // music21 reads the percussion map of any instrument in it,
665            // a `Whistle` -- a `Flute` -- among them.
666            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        // The note-offs come in the order of the note-ons they end.
684        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
721/// music21's `elementToMidiEventList`: what an element says as events, in
722/// the order music21 lists them, or nothing.
723fn 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(&note.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(&note.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                                    &note.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                            &note.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
871/// The cents a pitch between the keys is bent by, from the key it is
872/// played on.
873fn pitch_shift(pitch: &crate::pitch::Pitch) -> Option<IntegerType> {
874    (!pitch.is_twelve_tone()).then(|| pitch.cent_shift_from_midi())
875}
876
877/// music21's `GeneralNote.lyric`: every lyric's text, a line each.
878fn lyric_text(lyrics: &[Lyric]) -> String {
879    lyrics
880        .iter()
881        .filter_map(Lyric::explicit_text)
882        .collect::<Vec<_>>()
883        .join("\n")
884}
885
886/// music21's `noteToMidiEvents`.
887fn 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
916/// music21's `chordToMidiEvents`: every note-on, then every note-off.
917fn 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
946/// music21's `assignPacketsToChannels`: every event given its track's
947/// channel, a note sounding a pitch between the keys moved to a channel of
948/// its own while another note sounds, and a bend before it and after it.
949fn 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    /// The key a stroke sounds on: music21 reads the percussion map of any
1092    /// instrument in it, a `Whistle` among them though it is a `Flute`.
1093    #[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        // Two tracks.
1150        assert_eq!(&bytes[10..12], [0, 2]);
1151        // 500,000 microseconds a quarter, and four-four.
1152        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        // The one note lasts three quarters.
1168        assert!(
1169            bytes
1170                .windows(4)
1171                .any(|window| window == [0x81, 0xEC, 0x20, 0x80])
1172        );
1173    }
1174}