
Code To Music
- 1 installs
- 404 repo stars
- Updated August 5, 2026
- aiskillstore/marketplace
code-to-music is a Claude Code skill that creates music with code and outputs it as a .mp3 file with realistic instrument sounds.
About
code-to-music is a Claude Code skill that composes music with code and outputs a downloadable MP3 file. It writes custom code using the music21 library to generate melodies algorithmically rather than replaying hardcoded sequences. A developer uses it to create original compositions across electronic or acoustic styles. It offers an electronic synthesis pipeline and a traditional soundfont pipeline and explicitly excludes vocal music, mixing, and mastering.
- Composes music algorithmically and exports it as a downloadable MP3
- Offers two pipelines: electronic synthesis and traditional soundfont orchestral
- Uses the music21 library with FluidR3_GM soundfonts and mido MIDI program control
Code To Music by the numbers
- 1 all-time installs (skills.sh)
- Ranked #1,200 of 1,335 Generative Media skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
code-to-music capabilities & compatibility
- Capabilities
- music generation · audio synthesis · midi composition
- Use cases
- image generation
- Pricing
- Free
What code-to-music says it does
Tools, patterns, and utilities for creating music with code. Output as a .mp3 file with realistic instrument sounds.
Always create downloadable MP3 files
npx skills add https://github.com/aiskillstore/marketplace --skill code-to-musicAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 404 |
| Last updated | August 5, 2026 |
| Repository | aiskillstore/marketplace ↗ |
What it does
Compose an original music track algorithmically with code and export it as a downloadable MP3 file.
Who is it for?
Generating original algorithmic music compositions as downloadable MP3 files.
Skip if: Vocal or lyrical music, audio mixing/mastering, real-time MIDI playback, or replicating existing songs.
When should I use this skill?
The user asks for music to be created.
What you get
A downloadable MP3 of an algorithmically composed track in the requested style.
- Downloadable MP3 track
By the numbers
- Two rendering pipelines (electronic synthesis and traditional soundfont)
- FluidR3_GM.sf2 soundfont provides 128 General MIDI instruments
Files
This skill provides tools and patterns for music composition, not pre-baked solutions. The intelligence and music21 library should be used 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: 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 such messages appear, the installation was successful.
Available SoundFonts
Traditional Pipeline (Orchestral/Acoustic):
- `/usr/share/sounds/sf2/FluidR3_GM.sf2` (141MB, General MIDI soundfont - use for high-quality orchestral/acoustic samples)
/usr/share/sounds/sf2/default.sf2(symlink to best available)- ⚠️ DO NOT use TimGM6mb.sf2 - inferior quality, guitars sound electronic/piano-like
Electronic Pipeline:
- Uses the same FluidR3_GM.sf2 soundfont as traditional music
- The difference is which GM programs you choose (synth programs 38-39, 80-95 instead of orchestral)
Key Concepts
- Always create downloadable MP3 files (not HTML players)
- music21.instrument classes can be used for convenience:
instrument.Violin(),instrument.Violoncello(),instrument.Piano(),instrument.Trumpet(), etc. - CRITICAL: ALWAYS use mido to set MIDI program numbers after export - music21's instrument classes do NOT reliably export program_change messages. See the mido workflow below.
- Generate notes programmatically - avoid hardcoded sequences
Choosing Your Music Generation Pipeline
This skill supports TWO rendering pipelines optimized for different musical styles. Read the user's request carefully and choose the correct pipeline by reading the correct next file.
Decision Process
Identify from the user's request:
- Genre keywords (house, techno, classical, orchestral, reggae)
- Instrument references (synthesizers vs acoustic instruments)
- Musical style descriptions (electronic/DJ vs traditional/acoustic)
- Artist mentions (DJs vs classical composers)
Electronic Pipeline
Use `electronic-music-pipeline.md` when request includes:
- Genres: House, techno, trance, EDM, electronic dance music, ambient electronic, acid house, deep house, club music, DJ sets
- Instruments: Synthesizers, synth bass/pads/leads, 808 drums, electronic drums, supersaw leads, sub-bass
- Context: References to DJs (Keinemusik, Black Coffee, Avicii, Swedish House Mafia), BPM 120-140, "for a club", "for dancing"
- Sound descriptions: "Fat synth sound", "buzzy leads", "electronic", "synth-heavy"
How it works:
- Uses real-time synthesis (no soundfonts)
- Synthesizes drums, bass, pads, leads on-the-fly with DSP
- Genre presets optimize synthesis parameters (deep_house, techno, trance, ambient, acid_house)
- Frequency-aware mixing automatically balances low/mid/high frequencies
- Best for: Authentic electronic sounds, modern EDM production
→ See `electronic-music-pipeline.md` for detailed instructions
---
Traditional Pipeline
Use `traditional-music-pipeline.md` when request includes:
- Genres: Classical, orchestral, jazz, blues, rock, country, folk, reggae, ska, symphonic, chamber music
- Instruments: Violin, cello, trumpet, flute, piano, acoustic guitar, acoustic bass, brass ensembles, string quartets, organ (reggae/gospel)
- Context: References to composers (Mozart, Beethoven, Bach), classical periods (Baroque, Romantic), "unplugged", "acoustic version"
- Sound descriptions: "Traditional", "acoustic", "orchestral"
How it works:
- Uses pre-recorded orchestral samples (soundfonts)
- FluidR3_GM.sf2 soundfont with 128 General MIDI instruments
- Good for realistic orchestral/acoustic instruments
- Best for: Classical, jazz, reggae, rock, traditional music
→ See `traditional-music-pipeline.md` for detailed instructions
---
If Unclear
- Default to TRADITIONAL for mixed requests or ambiguous genres
- Ask user for clarification if request could go either way
Why This Matters
- Using wrong pipeline = poor audio quality (technically works, sounds bad)
- Electronic pipeline: Real-time synthesis for authentic electronic sounds
- Traditional pipeline: Pre-recorded samples for realistic orchestral/acoustic
Available Scripts
All scripts are located in ./scripts:
Traditional Rendering:
- `midi_inventory.py` - Extract complete structure from ANY MIDI file to JSON format
- `midi_render.py` - Render JSON music structure to MP3 using FluidSynth with dynamic range compression
- `midi_utils.py` - MIDI utility functions (extract drums, get BPM, etc.)
Utilities:
- `audio_validate.py` - Validate audio file quality and format
Note: Both electronic and traditional music use the same rendering pipeline (FluidSynth + FluidR3_GM.sf2). The difference is which GM programs you choose.
Music Theory Reference
Complete General MIDI Instrument Map (Programs 0-127)
CRITICAL: music21 does NOT reliably export program_change messages. You MUST ALWAYS use mido to set program numbers after export, even for traditional instruments like saxophone, guitar, or bass. Without this step, tracks will default to piano (program 0).
INSTRUMENT CATEGORIZATION FOR SYNTHESIS: When composing electronic music, classify instruments by their sonic characteristics for proper synthesis:
- Guitar (24-31): Plucky attack, bright tone, medium sustain - needs distinct synthesis from pads
- Bass (32-39): Low-frequency fundamentals, sub-bass energy, short attack
- Strings (40-47): Sustained bowing, rich harmonics, smooth legato
- Brass (56-63): Bright attack, sustained tone, powerful projection
- Reed/Woodwinds (64-79): Breathy attack, organic timbre, expressive dynamics
- Synth Lead (80-87): Bright, cutting, aggressive - designed for melody
- Synth Pad (88-95): Soft attack, long sustain, atmospheric - designed for background
- Ethnic/Percussive (104-119): Plucky or struck sounds with unique timbres
# 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).
# 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)
CRITICAL RULE: music21 does NOT reliably export program_change messages, even when using instrument classes like instrument.TenorSaxophone() or instrument.AcousticGuitar(). mido MUST ALWAYS be used to INSERT program_change messages manually after exporting to MIDI. Without this step, all tracks will sound like piano (program 0).
Common mistake: Assuming that sax_part.insert(0, instrument.TenorSaxophone()) will automatically make the track sound like a saxophone. It won't! mido must still be used to set the program number.
Helper Function for Setting Instruments:
from mido import Message
def set_track_instrument(track, program):
"""Insert a program_change message at the beginning of a MIDI track."""
# Find position after all meta messages (track_name, etc.)
insert_pos = 0
for j, msg in enumerate(track):
if not msg.is_meta: # Insert before first non-meta message
insert_pos = j
break
else:
# If all messages are meta, insert at end
insert_pos = len(track)
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 BassStep 1: Compose with music21 (use placeholder or skip instrument)
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 using .insert() with explicit timing
# CRITICAL: Always use .insert(offset, note) not .append(note)
offset = 0.0
synth_lead.insert(offset, note.Note('E5', quarterLength=1.0))
offset += 1.0
synth_lead.insert(offset, note.Note('G5', quarterLength=1.0))
# ... compose your music with .insert()
score.append(synth_lead)
score.append(synth_pad)
score.append(bass)
# Export to MIDI
midi_path = # define a output path
score.write('midi', fp=midi_path)Step 2: Inspect MIDI structure and assign instruments with mido
from mido import MidiFile, Message
mid = MidiFile(midi_path)
# CRITICAL: DO NOT assume track numbers! Inspect the MIDI file first.
# Print track structure to see what tracks exist
print(f"Total tracks: {len(mid.tracks)}")
for i, track in enumerate(mid.tracks):
note_count = sum(1 for msg in track if msg.type == 'note_on' and msg.velocity > 0)
print(f"Track {i}: {note_count} notes")
# Helper function to set instruments
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 not msg.is_meta:
insert_pos = j
break
else:
insert_pos = len(track)
track.insert(insert_pos, Message('program_change', program=program, time=0))
# Based on inspection, assign instruments to the correct tracks
# Example: if [drums, bass, guitar] were appended and inspection shows tracks 1, 2, 3 have notes:
set_track_instrument(mid.tracks[1], 80) # First part - Square Lead
set_track_instrument(mid.tracks[2], 88) # Second part - Pad
set_track_instrument(mid.tracks[3], 38) # Third part - Bass
mid.save(midi_path)Step 3: For drums, ALSO set channel to 9 (channel 10)
# If track is drums, set ALL messages to channel 9
for i, track in enumerate(mid.tracks):
if i == 1: # This is the drum track
for msg in track:
if hasattr(msg, 'channel'):
msg.channel = 9 # Channel 10 in 1-indexedStep 4: Render to audio
from midi2audio import FluidSynth
from pydub import AudioSegment
fs = FluidSynth('/usr/share/sounds/sf2/FluidR3_GM.sf2')
wav_path = # define a path here
fs.midi_to_audio(midi_path, wav_path)
audio = AudioSegment.from_wav(wav_path)
mp3_path = # define a path here
audio.export(mp3_path, format='mp3', bitrate='192k')Common Chord Progressions & Styles
# Standard Progressions (Roman numerals)
"pop": ["I", "V", "vi", "IV"] # C-G-Am-F (Journey, Adele)
"epic": ["i", "VI", "III", "VII"] # Am-F-C-G (Epic trailer music)
"sad": ["i", "VI", "iv", "V"] # Am-F-Dm-E (Melancholic)
"jazz": ["ii", "V", "I", "vi"] # Dm-G-C-Am (Jazz standard)
"classical": ["I", "IV", "V", "I"] # C-F-G-C (Classical cadence)
"blues": ["I", "I", "I", "I", "IV", "IV", "I", "I", "V", "IV", "I", "I"] # 12-bar blues
"house": ["i", "VI", "III", "VII"] # Minor house progression
"reggae": ["I", "V", "vi", "IV"] # Offbeat rhythm style
"country": ["I", "IV", "V", "I"] # Simple and direct
"rock": ["I", "bVII", "IV", "I"] # Power chord style
"r&b": ["I", "V", "vi", "iii", "IV", "I", "IV", "V"] # Complex R&B
# Genre-Specific Characteristics
STYLES = {
"house": {
"bpm": 120-128,
"time_signature": "4/4",
"drum_pattern": "4-on-floor kick, offbeat hats",
"bass": "Synth bass with groove",
"common_instruments": [38, 80, 88, 4] # Synth bass, lead, pad, e-piano
},
"jazz": {
"bpm": 100-180,
"time_signature": "4/4 or 3/4",
"chords": "Extended (7th, 9th, 11th, 13th)",
"common_instruments": [0, 32, 64, 56, 73] # Piano, bass, sax, trumpet, drums
},
"orchestral": {
"bpm": 60-140,
"sections": ["strings", "woodwinds", "brass", "percussion"],
"common_instruments": [40, 41, 42, 56, 73, 47] # Violin, viola, cello, trumpet, flute, timpani
},
"rock": {
"bpm": 100-140,
"time_signature": "4/4",
"guitars": "Distorted (30) or clean (27)",
"common_instruments": [30, 33, 0, 128] # Distortion guitar, bass, piano, drums
},
"ambient": {
"bpm": 60-90,
"characteristics": "Long sustained notes, atmospheric pads",
"common_instruments": [88, 89, 90, 91, 52] # Various pads, choir
},
"trap": {
"bpm": 130-170,
"drums": "Tight snare rolls, 808 bass kicks",
"hi_hats": "Fast hi-hat patterns (1/16 or 1/32 notes)",
"common_instruments": [38, 128] # Synth bass, drums
}
}music21 Instrument Classes
from music21 import instrument
# Strings
instrument.Violin()
instrument.Viola()
instrument.Violoncello() # Note: NOT Cello()
instrument.Contrabass()
instrument.Harp()
# Piano
instrument.Piano()
instrument.Harpsichord()
# Brass
instrument.Trumpet()
instrument.Trombone()
instrument.Tuba()
instrument.Horn() # French horn
# Woodwinds
instrument.Flute()
instrument.Clarinet()
instrument.Oboe()
instrument.Bassoon()
instrument.SopranoSaxophone()
instrument.AltoSaxophone()
instrument.TenorSaxophone() # Most common for jazz
instrument.BaritoneSaxophone()
# Other
instrument.AcousticGuitar()
instrument.ElectricGuitar()
instrument.Bass()
instrument.Timpani()
# CRITICAL: music21 has LIMITED support for electronic instruments and drums
# For synths, drums, and electronic sounds, the following steps must be taken:
# 1. Create a Part without an instrument (or use a placeholder like Piano())
# 2. Use mido library to INSERT program_change messages after export
# 3. Set drums to MIDI channel 10 (channel 9 in 0-indexed) or they won't sound like drums
#
# Common mistakes:
# - instrument.Cello() doesn't exist - use Violoncello()
# - instrument.FrenchHorn() doesn't exist - use Horn()
# - Setting part.partName doesn't change the sound - MIDI program must be set with mido
# - Drums on channel 0 will play as pitched notes, not drum soundsNote Durations (Quarter Note = 1.0)
- Whole note: 4.0
- Half note: 2.0
- Quarter note: 1.0
- Eighth note: 0.5
- Sixteenth note: 0.25
- Dotted quarter: 1.5
- Triplet quarter: 0.667
mido Quick Reference
For electronic music and drums, use mido to set MIDI programs after music21 export:
from mido import MidiFile, Message
mid = MidiFile(midi_path)
# Define helper function
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 not msg.is_meta:
insert_pos = j
break
else:
insert_pos = len(track)
track.insert(insert_pos, Message('program_change', program=program, time=0))
# Insert program_change messages
for i, track in enumerate(mid.tracks):
if i == 1: # Your track (tracks start at 1, not 0)
set_track_instrument(track, 38) # Synth Bass 1
# For drums: Set channel to 9 (channel 10 in 1-indexed)
for i, track in enumerate(mid.tracks):
if i == 1: # Drum track
for msg in track:
if hasattr(msg, 'channel'):
msg.channel = 9
mid.save(midi_path)Common MIDI Programs:
- 38: Synth Bass 1
- 80: Square Lead
- 81: Sawtooth Lead
- 88: Pad 1 (New Age)
- 25: Acoustic Guitar (Steel) - loud, cuts through
- 33: Acoustic Bass
Common Techniques
Drum Programming (4-on-floor house beat)
CRITICAL: music21's .append() adds notes sequentially (one after another), not simultaneously. For layered drums where kicks, snares, and hats play at the same time, .insert(offset, note) MUST be used with explicit timing.
ALWAYS USE .insert() FOR ADDING NOTES TO PARTS:
Since layering is needed for nearly all good music composition, part.insert(offset, note) MUST ALWAYS be used for ALL tracks - drums, bass, guitar, pads, everything. This prevents timing bugs and ensures proper synchronization.
NEVER mix .insert() and .append() when adding notes to parts - If .insert() is used for drums and .append() for other instruments, music21 will miscalculate track lengths and create tracks that are 5-10× longer than intended (8 minutes instead of 1.5 minutes), with only the first 20-25% containing actual sound.
Note: Using score.append(part) to add Parts to the Score is fine - the issue is specifically about adding notes/chords to Parts, not adding Parts to Scores.
Correct Pattern:
# RIGHT: Use .insert() for notes, .append() for parts
drum_part = stream.Part()
bass_part = stream.Part()
offset = 0.0
for beat in range(16):
# Kick and hi-hat on same beat (layered)
drum_part.insert(offset, note.Note(36, quarterLength=0.5)) # Kick
drum_part.insert(offset, note.Note(42, quarterLength=0.5)) # Hi-hat
# Bass note
bass_part.insert(offset, note.Note('A2', quarterLength=0.5))
offset += 0.5 # Advance time
# Add parts to score (this .append() is fine)
score.append(drum_part)
score.append(bass_part)Ensuring Complete Bar Coverage
CRITICAL: When composing with loops, ensure your notes completely fill each bar without gaps. A common mistake is placing notes that don't span the full bar duration, leaving silent gaps.
Problem Pattern (creates gaps):
bar_length = 1.5 # 6/8 time signature
offset = 0.0
for bar in range(8):
# Only fills first 1.0 quarter lengths, leaving 0.5 silent!
guitar_part.insert(offset, note.Note('D3', quarterLength=0.5))
guitar_part.insert(offset + 0.5, chord.Chord(['D3', 'F#3', 'A3'], quarterLength=0.5))
# Missing: offset + 1.0 to offset + 1.5
offset += bar_length # Advances to next bar, but gap remains
# Result: Guitar plays for 10 seconds, then silence for remaining durationSolution Pattern (complete coverage):
bar_length = 1.5 # 6/8 time signature
offset = 0.0
for bar in range(8):
# Fill the ENTIRE bar with notes
guitar_part.insert(offset, note.Note('D3', quarterLength=0.5)) # 0.0 to 0.5
guitar_part.insert(offset + 0.5, chord.Chord(['D3', 'F#3', 'A3'], quarterLength=0.5)) # 0.5 to 1.0
guitar_part.insert(offset + 1.0, note.Note('D3', quarterLength=0.5)) # 1.0 to 1.5 ✓ Complete!
offset += bar_length
# Result: Guitar plays continuously throughout the entire pieceBest Practice: Before advancing offset += bar_length, verify that your last note ends exactly at offset + bar_length. For example:
- If
bar_length = 1.5, your last note should start atoffset + 1.0withquarterLength=0.5to reachoffset + 1.5 - If
bar_length = 2.0, your last note should start atoffset + 1.5withquarterLength=0.5to reachoffset + 2.0
Best Practices
Composition Quality
- Generate variety: Don't repeat the same 4 bars for entire piece
- Use music theory: Real chord progressions, proper voice leading
- Respect instrument ranges: Violin (G3-E7), Cello (C2-C6), Trumpet (E3-C6)
- Add dynamics: Use p, mp, mf, f, ff markings and crescendos
- Structure: Intro → Development → Climax → Resolution
- Add humanization: Vary timing and velocity to avoid robotic sound
import random
n = note.Note('C5', quarterLength=1.0 + random.uniform(-0.05, 0.05))
n.volume.velocity = 80 + random.randint(-5, 5)Technical Quality
- SoundFont: Use FluidR3_GM.sf2 for best quality
- Bitrate: 192kbps minimum, 320kbps for high quality
- Limiting: Applied via
audio.compress_dynamic_range()to prevent clipping - Timing precision: Use quarterLength values carefully
- Cleanup: Remove temporary MIDI/WAV files after MP3 conversion
Common Pitfalls
- CRITICAL: NEVER assume track numbers - DO NOT blindly use
if i == 3for guitar. ALWAYS inspect the MIDI file first with note counts to verify which track is which - CRITICAL: Always use part.insert(offset, note) for adding notes to parts - Never use
part.append(note)or mix.insert()and.append(). This causes timing bugs where tracks are 5-10x longer than intended with only 20-25% containing sound. See "Drum Programming" section for details - CRITICAL: INSERT program_change messages - Use
track.insert(pos, Message('program_change', ...))notmsg.program = X. See mido Quick Reference - CRITICAL: Set velocities - Lead 90-105, background 50-65. See "Mixing and Balance" section
- CRITICAL: Bass octaves - Use A1-A2 (55-110 Hz), never A0-G0 (inaudible on most systems)
- instrument.Cello() doesn't exist - use
Violoncello() - Forgetting tempo - Add
tempo.MetronomeMark()to first part - Drums not sounding like drums - Set channel to 9 with mido (see mido Quick Reference)
Mixing and Balance
CRITICAL: Setting MIDI program numbers alone is not enough. Without explicit velocity control, some instruments will be completely inaudible.
Setting Velocities
music21 uses default velocity 64 for all notes, which causes poor mixing. Use mido to set velocities after MIDI export:
from mido import MidiFile
def set_track_velocity(track, velocity):
"""Set velocity for all note_on messages in a track."""
for msg in track:
if msg.type == 'note_on' and msg.velocity > 0:
msg.velocity = velocity
mid = MidiFile(midi_path)
for i, track in enumerate(mid.tracks):
if i == 1: # Drums
set_track_velocity(track, 75)
elif i == 2: # Bass
set_track_velocity(track, 80)
elif i == 3: # Lead instrument (sax, guitar, trumpet)
set_track_velocity(track, 95)
elif i == 4: # Background (organ, pads)
set_track_velocity(track, 55)
mid.save(midi_path)Velocity Guidelines
By Role:
- Lead instruments (melody, solos): 90-105
- Rhythm instruments (guitar skanks, comping): 85-100
- Bass: 75-85
- Drums: 70-90
- Background (pads, organs): 50-65
By Frequency Range:
- Low (20-250 Hz): Bass, kick - only ONE dominant at 75-85
- Mid (250-2000 Hz): Most crowded - use velocity to separate (lead 90+, background 50-65)
- High (2000+ Hz): Hi-hats, cymbals - 70-85 for clarity without harshness
Soundfont Level Issues
FluidR3_GM instruments are recorded at different levels. Even with correct velocities, some instruments may be inaudible:
Quiet programs (avoid for lead/rhythm):
- Program 27 (Electric Guitar - clean) - Very quiet
- Program 24 (Acoustic Guitar - nylon)
- Program 73 (Flute)
Better alternatives:
- Program 25 (Acoustic Guitar - steel) - 8-10dB louder, cuts through
- Program 28 (Electric Guitar - muted) - Percussive
- Program 30 (Distortion Guitar) - Aggressive
Additional fixes:
- Increase note duration (0.4 quarterLength minimum vs 0.25)
- Use octave separation (move competing instruments to different octaves)
- Extreme velocity contrast (quiet instrument at 110, loud at 40)
Mixing Checklist
Before rendering:
- ✅ Lead at velocity 90-105
- ✅ Background at velocity 50-65
- ✅ Bass at velocity 75-85
- ✅ Check for quiet instruments (programs 24, 27, 73) and use alternatives
- ✅ Minimum 0.4 quarterLength for rhythm instruments
Resources
- music21 Documentation: https://web.mit.edu/music21/doc/
- General MIDI Spec: https://www.midi.org/specifications-old/item/gm-level-1-sound-set
- Music Theory: https://www.musictheory.net/
- IMSLP (Free Scores): https://imslp.org/ - Download classical MIDIs here!
Limitations
- Instrumental only - No lyrics/vocals
- MIDI-based synthesis - Not studio-quality recordings
- No real-time playback - Files must be rendered before playback
Electronic Music Generation Pipeline
Prerequisites: Before using this pipeline, the generic music21 and MIDI content in SKILL.md should have been read, including:
- Basic music21 composition patterns
- Complete GM Instrument Map (programs 0-127)
- Complete Drum Map (notes 35-81)
- mido workflow for setting instruments
- Generic music theory (chord progressions, scales, timing)
How the Electronic Pipeline Works
Key principle: Electronic music uses the SAME FluidR3_GM soundfont and rendering pipeline as traditional music. The difference is in which GM instruments are chosen.
Why it works:
- FluidR3_GM includes dedicated synth programs (38-39 for bass, 80-87 for leads, 88-95 for pads)
- Channel 10 drums include 808-style electronic drum sounds
- The soundfont was designed with both traditional AND electronic music in mind
The "terrible electronic sound" myth: The soundfont only sounds bad if wrong programs are used (like Violin for a synth lead). Programs 38-39, 80-95 should be used for authentic electronic sound.
Rendering Electronic Music
Electronic music uses the SAME rendering pipeline as traditional music:
from music21 import stream, note, tempo
from mido import MidiFile, Message
from midi2audio import FluidSynth
from pydub import AudioSegment
# 1. Compose with music21
score = stream.Score()
# ... compose your parts
# 2. Export MIDI
midi_path = "/path/to/output.mid"
score.write('midi', fp=midi_path)
# 3. Set instruments with mido (CRITICAL!)
def set_track_instrument(track, program):
insert_pos = 0
for j, msg in enumerate(track):
if not msg.is_meta:
insert_pos = j
break
else:
insert_pos = len(track)
track.insert(insert_pos, Message('program_change', program=program, time=0))
mid = MidiFile(midi_path)
# Inspect and set programs for each track
# ...
mid.save(midi_path)
# 4. Render with FluidSynth (same as traditional)
wav_path = "/path/to/output.wav"
mp3_path = "/path/to/output.mp3"
fs = FluidSynth('/usr/share/sounds/sf2/FluidR3_GM.sf2')
fs.midi_to_audio(midi_path, wav_path)
audio = AudioSegment.from_wav(wav_path)
# Note: Dynamic range compression is automatically applied to prevent clipping
audio.export(mp3_path, format='mp3', bitrate='192k')Choosing Electronic Instruments
CRITICAL: The key to electronic music is choosing the right GM programs. FluidR3_GM includes authentic electronic sounds.
Core Electronic Programs
Bass (use these for electronic bass):
- 38: Synth Bass 1 (fat, warm - best for house/techno)
- 39: Synth Bass 2 (bright, aggressive - good for trance/acid)
- 32-37: Acoustic/electric bass (for more organic styles)
Lead (use these for melodic lines):
- 80: Square Lead (classic chip-tune/retro style)
- 81: Sawtooth Lead (bright, buzzy - most versatile for EDM)
- 82: Calliope Lead (circus-like, bright)
- 83: Chiff Lead (breathy attack)
- 84: Charang Lead (aggressive, distorted)
- 85-87: Other synth leads
Pad (use these for atmospheric backgrounds):
- 88: Pad 1 (New Age) (soft, warm - best for house)
- 89: Pad 2 (Warm) (rich, full background)
- 90: Pad 3 (Polysynth) (bright, present - good for trance)
- 91: Pad 4 (Choir) (vocal-like)
- 92-95: Other atmospheric pads
Additional Electronic Sounds:
- 50-51: Synth Strings (electronic string sounds)
- 62-63: Synth Brass (electronic brass sounds)
- 96-103: FX sounds (rain, soundtrack, crystal, atmosphere, etc.)
Using Non-Synth Programs in Electronic Music
IMPORTANT: You can (and should) use traditional instrument programs when appropriate. The key is matching the program to your musical intent:
Guitars (24-31) work great for:
- Rhythm guitar parts in electronic-rock fusion
- Plucky melodic riffs
- Strummed chord progressions
- Programs 24-31 are fully supported - don't avoid them
Strings/Brass/Woodwinds (40-79) work for:
- Orchestral stabs and hits
- Melodic counterpoint
- Adding organic texture to electronic beats
- Buildups and transitions
Ethnic instruments (104-111) work for:
- World music fusion (sitar, koto, shamisen)
- Unique plucky textures
Instrument Selection Guidelines
1. Match program to sonic role:
- Plucky rhythm → Guitar (25), Synth Lead (80-81)
- Smooth background → Pad (88-90), Strings (48-49)
- Melodic lead → Synth Lead (80-87), Brass (56-60), Sax (64-67)
- Sub-bass → Synth Bass (38-39)
2. Don't limit yourself to programs 38-39, 80-95:
- House music with acoustic guitar (25) = authentic
- Trance with strings (48) = classic sound
- Techno with brass stabs (61) = proper
3. ALL GM instruments are available:
- Guitar programs 24-31 are fully supported
- Strings 40-47 work perfectly
- Brass 56-63, woodwinds 64-79 all render correctly
Handling All General MIDI Instruments
When composing electronic music, the agent will classify instruments by sonic characteristics:
Fast attack, plucky (guitars, harps, ethnic):
- Programs 24-31 (Guitars)
- Programs 46, 104-111 (Harp, Sitar, Banjo, Koto)
- Work well for: rhythm parts, strums, melodic riffs
Sustained melody (strings, brass, woodwinds):
- Programs 40-47 (Strings)
- Programs 56-63 (Brass)
- Programs 64-79 (Woodwinds)
- Work well for: lead melodies, sustained lines, buildups
Bass (all bass types):
- Programs 32-39 (Acoustic, electric, synth bass)
- Work well for: grooves, sub-bass, walking bass
Synth lead (cutting leads):
- Programs 80-87 (Synth leads)
- Programs 0-7 (Pianos for percussive melodies)
- Work well for: main melodic hooks, solos
Atmospheric (soft backgrounds):
- Programs 88-95 (Synth pads)
- Programs 52-54 (Choir/voice)
- Work well for: background textures, atmosphere
Genre-Specific Guidelines
Deep House / House Music
Philosophy: House is harmonically simple but rhythmically complex. Groove emerges from how elements interact, not from held notes.
Key characteristics:
- Driving 4-on-floor kick with syncopated bassline that grooves between kicks (not on every beat)
- Layered percussion: shakers (continuous 16ths for motion), congas/bongos (accents on 2.5, 3.5), claps (double with snare), rim shots
- Deep sub-bass in octave 1-2 range (A1-E2) playing rhythmic patterns that follow chord roots for 4-8 bars
- Harmonic bed: sustained pads (octaves 2-4) or Rhodes/piano chords that change every 4-8 bars, not 16
- Sparse melodic elements: synth leads, piano riffs, vocal samples - play 2-4 bars then rest, enter after intro builds
- Arrangement arc: intro (8-16 bars, gradual layering) → full groove (16-32 bars) → breakdown (8-16 bars, strip to bass+pads, no kick) → drop (full return) → outro
- Groove through syncopation: bass hits on offbeats (1.5, 2.5, 3.5), rests create pocket, varied note lengths within repeated patterns
Techno
Philosophy: Aggressive, hypnotic, industrial. Repetition with subtle evolution.
Key characteristics:
- Hard-hitting kick at 128-135 BPM, often every quarter note with no variation
- Aggressive synth bass (program 39) with distorted/gritty tone, tight rhythmic patterns locked to kick
- Minimal but evolving: start with kick+bass loop, gradually add hi-hats (16th rolls), claps, then pads/leads after 16+ bars
- Harsh timbres: metallic percussion, distorted leads (program 84), bright polysynth pads (program 90)
- Long builds with filter sweeps: bring elements in/out over 8-16 bar cycles, create tension through repetition and subtle changes
Trance
Philosophy: Euphoric, melodic, emotional. Big buildups and drops.
Key characteristics:
- Driving 4-on-floor at 130-140 BPM with rolling hi-hat patterns (continuous 16ths)
- Prominent melodic leads (program 81) playing arpeggios or sustained melodies - louder than other genres
- Layered pads (programs 88-91) creating lush atmospheric bed, often multiple pad tracks
- String buildups (program 48) for emotional peaks and transitions, crescendo over 8-16 bars
- Dramatic arrangement: long intro (16-32 bars) → breakdown with just pads+arp (16 bars) → massive drop with full energy
Ambient / Downtempo
Philosophy: Atmospheric, spacious, meditative. Focus on texture over rhythm.
Key characteristics:
- Minimal or no drums: if present, very sparse kick/percussion at 80-110 BPM with lots of space between hits
- Layered pad textures (programs 88-95) at different octaves and velocities, slowly evolving
- Atmospheric FX (programs 96-103) for movement: rain, crystal, atmosphere sounds at low velocity (40-55)
- Subtle bass (program 38) if used at all - long sustained notes (16.0-32.0 quarterLength), octave 1-2
- Space and silence: embrace long sections with only 1-2 elements, let sounds breathe and decay naturally
Synthesis Features & Intelligent Volume
Note: While the soundfont handles synthesis, velocities should still be managed intelligently:
Velocity Guidelines for Electronic Music
By Role:
- Lead synths: 95-110 (bright, cutting)
- Bass: 75-85 (powerful but not overpowering)
- Pads: 50-65 (background, atmospheric)
- Drums: 75-95 (punchy, present)
By Frequency:
- Sub-bass (<100Hz): Keep at 75-80 velocity (powerful but controlled)
- Mid-range (200-800Hz): Most important range - leads at 95+, pads at 50-65
- High-range (>800Hz): Hi-hats and bright leads at 85-95
Best Practices for Electronic Music
Groove and Rhythm:
- Create syncopated patterns where bass/percussion hit between main beats (offbeats like 1.5, 2.5, 3.5) - this is what creates groove
- Layer multiple percussion elements at different rhythmic densities (shakers on 16ths, congas on offbeats, claps on 2 and 4)
- Use rests and space in basslines and melodies - silence creates pocket and anticipation
Arrangement and Structure:
- Build dynamic arcs: intro (sparse, gradual layering) → full section → breakdown (strip elements) → drop (full return) → outro
- Enter elements gradually over 8-16 bar cycles, not all at once - each element should have its entrance moment
- Create contrast between sections: breakdown strips to just pads+bass (no kick), drop brings everything back with impact
Harmonic Approach:
- Stay on the same chord root for 4-8 bars (harmonic simplicity) but create rhythmic variation within that (groove complexity)
- Keep bass in octave 1-2 range (A1, C2, E2) - octave 0 is inaudible on most systems
- Use extended chords (7ths, 9ths) in pads for color without harmonic complexity
Sound Selection and Velocity:
- Use all 128 GM programs appropriately: synths (38-39, 80-95), but also guitars (24-31), strings (40-47), brass (56-63) for texture
- Set velocities by role: leads 95-110 (cutting), bass 75-85 (powerful), pads 50-65 (atmospheric), drums 75-95 (punchy)
- Add humanization: slight random variations in velocity (±3-5) and timing (±0.05 quarterLength) to avoid robotic feel
Common Mistakes
1. Confusing harmonic with rhythmic simplicity - Staying on Am for 8 bars is good (harmonic), holding A1 for 32 seconds is boring (no rhythm) 2. Static arrangements - Electronic music needs intro/build/drop/breakdown structure, not 64 bars of identical loop 3. Missing the groove - Bass should syncopate with kick, not play on every beat or hold long notes 4. No percussion layering - One hi-hat pattern isn't enough, layer shakers/congas/claps/rims at different densities 5. Default velocity 64 - Will create flat, lifeless mix; explicitly set velocities based on role (leads bright, pads quiet)
#!/bin/bash
echo "====================================="
echo "Music Generation Skill Installation"
echo "====================================="
echo ""
# Update package list
echo "Updating package list..."
apt-get update -qq
# Install FluidSynth and SoundFonts
echo "Installing FluidSynth and SoundFonts..."
apt-get install -y fluidsynth fluid-soundfont-gm fluid-soundfont-gs
# Install FFmpeg for audio conversion
echo "Installing FFmpeg..."
apt-get install -y ffmpeg
# Install Python dependencies
echo "Installing Python dependencies..."
pip install --quiet --upgrade pip
pip install --quiet -r requirements.txt
# Verify installations
echo ""
echo "====================================="
echo "Verification"
echo "====================================="
# Check FluidSynth
if command -v fluidsynth &> /dev/null; then
echo "✓ FluidSynth installed successfully"
else
echo "✗ FluidSynth installation failed"
fi
# Check FFmpeg
if command -v ffmpeg &> /dev/null; then
echo "✓ FFmpeg installed successfully"
else
echo "✗ FFmpeg installation failed"
fi
# Check SoundFont files
if [ -f "/usr/share/sounds/sf2/FluidR3_GM.sf2" ] || [ -f "/usr/share/sounds/sf2/default.sf2" ]; then
echo "✓ SoundFont files found"
ls -lh /usr/share/sounds/sf2/*.sf2 2>/dev/null || ls -lh /usr/share/soundfonts/*.sf2
else
echo "✗ SoundFont files not found"
fi
# Check Python packages
echo ""
python3 -c "import music21; print('✓ music21 version:', music21.__version__)"
python3 -c "import midi2audio; print('✓ midi2audio installed')"
python3 -c "import pydub; print('✓ pydub installed')"
echo ""
echo "====================================="
echo "Installation Complete!"
echo "====================================="
echo ""
echo "You can now run: python music_generator.py"
echo "Or import the module in your own code"music21>=9.1.0
midi2audio>=0.1.1
pydub>=0.25.1
mido>=1.3.0
numpy>=1.24.0
scipy>=1.10.0"""Generic music operation scripts for musicgeneration skill."""#!/usr/bin/env python3
"""
Validate audio quality and detect issues in MP3/WAV files.
This script performs quality control checks on any audio file:
- Audio clipping detection (peaks > 0dB)
- Silence detection (empty sections)
- Duration verification
- Abrupt volume changes
- Dynamic range analysis
Usage:
python audio_validate.py output.mp3 [--expected-duration 90]
"""
import argparse
import sys
from pathlib import Path
import numpy as np
from pydub import AudioSegment
from pydub.utils import db_to_float
class ValidationResult:
def __init__(self):
self.passed = True
self.errors = []
self.warnings = []
def add_error(self, message: str):
self.errors.append(message)
self.passed = False
def add_warning(self, message: str):
self.warnings.append(message)
def print_results(self):
if self.passed and not self.warnings:
print("\n✓ VALIDATION PASSED - No issues detected")
return
if self.errors:
print("\n✗ VALIDATION FAILED")
print("\nErrors (must fix):")
for error in self.errors:
print(f" ✗ {error}")
if self.warnings:
print("\nWarnings (should review):")
for warning in self.warnings:
print(f" ⚠ {warning}")
if not self.errors:
print("\n⚠ VALIDATION PASSED WITH WARNINGS")
def validate_audio(audio_path: Path, expected_duration: float = None) -> ValidationResult:
"""Perform comprehensive audio validation."""
result = ValidationResult()
try:
audio = AudioSegment.from_file(str(audio_path))
except Exception as e:
result.add_error(f"Failed to load audio file: {e}")
return result
actual_duration = len(audio) / 1000.0
if expected_duration:
duration_diff = abs(actual_duration - expected_duration)
if duration_diff > 2.0:
result.add_error(
f"Duration mismatch: expected {expected_duration:.1f}s, got {actual_duration:.1f}s "
f"(diff: {duration_diff:.1f}s)"
)
elif duration_diff > 0.5:
result.add_warning(
f"Duration slightly off: expected {expected_duration:.1f}s, got {actual_duration:.1f}s "
f"(diff: {duration_diff:.1f}s)"
)
max_db = audio.max_dBFS
if max_db > -0.1:
result.add_error(
f"Audio clipping detected: peak level is {max_db:.1f} dBFS "
f"(should be below -0.1 dBFS to avoid distortion)"
)
elif max_db > -1.0:
result.add_warning(
f"Audio very loud: peak level is {max_db:.1f} dBFS "
f"(recommended: -3 to -6 dBFS for headroom)"
)
rms_db = audio.dBFS
if rms_db < -40.0:
result.add_warning(
f"Audio very quiet: RMS level is {rms_db:.1f} dBFS "
f"(recommended: -15 to -20 dBFS)"
)
silent_threshold = -50.0
chunk_length = 1000
silent_chunks = 0
total_chunks = len(audio) // chunk_length
for i in range(0, len(audio), chunk_length):
chunk = audio[i:i + chunk_length]
if chunk.dBFS < silent_threshold:
silent_chunks += 1
silence_percent = (silent_chunks / total_chunks) * 100 if total_chunks > 0 else 0
if silence_percent > 50:
result.add_error(
f"Excessive silence detected: {silence_percent:.1f}% of audio is silent "
f"(threshold: {silent_threshold} dBFS)"
)
elif silence_percent > 20:
result.add_warning(
f"Significant silence: {silence_percent:.1f}% of audio is silent"
)
window_size = 2000
volumes = []
for i in range(0, len(audio) - window_size, window_size // 2):
chunk = audio[i:i + window_size]
volumes.append(chunk.dBFS)
if len(volumes) > 1:
volume_changes = [abs(volumes[i+1] - volumes[i]) for i in range(len(volumes)-1)]
max_change = max(volume_changes) if volume_changes else 0
if max_change > 20:
result.add_warning(
f"Abrupt volume change detected: {max_change:.1f} dB jump "
f"(may sound jarring to listeners)"
)
dynamic_range = max_db - rms_db
if dynamic_range < 3:
result.add_warning(
f"Limited dynamic range: {dynamic_range:.1f} dB "
f"(audio may sound compressed or lifeless)"
)
print(f"\nAudio Analysis:")
print(f" Duration: {actual_duration:.2f}s")
print(f" Peak level: {max_db:.1f} dBFS")
print(f" RMS level: {rms_db:.1f} dBFS")
print(f" Dynamic range: {dynamic_range:.1f} dB")
print(f" Silence: {silence_percent:.1f}%")
print(f" Channels: {audio.channels}")
print(f" Sample rate: {audio.frame_rate} Hz")
print(f" Bit depth: {audio.sample_width * 8} bit")
return result
def main():
parser = argparse.ArgumentParser(
description="Validate audio quality and detect issues",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python audio_validate.py output.mp3
Check audio file for quality issues
python audio_validate.py output.mp3 --expected-duration 90
Verify duration matches expected 90 seconds
python audio_validate.py output.wav --expected-duration 60
Validate WAV file with expected duration
Checks performed:
- Audio clipping (peaks > 0dB)
- Excessive silence
- Duration accuracy
- Abrupt volume changes
- Dynamic range
- Overall loudness levels
"""
)
parser.add_argument("input", help="Input audio file (MP3, WAV, etc.)")
parser.add_argument(
"--expected-duration",
type=float,
help="Expected duration in seconds (for verification)"
)
args = parser.parse_args()
input_path = Path(args.input)
if not input_path.exists():
print(f"Error: Input file not found: {args.input}")
sys.exit(1)
print(f"Validating: {args.input}")
result = validate_audio(input_path, args.expected_duration)
result.print_results()
sys.exit(0 if result.passed else 1)
if __name__ == "__main__":
main()#!/usr/bin/env python3
"""
Render JSON music structure to MP3 audio file.
This script takes a JSON structure (from midi_inventory.py or custom)
and renders it to MP3 using FluidSynth and the existing rendering pipeline.
Usage:
python midi_render.py structure.json output.mp3
"""
import argparse
import json
import os
import sys
from pathlib import Path
from music21 import stream, note, instrument, tempo, key, meter
from midi2audio import FluidSynth
from pydub import AudioSegment
# Standard system paths for soundfont files
# These are installed by the skill's install.sh script via: apt-get install fluid-soundfont-gm
SOUNDFONT_PATHS = [
'/usr/share/sounds/sf2/FluidR3_GM.sf2',
'/usr/share/sounds/sf2/default.sf2',
'/usr/share/soundfonts/default.sf2',
]
def get_soundfont_path():
"""Find the best available SoundFont."""
for path in SOUNDFONT_PATHS:
if os.path.exists(path):
return path
raise FileNotFoundError("No SoundFont file found. Install with: apt-get install fluid-soundfont-gm")
INSTRUMENTS = {
"piano": (instrument.Piano(), 0),
"bright_piano": (instrument.Piano(), 1),
"electric_piano": (instrument.Piano(), 4),
"acoustic_guitar": (instrument.AcousticGuitar(), 24),
"electric_guitar": (instrument.ElectricGuitar(), 27),
"bass": (instrument.Bass(), 32),
"violin": (instrument.Violin(), 40),
"viola": (instrument.Viola(), 41),
"cello": (instrument.Violoncello(), 42),
"contrabass": (instrument.Contrabass(), 43),
"harp": (instrument.Harp(), 46),
"timpani": (instrument.Timpani(), 47),
"trumpet": (instrument.Trumpet(), 56),
"trombone": (instrument.Trombone(), 57),
"tuba": (instrument.Tuba(), 58),
"french_horn": (instrument.Horn(), 60),
"oboe": (instrument.Oboe(), 68),
"bassoon": (instrument.Bassoon(), 70),
"clarinet": (instrument.Clarinet(), 71),
"flute": (instrument.Flute(), 73),
"synth_pad": (instrument.Piano(), 88),
}
def render_json_to_mp3(json_structure: dict, output_mp3: Path) -> str:
"""Render JSON music structure to MP3 file."""
score = stream.Score()
if "tempo" in json_structure:
score.insert(0, tempo.MetronomeMark(number=json_structure["tempo"]))
if "key_signature" in json_structure and json_structure["key_signature"]:
score.insert(0, key.Key(json_structure["key_signature"]))
if "time_signature" in json_structure:
score.insert(0, meter.TimeSignature(json_structure["time_signature"]))
for track_id, track_data in json_structure.get("tracks", {}).items():
part = stream.Part()
inst_name = track_data.get("instrument", "piano")
if inst_name in INSTRUMENTS:
inst_obj, midi_num = INSTRUMENTS[inst_name]
part.insert(0, inst_obj)
else:
part.insert(0, instrument.Piano())
if "tempo" in json_structure:
part.insert(0, tempo.MetronomeMark(number=json_structure["tempo"]))
for note_data in track_data.get("notes", []):
pitch = note_data.get("pitch")
duration = note_data.get("duration", 0.5)
start_time = note_data.get("start", 0.0)
tempo_bpm = json_structure.get("tempo", 120)
quarter_length = duration * (tempo_bpm / 60)
n = note.Note(pitch, quarterLength=quarter_length)
part.insert(start_time, n)
score.append(part)
title = output_mp3.stem
midi_path = output_mp3.parent / f"{title}.mid"
wav_path = output_mp3.parent / f"{title}.wav"
score.write('midi', fp=str(midi_path))
print(f"MIDI created: {midi_path}")
sf_path = get_soundfont_path()
fs = FluidSynth(sf_path)
fs.midi_to_audio(str(midi_path), str(wav_path))
print(f"WAV rendered: {wav_path}")
audio = AudioSegment.from_wav(str(wav_path))
# Apply gentle limiter to prevent clipping (threshold=-10dB, ratio=2.5:1, attack=5ms)
audio = audio.compress_dynamic_range(threshold=-10.0, ratio=2.5, attack=5.0)
if json_structure.get("length_seconds"):
target_ms = int(json_structure["length_seconds"] * 1000)
if len(audio) > target_ms:
audio = audio[:target_ms]
audio = audio.fade_out(2000)
audio.export(str(output_mp3), format='mp3', bitrate='192k')
print(f"MP3 exported: {output_mp3}")
try:
os.remove(midi_path)
os.remove(wav_path)
except:
pass
return str(output_mp3)
def main():
parser = argparse.ArgumentParser(
description="Render JSON music structure to MP3 audio",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python midi_render.py structure.json output.mp3
Render JSON structure to MP3
python midi_render.py modified-song.json new-version.mp3
Render modified structure
JSON structure format:
{
"tempo": 120,
"key_signature": "C",
"time_signature": "4/4",
"tracks": {
"track-0": {
"instrument": "violin",
"notes": [
{"pitch": "E5", "start": 0.0, "duration": 0.5}
]
}
}
}
"""
)
parser.add_argument("input", help="Input JSON structure file")
parser.add_argument("output", help="Output MP3 file")
args = parser.parse_args()
input_path = Path(args.input)
if not input_path.exists():
print(f"Error: Input file not found: {args.input}")
sys.exit(1)
if not input_path.suffix.lower() == '.json':
print("Error: Input must be a JSON file")
sys.exit(1)
try:
print(f"Loading JSON structure from: {args.input}")
with open(input_path, 'r') as f:
structure = json.load(f)
output_path = Path(args.output)
output_path.parent.mkdir(parents=True, exist_ok=True)
if not output_path.suffix.lower() == '.mp3':
output_path = output_path.with_suffix('.mp3')
result = render_json_to_mp3(structure, output_path)
print(f"\nRendering complete!")
print(f"Output: {result}")
except Exception as e:
print(f"Error rendering audio: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()import os
from mido import Message, MetaMessage, MidiFile, MidiTrack
def get_midi_bpm(midi_path: str) -> int:
"""Extract BPM from MIDI file, default to 120 if not found."""
mid = MidiFile(midi_path)
for track in mid.tracks:
for msg in track:
if msg.type == "set_tempo":
return int(60_000_000 / msg.tempo)
return 120
def get_midi_duration_ms(midi_path: str) -> int:
"""Calculate total duration of MIDI file in milliseconds."""
mid = MidiFile(midi_path)
total_ticks = 0
for track in mid.tracks:
track_ticks = sum(msg.time for msg in track)
total_ticks = max(total_ticks, track_ticks)
bpm = get_midi_bpm(midi_path)
seconds_per_tick = 60 / (bpm * mid.ticks_per_beat)
total_seconds = total_ticks * seconds_per_tick
return int(total_seconds * 1000)
def extract_drum_events(midi_path: str) -> list[dict]:
"""
Extract all drum events (channel 9/10) from MIDI file.
Returns list of dicts with: time_ms, note, velocity
"""
mid = MidiFile(midi_path)
bpm = get_midi_bpm(midi_path)
seconds_per_tick = 60 / (bpm * mid.ticks_per_beat)
events = []
for track in mid.tracks:
current_time_ticks = 0
for msg in track:
current_time_ticks += msg.time
if msg.type == "note_on" and msg.channel == 9 and msg.velocity > 0:
time_ms = int(current_time_ticks * seconds_per_tick * 1000)
events.append(
{"time_ms": time_ms, "note": msg.note, "velocity": msg.velocity}
)
return events
def strip_channel(midi_path: str, channel: int, output_path: str = None) -> str:
"""
Remove all events from a specific MIDI channel.
Returns path to new MIDI file without that channel.
"""
if output_path is None:
base, ext = os.path.splitext(midi_path)
output_path = f"{base}_no_ch{channel}{ext}"
mid = MidiFile(midi_path)
new_mid = MidiFile(ticks_per_beat=mid.ticks_per_beat)
for track in mid.tracks:
new_track = MidiTrack()
for msg in track:
if not hasattr(msg, "channel") or msg.channel != channel:
new_track.append(msg.copy())
if len(new_track) > 0:
new_mid.tracks.append(new_track)
new_mid.save(output_path)
return output_path
def extract_drum_channel(midi_path: str, output_path: str = None) -> str:
"""
Extract ONLY drum channel (9) from MIDI file.
Returns path to drums-only MIDI file.
"""
if output_path is None:
base, ext = os.path.splitext(midi_path)
output_path = f"{base}_drums_only{ext}"
mid = MidiFile(midi_path)
new_mid = MidiFile(ticks_per_beat=mid.ticks_per_beat)
tempo_track = MidiTrack()
for track in mid.tracks:
for msg in track:
if msg.type == "set_tempo":
tempo_track.append(msg.copy())
if len(tempo_track) > 0:
new_mid.tracks.append(tempo_track)
for track in mid.tracks:
new_track = MidiTrack()
for msg in track:
if hasattr(msg, "channel") and msg.channel == 9:
new_track.append(msg.copy())
elif msg.type in ["track_name", "end_of_track"]:
new_track.append(msg.copy())
if any(msg.type == "note_on" for msg in new_track):
new_mid.tracks.append(new_track)
new_mid.save(output_path)
return output_path
{
"schema_version": "2.0",
"meta": {
"generated_at": "2026-01-16T20:03:33.204Z",
"slug": "cam10001110101-code-to-music",
"source_url": "https://github.com/Cam10001110101/claude-skills-base/tree/main/mnt/skills/examples/code-to-music",
"source_ref": "main",
"model": "claude",
"analysis_version": "3.0.0",
"source_type": "community",
"content_hash": "9603036ba442d311d500cbc9d3da0b320e3ca67c1eea14bd87aba3395cb3c29b",
"tree_hash": "c85135ce6f1a20f1a817607f042d296ca8fcc11c1983b17c9c7fb7c69134b350"
},
"skill": {
"name": "code-to-music",
"description": "Tools, patterns, and utilities for creating music with code. Output as a .mp3 file with realistic instrument sounds. Write custom compositions to bring creativity to life through music. This skill should be used whenever the user asks for music to be created. Never use this skill for replicating songs, beats, riffs, or other sensitive works. The skill is not suitable for vocal/lyrical music, audio mixing/mastering (reverb, EQ, compression), real-time MIDI playback, or professional studio recording quality.",
"summary": "Tools, patterns, and utilities for creating music with code. Output as a .mp3 file with realistic in...",
"icon": "🎵",
"version": "1.0.0",
"author": "Cam10001110101",
"license": "MIT",
"category": "coding",
"tags": [
"music",
"audio",
"midi",
"composition",
"creative"
],
"supported_tools": [
"claude",
"codex",
"claude-code"
],
"risk_factors": [
"filesystem",
"scripts",
"external_commands"
]
},
"security_audit": {
"risk_level": "low",
"is_blocked": false,
"safe_to_publish": true,
"summary": "This is a legitimate music generation skill using standard audio libraries. All 202 static findings are FALSE POSITIVES caused by music terminology being misinterpreted as security patterns. No network calls, no credential access, no data exfiltration. All file operations are for local MIDI/audio files only.",
"risk_factor_evidence": [
{
"factor": "filesystem",
"evidence": [
{
"file": "scripts/midi_render.py",
"line_start": 132,
"line_end": 133
},
{
"file": "scripts/midi_render.py",
"line_start": 108,
"line_end": 108
}
]
},
{
"factor": "scripts",
"evidence": [
{
"file": "scripts/midi_utils.py",
"line_start": 1,
"line_end": 10
},
{
"file": "scripts/audio_validate.py",
"line_start": 1,
"line_end": 22
}
]
},
{
"factor": "external_commands",
"evidence": [
{
"file": "SKILL.md",
"line_start": 129,
"line_end": 289
},
{
"file": "electronic-music-pipeline.md",
"line_start": 25,
"line_end": 30
}
]
}
],
"critical_findings": [],
"high_findings": [],
"medium_findings": [],
"low_findings": [],
"dangerous_patterns": [],
"files_scanned": 10,
"total_lines": 2150,
"audit_model": "claude",
"audited_at": "2026-01-16T20:03:33.204Z"
},
"content": {
"user_title": "Generate original music with code",
"value_statement": "Transform creative ideas into original music compositions. Create algorithmic melodies, chords, and rhythms programmatically. Export finished pieces as MP3 files with realistic instrument sounds.",
"seo_keywords": [
"Claude music generation",
"code to music",
"MIDI composition",
"algorithmic music",
"FluidSynth",
"music21",
"Claude Code music",
"programmatic audio",
"instrumental music",
"MP3 export"
],
"actual_capabilities": [
"Compose music programmatically using music21 library for algorithmic composition",
"Set MIDI instruments and velocities using mido for proper sound mapping",
"Render MIDI to audio using FluidSynth with general MIDI soundfonts",
"Convert audio to MP3 format with pydub for portable output",
"Validate audio quality including clipping, silence, and dynamic range checks",
"Extract and modify MIDI structures from existing files for analysis"
],
"limitations": [
"Instrumental music only - no vocals, lyrics, or singing",
"MIDI-based synthesis using soundfonts - not studio recording quality",
"No real-time playback - files render to disk before listening"
],
"use_cases": [
{
"target_user": "Content creators",
"title": "Original background music",
"description": "Generate unique instrumental tracks for videos, games, or presentations without copyright concerns."
},
{
"target_user": "Music students",
"title": "Learn music theory",
"description": "Explore chord progressions, scales, and arrangements through code-based composition experiments."
},
{
"target_user": "Developers",
"title": "Audio in applications",
"description": "Add procedurally generated music to apps, games, or creative projects through programmatic composition."
}
],
"prompt_templates": [
{
"title": "Simple melody",
"scenario": "Create a basic melody",
"prompt": "Create a simple piano melody in C major at 120 BPM. Export as my_melody.mp3."
},
{
"title": "With chords",
"scenario": "Add harmonic backing",
"prompt": "Compose a jazz piece with piano, bass, and drums. Use ii-V-I chord progression. Export as jazz_piece.mp3."
},
{
"title": "Full arrangement",
"scenario": "Multi-instrument composition",
"prompt": "Create an orchestral piece with strings, brass, and woodwinds. Include a crescendo in the middle section. Export as orchestral.mp3."
},
{
"title": "Custom structure",
"scenario": "From JSON structure",
"prompt": "Render this music structure to MP3: read structure.json and create output.mp3 with the specified instruments and notes."
}
],
"output_examples": [
{
"input": "Create a relaxing ambient piece with pads and soft textures",
"output": [
"An MP3 file with atmospheric synthesizer pads",
"Layered chord textures in the background",
"Soft attack and long sustain on all notes",
"No drums - peaceful and spacious arrangement"
]
},
{
"input": "Write an upbeat house track with driving bass and crisp drums",
"output": [
"A 4-on-floor kick pattern at 124 BPM",
"Syncopated synth bass following chord roots",
"Layered percussion with shakers, congas, and claps",
"Fade-out ending with gradual element removal"
]
}
],
"best_practices": [
"Use music theory - real chord progressions and proper voice leading create better compositions",
"Set appropriate velocities - lead instruments 90-105, background 50-65, drums 70-90",
"Choose the right pipeline - traditional for orchestral/acoustic, electronic for synth-based music"
],
"anti_patterns": [
"Hardcoding melodies - generate notes algorithmically for variety and uniqueness",
"Using default velocity 64 - instruments will sound flat and lifeless without proper dynamics",
"Forgetting mido program numbers - tracks will default to piano sound instead of selected instrument"
],
"faq": [
{
"question": "What AI tools work with this skill?",
"answer": "Works with Claude, Codex, and Claude Code. Use Claude for creative composition and complex music theory."
},
{
"question": "How long does rendering take?",
"answer": "Most pieces render in 5-30 seconds. Complex arrangements with many instruments may take longer."
},
{
"question": "Can I use my own soundfonts?",
"answer": "Yes. Point to your custom .sf2 file when initializing FluidSynth. FluidR3_GM is the default."
},
{
"question": "Is my data safe?",
"answer": "All processing is local. No audio data is sent anywhere. Files are created only in locations you specify."
},
{
"question": "Why does my MIDI sound like piano?",
"answer": "Music21 instrument classes do not set MIDI program numbers. You must use mido to insert program_change messages."
},
{
"question": "How is this different from text-to-music AI?",
"answer": "This skill generates music through code - you control every note, instrument, and timing directly."
}
]
},
"file_structure": [
{
"name": "scripts",
"type": "dir",
"path": "scripts",
"children": [
{
"name": "__init__.py",
"type": "file",
"path": "scripts/__init__.py",
"lines": 1
},
{
"name": "audio_validate.py",
"type": "file",
"path": "scripts/audio_validate.py",
"lines": 208
},
{
"name": "midi_render.py",
"type": "file",
"path": "scripts/midi_render.py",
"lines": 206
},
{
"name": "midi_utils.py",
"type": "file",
"path": "scripts/midi_utils.py",
"lines": 121
}
]
},
{
"name": "electronic-music-pipeline.md",
"type": "file",
"path": "electronic-music-pipeline.md",
"lines": 260
},
{
"name": "install.sh",
"type": "file",
"path": "install.sh",
"lines": 65
},
{
"name": "requirements.txt",
"type": "file",
"path": "requirements.txt",
"lines": 6
},
{
"name": "SKILL.md",
"type": "file",
"path": "SKILL.md",
"lines": 822
},
{
"name": "traditional-music-pipeline.md",
"type": "file",
"path": "traditional-music-pipeline.md",
"lines": 198
}
]
}
Traditional Music Generation Pipeline
Prerequisites: Before using this pipeline, the generic music21 and MIDI content in SKILL.md should have been read, including:
- Basic music21 composition patterns
- Complete GM Instrument Map (programs 0-127)
- Complete Drum Map (notes 35-81)
- mido workflow for setting instruments and velocities
- Generic music theory (chord progressions, scales, timing)
How the Traditional Pipeline Works
The traditional pipeline uses pre-recorded samples from soundfonts to render acoustic instruments:
# 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)
# Note: You should make sure dynamic range compression is applied to prevent clipping
audio.export(mp3_path, format='mp3', bitrate='192k')Available Soundfonts
/usr/share/sounds/sf2/FluidR3_GM.sf2(141MB, General MIDI soundfont for orchestral/classical)/usr/share/sounds/sf2/default.sf2(symlink to best available)
Rendering Traditional Music
The traditional pipeline uses pre-recorded samples from soundfonts:
Conceptual workflow: 1. Compose with music21 (create parts, add notes/chords) 2. Export to MIDI 3. CRITICAL: ALWAYS use mido to set instruments and velocities - even if music21 instrument classes like instrument.TenorSaxophone() or instrument.AcousticGuitar() were used, mido MUST still be used to set program numbers. Without this step, all tracks will default to piano. 4. Render MIDI to WAV using FluidSynth 5. Convert WAV to MP3 with pydub
Advanced Workflow: Learning from Existing MIDI
For classical pieces or complex compositions, the following approach can be used:
1. Extract Structure from ANY MIDI File
python ./scripts/midi_inventory.py \
path/to/mozart.mid \
path/to/output_structure.json # Output path for extracted structureThis extracts:
- Tempo, key signature, time signature
- Track information and instruments
- Complete note sequences with timing
- Musical structure
2. Modify the JSON Structure
import json
# Load extracted structure
with open('path/to/output_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('path/to/modified_structure.json', 'w') as f:
json.dump(structure, f)3. Render Modified Structure to MP3
python ./scripts/midi_render.py \
path/to/modified_structure.json \
path/to/output.mp3 # Final MP3 output pathThis workflow allows "recreating" any classical piece with different instruments!
Additional Scripts
- `midi_transform.py` - Generic MIDI transformations (transpose, tempo change, instrument swap)
- `audio_validate.py` - Validate audio file quality and format
music21 Instrument Classes
from music21 import instrument
# Strings
instrument.Violin()
instrument.Viola()
instrument.Violoncello() # Note: NOT Cello()
instrument.Contrabass()
instrument.Harp()
# Piano
instrument.Piano()
instrument.Harpsichord()
# Brass
instrument.Trumpet()
instrument.Trombone()
instrument.Tuba()
instrument.Horn() # French horn
# Woodwinds
instrument.Flute()
instrument.Clarinet()
instrument.Oboe()
instrument.Bassoon()
instrument.SopranoSaxophone()
instrument.AltoSaxophone()
instrument.TenorSaxophone() # Most common for jazz
instrument.BaritoneSaxophone()
# Other
instrument.AcousticGuitar()
instrument.ElectricGuitar()
instrument.Bass()
instrument.Timpani()Common mistakes:
- CRITICAL: Using music21 instrument classes (like
instrument.TenorSaxophone()orinstrument.AcousticGuitar()) but forgetting to ALSO set programs with mido afterwards - the track will sound like piano (program 0) instead of your intended instrument - instrument.Cello() doesn't exist - use Violoncello()
- instrument.FrenchHorn() doesn't exist - use Horn()
- Setting part.partName doesn't change the sound - MIDI program must be set with mido
- Drums on channel 0 will play as pitched notes, not drum sounds
Traditional-Specific Mixing and Balance
CRITICAL: Setting MIDI program numbers alone is not enough. Without explicit velocity control, some instruments will be completely inaudible.
Setting Velocities
music21 uses default velocity 64 for all notes, which causes poor mixing. Use mido to set velocities after MIDI export:
Velocity Guidelines
By Role:
- Lead instruments (melody, solos): 90-105
- Rhythm instruments (guitar skanks, comping): 85-100
- Bass: 75-85
- Drums: 70-90
- Background (pads, organs): 50-65
By Frequency Range:
- Low (20-250 Hz): Bass, kick - only ONE dominant at 75-85
- Mid (250-2000 Hz): Most crowded - use velocity to separate (lead 90+, background 50-65)
- High (2000+ Hz): Hi-hats, cymbals - 70-85 for clarity without harshness
Soundfont Level Issues
FluidR3_GM instruments are recorded at different levels. Even with correct velocities, some instruments may be inaudible:
Quiet programs (avoid for lead/rhythm):
- Program 27 (Electric Guitar - clean) - Very quiet
- Program 24 (Acoustic Guitar - nylon)
- Program 73 (Flute)
Better alternatives:
- Program 25 (Acoustic Guitar - steel) - 8-10dB louder, cuts through
- Program 28 (Electric Guitar - muted) - Percussive
- Program 30 (Distortion Guitar) - Aggressive
Additional fixes:
- Increase note duration (0.4 quarterLength minimum vs 0.25)
- Use octave separation (move competing instruments to different octaves)
- Extreme velocity contrast (quiet instrument at 110, loud at 40)
Mixing Checklist
Before rendering:
- Lead at velocity 90-105
- Background at velocity 50-65
- Bass at velocity 75-85
- Check for quiet instruments (programs 24, 27, 73) and use alternatives
- Minimum 0.4 quarterLength for rhythm instruments
Best Practices for Traditional Music
- ALWAYS use mido to set program numbers for ALL tracks (even if music21 instrument classes were used)
- You can optionally use music21 instrument classes for documentation (Violin, Piano, etc.) but they don't set the MIDI program
- Respect instrument ranges (Violin G3-E7, Cello C2-C6, Trumpet E3-C6)
- Watch for soundfont level issues (program 27 too quiet, use 25 instead)
- Use 0.4+ quarterLength for rhythm instruments (below this is inaudible)
- Set velocities explicitly with mido: lead 90-105, rhythm 85-100, background 50-65
- CRITICAL: For acoustic guitar (programs 24-31), use individual `note.Note()` calls, NOT `chord.Chord()`. The FluidR3_GM soundfont renders guitar chords with a keyboard/piano-like timbre. Use arpeggiated notes instead for authentic guitar sound.
- For fingerpicked acoustic guitar, use the proper register: bass notes D3-A3, treble notes G3-D5. Notes below D3 sound muddy and keyboard-like in the FluidR3_GM soundfont. Standard guitar range is E2-E5, but fingerpicking sounds best in the mid-to-high register.
Related skills
FAQ
What format is the output?
A downloadable MP3 file, not an HTML player.
What can it not do?
It is not suitable for vocal or lyrical music, audio mixing and mastering, real-time MIDI playback, or replicating existing songs.