music-generation
Tools, patterns, and utilities for generating professional music with realistic instrument sounds. Write custom compositions using music21 or learn from existing MIDI files.
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o music-generation.zip https://jpskill.com/download/17735.zip && unzip -o music-generation.zip && rm music-generation.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/17735.zip -OutFile "$d\music-generation.zip"; Expand-Archive "$d\music-generation.zip" -DestinationPath $d -Force; ri "$d\music-generation.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
music-generation.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
music-generationフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 11
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
クイックスタート(まずこちらをお読みください!)
重要:このファイルは /mnt/skills/private/music-generation/SKILL.md にあります
セッション中にこのスキルを再度参照する必要がある場合は、上記の正確なパスを直接読んでください。ディレクトリを探索したり、find コマンドを使用したりしないでください。上記のファイルパスを読んでください。
哲学
このスキルは、既製のソリューションではなく、音楽を作曲するためのツールとパターンを提供します。ユーザーのリクエストに基づいて動的に作曲するには、あなたの知性と music21 ライブラリを使用する必要があります。
中心となる原則: ハードコードされたメロディーで関数を呼び出すのではなく、アルゴリズム的に音楽を作曲するカスタムコードを記述します。
インストールとセットアップ
クイックインストール
完全なセットアップのために、自動インストーラーを実行します。
bash /mnt/skills/private/music-generation/install.sh
これにより、すべてのシステム依存関係、Python パッケージがインストールされ、インストールが検証されます。
注: インストールスクリプトの最後に「error: externally-managed-environment」というメッセージが表示される場合があります。これらは想定されており、無視しても問題ありません。依存関係はすでにインストールされています。これらのメッセージが表示された場合、インストールは成功しています。
手動インストール
または、依存関係を手動でインストールします。
システム依存関係:
apt-get update
apt-get install -y fluidsynth fluid-soundfont-gm fluid-soundfont-gs ffmpeg
Python 依存関係:
pip install -r /mnt/skills/private/music-generation/requirements.txt
requirements.txt には、music21、midi2audio、pydub、mido、numpy、scipy が含まれています。
利用可能な SoundFont
従来のパイプライン(オーケストラ/アコースティック):
/usr/share/sounds/sf2/FluidR3_GM.sf2(141MB、オーケストラ/クラシック用の General MIDI soundfont)/usr/share/sounds/sf2/default.sf2(利用可能な最適なものへのシンボリックリンク)
エレクトロニックパイプライン:
- SoundFont は不要 - すべてのエレクトロニックサウンドにリアルタイムシンセシスを使用
クイックスタート:カスタム作曲を書く
基本的な音楽生成パターン
from music21 import stream, note, chord, instrument, tempo, dynamics
from midi2audio import FluidSynth
from pydub import AudioSegment
# 1. スコアとパートを作成
score = stream.Score()
violin_part = stream.Part()
violin_part.insert(0, instrument.Violin())
violin_part.insert(0, tempo.MetronomeMark(number=120))
# 2. ノートをアルゴリズム的に生成
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. MIDI にエクスポート
score.append(violin_part)
midi_path = '/mnt/user-data/outputs/composition.mid'
score.write('midi', fp=midi_path)
# 4. 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. MP3 に変換
audio = AudioSegment.from_wav(wav_path)
mp3_path = '/mnt/user-data/outputs/composition.mp3'
audio.export(mp3_path, format='mp3', bitrate='192k')
主要な概念
- 常にダウンロード可能な MP3 ファイルを作成する (HTML プレーヤーではない)
- すべての出力は
/mnt/user-data/outputs/に出力される - music21.instrument クラスを使用する:
instrument.Violin()、instrument.Violoncello()、instrument.Piano()、instrument.Trumpet()など - プログラムでノートを生成する - ハードコードされたシーケンスは避ける
適切なレンダリングパイプラインの選択
重要: このスキルは 2 つのレンダリングパイプラインをサポートしています。音楽のジャンルに基づいて選択する必要があります。
従来のパイプライン(オーケストラ、クラシック、アコースティック)
以下を作成する場合に使用します:
- オーケストラ音楽(バイオリン、チェロ、トランペットなど)
- クラシック作曲(モーツァルト、ベートーベンスタイル)
- ピアノ音楽、室内楽、交響曲
- アコースティックギター、ブラスアンサンブル
- 伝統的/アコースティック楽器を使用した音楽
レンダリング方法:
# music21 で作曲し、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')
エレクトロニックパイプライン(ハウス、テクノ、EDM、エレクトロニック)
以下を作成する場合に使用します:
- ハウス、テクノ、トランス、EDM
- シンセベース/パッド/リードを使用したエレクトロニックダンスミュージック
- DJ ビート、クラブミュージック
- 「エレクトロニック」または「シンセヘビー」と表現される音楽
- Keinemusik、Black Coffee などの DJ を参照する音楽
レンダリング方法:
# music21 で作曲し、楽器に mido を使用し、MIDI をエクスポートした後...
import subprocess
# エレクトロニックレンダリングスクリプトを使用する
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}")
これが重要な理由:
- オーケストラの soundfont (FluidR3_GM.sf2) はエレクトロニック音楽にはひどい音です
- その「シンセ」楽器は基本的な 1990 年代の近似です
- エレクトロニックパイプラインは、本格的なエレクトロニックサウンドのためにリアルタイムシンセシスを使用します
- 808 スタイルのキック、エレクトロニックスネア、ハイハットをその場で合成します (外部サンプルは不要)
- ベース/パッド/リードは、フィルターと ADSR エンベロープを使用した減算合成を使用します
- ジャンルのプリセット (deep_house、techno、trance、ambient) は、合成パラメーターを自動的に調整します
ドラムシンセシス:
エレクトロニックレンダラーはリアルタイムドラムシンセシスを使用します(外部サンプルは不要)。すべてのドラムサウンド(キック、スネア、ハイハット、クラップ)は、ジャンル固有のパラメーターを使用してその場で合成されます。
例:ハウストラック
# 1. music21 で作曲 (いつもと同じ)
score = stream.Score()
drums = stream.Part()
bass = stream.Part()
pads = stream.Part()
# ... 音楽を作曲
# 2. MIDI をエクスポート
midi_path = '/mnt/user-data/outputs/deep_house.mid'
score.write('midi', fp=midi_path)
# 3. mido で楽器を修正 (program_change メッセージを挿入)
from mido import MidiFile, Messa
(原文はここで切り詰められています) 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
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 /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:
apt-get update
apt-get install -y fluidsynth fluid-soundfont-gm fluid-soundfont-gs ffmpeg
Python Dependencies:
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
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:
# 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:
# 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
# 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:
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:
{
"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:
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
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
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
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 formatmidi_render.py- Render JSON music structure to MP3 using FluidSynthmidi_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-flymelodic_synthesizer.py- Synthesizes bass, pads, and lead sounds using subtractive synthesissynthesis_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.
# 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)
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:
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)
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
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(insert_pos, Message('program_change', program=38, time=0))
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-indexed
Step 4: Render to audio
from midi2audio import FluidSynth
from pydub import AudioSegment
fs = FluidSynth('/usr/share/sounds/sf2/FluidR3_GM.sf2')
wav_path = '/mnt/user-data/outputs/track.wav'
fs.midi_to_audio(midi_path, wav_path)
audio = AudioSegment.from_wav(wav_path)
mp3_path = '/mnt/user-data/outputs/track.mp3'
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, you MUST:
# 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 - you must set MIDI program with mido
# - Drums on channel 0 will play as pitched notes, not drum sounds
Note 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)
# Insert program_change message
for i, track in enumerate(mid.tracks):
if i == 1: # Your track (tracks start at 1, not 0)
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))
# 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, you MUST use .insert(offset, note) with explicit timing.
⚠️ ALWAYS USE .insert() FOR ALL TRACKS:
Since layering is needed for nearly all good music composition, you should ALWAYS use .insert(offset, note) for ALL tracks - drums, bass, guitar, pads, everything. This prevents timing bugs and ensures proper synchronization.
NEVER mix .insert() and .append() - If you use .insert() 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.
The .append() method should only be used in rare cases where you have a single melodic line with no other instruments.
# WRONG: This plays kick, then 32 hats, then snare pattern (not layered!)
# for beat in range(16):
# drums.append(note.Note(36, quarterLength=1.0)) # Kicks play first
# for eighth in range(32):
# drums.append(note.Note(42, quarterLength=0.5)) # Hats play AFTER all kicks
# # Result: Timing is completely wrong!
# CORRECT: Use .insert() with explicit offsets for simultaneous layering
bars = 32
beats_per_bar = 4
total_beats = bars * beats_per_bar
# Layer 1: Four-on-the-floor kicks (every beat)
for beat in range(total_beats):
offset = float(beat) # Beat 0, 1, 2, 3, 4, 5, ...
drums.insert(offset, note.Note(36, quarterLength=1.0))
# Layer 2: Snare on beats 2 and 4 of each bar
for bar in range(bars):
# Snare on beat 2 (second beat of bar)
offset = float(bar * beats_per_bar + 1)
drums.insert(offset, note.Note(38, quarterLength=1.0))
# Snare on beat 4 (fourth beat of bar)
offset = float(bar * beats_per_bar + 3)
drums.insert(offset, note.Note(38, quarterLength=1.0))
# Layer 3: Hi-hats on eighth notes (every 0.5 beats) - creates groove
for bar in range(bars):
for eighth in range(8): # 8 eighth notes per bar
offset = float(bar * beats_per_bar) + (eighth * 0.5)
if eighth % 2 == 0:
# Closed hat on even eighths (on the beat)
drums.insert(offset, note.Note(42, quarterLength=0.5))
else:
# Open hat on odd eighths (offbeat) - signature house groove
drums.insert(offset, note.Note(46, quarterLength=0.5))
# Result: Properly layered four-on-the-floor with offbeat open hats
# Bar 0: Kicks at 0.0, 1.0, 2.0, 3.0
# Snares at 1.0, 3.0 (on top of kicks)
# Hats at 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5 (layered throughout)
Reggae Specific Guidelines
CRITICAL: Reggae has a unique rhythmic identity that requires precise drum patterns, offbeat accents, and heavy bass. If you don't follow these rules, it won't sound like reggae.
Drum Rules (Non-Negotiable):
- "One Drop" pattern: Kick drum on beat 3 ONLY (not beat 1), creating the signature reggae "drop"
- Snare/Rimshot: Beats 2 and 4 (or just beat 3 with the kick)
- Hi-hats: OFFBEAT eighth notes only (never on the beat), creating the "skank" rhythm
- Cross-stick (note 37): Optional on beats 2 and 4 for classic sound
- NO four-on-floor kicks - this is house/electronic, not reggae
# CORRECT Reggae "One Drop" Drum Pattern
bars = 32
beats_per_bar = 4
for bar in range(bars):
for beat in range(beats_per_bar):
offset = float(bar * beats_per_bar + beat)
# Kick ONLY on beat 3 (the "drop")
if beat == 2: # Beat 3 in 0-indexed (0, 1, 2, 3)
drums_part.insert(offset, note.Note(36, quarterLength=1.0))
# Snare on beats 2 and 4
if beat == 1 or beat == 3:
drums_part.insert(offset, note.Note(38, quarterLength=1.0))
# OFFBEAT hi-hats (the "skank") - CRITICAL for reggae feel
for eighth in range(8):
offset = float(bar * beats_per_bar) + (eighth * 0.5)
# ONLY odd eighths (offbeat) - never on the beat
if eighth % 2 == 1: # 0.5, 1.5, 2.5, 3.5 (offbeat)
drums_part.insert(offset, note.Note(42, quarterLength=0.5))
# WRONG - Four-on-floor (this is house, not reggae!)
# for beat in range(total_beats):
# drums_part.insert(float(beat), note.Note(36, quarterLength=1.0)) # ❌ Kick on every beat
Bass Rules (Non-Negotiable):
- Heavy and prominent - Bass is the lead instrument in reggae
- Octave 1-2 (A1, C2, E2, F1, G1) - not too low, not too high
- Syncopated rhythm - plays between beats, not just on downbeats
- Walking patterns - moves between root, third, fifth of chords
- Quarter to half notes (1.0-2.0 quarterLength) - NOT whole notes like House
# CORRECT Reggae Bass (Am-D-F-G progression, 8-bar pattern)
# This pattern has movement and syncopation - it "walks"
bass_pattern = [
# Bar 1-2: Am (root A)
('A1', 1.0), ('A1', 0.5), ('C2', 0.5), ('A1', 2.0), # Bar 1
('A1', 1.0), ('E2', 1.0), ('A1', 2.0), # Bar 2
# Bar 3-4: D (root D)
('D2', 1.0), ('D2', 0.5), ('F2', 0.5), ('D2', 2.0), # Bar 3
('D2', 1.0), ('A1', 1.0), ('D2', 2.0), # Bar 4
# Bar 5-6: F (root F)
('F1', 1.0), ('F1', 0.5), ('A1', 0.5), ('F1', 2.0), # Bar 5
('F1', 1.0), ('C2', 1.0), ('F1', 2.0), # Bar 6
# Bar 7-8: G (root G, with E for resolution)
('G1', 1.0), ('G1', 0.5), ('B1', 0.5), ('E2', 2.0), # Bar 7
('G1', 1.0), ('D2', 1.0), ('E2', 2.0), # Bar 8
]
# Use .insert() to place bass notes at explicit offsets (synchronizes with drums)
offset = 0.0
for repetition in range(bars // 8):
for pitch, duration in bass_pattern:
bass_part.insert(offset, note.Note(pitch, quarterLength=duration))
offset += duration
# WRONG - Using .append() will cause 8-minute tracks when mixed with .insert() drums
# for pitch, duration in bass_pattern * (bars // 8):
# bass_part.append(note.Note(pitch, quarterLength=duration)) # ❌ Causes timing bug!
Guitar "Skank" Rules (Non-Negotiable):
- OFFBEAT chords only - plays on upbeats (the "and" of beats), never downbeats
- CRITICAL: Sufficient duration - Minimum 0.35-0.4 quarterLength (NOT 0.25) to be audible in mix
- Mid-register voicings - Octaves 3-4 (A3, C4, E4)
- Muted/percussive - In real reggae, these are muted strums creating rhythm
⚠️ CRITICAL NOTE DURATION WARNING:
At 0.25 quarterLength, guitar will be completely inaudible in the mix:
- At 82 BPM:
(60 / 82) × 0.25 = 0.183 seconds(183 milliseconds) - Human perceptual threshold: ~200-300ms needed to register in dense mix
- Organ plays at 0.5 (366ms) - TWICE as long
Use 0.4 quarterLength minimum:
- At 82 BPM:
(60 / 82) × 0.4 = 0.293 seconds(293 milliseconds) - Crosses perceptual threshold while maintaining staccato feel
- Adjust rest to 0.1 to maintain 1.0 beat total per skank cycle
# CORRECT Reggae Guitar "Skank" (offbeat chords with AUDIBLE duration)
guitar_chords = [
['A3', 'C4', 'E4'], # Am
['A3', 'C4', 'E4'], # Am (repeat for 2 bars)
['D3', 'F#3', 'A3'], # D
['D3', 'F#3', 'A3'], # D (repeat for 2 bars)
['F3', 'A3', 'C4'], # F
['F3', 'A3', 'C4'], # F (repeat for 2 bars)
['G3', 'B3', 'D4'], # G
['G3', 'B3', 'D4'], # G (repeat for 2 bars)
]
# Use .insert() to place guitar at explicit offsets (synchronizes with drums/bass)
offset = 0.0
for repetition in range(bars // 8):
for chord_notes in guitar_chords:
# Each bar: 4 offbeat skanks
for beat in range(4):
# REST on the beat (downbeat)
guitar_part.insert(offset, note.Rest(quarterLength=0.5))
offset += 0.5
# CHORD on the offbeat (upbeat) - 0.4 duration for audibility
guitar_part.insert(offset, chord.Chord(chord_notes, quarterLength=0.4))
offset += 0.4
# SHORT REST after chord (creates staccato effect)
guitar_part.insert(offset, note.Rest(quarterLength=0.1))
offset += 0.1
# WRONG - Using .append() causes 8-minute tracks when mixed with .insert() drums
# guitar_part.append(chord.Chord(chord_notes, quarterLength=0.4)) # ❌ Causes timing bug!
# WRONG - Duration too short (will be inaudible!)
# guitar_part.insert(offset, chord.Chord(chord_notes, quarterLength=0.25)) # ❌ Only 183ms @ 82 BPM
Organ "Bubble" Rules:
- Alternating on-and-off pattern - Creates rhythmic "bubbling" effect
- Higher register (octaves 4-5) - Sits above guitar
- Plays same chords as guitar but different rhythm
- Shorter duration (0.5 quarterLength) with rests between
# CORRECT Reggae Organ "Bubble"
organ_chords = [
['A4', 'C5', 'E5'], # Am (high register)
['A4', 'C5', 'E5'],
['D4', 'F#4', 'A4'], # D
['D4', 'F#4', 'A4'],
['F4', 'A4', 'C5'], # F
['F4', 'A4', 'C5'],
['G4', 'B4', 'D5'], # G
['G4', 'B4', 'D5'],
]
# Use .insert() to place organ at explicit offsets (synchronizes with drums/bass/guitar)
offset = 0.0
for repetition in range(bars // 8):
for organ_chord in organ_chords:
# Each bar: bubble pattern (chord, rest, chord, rest)
for _ in range(2): # Twice per bar
organ_part.insert(offset, chord.Chord(organ_chord, quarterLength=0.5))
offset += 0.5
organ_part.insert(offset, note.Rest(quarterLength=0.5))
offset += 0.5
organ_part.insert(offset, chord.Chord(organ_chord, quarterLength=0.5))
offset += 0.5
organ_part.insert(offset, note.Rest(quarterLength=0.5))
offset += 0.5
# WRONG - Using .append() causes 8-minute tracks when mixed with .insert() drums
# organ_part.append(chord.Chord(organ_chord, quarterLength=0.5)) # ❌ Causes timing bug!
Reggae Instruments (MIDI Programs):
- Drums: Channel 9 (MIDI channel 10) - ALWAYS required
- Bass: Program 33 (Electric Bass - finger) or 34 (Electric Bass - pick)
- Guitar:
- PRIMARY: Program 25 (Acoustic Guitar - steel) - Bright, percussive, cuts through mix
- Alternative: Program 28 (Electric Guitar - muted) - Percussive skank sound
- ⚠️ AVOID: Program 27 (Electric Guitar - clean) - Recorded 12-15dB quieter in FluidR3_GM, will be inaudible even at velocity 95
- Organ: Program 16 (Drawbar Organ) or 17 (Percussive Organ)
Setting Instruments with mido (CRITICAL):
Since reggae Parts don't use music21 instrument classes, you MUST use mido to INSERT program_change messages:
from mido import MidiFile, Message
# After score.write('midi', fp=midi_path)
mid = MidiFile(midi_path)
for i, track in enumerate(mid.tracks):
if i == 1: # Drums track
for msg in track:
if hasattr(msg, 'channel'):
msg.channel = 9 # Drums on channel 9
elif i == 2: # Bass track
# INSERT program_change message (don't try to modify - it doesn't exist!)
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=33, time=0))
elif i == 3: # Guitar 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=25, time=0)) # Acoustic steel - bright and audible
elif i == 4: # Organ 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=16, time=0))
mid.save(midi_path)
Mixing and Balance (CRITICAL - Guitar Will Be Inaudible Without This!):
Setting the correct instrument programs and velocities is NOT enough. In reggae, the guitar will still be completely inaudible if you don't address THREE issues:
- Soundfont level: Program 27 (Electric Guitar - clean) recorded 12-15dB quieter than program 16 (Drawbar Organ) in FluidR3_GM
- Note duration: 0.25 quarterLength = 183ms @ 82 BPM (below perceptual threshold)
- Velocity difference: Need 40+ point separation
Complete Solution:
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
# After setting instruments, BEFORE saving
for i, track in enumerate(mid.tracks):
if i == 1: # Drums
set_track_velocity(track, 70)
elif i == 2: # Bass - prominent in reggae
set_track_velocity(track, 80)
elif i == 3: # Guitar - RHYTHM INSTRUMENT, needs to cut through
# Use program 25 (steel acoustic) instead of 27 (too quiet)
# Use 0.4 quarterLength instead of 0.25 (too short)
set_track_velocity(track, 95) # LOUD - this is critical!
elif i == 4: # Organ - BACKGROUND atmosphere
set_track_velocity(track, 55) # QUIET - don't overpower guitar
mid.save(midi_path)
Why this THREE-PART solution works:
- Program 25 (Acoustic Guitar - steel): Recorded 8-10dB louder than program 27, bright harmonics, percussive attack
- Duration 0.4 (293ms): Crosses perceptual threshold vs 0.25 (183ms) which is too short
- Velocity 95 vs 55: 40-point difference creates clear separation
- Combined effect: Guitar now has 3× more presence (program × duration × velocity)
Tempo: 70-90 BPM (classic roots reggae: 80-85 BPM, modern: 85-90 BPM)
Common Mistakes:
- Creating
drums_partbut callingdrums.insert()- use correct variable name - Trying to MODIFY
program_changemessages that don't exist - must INSERT them - Not setting drums to channel 9 - drums will sound like melody notes
- CRITICAL: Using program 27 (too quiet) instead of program 25
- CRITICAL: Using 0.25 quarterLength (too short) instead of 0.4
- CRITICAL: Not setting velocities - guitar will be completely inaudible, organ will dominate
House Specific Guidelines
CRITICAL: House requires extreme repetition and minimal variation to create the hypnotic, groovy feel. Standard composition rules don't apply.
Bass Rules (Non-Negotiable):
- ONE NOTE held for 8-16 bars minimum (32.0-64.0 quarterLength)
- Octave 1-2 RANGE (A1, C2, E2, F1, G1) - the actual bass guitar range (55-110 Hz)
- NEVER use octave 0 (A0, F0, G0, etc.) - these are 20-30 Hz sub-sonic frequencies inaudible on 99% of playback systems (laptop speakers, headphones, even many studio monitors can't reproduce them)
- Whole notes or longer (4.0+ quarterLength minimum, prefer 32.0+)
- Simple patterns: Root for 8 bars → Fifth for 8 bars → repeat
- NO octave jumps, NO busy basslines, NO quarter notes
# CORRECT House Bass (audible on all playback systems)
bass_pattern = [
('A1', 32.0), # A for 8 bars - 55 Hz (bass guitar's lowest note)
('E2', 32.0), # E for 8 bars - 82 Hz (bass guitar's open E string)
('F1', 32.0), # F for 8 bars - 43.7 Hz
('C2', 32.0), # C for 8 bars - 65.4 Hz
]
for pitch, duration in bass_pattern:
bass_part.append(note.Note(pitch, quarterLength=duration))
# WRONG - Octave 0 is inaudible on most systems!
bass_notes = ['A0', 'F0', 'C1', 'G0'] # ❌ 20-35 Hz - below hearing/speaker range!
for bar in range(8):
bass_part.append(note.Note(bass_notes[bar % 4], quarterLength=32.0)) # ❌ Inaudible!
# ALSO WRONG - Too busy, octave jumps
bass_notes = ['A1', 'A2', 'C2', 'F1'] # ❌ Octave jumps
for bar in range(32):
bass_part.append(note.Note(bass_notes[bar % 4], quarterLength=1.0)) # ❌ Too short!
Pad Rules:
- ONE CHORD held for 8-16 bars (32.0-64.0 quarterLength)
- Mid-range octaves (2-4): A2, C3, E3 voicings
- Long attack/release for smooth transitions
- Change chords rarely (every 8-16 bars, not every 4 bars)
# CORRECT House Pads
pad_progression = [
(['A2', 'C3', 'E3'], 64.0), # Am for 16 bars
(['F2', 'A2', 'C3'], 64.0), # F for 16 bars
]
for chord_notes, duration in pad_progression:
pad_part.append(chord.Chord(chord_notes, quarterLength=duration))
Lead Rules:
- Sparse - only play every 4-8 bars, lots of silence
- Long notes (4.0-16.0 quarterLength)
- Enter late (bar 16+, not immediately)
- Mid octaves (A4, C5, E5 max)
# CORRECT House Lead (enters bar 16)
lead_pattern = [
('A4', 8.0), # 2 bars
('C5', 8.0), # 2 bars
('E5', 16.0), # 4 bars
]
Core Principle: If it feels repetitive, you're doing it right. House = hypnotic loop repeated for minutes with minimal changes.
Mixing and Balance:
In House, the bass is the star. But if you don't control velocities, the pads and leads will overpower everything:
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
# After setting instruments with mido
for i, track in enumerate(mid.tracks):
if i == 1: # Drums
set_track_velocity(track, 90) # Driving rhythm
elif i == 2: # Bass (program 38, octaves 1-2)
set_track_velocity(track, 75) # Prominent but not overpowering
elif i == 3: # Pad (program 88, octaves 2-4)
set_track_velocity(track, 50) # Atmospheric background
elif i == 4: # Lead (program 80, octaves 4-5)
set_track_velocity(track, 95) # Melodic focus (when present)
mid.save(midi_path)
Bassline Patterns
# Groovy syncopated house bass
bass_pattern = [
('A1', 1.0), # Downbeat
('A1', 0.5), # Short hit
('rest', 0.25), # Space
('A1', 0.25), # Syncopation
('A2', 0.5), # Octave jump
('C2', 0.5), # Chord tone
('A1', 1.0) # Resolution
]
for pitch, duration in bass_pattern:
if pitch == 'rest':
bass_part.append(note.Rest(quarterLength=duration))
else:
bass_part.append(note.Note(pitch, quarterLength=duration))
Chord Voicings
# Jazz voicing (7th chords)
jazz_chords = [
['C4', 'E4', 'G4', 'B4'], # Cmaj7
['D4', 'F4', 'A4', 'C5'], # Dm7
['G3', 'B3', 'D4', 'F4'] # G7
]
# House pad voicing (open, atmospheric)
house_pads = [
['A3', 'C4', 'E4'], # Am
['F3', 'A3', 'C4'], # F
['C3', 'E3', 'G3'] # C
]
# Classical voicing (close position)
classical_chords = [
['C4', 'E4', 'G4'], # C major
['B3', 'D4', 'G4'], # G major
['C4', 'F4', 'A4'] # F major
]
Melody Construction
# Pentatonic scale (versatile, no "wrong" notes)
pentatonic_c = ['C', 'D', 'E', 'G', 'A']
# Major scale
major_c = ['C', 'D', 'E', 'F', 'G', 'A', 'B']
# Minor scale
minor_a = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
# Blues scale
blues_c = ['C', 'Eb', 'F', 'F#', 'G', 'Bb']
# Generate melody algorithmically
for i in range(16):
octave = 4 + (i // 8) # Move up octave halfway through
scale_degree = i % len(pentatonic_c)
pitch = pentatonic_c[scale_degree] + str(octave)
melody.append(note.Note(pitch, quarterLength=0.5))
Dynamic Control (Crescendos, Volume Changes)
from music21 import dynamics
# Set initial volume
part.insert(0, dynamics.Dynamic('p')) # Piano (soft)
# Add crescendo at bar 8
part.insert(32, dynamics.Crescendo()) # 32 quarter notes = 8 bars
# Peak at bar 10
part.insert(40, dynamics.Dynamic('ff')) # Fortissimo (very loud)
# Decrescendo
part.insert(60, dynamics.Diminuendo())
# Return to soft
part.insert(72, dynamics.Dynamic('p'))
# Dynamic markings: ppp, pp, p, mp, mf, f, ff, fff
Timing & Tempo
# Set tempo (BPM)
part.insert(0, tempo.MetronomeMark(number=120)) # 120 BPM
# Tempo changes
part.insert(32, tempo.MetronomeMark(number=140)) # Speed up at bar 8
# Timing variations (humanization)
import random
note.Note('C5', quarterLength=1.0 + random.uniform(-0.05, 0.05))
# Common time signatures (set on first part)
from music21 import meter
part.insert(0, meter.TimeSignature('4/4')) # Most common
part.insert(0, meter.TimeSignature('3/4')) # Waltz
part.insert(0, meter.TimeSignature('6/8')) # Compound meter
Complete Example: Deep House Track
from music21 import stream, note, chord, tempo
from mido import MidiFile
import subprocess
# 1. Compose with music21
score = stream.Score()
drums = stream.Part()
bass_part = stream.Part()
pad_part = stream.Part()
lead_part = stream.Part()
# Set tempo
drums.insert(0, tempo.MetronomeMark(number=122))
# Add drums using .insert() for proper layering (not .append()!)
bars = 32
beats_per_bar = 4
# Layer 1: Four-on-the-floor kicks
for bar in range(bars):
for beat in range(beats_per_bar):
offset = float(bar * beats_per_bar + beat)
drums.insert(offset, note.Note(36, quarterLength=1.0))
# Layer 2: Snare on beats 2 and 4 of each bar
for bar in range(bars):
drums.insert(float(bar * beats_per_bar + 1), note.Note(38, quarterLength=1.0)) # Beat 2
drums.insert(float(bar * beats_per_bar + 3), note.Note(38, quarterLength=1.0)) # Beat 4
# Layer 3: Hi-hats on eighth notes (offbeat open hats for groove)
for bar in range(bars):
for eighth in range(8):
offset = float(bar * beats_per_bar) + (eighth * 0.5)
if eighth % 2 == 0:
drums.insert(offset, note.Note(42, quarterLength=0.5)) # Closed hat
else:
drums.insert(offset, note.Note(46, quarterLength=0.5)) # Open hat (offbeat)
# House BASS: Long sustained notes using .insert() (NOT .append())
bass_offset = 0.0
bass_pattern = [
('A1', 32.0), # A root for 8 bars - 55 Hz (audible on all systems)
('E2', 32.0), # E fifth for 8 bars - 82 Hz
('F1', 32.0), # F for 8 bars - 43.7 Hz
('C2', 32.0), # C for 8 bars - 65.4 Hz
]
for pitch, duration in bass_pattern:
bass_part.insert(bass_offset, note.Note(pitch, quarterLength=duration))
bass_offset += duration
# House PADS: Long sustained chords using .insert() (NOT .append())
pad_offset = 0.0
pad_progression = [
(['A2', 'C3', 'E3'], 64.0), # Am for 16 bars
(['F2', 'A2', 'C3'], 64.0), # F for 16 bars
]
for chord_notes, duration in pad_progression:
pad_part.insert(pad_offset, chord.Chord(chord_notes, quarterLength=duration))
pad_offset += duration
# House LEAD: Sparse, long notes, enters late using .insert() (NOT .append())
lead_pattern = [
('A4', 8.0), # 2 bars
('C5', 8.0), # 2 bars
('E5', 16.0), # 4 bars (long sustain)
]
# Lead enters at bar 16 (64 beats in)
lead_offset = 64.0
for pitch, duration in lead_pattern:
lead_part.insert(lead_offset, note.Note(pitch, quarterLength=duration))
lead_offset += duration
score.append(drums)
score.append(bass_part)
score.append(pad_part)
score.append(lead_part)
midi_path = '/mnt/user-data/outputs/deep_house_track.mid'
score.write('midi', fp=midi_path)
# 2. Fix instruments with mido (INSERT program_change messages)
from mido import Message
mid = MidiFile(midi_path)
for i, track in enumerate(mid.tracks):
if i == 1: # Drums track
for msg in track:
if hasattr(msg, 'channel'):
msg.channel = 9 # Drums must be on channel 9
elif i == 2: # Bass track - 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))
elif i == 3: # Pad track - 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=88, time=0))
elif i == 4: # Lead track - 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=81, time=0))
mid.save(midi_path)
# 3. Render with ELECTRONIC pipeline with deep_house preset!
mp3_path = '/mnt/user-data/outputs/deep_house_track.mp3'
result = subprocess.run([
'python',
'/mnt/skills/private/music-generation/scripts/render_electronic.py',
midi_path,
mp3_path,
'--genre', 'deep_house'
], capture_output=True, text=True)
print(result.stdout)
if result.returncode == 0:
print(f"✓ House track created: {mp3_path}")
else:
print(f"Error: {result.stderr}")
House Characteristics in This Example:
- Bass: ONE note (A1, E2, F1, C2) held for 8 bars each - deep bass in octave 1-2 (55-82 Hz range, audible on all playback systems)
- Pads: ONE chord held for 16 bars - extreme sustain creates hypnotic atmosphere
- Lead: Sparse (enters bar 16), long notes (2-4 bars each) - not busy
- Repetition: Minimal variation = hypnotic, groovy House feel
- Uses
render_electronic.pywithdeep_housepreset for warm, subby synthesis - Bass synthesized with proper low-end frequencies that consumer audio equipment can reproduce
- Pads use slow attack (0.9s) and long release (1.2s) for smooth transitions
- Genre preset automatically tunes all synthesis parameters
- No external samples or soundfonts needed - fully self-contained
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
- Timing precision: Use quarterLength values carefully
- Cleanup: Remove temporary MIDI/WAV files after MP3 conversion
Common Pitfalls
- CRITICAL: Always use .insert() for ALL tracks - Never mix
.insert()and.append(). 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
- SoundFont quality - Good but not as realistic as sample libraries
When to Use This Skill
✅ User requests:
- Original compositions with specific moods/styles
- Classical music in MP3 format
- Timed music for videos/presentations
- Specific instrumentation (orchestral, piano, strings, etc.)
- Dynamic music with crescendos, tempo changes
❌ Not suitable for:
- Vocal/lyrical music
- Audio mixing/mastering (reverb, EQ, compression)
- Real-time MIDI playback
- Professional studio recording quality
同梱ファイル
※ ZIPに含まれるファイル一覧。`SKILL.md` 本体に加え、参考資料・サンプル・スクリプトが入っている場合があります。
- 📄 SKILL.md (56,429 bytes)
- 📎 scripts/__init__.py (64 bytes)
- 📎 scripts/audio_validate.py (6,233 bytes)
- 📎 scripts/drum_synthesizer.py (7,358 bytes)
- 📎 scripts/melodic_synthesizer.py (12,616 bytes)
- 📎 scripts/midi_inventory.py (6,419 bytes)
- 📎 scripts/midi_render.py (6,080 bytes)
- 📎 scripts/midi_transform.py (8,484 bytes)
- 📎 scripts/midi_utils.py (3,519 bytes)
- 📎 scripts/render_electronic.py (16,647 bytes)
- 📎 scripts/synthesis_presets.py (13,294 bytes)