Facilitate reading and writing midi files.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
PieterPenninckx ce15aae3af Merge pull request 'Update .gitignore' (#7) from update-gitignore into main 4 months ago
src Update docs, remove TrackSeparator::from_iterator() method, change signature of TrackSeparator::extend to accept IntoIterators. 4 months ago
.gitignore Update .gitignore 4 months ago
CONTRIBUTING.md Put midly behind a feature. 4 months ago
COPYRIGHT Initial commit, copied from rsynth. 5 months ago
Cargo.toml Put midly behind a feature. 4 months ago
LICENSE-APACHE.txt Initial commit, copied from rsynth. 5 months ago
LICENSE-MIT.txt Initial commit, copied from rsynth. 5 months ago
README.md Update docs, remove TrackSeparator::from_iterator() method, change signature of TrackSeparator::extend to accept IntoIterators. 4 months ago
test.sh Separate midly_0_5.rs from lib.rs 4 months ago

README.md

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.

Example

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,
        tracks,
    };
    output_midi_file.save(output_filename)?;
    Ok(())
}

Features

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.

Ecosystem

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.

  • 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).

Contributing

See CONTRIBUTING.md for more information.

License

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".