Skip to content

Commit

Permalink
improve code organization and documentation (#5)
Browse files Browse the repository at this point in the history
- Move code to separate files and modules.
- Expose two top-level modules, file and core which represent MIDI file
  specifics, and MIDI generalities, respectively.
- Add a bit more documentation.
  • Loading branch information
webern authored Jan 30, 2021
1 parent feef83f commit d5c8037
Show file tree
Hide file tree
Showing 27 changed files with 1,232 additions and 1,127 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v0.0.1] 2021-01-30
## Changed
- Re-organized modules and code files, added some documentation.


## [v0.0.0] 2021-01-18
## Added
- Everything: you can create simple MIDI files with this library.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "midi_file"
version = "0.0.0"
version = "0.0.1"
authors = ["Matthew James Briggs <[email protected]>"]
edition = "2018"
exclude = [
Expand Down
40 changes: 8 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,14 @@
The purpose of this library is primarily to be able to author and write MIDI files in Rust.
The library also parses MIDI files and thus can "round trip" files.

The status is un-released.
Note that the current name `midi` is taken on crates.io,
so the name will need to be changed if published.

It feels like most features are implemented, except for `sysex` messages,
which I haven't happened to encounter yet in the files I have downloaded.

### Unimplemented Features

- `sysex` messages
- sequence number messages
- sequencer specific messages
- some horrible bug (see below)

### Horrible Lurking Bug
### Bug

I need help figuring out why I am seeing what I expect to be [midi messages],
but which start with status bytes that I don't recognize.
Expand All @@ -33,31 +26,14 @@ Please see issue [#1] and help if you understand MIDI.
[#1]: https://github.com/webern/midi/issues/1
[midi messages]: http://www.music.mcgill.ca/~ich/classes/mumt306/StandardMIDIfileformat.html#BMA1_

### Mess

Though I'm relatively happy with the data structure representing MIDI,
the code is messy and needs to be re-organized (and documented).
I'll get to it.

### Limited Interface
### Interface

All the bytes, messages and such are represented with pub structs and enums,
but the structs have private members.
So to create a basic file, as I have done in an [example], I have added functions at the `Track`
level for pushing things onto the `Vec` without needing to know the underlying data structure.
This is may be a direction I want to continue with.
All the bytes, messages and such are represented with pub structs and enums, but the structs have private members.
To create a basic file, as I have done in an [example], I have added functions at the `Track` level.
With these functions you can build up a file without as much knowledge of the underlying data structure.

The API contract that I'm going for here is low-level-ish.
You need to understand MIDI in order to create a meaningful MIDI file,
but any file you create with the library will be technically valid per the spec.
You *do not* need to know what bytes equal what or what the bounds of an allowable value are.
This is all constrained with types.
You need to understand MIDI in order to create a meaningful MIDI file, but any file you create with the library should
be technically valid per the spec.
You do not need to understand the meaning of any particular byte's numeric value.

[example]: https://github.com/webern/midi/blob/main/examples/main.rs

### Random Links

Things I've been looking at from time-to-time:

- https://www.music.mcgill.ca/~gary/306/week9/smf.html
- https://github.com/Shkyrockett/midi-unit-test-cases
7 changes: 3 additions & 4 deletions examples/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use midi_file::channel::Channel;
use midi_file::core::{Clocks, DurationName, GeneralMidi};
use midi_file::message::{NoteNumber, Velocity};
use midi_file::{Division, Format, MidiFile, QuartersPerMinute, Track};
use midi_file::core::{Channel, Clocks, DurationName, GeneralMidi, NoteNumber, Velocity};
use midi_file::file::{Division, Format, QuartersPerMinute, Track};
use midi_file::MidiFile;

// durations
const QUARTER: u32 = 1024;
Expand Down
4 changes: 3 additions & 1 deletion src/byte_iter.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::vlq::{decode_slice, VlqError, CONTINUE};
//! The `byte_iter` module provides a wrapper for iterating over the bytes of a MIDI file.
use crate::core::vlq::{decode_slice, VlqError, CONTINUE};
use log::trace;
use snafu::{ensure, OptionExt, ResultExt, Snafu};
use std::fs::File;
Expand Down
2 changes: 0 additions & 2 deletions src/channel.rs

This file was deleted.

86 changes: 0 additions & 86 deletions src/clamp.rs

This file was deleted.

38 changes: 37 additions & 1 deletion src/core/clocks.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,39 @@
/// There are 24 MIDI Clocks in every quarter note. (12 MIDI Clocks in an eighth note, 6 MIDI Clocks in a 16th, etc).
/// There are 24 MIDI Clocks in every quarter note. (12 MIDI Clocks in an eighth note, 6 MIDI Clocks
/// in a 16th, etc). One example of using this enum is in the `TimeSignature`, where we can specify
/// the frequency of the metronome click.
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
pub enum Clocks {
/// 142 MIDI clocks.
DottedWhole,

/// 96 MIDI clocks.
Whole,

/// 72 MIDI clocks.
DottedHalf,

/// 48 MIDI clocks.
Half,

/// 32 MIDI clocks.
DottedQuarter,

/// 24 MIDI clocks.
Quarter,

/// 18 MIDI clocks.
DottedEighth,

/// 12 MIDI clocks.
Eighth,

/// 9 MIDI clocks.
DottedSixteenth,

/// 6 MIDI clocks.
Sixteenth,

/// Any number of MIDI clocks, intended for durations not named above.
Other(u8),
}

Expand All @@ -21,6 +44,8 @@ impl Default for Clocks {
}

impl Clocks {
/// Create a new `Clocks` value from a `u8`, choosing one of the named variants if possible, and
/// falling back to `Other` if the value does not correspond to one of the named variants.
pub(crate) fn from_u8(v: u8) -> Clocks {
match v {
142 => Clocks::DottedWhole,
Expand All @@ -37,6 +62,7 @@ impl Clocks {
}
}

// Get the `u8` value represented by the enum.
pub(crate) fn to_u8(&self) -> u8 {
match self {
Clocks::DottedWhole => 142,
Expand All @@ -53,10 +79,20 @@ impl Clocks {
}
}

/// Create a new `Clocks` value from a `u8`, choosing one of the named variants if possible, and
/// falling back to `Other` if the value does not correspond to one of the named variants.
pub fn new(clocks: u8) -> Self {
Self::from_u8(clocks)
}

/// If you create a `Clocks` value with a standard value, this will resolve the `Clocks` value
/// to a named variant instead of `Other`. For example:
/// ```
/// use midi_file::core::Clocks;
/// let mut clocks = Clocks::Other(24);
/// clocks.resolve();
/// assert!(matches!(clocks, Clocks::Quarter));
/// ```
pub fn resolve(&mut self) {
*self = Self::from_u8(self.to_u8())
}
Expand Down
8 changes: 7 additions & 1 deletion src/core/duration_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ use crate::error::LibResult;
use crate::Error;
use std::convert::TryFrom;

/// `DurationName` is used when specifying the denominator of a [`TimeSignature`]. When defining
/// time signatures, the MIDI file spec says:
/// ```text
/// The denominator is a negative power of two: 2 represents a quarter-note, 3 represents an'
/// eighth-note, etc.
/// ```
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
pub enum DurationName {
Expand Down Expand Up @@ -35,7 +41,7 @@ pub enum DurationName {
/// Five-Twelfth Note
D512 = 9,

/// One Thousand, Twenty-Fourth Note
/// One Thousand Twenty-Fourth Note
D1024 = 10,
}

Expand Down
Loading

0 comments on commit d5c8037

Please sign in to comment.