Div's MIDI Utilities

Introduction

Here is a collection of MIDI utilities I wrote for myself, which you may find useful as well. They are designed to be platform neutral, and work on current versions of Windows, Linux, and MacOS. Regardless of platform, the utilities follow the Unix design philosophy; most run from the command line instead of providing a GUI, and each is small and dedicated to a specific task. Some work in realtime, while others act upon saved MIDI files.

Download midi-utilities-20210131.zip (includes Windows binaries) or go to the Github project.

News

2021-01-28 - Major update! All of the realtime utilities have been rewritten to use RtMidi and where necessary wxWidgets, thus allowing the whole suite to work on MacOS. Some of the older, less-useful utilities have been retired to the "extras" directory; please contact me if you'd like me to revive support for any of them. Also added several new utilities plus lots of new features, fixes, and cleanups.

Realtime MIDI utilities

lsmidiins and lsmidiouts

These utilities display a numbered list of the MIDI input and output ports on your system. The realtime utilities can refer to ports by either number or name.

Usage: lsmidiins

Usage: lsmidiouts

playsmf and recordsmf

A simple MIDI file player and recorder.

Usage: playsmf --out <port> [ --from <time> ] [ --to <time> ] [ ( --solo-track <n> ) ... | ( --mute-track <n> ) ... ] [ --extra-time <seconds> ] <filename.mid>

Usage: recordsmf --in <port> [ --save-every <msecs> ] <filename.mid>

dispmidi

dispmidi pretty-prints incoming MIDI messages, which can be useful for debugging complex MIDI setups.

Usage: dispmidi --in <port>

sendmidi

sendmidi sends a single specified MIDI message. It can be used for scripting or as a simple knob box.

Usage: sendmidi --out <port> ( --note-off <channel> <note> <velocity> | --note-on <channel> <note> <velocity> | --key-pressure <channel> <note> <amount> | --control-change <channel> <number> <value> | --program-change <channel> <number> | --channel-pressure <channel> <amount> | --pitch-wheel <channel> <amount> | --panic )

routemidi

routemidi lets you define multiple busses, each of which reads from multiple input ports, merges the streams together, and copies the result to multiple output ports. You can route channels freely between busses. On Linux and MacOS it also lets you establish virtual ports for other applications to connect to later.

Usage: routemidi [ --bus | --in <port> | --out <port> | --virtual-in <port> | --virtual-out <port> | --channel <input bus number> <input channel number> <output bus number> <output channel number> ] ...

alsamidicable

alsamidicable works on Linux only, and has a different way of referring to ports than the other utilities. It's similar to the system utility aconnect but will wait for ports with the specified names to be created before connecting to them. This allows it to be used in startup scripts which would otherwise have timing issues.

Usage: alsamidicable --list-ports

Usage: alsamidicable --list-connections

Usage: alsamidicable --connect --from <client> <port> --to <client> <port> [ --timeout <seconds> ]

Usage: alsamidicable --disconnect --from <client> <port> --to <client> <port>

brainstorm

brainstorm functions as a dictation machine for MIDI. It listens for incoming MIDI events and saves them to a new MIDI file every time you pause in your playing for a few seconds. The filenames are generated automatically based on the current time, so it requires no interaction. I find it useful for recording brainstorming sessions, hence the name, and use it more than all the other utilities put together.

Usage: brainstorm --in <port> [ --prefix <filename prefix> ] [ --timeout <seconds> ] [ --confirmation <command line> ]

The confirmation option allows you to specify a command to execute whenever a file is saved, so that you know your music is safe. I use it to play a short audio file, in keeping with brainstorm's "interfaceless" design, but you can get fairly elaborate if you want. The filename will be substituted for each %s in the command.

qwertymidi and delta

qwertymidi lets you use your computer keyboard as if it were a synthesizer keyboard. It supports custom mapping of the keyboard layout by means of a config file. Sample maps are provided for conventional and von Janko pianos, Hayden, English, Anglo, and Maccann concertinas, different chromatic button accordions, and more. For portability reasons this has to be a GUI application, but it still takes all its options from the command line.

delta is a fun to play, monophonic variation on qwertymidi which maps keys on the computer keyboard to relative intervals instead of absolute pitches. It was inspired by the unusual Samchillian MIDI controller I read about online. It also supports custom keyboard mappings.

Usage: qwertymidi [ --out <port> ] [ --channel <n> ] [ --program <n> ] [ --velocity <n> ] [ --transpose <n> ] [ --map <filename.xml> ]

Usage: delta [ --out <port> ] [ --channel <n> ] [ --program <n> ] [ --velocity <n> ] [ --map <filename.xml> ]

onmidi

onmidi runs programs or scripts in response to MIDI input. It's particularly useful for turning pages in on-screen sheet music.

Usage: onmidi --in <port> [ --out <port> ] [ --hold-length <msecs> ] [ --note-command <note> <command> | --controller-command <controller number> <command> | --controller-hold-command <controller number> <command> | --pitch-wheel-up-command <command> | --pitch-wheel-down-command <command> ] ...

noteflurry

noteflurry outputs a configurable sequence of notes for each note you play, transposed and velocity-scaled to match. Trigger means that the sequence should start each time you play a note, like an echo. Gate means that the notes you play are pulsed according to the notes going by in the sequence. Using them together produces a complicated rhythmic texture. Overall, noteflurry can sound like a multi-tap delay, an arpeggiator, or an analogue-style sequencer, including the distinctive pulsing effects in the Who's "Won't Get Fooled Again" and "Baba O'Riley," Pink Floyd's "On the Run," and many songs by U2. In addition, it can simply transpose or add parallel intervals if you use trigger mode with notes on beat zero.

Usage: noteflurry --in <port> --out <port> [ --trigger ] [ --gate ] [ --note <beat> <duration beats> <note interval> <velocity> ] ... [ --loop <beats> ] [ --tempo <bpm, default 100> ]

pedalsim

pedalsim simulates the pedals of a piano when you want to use a controller's physical pedals to drive a synthesis engine that doesn't understand what those pedals mean. This is very common for sostenuto, bass sustain, and the soft pedal, but even regular sustain isn't natively supported by more simplistic soft synths. It also tries to simulate an organ-style volume pedal, but this feature is more limited in its usefulness; because it works by adjusting incoming note velocities, it only affects new notes coming in, leaving the sustained ones alone.

Usage: pedalsim --in <port> --out <port> [ --sustain ] [ --sostenuto ] [ --bass-sustain ] [ --soft ] [ --volume ] [ --independent-sostenuto ] [ --highest-bass-note <default B3> ] [ --max-soft-velocity <default 95> ] [ --sustain-controller <default 64> ] [ --sostenuto-controller <default 66> ] [ --bass-sustain-controller <default 69> ] [ --soft-controller <default 67> ] [ --volume-controller <default 12> ]

jumpoctave

jumpoctave lets you use the pitch bend wheel as an octave jump control. Useful for small synths that lack proper transpose buttons.

Usage: jumpoctave --in <port> --out <port>

notemap

notemap lets you remap the layout of notes on your MIDI keyboard. Explore the guitar concept of alternate tunings on the piano, set up an ergonomic drum kit for your fingers, etc.

Usage: notemap --in <port> --out <port> [ --transpose <n> ] [ --map <filename.xml> ]

netmidic and netmidid

These utilities speak NetMIDI, a trivial network protocol I created which sends standard MIDI messages over a TCP/IP connection as fast as possible. They can be used to connect up the MIDI systems on two different machines over a network connection, even if they are running different operating systems. The client forwards messages from the local MIDI system to a NetMIDI server. The server forwards messages sent by the client to the local MIDI system.

Usage: netmidic --in <midi port> --server <hostname> <network port>

Usage: netmidid --port <network port> --out <midi port>

MIDI file utilities

midifile and normalizesmf

midifile is a powerful and practical C language library that allows you to read and write Standard MIDI Files (SMF), and provides a data structure for MIDI sequences. Essentially, it is the core of a MIDI sequencer without the user interface and the realtime recording and playback functionality. It has no dependencies, so it should be easy to include in your own projects.

normalizesmf is a utility which provides a minimal demonstration of the midifile library. It reads in a MIDI file, then writes it out again. If your sequencer complains that a file is invalid, this normalizer might make it more palatable.

Usage: normalizesmf <filename>

convert-time

convert-time displays a timestamp in a given MIDI file in all the different formats supported by the other utilities.

Usage: convert-time <filename> <time>

tempo-map

tempo-map is a utility for "metercasting", a unique algorithm for adjusting the timing of a MIDI sequence. Unlike conventional quantization, metercasting does not replace the human characteristics of your playing with a mechanistic feel. First, you record your performance without a metronome, as with brainstorm. Next, ignoring any beat markers your sequencer might display, you add a click track consisting of one note per beat, synchronized with your performance. tempo-map then processes the file, adjusting the timestamps of existing events and inserting new tempo events so that performance sounds the same when played back, but the notes will line up with beats when displayed in the sequencer. You can then selectively delete the inserted tempo events, resulting in a steady but still completely nuanced recording.

Usage: tempo-map --click-track <n> [ --out <filename.mid> ] <filename.mid>

click-track

Adds a click track that corresponds to the sequence's notion of beats. Note that running tempo-map directly on this program's output will have no effect, but it can be useful as a starting point if you manually edit the clicks before running tempo-map.

Usage: click-track --click-to-beat-ratio <clicks> <beats> [ --channel <default 0> ] [ --note <default 64> ] [ --velocity <default 64> ] [ --out <filename.mid> ] <filename.mid>

align-clicks

Between the time when you record a click track and when you use it as input to tempo-map, you usually have to go through and manually align the click events with nearby notes that you played on the real tracks. align-clicks is a heuristic attempt to do that alignment automatically.

Usage: align-clicks --click-track <n> [ --out <filename.mid> ] <filename.mid>

quantize

A naive quantizer, by user request; I prefer metercasting, myself. Rounds event timing to the nearest (specified division of a) quarter note. Preserves note durations rather than lining up note off events with the grid; this avoids having a very clipped sound, but can potentially move the note off to the wrong side of a sustain pedal change.

Usage: quantize --beat-division <division> [ --out <filename.mid> ] <filename.mid>

smooth-tempo

When metercasting, you often end up with a MIDI file that has lots of jittery little tempo changes. smooth-tempo smoothes them out using a three sample average.

Usage: smooth-tempo [ --out <filename.mid> ] <filename.mid>

average-tempo, scale-tempo, offset-tempo, average-velocity, scale-velocity, and offset-velocity

These utilities can be used together for patching up multiple takes of a song to match one another. The ability to analyze a specified section of a song is particularly useful if the tempo varies frequently, as is the case when the song has been through tempo-map.

Usage: average-tempo [ --from <time> ] [ --to <time> ] <filename.mid>

Usage: scale-tempo [ --from <time> ] [ --to <time> ] --amount <n> [ --out <filename.mid> ] <filename.mid>

Usage: offset-tempo [ --from <time> ] [ --to <time> ] --amount <n> [ --out <filename.mid> ] <filename.mid>

Usage: average-velocity [ --from <time> ] [ --to <time> ] [ --track <n> ] <filename.mid>

Usage: scale-velocity [ --from <time> ] [ --to <time> ] [ --track <n> ] --amount <n> [ --out <filename.mid> ] <filename.mid>

Usage: offset-velocity [ --from <time> ] [ --to <time> ] [ --track <n> ] --amount <n> [ --out <filename.mid> ] <filename.mid>

smf-length

smf-length shows how long the file is.

Usage: smf-length <filename.mid>

cut-time

cut-time removes a section of the file.

Usage: cut-time [ --from <time> ] [ --to <time> ] [ --out <filename.mid> ] <filename.mid>

mish

A compiler for a text-based music notation language which I invented, called "Mish" (MIDI shorthand). It converts Mish files into standard MIDI files.

Usage: mish --in <input.mish> --out <output.mid>

smftoxml and xmltosmf

These utilities convert a MIDI file into an ad hoc XML equivalent and back. This can be useful for seeing exactly what is in the file. Using the two utilities together, you can modify MIDI files with a text editor.

Usage: smftoxml <filename.xml>

Usage: xmltosmf <filename.xml> <filename.mid>

Extras

Also provided is the source code to several obsolete or incomplete programs in the "extras" directory. They're included in the package primarily so that I don't lose track of them, but there's useful code to borrow in there, and I may return to working on some of them if there's enough demand. These include alsamidi2net, alsamidi2pipe, beatbox, fakesustain, imp, intervals, joycc, joypedal, mciplaysmf, metercaster, midimon, midithru, multiecho, net2alsamidi, net2pipe, onmessage, onpedal, padpedal, pedalnote, pipe2alsamidi, pipe2net, Piano Protagonist, pulsar, rw, smftosqlite, sqlitetosmf, transpose, velocityfader, velocity-map, verbosify, xmidiqwerty, older platform-specific versions of the main utilities, and several attempts to build a full linear or step-based sequencer, the latest of which is called Seqer. This last is something of a quixotic quest for me which has been going on for many years, and most of my standalone utilities are actually spin-offs from that project.

Helpful hints

These programs are designed to be run from the command line or from scripts. If you just double-click on their icons you won't be able to provide the required command line arguments, and may not even have a chance to read the help message before the command prompt window disappears.

Most of the realtime utilities are designed to connect to existing MIDI ports. That's fine if you want them to talk directly to hardware, but isn't sufficient if you want to use the output of one utility as the input of another. Different platforms have different solutions to this problem. On Linux or MacOS you can use routemidi to create virtual ports. This isn't possible on Windows, but there you can use a MIDI loopback driver such as the free LoopMIDI. On Linux you can also use alsamidicable to reconfigure MIDI connections between programs which are already running.

If you don't have a hardware synthesizer or virtual instrument software, you'll probably want a simple fallback software synthesizer to turn MIDI messages into actual sound. Windows comes with the Microsoft GS Wavetable Synth. On Linux you can use Timidity or Fluidsynth. On MacOS you can use the free SimpleSynth.

License

These utilities are free and open source, provided under terms of the BSD license. Specifically:

© Copyright 1998-2021 David G. Slomin, all rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.