Expremigen is an expressive midi generation library
For quite a while I’ve been searching for a library that allows
generation of expressive midi information. With expressive I mean that
it should allow generating midi that doesn’t sound like a mechanic
robot. My current attempt is to make this possible by incorporating a
way of animating midi properties over time.
Expremigen models music as a collection of phrases. Within such a
phrase you can specify and/or animate:
- note values (melody, harmony)
- durations (rhythm)
- played durations (legato versus staccato)
- lag (rubato),
- volume (pppp to fffff, crescendo, decrescendo)
- tempo (rallentando, accelerando)
- midi control changes and pitchbend
Specification of properties like note values or durations is done
using a library of patterns. Each pattern can generate numbers
according to its specifications. Patterns internallly use python
generators to avoid memory and time explosion when you specify
patterns with very large repeat values. These patterns are modeled
after the more or less equivalent concept in supercollider and isobar. Compared to isobar in particular, the patterns in expremigen are written in python 3, and heavily based on generators, which allows things like expressing patterns of very long lengths without blowing up the required time and memory space.
Animations of properties are implemented by leveraging an
animation library pyvectortween which has ample possibilities for
specifying and combining advanced animations, and enrich them with
all kinds of tweening and noise functions for maximum expressive
results. A special pattern called Ptween applies the animations
calculated by pyvectortween to the midi properties generated by
patterns.
To get the most out of this library you will need to install MIDIUtil (pip install MIDIUtil), textX (pip install textX) and vectortween (pip install vectortween).
Probably the easiest way to get started with expremigen is to use its domain specific MIdi SPEcification Language, mispel. Mispel syntax reuses ideas from lilypond, abc notation and music21‘s tinynotation. A mispel based program could look as follows:
“””)
p2m = Pat2Midi(m.get_no_of_tracks())
m.add_to_pattern2midi(p2m)
p2m.write(outputfile)
if __name__ == “__main__”:
make_midi()”>
from expremigen.io.pat2midi import Pat2Midi from expremigen.mispel.mispel import Mispel outputfile = "output/example_readme.mid" def make_midi(): m = Mispel() m.parse(r""" with track 0 channel 0: c4_16vol{p} e g c5 b4 g f d c_4 rvol{ff} with track 1 channel 1:""") p2m = Pat2Midi(m.get_no_of_tracks()) m.add_to_pattern2midi(p2m) p2m.write(outputfile) if __name__ == "__main__": make_midi()
This program generates a midi file containing two tracks on two different midi channels. The first track plays a crescendo arpeggio and the second track specifies three chords.
Specifying notes and rhythms
-
As you can see you specify events in the form of notenames with octave numbers, e.g. a4 is a “la” in octave 4 (typically 440Hz). Acceptable note names are a, b, c, d, e, f, g, and r for a rest.
-
Sharps are indicated with #, e.g.
a#
, double sharps with x, e.g.cx
, flats with -, e.g.e-
and double flats with –, e.g.g--
. -
To make chords, put notes in angular brackets:
. The properties of the first note in the chord are used for the whole chord. Properties other than note name and octave attached to the second and later notes are discarded. -
Rhythm is indicated by using underscore and an (inverse) duration in beats, e.g. _16 means a sixteenth note. You can add one or more dots to indicate a dotted rhythm, e.g.
d#3_8.
is a “d sharp” in octave 3 with a length of one eighth plus one sixteenth. -
To simplify specifying drum tracks (by convention these are always specified on channel 10) you can also use drum notes. Each drum note has a long form and a short form:
long | short | long | short |
---|---|---|---|
acousticbassdrum | abd | bassdrum | bad |
sidestick | sis | acousticsnare | acs |
brushtap | brt | handclap | hac |
brushslap | brs | electricsnare | els |
brushswirl | brw | lowfloortom | lft |
closedhihat | chh | highfloortom | hft |
pedalhihat | phh | lowtom | lot |
openhihat | ohh | lowmidtom | lmt |
highmidtom | hmt | crashsymbal1 | cs1 |
hightom | hit | ridecymbal1 | rc1 |
chinesecymbal | chc | ridebell | rib |
tambourine | tam | splashcymbal | spc |
cowbell | cob | crashsymbal2 | cc2 |
vibraslap | vis | ridecymbal2 | rc2 |
highbongo | hib | lowbongo | lob |
mutehiconga | mhc | openhiconga | ohc |
lowconga | loc | hightimbale | him |
lowtimbale | lom | highagogo | hag |
lowagogo | lag | cabasa | cab |
maracas | mar | shortwhistle | swh |
longwhistle | lwh | shortguiro | shg |
longguiro | log | claves | cla |
hiwoodblock | hwb | lowoodblock | lwb |
mutecuica | muc | opencuica | opc |
mutetriangle | mut | opentriangle | opt |
shaker | shk |
Adding expressivity
-
To the notes you can add properties. The main difference between mispel and other midi domain specific languages (like skini, alda, semitone, micromidi) is the combination of a fairly concise syntax with an easy to use built-in property animation system.
-
Properties in curly braces
property{}
are animated from this occurrence of the property to the next. In track 0 in the example above, the volume will be animated from p to ff (crescendo). -
Properties in square braces
property[]
keep a constant value until the next property of the same kind is encountered. -
The properties you can specify are:
- vol for volume – can be ppppp to ffff or an integer between 0-127
- pdur for played duration – can be staccatissimo, staccato, normal, legato or legatissimo, or a number. The number is interpreted as a multiplier with which to multiply the specified duration. E.g. number 0.1 would specify staccatissimo and number 1 would specify legato.
- lag for lag. Lag is a numeric value (where 1 stands for a full beat). By animating lag you can create a convincing rubato.
- tempo by animating tempo you can speed up or slow down (accelerando and ritardando)
- cc midi control changes. These are specified using two parameters, e.g. cc{15, 100} specifies that midi control change for controller 15 should be set to 100 and this va