Skip to main content

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