Expremigen
expressive midi generation with python 3
Install / Use
/learn @shimpe/ExpremigenREADME
Expremigen
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.
Dependencies
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).
Getting started: MISPEL
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:
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_16\vol{p} e g c5 b4 g f d c_4 r\vol{ff}
with track 1 channel 1:
<c3_4\vol[mp] e3 g> <b2\vol[f] d3 g> <c3_2\vol[mf] e g c4>
""")
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:
<a4_8 c# e>. 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 value will be animated until the next midi control change for controller 15 is encountered. Midi control changes are animated with a higher time resolution than individual notes meaning you can perfectly animate control message values between one long note and the next note.
- NOTE: pitchbend in midi is not specified using a control change message, but in mispel it is. Just use controller value 128.
-
Vectortween also allows to specifying tweening options and noise function options. Only the tweening options are exposed in mispel, so if you wanted to get fancy you could write an animation like
with track 0 channel 0:
c4_16\vol{p, easeOutElastic, 30, 0.1} e g c5 b4 g f d c_4 r\vol{ff}
- The supported tweening options are (see this picture) for a graphical display of what these mean:
- linear,
- easeInQuad, easeOutQuad,easeInOutQuad,
- easeInCubic,easeOutCubic,easeInOutCubic,
- easeInQuart,easeOutQuart,easeInOutQuart,
- easeInQuint,easeOutQuint,easeInOutQuint,
- easeInSine,easeOutSine,easeInOutSine,
- easeInExpo,easeOutExpo,easeInOutExpo,
- easeInCirc,easeOutCirc,easeInOutCirc,
- easeInBounce,easeOutBounce,easeInOutBounce,
- easeInElastic,easeOutElastic,easeInOutElastic (these require two extra float parameters: amplitude and damping)
- easeInBack,easeOutBack,easeInOutBack (these require one extra float parameter: damping)
Reducing duplication
If two successive notes share the same octave you can leave out the octave in the second note. If two successive notes share the same duration, you can leave out the duration in the second notes. The following fragments are equivalent:
'a4_4 b4_4 c#4_4 d#4_4 e4_1'
and
'a4_4 b c# d# e_1'
Going deeper
If you don't like mispel, or you need more power than mispel exposes, you can of course tap into the raw power of patterns. Although a typical pattern-based expremigen program may look a bit daunting at first, it's actually quite simple. In a typical use case, you will concatenate phrases into a piece. Phrases are dictionaries containing patterns that specify the notes, the volumes, the durations, the played durations, the lag and the tempo.
The following example shows off some of the possibilities for creating a phrase using tweened animations.
The examples folder has more things to examine. The tests folder has unit tests for a substantial part of the library.
from expremi
