A melody generation system implementing a Maximum Entropy approach with the Ising/Potts model. It is based on the paper: Sakellariou, J., Tria, F., Loreto, V. et al. Maximum entropy models capture melodic styles. Sci Rep 7, 9172 (2017), which is available here.
This model captures longer range interactions than Markov models (up to
There are two implementations of the model: a pedagogical one, using Python loops, and a fast one, using NumPy. The pedagogical version is useful to understand the model and the approach. The fast version is about 100 times faster and is useful for training and generating melodies.
The Numpy implementation references equations as they are numbered in this paper.
The model is applicable to the generation of any type of sequences. It is trained on a single training sequence, such as melodies, chord sequences, or character strings.
The examples
directory contains examples for each type of sequences.
The installation of the project is done using pip. The following steps will guide you through the installation process. It installs several Python packages, so it is best to create a virtual environment before installing the project.
- Clone the repository:
git clone https://github.com/fpachet/max-entropy-music.git
cd max_entropy_music
- Create a virtual environment:
python -m venv venv
source venv/bin/activate
- Install the required dependencies:
pip install .
The project requires the following Python packages:
numpy ~= 2.2
scipy ~= 1.15
tqdm ~= 4.67
mido ~= 1.3
datasets ~= 3.4
See Python files in the examples
directory for examples of how to use the model.
The common approach to generate a sequence from a model trained on a training sequence is as follows:
- Create a
SequenceGenerator
object from the sequence. You can optionally specify the context size of the model and select to use the slow, pedagogical implementation if your goal is to see how the model is implemented (for instance using a step-by-step execution using a debugger). - Train the model by calling method
train
on theSequenceGenerator
object. Note that the model works on a list of indexes, not on a list of elements of the sequence. TheSequenceGenerator
object is a helper object that maps elements of the sequence to indexes and vice versa. So it is a lot easier to use the sequence generator instead of a model. - Generate a new sequence by calling method
generate
on theSequenceGenerator
object. You can specify the length of the generated sequence.
You can also save the trained model to a file and load it later from a SequenceGenerator
object. In this case, you need not train the model
again. The example examples/save_load_model.py
illustrate this.
The following example shows how to generate a melody from a MIDI file containing a Bach violin partita. The model is trained on the pitches extracted from the MIDI file. The model is then used to generate a new sequence of 100 notes, which is saved to a new MIDI file.
This example is also available in the file examples/bach_partita.py
.
# import helper functions to extract pitches from a MIDI file and save a MIDI file
from mem.midi.midi import (
save_midi,
extract_pitches_from_midi,
)
# SequenceGenerator is the class that encapsulates the model
from mem.training.generator import SequenceGenerator
# extract pitches from a MIDI file
midi_pitches = extract_pitches_from_midi("data/midi/bach_partita_violin.mid")
# create a SequenceGenerator object from the extracted pitches
g = SequenceGenerator(midi_pitches, k_max=1, fast=True)
# train the model encapsulated in the SequenceGenerator object
g.train(max_iter=10)
# generate a sequence of 100 notes
seq = g.sample_seq(100)
# save the generated sequence to a MIDI file
save_midi(seq, f"generated/bach-partita.mid")
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.