Generates MIDI Pitch Events From Integer Sequences
The CDF version of running-man should be runnable using this free CDF Player. To edit the running-man notebook, you'll need Mathematica.
running-man uses integer notation to represent pitch classes. Integer notation is a translation of pitch classes into natural numbers. For example, if C = 0, then C# = 1, D = 2, D# = 3, and so on. By extension, integer notation can also represent harmonies and scales:
Hamony / Scale | Note Names | Pitch Classes |
---|---|---|
C Maj | (C, E, G) | {0, 4, 7} |
G Min / D | (D, G, Bb) | {7, 10, 2} |
F Maj / C | (A, C, F) | {9, 0, 5} |
C Dorian | (C, D, Eb, F, G, A, Bb) | {0, 2, 3, 5, 7, 9, 10} |
D Lydian | (D, E, F#, G#, A, B, C#) | {2, 4, 6, 8, 8, 10, 1} |
C Shang | (C, D, F, G, C, Bb) | {0, 2, 5, 7, 10} |
running-man frequently uses the following basic Wolfram Language functions. For more detailed explanations of these functions, visit the Wolfram Language Reference Site.
- Range[n] generates the list {1,2,...,n}.
- Table[f, n] generates the list of values n of function f.
- With[{a=x, b=y, ...}, exp] specifies that all occurrences of a, b, ... in exp should be replaced by x, y, ...
- Take[lst, n] gives the first n elements of list.
- Mod[m, n, o] gives the remainder on division of m by n using an offset o.
- Map[f, lst ] or f/@exp applies function f to each element in list lst.
- Flatten[lst] flattens the nested list lst.
- Differences[lst] gives the differences between each successive element in list lst.
- LinearRecurrence[ker, int ,n] gives the sequence of length n obtained by iterating the linear recurrence with kernel ker starting with initial values int.
The Wolfram Language provides an extensive selection of inbuilt integer sequence functions. For example, the fourth integer in the Fibonacci sequence (i.e., 1, 1, 2, 3, 4, ...) is given by simply entering:
In[]:= Fibonoacci[4]
Out[]:= 3
running-man uses functions such as Table[] and Range[] to iterate these sequence functions:
In[]:= Fibonacci[Range[12]]
Out[]:= {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144}
In[]:= Table[Fibonacci[x], {x, 1, 12}]
Out[]:= {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144}
The Online Encyclopedia of Interger Sequences provides formulae for integer sequences that are not inbuilt into the Wolfram Language. Some OEIS entries also provide these formulae using inbuilt Wolfram Language functions. For example, the OEIS entry for the square numbers OEIS A000290 includes the following code for generating the square numbers using the Wolfram Language LinearRecurrence[] function:
In[]:= LinearRecurrence[{3, -3, 1}, {0, 1, 4}, 12]
Out[]:= 0,1,4,9,16,25,36,49,64,81,100,121,144
running-man uses a moving window function:
MovingWindow[f_, exp_, n_, o_] :=
Module[{len = Length[exp], end}, end = Min[n, len] - 1;
Table[Apply[f, {exp[[i ;; i + end]]}], {i, 1, len - end, o}]];
The MovingWindow[f, lst, n, o] function moves a window over lst of function f by groups of n at an offset of o. For example, using MovingWindow[] with function Take[list,n] and offset and group both set to 3 gives:
In[]:= MovingWindow[Take, Fibonacci[Range[9]], 3, 3]
Out[]:={{1, 1, 2}, {3, 5, 8}, {13, 21, 34}}
An offset of 1 gives:
In[]:= MovingWindow[Take, Fibonacci[Range[9]], 3, 1]
Out[]:= {{1, 1, 2}, {1, 2, 3}, {2, 3, 5}, {3, 5, 8}}
An offset of 3 and a group 1 gives:
In[]:=MovingWindow[Take, Fibonacci[Range[9]], 1, 3]
Out[]:={{1}, {3}, {13}}
running-man uses the Mono[] function to generate a list of MIDI pitch events from a preselected mode:
Mono[exp_] :=
With[{arb = exp}, Take[arb, {#}] & /@ Mod[d@F[ln], mB, mI]];
c.f., Programming Avro Part, link
The variables of the Mono[] function are nested in Mathematica's Manipulate[] template, because of this fact they are assigned by way of the Manipulate[] template's modest GUI. However, when the running-man notebook is first initialized, the variables of the Mono[] function (e.g., d, F, len, ...) are automatically assigned the following values:
Variable | Assignment |
---|---|
mB = 7 | The Modular Divisor |
mI = 1 | The Modular Offset |
d = Flatten | If d is set to Flatten, pitches are given from successive cardinalities. If d = Differences, pitches are given from successive differences. |
F = Fibonacci[Range[len]] | The integer sequence function used to generate pitches with the Range function nested such that it generates a list instead of single integer. |
len = 12 | The length of the pitch set to be generated |
dor = {0, 2, 3, 5, 7, 9, 10, 0, 2, 3, 5, 7, 9, 10} | The Integer Notation of the Dorian mode |
In[]:= Mono[dor]
Out[]:= {0, 0, 2, 3, 7, 0, 9, 10, 9, 9, 7, 5}
Change a variable to generate pitches from differences rather than from cardinalities:
In[]:= d = Differences;
In[]:= Mono[dor]
Out[]:= {10, 0, 0, 2, 3, 7, 0, 9, 10, 9, 9}
Following the Fibonacci sequence (1, 1, 2, 3, 5, ...). The 1st element in dor is 0, the 2nd element is 0, the 3rd element is 2, the 4th element is 3, the fifth element is 7, etc.
Function | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
dor PCs | 0 | 2 | 3 | 5 | 7 | 9 | 10 | 0 | 2 | 3 | 5 | 7 |
Fn+1 mod 7 | 1 | 1 | 2 | 3 | 5 | 1 | 6 | 7 | 6 | 6 | 5 | 4 |
Fn+1 mod 7 /@ dor PCs | 0 | 0 | 2 | 3 | 7 | 0 | 9 | 10 | 9 | 9 | 7 | 5 |
Δ Fn+1 mod 7 /@ dor PCs | N/A | 10 | 0 | 0 | 2 | 3 | 7 | 0 | 9 | 10 | 9 | 9 |
The Harmonize[] function gives a nested sublist (i.e., aggregate) of any three consecutive PCs that constitute a major or a minor triad.
Harmonize[exp_] :=
With[{n1 = Take[exp, {1}], n2 = Take[exp, {2}],
n3 = Take[exp, {3}]},
If[n2 == (n1 + 0) && n3 == (n1 + 0) ||
n2 == (n1 + 0) && n3 == (n1 + 3) ||
n2 == (n1 + 0) && n3 == (n1 + 7) ||
etc. etc.
The Poly[] function runs a MovingWindow[] of Harmonize[] over the lists given by Mono[].
Poly[exp_] := MovingWindow[Harmonize, Mono[m], 3, 3];
In this way, the Poly[] function allows the generation of diatonic, polyphonic, MIDI files from integer sequences.
running-man is pretty simple code. I tried messing around with serializing the MIDI event durations (the Y and N buttons on the GUI), but I never figured out a procedure that produced appropriate sounding results. I also tried expanding the Harmonize[] function to include four-voice (e.g., 6ths, 7ths), and even five-voice (e.g., 9ths) harmonies, but that only ended up making the whole program too unmanageable. Please mess around with the code and share! You'll notice that upon initialization (BASE M = 11), the MIDI events are the same as the verse for The Running Man. The structure of The Running Man, from section to section, is simply an adjustment of the BASE M parameter (part a = 2, part b = 4, part c = 5, etc.).