Hmm (Haskell MIDI Manipulator) is a Haskell library that allows you to
- Read MIDI files
- Functionally manipulate MIDI in memory
- Write MIDI files
-- examples/info.hs
import Hmm
import System.Environment
main = do
args <- getArgs
let path = head args
midi <- readMidi path
print $ header midi
print $ midiNames midiExample run:
./info never-gonna-give-you-up.midOutput:
MidiHeader {format = MultiTrack, ntracks = 17, division = 384}
["E.PIANO 2","SYN BASS 2","CLEAN GTR","MELODY","PICCOLO","SYNTH DRUM","SYNTH DRUM","SAW WAVE","SOPRAN SAX","DRUMS","STRINGS","TRUMPET","BRASS 1","WHISTLE","MUTED GTR","GS/RESET"]
-- examples/transpose.hs
import Hmm
import System.Environment
main = do
args <- getArgs
let (semitonesStr : input : output : _) = args
let semitones = read semitonesStr :: Int
midi <- readMidi input
let midi' = Midi {header = header midi,
tracks = map (transposeTrack semitones) (tracks midi)}
writeMidi output midi'Example run:
./transpose 7 never-gonna-give-you-up.mid never-gonna-give-you-up-transposed.midNow, never-gonna-give-you-up-transposed.mid contains the original track transposed up by 7 semitones (i.e. the perfect fifth).
# Clone the repo
git clone https://github.com/patztablook22/hmm
# Use cabal to build and install it
cd hmm
cabal build
cabal install --libreadMidi :: FilePath -> IO Midi
Reads given MIDI file.
main = do
midi <- readMidi "myFile.mid"writeMidi :: FilePath -> Midi -> IO()
Writes given MIDI into file.
main = do
let midi = ... :: Midi
writeMidi "myFile.mid" midisemitones :: Note -> Note -> Int
Returns the interval between two notes in semitones.
let fifth = semitones (Note C 2) (Note G 2)
-- returns 7
let back = semitones (Note G 2) (Note C 2)
-- returns -7transposeNote :: Int -> Note -> Note
Transposes a note by the given interval in semitones.
let fifthUp = transpose 7 (Note C 2)
-- returns (Note G 2)
let fifthDn = transpose (-7) (Note C 2)
-- returns (Note F 1)transposeSignature :: Int -> Int -> Int
Transposes by a given interval in semitones (1st argument) the given key signature (2nd argument).
let twoFlats = -2
let upMaj3rd = 4
let newSignature = transposeSignature upMaj3rd twoFlats
-- returns 2, i.e. 2 sharpstransposeTrack :: Int -> MidiTrack -> MidiTrack
Transposes by a given interval the entire MIDI track. This includes all note-related events and all key signature events.
let track = ... :: MidiTrack
let octave = 12
let track' = transposeTrack octave trackisTextEvent :: MidiEvent -> Bool
Returns true iff the given MidiEvent contains Text payload.
let a = isTextEvent $ MidiEvent 0 (Text "Never gonna give you up, never gonna let you down.")
-- returns True
let b = isTextEvent $ MidiEvent 0 (NoteOn 1 (Note E 3), 127)
-- returns FalseisNameEvent :: MidiEvent -> Bool
Returns true iff the given MidiEvent contains Name payload.
See isTextEvent.
isSysExEvent :: MidiEvent -> Bool
Returns true iff the given MidiEvent contains SysEx payload.
See isTextEvent.
isNoteOnEvent :: MidiEvent -> Bool
Returns true iff the given MidiEvent contains NoteOn payload.
See isTextEvent.
isNoteOffEvent :: MidiEvent -> Bool
Returns true iff the given MidiEvent contains NoteOff payload.
See isTextEvent.
isNoteEvent :: MidiEvent -> Bool
Returns true iff the given MidiEvent contains NoteOn or NoteOff payload.
See isTextEvent.
isCopyrightEvent :: MidiEvent -> Bool
Returns true iff the given MidiEvent contains Copyright payload.
See isTextEvent.
isInstrumentEvent :: MidiEvent -> Bool
Returns true iff the given MidiEvent contains Instrument payload.
See isTextEvent.
isUnknownMetaEvent :: MidiEvent -> Bool
Returns true iff the given MidiEvent contains UnknownMeta payload.
See isTextEvent.
merge :: MidiTrack -> MidiTrack -> MidiTrack
Merges two MIDI tracks into one.
let pianoLeftHand = ... :: MidiTrack
let pianoRightHand = ... :: MidiTrack
let pianoBothHands = merge pianoLeftHand pianoRightHandmidiTrackName :: MidiTrack -> Maybe String
Extracts the track's name, if provided by a Name event.
let track = [(MidiEvent 0 (Name "Rick Astley - Never gonna give you up")),
...]
let name = midiTrackName track
-- returns Just "Rick Astley - Never gonna give you up"midiNames :: Midi -> [String]
Returns the names of all named MidiTracks. See midiTrackName.
midiTrackInstruments :: MidiTrack -> [String]
Returns the names of all instruments in a single MidiTrack. See midiTrackName.
midiInstruments :: Midi -> String
Returns the names of all instruments in the entire MIDI. See midiTrackInstruments.
midiCopyright :: Midi -> Maybe String
Returns the MIDI's copyright if present. Read MIDI specification for copyright placement.
rootedChord :: [Note] -> Maybe Chord
Heuristically interprets given (unordered) list of notes as a rooted chord.
let notes = [Note Fsharp 3,
Note D 3,
Note E 2,
Note B 1,
Note C 1]
let notes' = [Note Csharp 4,
Note G 3,
Note Fsharp 3,
Note E 2]
let notes'' = [Note G 3]
let chord = rootedChord notes
-- returns Just (Chord C Maj [Natural 9, Sharp 11])
let chord' = rootedChord notes'
-- returns Just (Chord E Min [Natural 6, Natural 2])
let chord'' = rootedChord notes''
-- returns NothingannotateChords :: ([Note] -> Maybe Chord) -> MidiTrack -> MidiTrack
Adds a chord annotation (Text event containing string chord representation) into the given track whenever the set of currently active notes changes and the annotator function returns (Just _).
let track = ... :: MidiTrack
let annotator = rootedChord
let track' = annotateChords annotator trackPitchClass
Represents the cross-octave pitch class, e.g. C, Csharp, D. This type is concerned purely with pitch, not with theoretical interpretation. Therefore, since MIDI uses the equal temperament, enharmonic equivalence applies, i.e. instead of Dflat (which is not provided), use Csharp.
Note
Represents a single note. Consists of its PitchClass and its Octave (represented as an Int).
let note = Note C 2
let note' = Note Asharp 5Midi
Representation of an entire MIDI object. Consists of
header :: MidiHeadertracks :: [MidiTrack]
MidiFormat
See MIDI specification for MIDI formats.
MidiHeader
See MIDI specification for the MIDI header. Consists of
format :: MidiFormatntracks :: Intdivision :: Int
ChurchMode
Represents the diatonic church modes, most importantly Ionian ("Major") and Aeolian ("Minor").
MidiTrack
Represents a single midi track - a list of ordered MidiEvents.
MidiEvent
An element of MidiTrack - consists of its timestamp and Event payloads.
Event
The payload of a MidiEvent. Can be of many types, such as NoteOn, Text, Copyright, PitchWheel.
Chord
A jazz theory influenced representation of a chord. Consists of
- its
PitchClass - its
ChordType, e.g.Maj,Min,Sus4 - its relevant (the highest natural and all altered)
[Extension], e.g.[Flat 7]
see e.g. rootedChord.