---
name: music-generation
description: Tools, patterns, and utilities for generating professional music with realistic instrument sounds. Write custom compositions using music21 or learn from existing MIDI files.
when_to_use: When users request downloadable .mp3 or .wav music files, original compositions, classical pieces, or timed music for videos. This skill emphasizes intelligence and composition over pre-made functions.
version: 2.0.0
dependencies: music21, midi2audio, pydub, mido, numpy, scipy
---

## Quick Start (Read This First!)

**IMPORTANT: This file is located at `/mnt/skills/private/music-generation/SKILL.md`**

If you need to reference this skill again during your session, read that exact path directly. Do not explore directories or use find commands - just read the file path above.

## Philosophy

This skill provides **tools and patterns** for music composition, not pre-baked solutions. You should use your intelligence and the music21 library to compose dynamically based on user requests.

**Core Principle**: Write custom code that composes music algorithmically rather than calling functions with hardcoded melodies.

## Installation & Setup

### Quick Installation

Run the automated installer for complete setup:

```bash
bash /mnt/skills/private/music-generation/install.sh
```

This installs all system dependencies, Python packages, and verifies the installation.

**Note:** The install script may display "error: externally-managed-environment" messages at the end. These are expected and can be safely ignored - the dependencies are already installed. If you see these messages, the installation was successful.

### Manual Installation

Alternatively, install dependencies manually:

**System Dependencies:**
```bash
apt-get update
apt-get install -y fluidsynth fluid-soundfont-gm fluid-soundfont-gs ffmpeg
```

**Python Dependencies:**
```bash
pip install -r /mnt/skills/private/music-generation/requirements.txt
```

The `requirements.txt` includes: music21, midi2audio, pydub, mido, numpy, scipy.

### Available SoundFonts

**Traditional Pipeline (Orchestral/Acoustic):**
- `/usr/share/sounds/sf2/FluidR3_GM.sf2` (141MB, General MIDI soundfont for orchestral/classical)
- `/usr/share/sounds/sf2/default.sf2` (symlink to best available)

**Electronic Pipeline:**
- No soundfonts required - uses real-time synthesis for all electronic sounds

## Quick Start: Write Custom Compositions

### Basic Music Generation Pattern

```python
from music21 import stream, note, chord, instrument, tempo, dynamics
from midi2audio import FluidSynth
from pydub import AudioSegment

# 1. Create score and parts
score = stream.Score()
violin_part = stream.Part()
violin_part.insert(0, instrument.Violin())
violin_part.insert(0, tempo.MetronomeMark(number=120))

# 2. Generate notes algorithmically
for measure in range(16):
    violin_part.append(note.Note('E5', quarterLength=1.0))
    violin_part.append(note.Note('G5', quarterLength=1.0))
    violin_part.append(note.Note('A5', quarterLength=2.0))

# 3. Export to MIDI
score.append(violin_part)
midi_path = '/mnt/user-data/outputs/composition.mid'
score.write('midi', fp=midi_path)

# 4. Render with FluidSynth
fs = FluidSynth('/usr/share/sounds/sf2/FluidR3_GM.sf2')
wav_path = '/mnt/user-data/outputs/composition.wav'
fs.midi_to_audio(midi_path, wav_path)

# 5. Convert to MP3
audio = AudioSegment.from_wav(wav_path)
mp3_path = '/mnt/user-data/outputs/composition.mp3'
audio.export(mp3_path, format='mp3', bitrate='192k')
```

### Key Concepts

- **Always create downloadable MP3 files** (not HTML players)
- **All output goes to** `/mnt/user-data/outputs/`
- **Use music21.instrument classes**: `instrument.Violin()`, `instrument.Violoncello()`, `instrument.Piano()`, `instrument.Trumpet()`, etc.
- **Generate notes programmatically** - avoid hardcoded sequences

## Choosing the Right Rendering Pipeline

**CRITICAL**: This skill supports TWO rendering pipelines. You MUST choose based on the musical genre:

### Traditional Pipeline (Orchestral, Classical, Acoustic)

**Use when creating:**
- Orchestral music (violin, cello, trumpet, etc.)
- Classical compositions (Mozart, Beethoven style)
- Piano music, chamber music, symphonies
- Acoustic guitar, brass ensembles
- Any music with traditional/acoustic instruments

**How to render:**
```python
# After composing with music21 and exporting MIDI...
from midi2audio import FluidSynth
from pydub import AudioSegment

fs = FluidSynth('/usr/share/sounds/sf2/FluidR3_GM.sf2')
fs.midi_to_audio(midi_path, wav_path)

audio = AudioSegment.from_wav(wav_path)
audio.export(mp3_path, format='mp3', bitrate='192k')
```

### Electronic Pipeline (House, Techno, EDM, Electronic)

**Use when creating:**
- House, techno, trance, EDM
- Electronic dance music with synth bass/pads/leads
- DJ beats, club music
- Any music described as "electronic" or "synth-heavy"
- Music referencing DJs like Keinemusik, Black Coffee, etc.

**How to render:**
```python
# After composing with music21, using mido for instruments, and exporting MIDI...
import subprocess

# Use the electronic rendering script
result = subprocess.run([
    'python',
    '/mnt/skills/private/music-generation/scripts/render_electronic.py',
    midi_path,
    mp3_path
], capture_output=True, text=True)

print(result.stdout)
if result.returncode != 0:
    print(f"Error: {result.stderr}")
```

**Why this matters:**
- The orchestral soundfont (FluidR3_GM.sf2) sounds **terrible** for electronic music
- Its "synth" instruments are basic 1990s approximations
- The electronic pipeline uses **real-time synthesis** for authentic electronic sound
- Synthesizes 808-style kicks, electronic snares, and hi-hats on-the-fly (NO external samples required)
- Bass/pads/leads use subtractive synthesis with filters and ADSR envelopes
- Genre presets (deep_house, techno, trance, ambient) tune synthesis parameters automatically

**Drum Synthesis:**

The electronic renderer uses **real-time drum synthesis** (no external samples needed). All drum sounds (kicks, snares, hi-hats, claps) are synthesized on-the-fly with genre-specific parameters.

**Example: House Track**
```python
# 1. Compose with music21 (same as always)
score = stream.Score()
drums = stream.Part()
bass = stream.Part()
pads = stream.Part()
# ... compose your music

# 2. Export MIDI
midi_path = '/mnt/user-data/outputs/deep_house.mid'
score.write('midi', fp=midi_path)

# 3. Fix instruments with mido (INSERT program_change messages)
from mido import MidiFile, Message
mid = MidiFile(midi_path)
for i, track in enumerate(mid.tracks):
    if i == 1:  # Drums
        for msg in track:
            if hasattr(msg, 'channel'):
                msg.channel = 9
    elif i == 2:  # Bass - INSERT program_change
        insert_pos = 0
        for j, msg in enumerate(track):
            if msg.type == 'track_name':
                insert_pos = j + 1
                break
        track.insert(insert_pos, Message('program_change', program=38, time=0))
mid.save(midi_path)

# 4. Render with ELECTRONIC pipeline with deep_house preset!
import subprocess
subprocess.run([
    'python',
    '/mnt/skills/private/music-generation/scripts/render_electronic.py',
    midi_path,
    '/mnt/user-data/outputs/deep_house.mp3',
    '--genre', 'deep_house'
])
```

### Available Genre Presets

The electronic renderer includes pre-tuned synthesis presets with **supersaw lead synthesis** for thick, professional EDM sounds:

- **deep_house**: Warm bass with 3-voice leads (120-125 BPM)
- **techno**: Hard-hitting with 7-voice supersaw leads (125-135 BPM)
- **trance**: Uplifting with massive 9-voice supersaw leads (130-140 BPM)
- **ambient**: Soft, atmospheric with 5-voice pads (60-90 BPM)
- **acid_house**: Squelchy TB-303 bass with 5-voice leads (120-130 BPM)
- **default**: Balanced 5-voice leads (120-130 BPM)

**Supersaw Synthesis (Swedish House Mafia / Progressive House Sound):**

The electronic renderer now includes **unison voice synthesis** for fat, buzzy leads:
- **Multiple detuned oscillators**: 3-9 sawtooth waves per note (genre-dependent)
- **Aggressive detuning**: 6-15 cents spread creates buzzy chorus effect
- **Enhanced saturation**: 2.5x distortion for punch and aggression
- **Phase spreading**: Creates wide stereo image

**How It Works:**
- House: 3 voices, ±8 cents (subtle, warm)
- Techno: 7 voices, ±12 cents (aggressive, punchy)
- Trance: 9 voices, ±15 cents (massive, soaring)
- Acid house: 5 voices, ±12 cents (squelchy, aggressive)

This replicates the classic supersaw sound from Swedish House Mafia, Avicii, and modern EDM productions.

Each preset tunes:
- **Drum synthesis**: kick pitch/decay/punch, snare tone/snap, hat brightness/metallic
- **Bass synthesis**: waveform, filter cutoff/resonance, ADSR envelope
- **Pad synthesis**: attack/release times, detune amount, brightness
- **Lead synthesis**: brightness, envelope, portamento
- **Volume balance**: intelligent mix levels per instrument with frequency-aware compensation
- **Velocity curves**: exponential, linear, or logarithmic response to MIDI velocity

### Intelligent Volume Management

The electronic renderer uses **frequency-aware volume balancing** to prevent any instrument from overpowering the mix:

**How it works:**
- **Bass frequencies (<100Hz)**: Automatically reduced by -4 to -6dB (sub-bass has high perceived energy)
- **Mid frequencies (200-800Hz)**: Balanced naturally
- **High frequencies (>800Hz)**: Slightly boosted for clarity (+1 to +1.5dB)
- **Genre-specific balance**: Each preset has optimized levels (e.g., House bass gets -3dB)
- **Velocity curves**: MIDI velocity maps intelligently (not just linear)
- **Auto-limiting**: Final mix is limited to -1dB to prevent clipping

**Why this matters:**
- House bass (A1, E2) at 55-82Hz naturally has more power - now automatically compensated
- Prevents "bass overpowering everything" issues
- Maintains balanced mix across all genres
- No manual volume tweaking needed

To see all available presets:
```bash
python /mnt/skills/private/music-generation/scripts/render_electronic.py --list-genres
```

### Customizing Synthesis Parameters

For advanced control, you can create custom preset JSON files:

```json
{
  "drums": {
    "kick": {"pitch": 52.0, "decay": 0.6, "punch": 0.9},
    "snare": {"tone_mix": 0.25, "snap": 0.8}
  },
  "bass": {
    "waveform": "sawtooth",
    "cutoff": 180,
    "resonance": 0.7
  },
  "pad": {
    "attack": 1.0,
    "brightness": 0.35
  }
}
```

Then use with `--preset`:
```bash
python render_electronic.py track.mid output.mp3 --preset my_preset.json
```

## Advanced Workflow: Learn from Existing MIDI

For classical pieces or complex compositions, you can:

### 1. Extract Structure from ANY MIDI File

```bash
python /mnt/skills/private/music-generation/scripts/midi_inventory.py \
    path/to/mozart.mid \
    /mnt/user-data/outputs/mozart_structure.json
```

This extracts:
- Tempo, key signature, time signature
- Track information and instruments
- Complete note sequences with timing
- Musical structure

### 2. Modify the JSON Structure

```python
import json

# Load extracted structure
with open('/mnt/user-data/outputs/mozart_structure.json', 'r') as f:
    structure = json.load(f)

# Modify instruments, notes, timing, etc.
structure['tracks']['track-0']['instrument'] = 'violin'  # Change piano to violin!

# Save modified structure
with open('/mnt/user-data/outputs/mozart_violin.json', 'w') as f:
    json.dump(structure, f)
```

### 3. Render Modified Structure to MP3

```bash
python /mnt/skills/private/music-generation/scripts/midi_render.py \
    /mnt/user-data/outputs/mozart_violin.json \
    /mnt/user-data/outputs/mozart_violin.mp3
```

**This workflow lets you "recreate" any classical piece with different instruments!**

## Available Scripts

All scripts are located in `/mnt/skills/private/music-generation/scripts/`:

**Main Workflow Scripts:**
- **`render_electronic.py`** - Electronic music renderer with real-time synthesis (drums, bass, pads, leads)
- **`midi_inventory.py`** - Extract complete structure from ANY MIDI file to JSON format
- **`midi_render.py`** - Render JSON music structure to MP3 using FluidSynth
- **`midi_transform.py`** - Generic MIDI transformations (transpose, tempo change, instrument swap)
- **`audio_validate.py`** - Validate audio file quality and format

**Synthesis Engine (used by render_electronic.py):**
- **`drum_synthesizer.py`** - Synthesizes kicks, snares, hi-hats, claps on-the-fly
- **`melodic_synthesizer.py`** - Synthesizes bass, pads, and lead sounds using subtractive synthesis
- **`synthesis_presets.py`** - Genre presets (deep_house, techno, trance, ambient, etc.)
- **`midi_utils.py`** - MIDI parsing utilities for extracting events and metadata
- **`__init__.py`** - Python package marker (allows importing scripts as modules)

**Utility Scripts:**

## Music Theory Reference

### Complete General MIDI Instrument Map (Programs 0-127)

**CRITICAL: music21 has limited instrument support. For most sounds (especially electronic), you MUST use mido to set program numbers after export.**

```python
# Piano (0-7)
0: "Acoustic Grand Piano"
1: "Bright Acoustic Piano"
2: "Electric Grand Piano"
3: "Honky-tonk Piano"
4: "Electric Piano 1"
5: "Electric Piano 2"
6: "Harpsichord"
7: "Clavinet"

# Chromatic Percussion (8-15)
8: "Celesta"
9: "Glockenspiel"
10: "Music Box"
11: "Vibraphone"
12: "Marimba"
13: "Xylophone"
14: "Tubular Bells"
15: "Dulcimer"

# Organ (16-23)
16: "Drawbar Organ"
17: "Percussive Organ"
18: "Rock Organ"
19: "Church Organ"
20: "Reed Organ"
21: "Accordion"
22: "Harmonica"
23: "Tango Accordion"

# Guitar (24-31)
24: "Acoustic Guitar (nylon)"
25: "Acoustic Guitar (steel)"
26: "Electric Guitar (jazz)"
27: "Electric Guitar (clean)"
28: "Electric Guitar (muted)"
29: "Overdriven Guitar"
30: "Distortion Guitar"
31: "Guitar Harmonics"

# Bass (32-39)
32: "Acoustic Bass"
33: "Electric Bass (finger)"
34: "Electric Bass (pick)"
35: "Fretless Bass"
36: "Slap Bass 1"
37: "Slap Bass 2"
38: "Synth Bass 1"
39: "Synth Bass 2"

# Strings (40-47)
40: "Violin"
41: "Viola"
42: "Cello"
43: "Contrabass"
44: "Tremolo Strings"
45: "Pizzicato Strings"
46: "Orchestral Harp"
47: "Timpani"

# Ensemble (48-55)
48: "String Ensemble 1"
49: "String Ensemble 2"
50: "Synth Strings 1"
51: "Synth Strings 2"
52: "Choir Aahs"
53: "Voice Oohs"
54: "Synth Voice"
55: "Orchestra Hit"

# Brass (56-63)
56: "Trumpet"
57: "Trombone"
58: "Tuba"
59: "Muted Trumpet"
60: "French Horn"
61: "Brass Section"
62: "Synth Brass 1"
63: "Synth Brass 2"

# Reed (64-71)
64: "Soprano Sax"
65: "Alto Sax"
66: "Tenor Sax"
67: "Baritone Sax"
68: "Oboe"
69: "English Horn"
70: "Bassoon"
71: "Clarinet"

# Pipe (72-79)
72: "Piccolo"
73: "Flute"
74: "Recorder"
75: "Pan Flute"
76: "Blown Bottle"
77: "Shakuhachi"
78: "Whistle"
79: "Ocarina"

# Synth Lead (80-87)
80: "Lead 1 (square)"
81: "Lead 2 (sawtooth)"
82: "Lead 3 (calliope)"
83: "Lead 4 (chiff)"
84: "Lead 5 (charang)"
85: "Lead 6 (voice)"
86: "Lead 7 (fifths)"
87: "Lead 8 (bass + lead)"

# Synth Pad (88-95)
88: "Pad 1 (new age)"
89: "Pad 2 (warm)"
90: "Pad 3 (polysynth)"
91: "Pad 4 (choir)"
92: "Pad 5 (bowed)"
93: "Pad 6 (metallic)"
94: "Pad 7 (halo)"
95: "Pad 8 (sweep)"

# Synth Effects (96-103)
96: "FX 1 (rain)"
97: "FX 2 (soundtrack)"
98: "FX 3 (crystal)"
99: "FX 4 (atmosphere)"
100: "FX 5 (brightness)"
101: "FX 6 (goblins)"
102: "FX 7 (echoes)"
103: "FX 8 (sci-fi)"

# Ethnic (104-111)
104: "Sitar"
105: "Banjo"
106: "Shamisen"
107: "Koto"
108: "Kalimba"
109: "Bag pipe"
110: "Fiddle"
111: "Shanai"

# Percussive (112-119)
112: "Tinkle Bell"
113: "Agogo"
114: "Steel Drums"
115: "Woodblock"
116: "Taiko Drum"
117: "Melodic Tom"
118: "Synth Drum"
119: "Reverse Cymbal"

# Sound Effects (120-127)
120: "Guitar Fret Noise"
121: "Breath Noise"
122: "Seashore"
123: "Bird Tweet"
124: "Telephone Ring"
125: "Helicopter"
126: "Applause"
127: "Gunshot"
```

### Complete Drum Map (MIDI Channel 10, Notes 35-81)

**Drums use note numbers for different sounds, NOT pitch. Must be on channel 10 (9 in 0-indexed).**

```python
# Bass Drums
35: "Acoustic Bass Drum"
36: "Bass Drum 1"  # Most common kick

# Snares
38: "Acoustic Snare"  # Standard snare
40: "Electric Snare"

# Toms
41: "Low Floor Tom"
43: "High Floor Tom"
45: "Low Tom"
47: "Low-Mid Tom"
48: "Hi-Mid Tom"
50: "High Tom"

# Hi-Hats
42: "Closed Hi-Hat"  # Most used
44: "Pedal Hi-Hat"
46: "Open Hi-Hat"

# Cymbals
49: "Crash Cymbal 1"
51: "Ride Cymbal 1"
52: "Chinese Cymbal"
53: "Ride Bell"
55: "Splash Cymbal"
57: "Crash Cymbal 2"
59: "Ride Cymbal 2"

# Percussion
37: "Side Stick"
39: "Hand Clap"
54: "Tambourine"
56: "Cowbell"
58: "Vibraslap"
60: "Hi Bongo"
61: "Low Bongo"
62: "Mute Hi Conga"
63: "Open Hi Conga"
64: "Low Conga"
65: "High Timbale"
66: "Low Timbale"
67: "High Agogo"
68: "Low Agogo"
69: "Cabasa"
70: "Maracas"
71: "Short Whistle"
72: "Long Whistle"
73: "Short Guiro"
74: "Long Guiro"
75: "Claves"
76: "Hi Wood Block"
77: "Low Wood Block"
78: "Mute Cuica"
79: "Open Cuica"
80: "Mute Triangle"
81: "Open Triangle"
```

### How to Use Any Instrument (mido workflow)

music21 has built-in classes for orchestral instruments (Violin, Piano, Trumpet, etc.) but NO support for synths, electronic instruments, or many others. To use any GM instrument:

**CRITICAL RULE**: When you create a `stream.Part()` WITHOUT assigning a music21 instrument class, music21 WILL NOT create `program_change` messages in the MIDI file. You MUST use mido to INSERT these messages manually. Simply trying to modify them with `if msg.type == 'program_change': msg.program = X` will fail silently because no such messages exist!

**Helper Function for Setting Instruments**:

```python
from mido import Message

def set_track_instrument(track, program):
    """Insert a program_change message at the beginning of a MIDI track."""
    insert_pos = 0
    for j, msg in enumerate(track):
        if msg.type == 'track_name':
            insert_pos = j + 1
            break
    track.insert(insert_pos, Message('program_change', program=program, time=0))

# Usage after loading MIDI with mido:
# set_track_instrument(mid.tracks[2], 33)  # Set track 2 to Electric Bass
```

**Step 1: Compose with music21 (use placeholder or skip instrument)**
```python
from music21 import stream, note, chord, tempo

score = stream.Score()

# Create parts - don't worry about instrument assignment yet
synth_lead = stream.Part()
synth_pad = stream.Part()
bass = stream.Part()

# Add your notes/chords
synth_lead.append(note.Note('E5', quarterLength=1.0))
# ... compose your music

score.append(synth_lead)
score.append(synth_pad)
score.append(bass)

# Export to MIDI
midi_path = '/mnt/user-data/outputs/track.mid'
score.write('midi', fp=midi_path)
```

**Step 2: Assign correct instruments with mido**
```python
from mido import MidiFile, Message

mid = MidiFile(midi_path)

# Track 0 is tempo/metadata, actual parts start at track 1
# CRITICAL: You must INSERT program_change messages, not just modify existing ones!
# music21 doesn't create program_change messages if you don't assign instruments

for i, track in enumerate(mid.tracks):
    if i == 1:  # First part (synth_lead)
        # Insert program_change at beginning of track (after track name if present)
        insert_pos = 0
        for j, msg in enumerate(track):
            if msg.type == 'track_name':
                insert_pos = j + 1
                break
        track.insert(insert_pos, Message('program_change', program=80, time=0))

    elif i == 2:  # Second part (synth_pad)
        insert_pos = 0
        for j, msg in enumerate(track):
            if msg.type == 'track_name':
                insert_pos = j + 1
                break
        track.insert(insert_pos, Message('program_change', program=88, time=0))

    elif i == 3:  # Third part (bass)
        insert_pos = 0
        for j, msg in enumerate(track):
            if msg.type == 'track_name':
                insert_pos = j + 1
                break
        track.insert(ins