A Markov Chain model for procedural fugue composition, trained on Bach's Well-Tempered Clavier (BWV 846-893). The model operates on a constrained state space derived from diatonic pitch classes and generates multi-voice output subject to counterpoint restrictions encoded as hard filters on the transition distribution.
Full paper: StochasticMethodsForMusicGeneration.pdf
Pitch states are restricted to diatonic pitch classes (C major):
N = {0, 2, 4, 5, 7, 9, 11} mod 12, 21 <= n <= 108
Nc = {1, 3, 6, 8, 10} mod 12 (excluded)
Time is quantized to multiples of 125ms, bucketed to the nearest standard note duration (sixteenth to whole).
For each piece (or collection of pieces), the parser constructs a transition frequency matrix F_{i,j} over N x tau by recording all observed note-to-note transitions and their durations. This is row-normalized to yield the TPM P_{i,j}:
F_{i,j} -> P_{i,j}
The generated sequence is a discrete-time, discrete-state stochastic process:
(x_i, t_i^x)_{i in Z+} in N x tau
which can equivalently be viewed as a continuous-time process (X_t)_{t in R+} -- a CTMC representation that enables cross-voice synchronization rules.
Rather than soft penalties, counterpoint rules are implemented as hard rejection filters on the transition sampler. Invalid transitions are resampled until a valid next state is found.
Single-voice constraints on x_i:
| Constraint | Formal Definition |
|---|---|
| Leap bound | |x_{i+1} - x_i| <= 9 |
| No non-ornamental trills | x_i = x_{i+2} and |x_{i+1} - x_i| <= 2 implies x_{i+3} != x_{i+1} |
| Leap recovery | x_{i+1} - x_i >= 4 implies x_{i+2} steps down; x_{i+1} - x_i <= -4 implies x_{i+2} steps up |
| Resolving cadence | After n >= 16 notes, leading tones {67,71,79,83} resolve to tonic on a downbeat (t = 0 mod 1000) |
Joint constraints on (X_t, Y_t) across voices:
| Constraint | Formal Definition |
|---|---|
| No parallel octaves | X_t = Y_t (mod 12) and X advances implies Y_{j+1} != X_{i+1} (mod 12) |
| No parallel fifths | X_t = Y_t +/- 7 (mod 12) and X advances implies Y_{j+1} != X_{i+1} +/- 7 (mod 12) |
The dual discrete/continuous time representation is necessary because two voices accumulate different note counts over the same wall-clock duration; cross-voice rules are therefore indexed by continuous time t in R+ rather than note index.
| File | Role |
|---|---|
| Parse.py | Reads MIDI via Mido, constructs F_{i,j} and normalizes to P_{i,j} |
| Simulation.py | Rejection-sampled Markov walk with counterpoint filter stack |
| Track.py | Voice management, multi-track assembly, MIDI output (entry point) |
| WriteMidi.py | Standalone MIDI serialization utility |
| bach_846.mid | Training data: BWV 846, Fugue in C Major |
pip install mido numpy
python Track.pyTrains on bach_846.mid, generates a soprano subject (16 notes), constructs a countersubject via retrograde at a time offset equal to the subject duration, and writes testTrack12.mid. To swap training data:
# Track.py -> Track.build_track()
file = Parse('your_fugue.mid')First-order Markov Chains have no long-range memory, which limits thematic coherence across a full fugue. Higher-order chains (order n) partially address this but cause state space explosion and quickly degrade to verbatim training data reproduction. The countersubject quality is the weakest output -- it requires denser joint restrictions between voices than are currently implemented. A deep learning approach (e.g., convolutional counterpoint) would better preserve long-term structure but sacrifices the interpretability and controllability of an explicit stochastic model.
- Ames, C. (1989). The Markov Process as a Compositional Model: A Survey and Tutorial. Leonardo 22(2), 175-187.
- Briot, J. P., Hadjeres, G., & Pachet, F. D. (2017). Deep learning techniques for music generation -- a survey. arXiv:1709.01620.
- Huang et al. (2019). Counterpoint by convolution. arXiv:1903.07227.
- Inesta, J. M., Conklin, D., & Ramirez, R. (2016). Machine learning and music generation. Journal of Mathematics and Music, 10(2), 87-91.
- Volchenkov, D., & Dawin, J. R. (2012). Musical Markov Chains. IJMPCS Vol. 16, pp. 116-135.