MidiGen
A Python library for creating and manipulating MIDI files with a music-theory-aware API. Build anything from simple melodies to multi-track compositions with chord progressions, scales, and rhythmic patterns.
Built on mido with a custom Roman numeral analysis engine for chord progression parsing. No heavy dependencies.
The package is published on PyPI as
midigen-libbut you import it asmidigen.
Installation
Requires Python 3.10+:
pip install midigen-lib
For development:
git clone https://github.com/cainky/midigen.git
cd midigen
uv sync
Quick Start
from midigen import Song, Section, Key, MidiCompiler
song = Song(key=Key("C", "major"), tempo=120)
song.add_section(Section("Verse", 8, "I-V-vi-IV"))
song.add_section(Section("Chorus", 4, "IV-I-V-vi"))
song.add_instrument("Acoustic Grand Piano")
MidiCompiler(song).compile().save("my_song.mid")
Song holds your musical intent. MidiCompiler handles MIDI protocol details (channels, tracks, timing).
Features
- Song structure - Compose with
SongandSectionobjects using familiar concepts like verses and choruses - Chord progressions - Roman numeral notation (e.g., "I-IV-V-I") with automatic voice leading
- 14 scale types - Major, minor (natural/harmonic/melodic), pentatonic, blues, whole tone, chromatic, and all seven modes
- 22 chord types - Triads, suspended, seventh, ninth, eleventh, thirteenth, augmented, and diminished voicings
- Melody generation - From note names, scale degrees, or random walk algorithm
- Arpeggio patterns - Ascending, descending, and alternating
- Drum programming -
DrumKitclass with standard General MIDI drum names - Multi-track support - Layer instruments across separate MIDI tracks with General MIDI instrument names
- Time utilities - Convert between musical time (measures, beats) and MIDI ticks
Examples
Melodies
from midigen import Melody, Scale
# From note names
melody = Melody.from_note_names("C4 E4 G4 E4 C4", durations=480)
# From scale degrees with rhythm
scale = Scale.major(60) # C major
melody = Melody.from_degrees(
scale,
degrees=[1, 3, 5, 8, 5, 3, 1],
rhythms="quarter quarter quarter half quarter quarter half"
)
# Random walk (reproducible with seed)
melody = Melody.random_walk(
start_pitch=60, length=16, scale=scale, max_interval=3, seed=42
)
# Transform
transposed = melody.transpose(5)
retrograde = melody.reverse()
Scales and Modes
from midigen import Scale
# Basic
c_major = Scale.major(60)
a_minor = Scale.minor(57)
a_harmonic_minor = Scale.harmonic_minor(57)
# Pentatonic and blues
c_pent = Scale.major_pentatonic(60)
c_blues = Scale.blues(60)
# Modes
d_dorian = Scale.dorian(62)
e_phrygian = Scale.phrygian(64)
f_lydian = Scale.lydian(65)
g_mixolydian = Scale.mixolydian(67)
b_locrian = Scale.locrian(71)
# Other
c_whole_tone = Scale.whole_tone(60)
c_chromatic = Scale.chromatic(60)
Multi-Track Compositions
from midigen import Song, Section, Key, MidiCompiler
song = Song(key=Key("Am", "minor"), tempo=90)
song.add_section(Section(name="Intro", length=4, chord_progression="i-VI-III-VII"))
song.add_section(Section(name="Verse", length=8, chord_progression="i-VI-III-VII-i-VI-iv-V"))
song.add_instrument("Synth Bass 1")
song.add_instrument("String Ensemble 1")
song.add_instrument("Lead 1 (square)")
compiler = MidiCompiler(song)
compiler.compile_instrument("Synth Bass 1", octave=3)
compiler.compile_instrument("String Ensemble 1", octave=4)
compiler.compile_instrument("Lead 1 (square)", octave=5)
compiler.save("multi_track.mid", output_dir="./output")
Drum Patterns
from midigen import MidiGen, DrumKit, Key
midi = MidiGen(key=Key("C"))
drum_kit = DrumKit()
# Simple 4/4 rock beat
for i in range(4):
t = i * 480
drum_kit.add_drum("Bass Drum 1", time=t)
drum_kit.add_drum("Acoustic Snare", time=t + 240)
drum_kit.add_drum("Closed Hi Hat", time=t)
drum_kit.add_drum("Closed Hi Hat", time=t + 120)
drum_kit.add_drum("Closed Hi Hat", time=t + 240)
drum_kit.add_drum("Closed Hi Hat", time=t + 360)
track = midi.get_active_track()
track.add_drum_kit(drum_kit)
midi.save("drum_beat.mid")
Arpeggios
from midigen import MidiGen, Note, Arpeggio, ArpeggioPattern, Key, KEY_MAP
midi = MidiGen(tempo=140, key=Key("C"))
track = midi.get_active_track()
notes = [
Note(pitch=KEY_MAP["C4"], velocity=70, duration=120, time=0),
Note(pitch=KEY_MAP["E4"], velocity=70, duration=120, time=0),
Note(pitch=KEY_MAP["G4"], velocity=70, duration=120, time=0),
Note(pitch=KEY_MAP["B4"], velocity=70, duration=120, time=0),
]
arpeggio = Arpeggio(
notes=notes,
pattern=ArpeggioPattern.ASCENDING, # or DESCENDING, ALTERNATING
delay=120,
loops=4,
)
track.add_arpeggio(arpeggio)
midi.save("arpeggio.mid")
Time Conversions
from midigen import TimeConverter
tc = TimeConverter(ticks_per_quarter_note=480)
one_measure = tc.measures_to_ticks(1) # 1920 ticks in 4/4
half_note = tc.note_duration("half") # 960 ticks
dotted_quarter = tc.note_duration("dotted_quarter") # 720
triplet_eighth = tc.note_duration("triplet_eighth") # 160
# Different time signatures
waltz_measure = tc.measures_to_ticks(
1, time_signature_numerator=3, time_signature_denominator=4
)
Low-Level API
For full control, bypass Song/MidiCompiler and work directly with tracks and notes:
from midigen import MidiGen, Note, Chord, Key, KEY_MAP
midi = MidiGen(tempo=120, time_signature=(4, 4), key_signature=Key("C"))
track = midi.get_active_track()
track.add_note(Note(pitch=KEY_MAP["C4"], velocity=64, duration=480, time=0))
chord = Chord([
Note(pitch=KEY_MAP["E4"], velocity=64, duration=480, time=480),
Note(pitch=KEY_MAP["G4"], velocity=64, duration=480, time=480),
])
track.add_chord(chord)
midi.save("low_level.mid")
Architecture
The library is organized into four layers:
midigen.theory— Pure music theory (Note, Key, Scale, roman numeral parsing, time conversion)midigen.composition— Musical structures (Chord, ChordProgression, Arpeggio, Melody, DrumKit, Section)midigen.protocol— MIDI protocol (Track, ChannelPool, instruments)midigen.api— High-level API (Song, MidiGen, MidiCompiler)
All public names are available via from midigen import ....
Contributing
Contributions are welcome. See CONTRIBUTING.md for details.
License
GPL-3.0. See LICENSE.
Metadata
Release files for midigen-lib 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| midigen_lib-1.1.0.tar.gz | 89.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| midigen_lib-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 162.6 kB
Release files / midigen_lib-1.1.0.tar.gz
| Download URL | midigen_lib-1.1.0.tar.gz |
|---|---|
| Size | 89.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dacd33c8bd925a1915017d5baca991b1a9e88118835f720225036ceb21977ebb
|
|
BLAKE2b-256 checksum How to use checksums |
ff38a1507364d449941088b46c1d08d0f92ff0593b1dc271f0ee8c82238eaaca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 13, 2026.
Transparency logRelease files / midigen_lib-1.1.0-py3-none-any.whl
| Download URL | midigen_lib-1.1.0-py3-none-any.whl |
|---|---|
| Size | 73.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1bbf30ed47eed884fe37bf932a9205f315703aaed2cca77b9916f6d4c3d571b7
|
|
BLAKE2b-256 checksum How to use checksums |
418a8e89bb095e99fa0a4ba0fd0551065974536da01767a2eb58e9857caa33d3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 13, 2026.
Transparency log