created_at2021-08-13 08:02:40.98948
updated_at2023-07-30 14:29:22.511045
descriptionFacilitate reading and writing midi files.



Midi reader writer

Facilitate reading and writing midi files. This library does not serialise or deserialise midi files, but uses another library for that. Currently supported is using the midly library, behind the engine-midly-0-5 feature.

In particular this create supports,

  • creating an iterator over all the tracks, merged;
  • given an iterator, separating the tracks, and
  • converting time stamps from ticks to microseconds and vice versa.


The following example illustrates the steps that could typically be used in an application that transforms midi data. Note: this example requires the convert-time feature, the engine-midly-0-5 feature, and the read feature.

use midi_reader_writer::{
    ConvertTicksToMicroseconds, ConvertMicroSecondsToTicks,
    midly_0_5::{exports::Smf, merge_tracks, TrackSeparator},
use std::{fs, error::Error, convert::TryFrom};

fn example(input_filename: &str, output_filename: &str) -> Result<(), Box<dyn Error>> {
    // Read the midi file
    let bytes = fs::read(input_filename)?;
    let input_midi_file = Smf::parse(&bytes)?;

    let mut ticks_to_microseconds = ConvertTicksToMicroseconds::try_from(input_midi_file.header)?;
    let mut microseconds_to_ticks = ConvertMicroSecondsToTicks::from(input_midi_file.header);
    let mut separator = TrackSeparator::new();

    // Iterate over the events from all tracks:
    for (ticks, track_index, event) in merge_tracks(&input_midi_file.tracks) {

        // Convert the ticks to microseconds:
        let microseconds = ticks_to_microseconds.convert(ticks, &event);

        // Do something with the event:
        // ... <- Insert your code here

        // Convert from microseconds to ticks:
        let new_ticks = microseconds_to_ticks.convert(microseconds, &event)?;

        // Push the event to the appropriate track.
        separator.push(ticks, track_index, event)?;

    // Save the output:
    let tracks = separator.collect();
    let output_midi_file = Smf {
        header: input_midi_file.header,


This crate has a number of cargo features:

  • convert-time (enabled by default): converting time stamps from ticks to microseconds and vice versa.
  • read (enabled by default): create an iterator over all the tracks of a midi file, merged
  • engine-midly-0-5: use version 0.5.x of the midly crate.

Note that at this point, this crate is not really useful without the engine-midly-0-5 feature.


The dependency tree of an application that plays midi files is currently probably similar to the following:

(your crate that plays midi files)
 ├── midi-reader-writer        : this crate
 │   ├── timestamp-stretcher   : high quality conversion from ticks to microseconds and vice versa
 │   └── midly                 : midi file parser and writer
 └── (a crate to play midi)

There are several options for playing midi, depending on the operating system(s) you want to target, and how you want to play the midi: as audio, or by sending midi messages. I would recommend to explore the list here. You can also ask for recommendations for your particular use case in the Rust audio discourse group.

Keep in mind that disk access is a no-go in a real-time audio context, since this may cause the audio to stutter. So it's best to do that in a separate thread. How that thread is managed, depends on the library that you use to play audio/midi. In order to communicate between the two threads, you can use the rtrb crate or any crate listed in the "Alternatives" section of its README.

Other related crates

  • Parsing MIDI
    • wmidi: high-level encoding and decoding of (real time) live midi events
    • midi-consts: only constants, useful for low-level do-it-yourself midi handling (same author as this crate).


See CONTRIBUTING.md for more information.


midi-reader-writer is licensed under the Apache License, Version 2.0 or the MIT license, at your option.

For the application of the MIT license, the examples included in the doc comments are not considered "substantial portions of this Software".

Commit count: 0

cargo fmt