Tiny VAE that writes loopable, catchy MIDI riffs.
Project description
Riffly
A tiny VAE that writes loopable, catchy MIDI riffs.
Riffly trains a Variational Autoencoder on piano-roll segments from a folder of MIDI files, then samples short loops you can save as MIDI, render to audio, or plot. Models are small enough to train on a single GPU in minutes and to run inference on CPU.
Sample output
Unconditional samples — piano rolls with pitch on the y axis (low → high) and time on the x axis.
Left: from the bundled pop checkpoint (see Pretrained checkpoint). Listen: melody_3.wav; MIDI in assets/preview/.
Right: a single roll plotted with riffly.plot().
Pretrained checkpoint
from riffly import Riffly
model = Riffly("pop") # downloads on first use, then cached
model.generate(n=4, save="out/", multi_track=True)
Riffly("pop") fetches a ~6 MB ConvVAE trained on the Pop subset of the
ADL Piano MIDI Dataset
(CC BY 4.0) from this repo's GitHub Releases. The file lands in
$XDG_CACHE_HOME/riffly/weights/ (or ~/.cache/riffly/weights/); override
with $RIFFLY_HOME. The sha256 is verified on every load.
Install
pip install riffly # core (training + MIDI export)
pip install "riffly[interactive]" # + audio rendering and plotting
Training, plotting, and .wav rendering need the interactive extra. With core only, pass wav=False, png=False to generate(save=...).
torch installs the CUDA (GPU) build by default. On a CPU-only machine, force the CPU build:
uv pip install torch --torch-backend=cpu # or set UV_TORCH_BACKEND=cpu
Quickstart
from riffly import Riffly
model = Riffly("convvae") # or "vae", "transformer"
model.train(data="datasets/adl-piano-midi", epochs=100)
model.generate(n=8, save="out/") # writes MIDI + WAV + PNG for each melody
model.save("riffly.pt")
Riffly("riffly.pt").generate(n=4, save="more/", wav=False) # opt out: MIDI + PNG only
That is the whole workflow: construct, train, generate, save.
Pass generate(..., multi_track=True) to split each melody into three voices, melody, chord, and 808 bass, in the saved .mid and .wav.
Plotting a generation
generate(save="out/") writes a .png for every melody. To show one on screen instead (the right-hand image at the top of this README):
from riffly import Riffly, plot
model = Riffly("convvae")
roll = model.generate(n=1)[0]
plot(roll)
Plotting needs the interactive extra (see Install).
Datasets
Point train(data=...) at any folder of .mid files. Three open datasets that work well out of the box:
| Dataset | Size | Source |
|---|---|---|
| ADL Piano MIDI | ~11k piano MIDIs across many genres | https://github.com/lucasnfe/adl-piano-midi |
| Classical MIDI (Kaggle) | ~300 classical MIDIs | https://www.kaggle.com/datasets/soumikrakshit/classical-music-midi |
| The Magic of MIDI v1 | ~169k MIDIs scraped from the web | https://archive.org/details/themagicofmidi |
Download one, unzip it, and pass the folder path. The first run caches the preprocessed dataset under ~/.cache/riffly (override with $RIFFLY_CACHE or train(..., cache_dir=...)) so later runs start instantly.
Advanced usage
The facade wraps lower-level modules that you can still use directly for full control:
riffly.models—VAE,ConvVAE,TransformerVAE.riffly.datasets.MIDIDataset— MIDI folder to(rows, columns)tensors.riffly.train—train(),validation_loop().riffly.processes—MIDIPostprocess,MultiTrackPostprocess(melody + chords + 808 bass),MIDIPreprocess.riffly.plots— dataset previews, training curves, reconstruction grids.
License
MIT. See LICENSE.
The pretrained pop checkpoint distributed via GitHub Releases was trained
on the ADL Piano MIDI Dataset
(Ferreira & Whitehead, 2020), licensed CC BY 4.0. Attribution is preserved
when the checkpoint is redistributed.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file riffly-0.1.0.tar.gz.
File metadata
- Download URL: riffly-0.1.0.tar.gz
- Upload date:
- Size: 58.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9261fb81745c1ef07466c647f78ce369edfdfbb6c483734f4ea4884a5e6f8996
|
|
| MD5 |
573babda607fb86325c1dd0fa7b619d6
|
|
| BLAKE2b-256 |
90ee51c0e83a784b451f2142c6b00b5a85b4a429b90f2c2e0dffd50a28d66092
|
File details
Details for the file riffly-0.1.0-py3-none-any.whl.
File metadata
- Download URL: riffly-0.1.0-py3-none-any.whl
- Upload date:
- Size: 64.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da55de23519d843b0c2fcfa18ace30a06b2654f4036621a8f07a2cde6ec60ee4
|
|
| MD5 |
3a47c1091dc3c458baeb6df99f6e22f7
|
|
| BLAKE2b-256 |
7c752519504bf5646361414964a7f1d4d3c4633171c3863dea5970ef80196ef3
|