code-to-music
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseThis 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.
该技能提供用于音乐创作的工具与模式,而非现成解决方案。你需要结合自身能力和music21库,根据用户请求动态完成作曲。
核心原则:编写自定义代码通过算法生成音乐,而非调用包含硬编码旋律的函数。
Installation & Setup
安装与设置
Quick Installation
快速安装
Run the automated installer for complete setup: . This installs all system dependencies, Python packages, and verifies the installation.
install.shNote: 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.
运行自动化安装脚本完成全套配置:。该脚本会安装所有系统依赖、Python包并验证安装结果。
install.sh注意:安装脚本末尾可能会显示「error: externally-managed-environment」的提示,这属于正常预期现象,可以直接忽略——依赖已经安装完成。如果出现这类提示,说明安装已经成功。
Available SoundFonts
可用SoundFont资源
Traditional Pipeline (Orchestral/Acoustic):
- (141MB, General MIDI soundfont - use for high-quality orchestral/acoustic samples)
/usr/share/sounds/sf2/FluidR3_GM.sf2 - (symlink to best available)
/usr/share/sounds/sf2/default.sf2 - ⚠️ 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)
传统管线(管弦乐/原声音乐):
- (141MB,General MIDI soundfont——用于高品质管弦乐/原声采样)
/usr/share/sounds/sf2/FluidR3_GM.sf2 - (指向最优可用资源的软链接)
/usr/share/sounds/sf2/default.sf2 - ⚠️ 请勿使用TimGM6mb.sf2——音质较差,吉他音色听起来像电子音/钢琴音
电子音乐管线:
- 和传统音乐使用相同的FluidR3_GM.sf2 soundfont
- 区别在于你选择的GM程序不同(选择合成器程序38-39、80-95,而非管弦乐程序)
Key Concepts
核心概念
- Always create downloadable MP3 files (not HTML players)
- music21.instrument classes can be used for convenience: ,
instrument.Violin(),instrument.Violoncello(),instrument.Piano(), etc.instrument.Trumpet() - 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
- 始终生成可下载的MP3文件(而非HTML播放器)
- 可便捷使用music21.instrument类:、
instrument.Violin()、instrument.Violoncello()、instrument.Piano()等instrument.Trumpet() - 关键提醒:导出MIDI后必须始终使用mido设置MIDI程序编号——music21的乐器类无法可靠导出program_change消息,参考下方mido工作流
- 通过编程方式生成音符——避免硬编码的音序
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)
从用户请求中识别以下信息:
- 风格关键词(house、techno、古典、管弦乐、雷鬼)
- 乐器提及(合成器 vs 原声乐器)
- 音乐风格描述(电子/DJ vs 传统/原声)
- 艺术家提及(DJ vs 古典作曲家)
Electronic Pipeline
电子音乐管线
Use when request includes:
electronic-music-pipeline.md- 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 for detailed instructions
electronic-music-pipeline.md当请求包含以下内容时使用:
electronic-music-pipeline.md- 风格:House、techno、trance、EDM、电子舞曲、氛围电子、acid house、deep house、俱乐部音乐、DJ set
- 乐器:合成器、合成贝斯/铺底音色/主音音色、808鼓、电子鼓、supersaw主音、超低音
- 场景:提及DJ(Keinemusik、Black Coffee、Avicii、Swedish House Mafia)、BPM 120-140、「适合俱乐部」、「适合跳舞」
- 音色描述:「厚重合成器音色」、「锐利主音」、「电子感」、「重合成器」
工作原理:
- 使用实时合成(无需soundfont)
- 通过DSP实时合成鼓组、贝斯、铺底音色、主音音色
- 风格预设可优化合成参数(deep_house、techno、trance、ambient、acid_house)
- 频率感知混音可自动平衡低/中/高频
- 最适合:原生电子音色、现代EDM制作
→ 查看获取详细说明
electronic-music-pipeline.mdTraditional Pipeline
传统音乐管线
Use when request includes:
traditional-music-pipeline.md- 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 for detailed instructions
traditional-music-pipeline.md当请求包含以下内容时使用:
traditional-music-pipeline.md- 风格:古典、管弦乐、爵士、布鲁斯、摇滚、乡村、民谣、雷鬼、斯卡、交响乐、室内乐
- 乐器:小提琴、大提琴、小号、长笛、钢琴、原声吉他、原声贝斯、铜管合奏、弦乐四重奏、风琴(雷鬼/福音音乐)
- 场景:提及作曲家(莫扎特、贝多芬、巴赫)、古典时期(巴洛克、浪漫主义)、「不插电」、「原声版本」
- 音色描述:「传统」、「原声」、「管弦乐」
工作原理:
- 使用预录制的管弦乐采样(soundfont)
- 包含128种General MIDI乐器的FluidR3_GM.sf2 soundfont
- 适合生成真实的管弦乐/原声乐器音色
- 最适合:古典、爵士、雷鬼、摇滚、传统音乐
→ 查看获取详细说明
traditional-music-pipeline.mdIf 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 :
./scriptsTraditional Rendering:
- - Extract complete structure from ANY MIDI file to JSON format
midi_inventory.py - - Render JSON music structure to MP3 using FluidSynth with dynamic range compression
midi_render.py - - MIDI utility functions (extract drums, get BPM, etc.)
midi_utils.py
Utilities:
- - Validate audio file quality and format
audio_validate.py
Note: Both electronic and traditional music use the same rendering pipeline (FluidSynth + FluidR3_GM.sf2). The difference is which GM programs you choose.
所有脚本都存放在目录下:
./scripts传统渲染脚本:
- ——将任意MIDI文件的完整结构提取为JSON格式
midi_inventory.py - ——使用带动态范围压缩的FluidSynth将JSON音乐结构渲染为MP3
midi_render.py - ——MIDI实用工具函数(提取鼓组、获取BPM等)
midi_utils.py
实用工具:
- ——验证音频文件的质量和格式
audio_validate.py
注意:电子音乐和传统音乐使用相同的渲染管线(FluidSynth + FluidR3_GM.sf2),区别仅在于选择的GM程序不同。
Music Theory Reference
乐理参考
Complete General MIDI Instrument Map (Programs 0-127)
完整General MIDI乐器映射表(程序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
python
undefined关键提醒:music21无法可靠导出program_change消息。导出MIDI后必须始终使用mido设置程序编号,哪怕是萨克斯、吉他、贝斯这类传统乐器也不例外。缺少这一步,所有音轨都会默认使用钢琴音色(程序0)。
合成用乐器分类:
创作电子音乐时,请按照乐器的音色特征分类,以便正确合成:
- 吉他(24-31):拨弦音头、明亮音色、中等延音——需要和铺底音色区分合成
- 贝斯(32-39):低频基音、超低音能量、短音头
- 弦乐(40-47):持续弓音、丰富泛音、平滑连音
- 铜管(56-63):明亮音头、持续音色、强穿透力
- 簧片/木管(64-79):呼吸感音头、有机音色、富有表现力的动态
- 合成主音(80-87):明亮、锐利、有冲击力——用于演奏旋律
- 合成铺底(88-95):柔和音头、长延音、氛围感——用于背景铺垫
- 民族/打击乐(104-119):拨弦或打击音色,音色独特
python
undefinedPiano (0-7)
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"
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)
Chromatic Percussion (8-15)
8: "Celesta"
9: "Glockenspiel"
10: "Music Box"
11: "Vibraphone"
12: "Marimba"
13: "Xylophone"
14: "Tubular Bells"
15: "Dulcimer"
8: "Celesta"
9: "Glockenspiel"
10: "Music Box"
11: "Vibraphone"
12: "Marimba"
13: "Xylophone"
14: "Tubular Bells"
15: "Dulcimer"
Organ (16-23)
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"
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)
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"
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)
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"
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)
Strings (40-47)
40: "Violin"
41: "Viola"
42: "Cello"
43: "Contrabass"
44: "Tremolo Strings"
45: "Pizzicato Strings"
46: "Orchestral Harp"
47: "Timpani"
40: "Violin"
41: "Viola"
42: "Cello"
43: "Contrabass"
44: "Tremolo Strings"
45: "Pizzicato Strings"
46: "Orchestral Harp"
47: "Timpani"
Ensemble (48-55)
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"
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)
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"
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)
Reed (64-71)
64: "Soprano Sax"
65: "Alto Sax"
66: "Tenor Sax"
67: "Baritone Sax"
68: "Oboe"
69: "English Horn"
70: "Bassoon"
71: "Clarinet"
64: "Soprano Sax"
65: "Alto Sax"
66: "Tenor Sax"
67: "Baritone Sax"
68: "Oboe"
69: "English Horn"
70: "Bassoon"
71: "Clarinet"
Pipe (72-79)
Pipe (72-79)
72: "Piccolo"
73: "Flute"
74: "Recorder"
75: "Pan Flute"
76: "Blown Bottle"
77: "Shakuhachi"
78: "Whistle"
79: "Ocarina"
72: "Piccolo"
73: "Flute"
74: "Recorder"
75: "Pan Flute"
76: "Blown Bottle"
77: "Shakuhachi"
78: "Whistle"
79: "Ocarina"
Synth Lead (80-87)
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)"
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)
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)"
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)
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)"
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)
Ethnic (104-111)
104: "Sitar"
105: "Banjo"
106: "Shamisen"
107: "Koto"
108: "Kalimba"
109: "Bag pipe"
110: "Fiddle"
111: "Shanai"
104: "Sitar"
105: "Banjo"
106: "Shamisen"
107: "Koto"
108: "Kalimba"
109: "Bag pipe"
110: "Fiddle"
111: "Shanai"
Percussive (112-119)
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"
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)
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"
undefined120: "Guitar Fret Noise"
121: "Breath Noise"
122: "Seashore"
123: "Bird Tweet"
124: "Telephone Ring"
125: "Helicopter"
126: "Applause"
127: "Gunshot"
undefinedComplete Drum Map (MIDI Channel 10, Notes 35-81)
完整鼓组映射表(MIDI通道10,音符35-81)
Drums use note numbers for different sounds, NOT pitch. Must be on channel 10 (9 in 0-indexed).
python
undefined鼓组使用音符编号对应不同音色,而非音高。必须放在通道10(0索引为9)。
python
undefinedBass Drums
Bass Drums
35: "Acoustic Bass Drum"
36: "Bass Drum 1" # Most common kick
35: "Acoustic Bass Drum"
36: "Bass Drum 1" # Most common kick
Snares
Snares
38: "Acoustic Snare" # Standard snare
40: "Electric Snare"
38: "Acoustic Snare" # Standard snare
40: "Electric Snare"
Toms
Toms
41: "Low Floor Tom"
43: "High Floor Tom"
45: "Low Tom"
47: "Low-Mid Tom"
48: "Hi-Mid Tom"
50: "High Tom"
41: "Low Floor Tom"
43: "High Floor Tom"
45: "Low Tom"
47: "Low-Mid Tom"
48: "Hi-Mid Tom"
50: "High Tom"
Hi-Hats
Hi-Hats
42: "Closed Hi-Hat" # Most used
44: "Pedal Hi-Hat"
46: "Open Hi-Hat"
42: "Closed Hi-Hat" # Most used
44: "Pedal Hi-Hat"
46: "Open Hi-Hat"
Cymbals
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"
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
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"
undefined37: "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"
undefinedHow to Use Any Instrument (mido workflow)
如何使用任意乐器(mido工作流)
CRITICAL RULE: music21 does NOT reliably export messages, even when using instrument classes like or . 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).
program_changeinstrument.TenorSaxophone()instrument.AcousticGuitar()Common mistake: Assuming that will automatically make the track sound like a saxophone. It won't! mido must still be used to set the program number.
sax_part.insert(0, instrument.TenorSaxophone())Helper Function for Setting Instruments:
python
from mido import Message
def set_track_instrument(track, program):
"""Insert a program_change message at the beginning of a MIDI track."""
# 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))核心规则:哪怕使用或这类乐器类,music21也无法可靠导出消息。导出为MIDI后,必须始终使用mido手动插入消息。缺少这一步,所有音轨都会默认是钢琴音色(程序0)。
instrument.TenorSaxophone()instrument.AcousticGuitar()program_changeprogram_change常见错误:认为会自动让音轨变成萨克斯音色,这是错误的!你仍然需要使用mido设置程序编号。
sax_part.insert(0, instrument.TenorSaxophone())设置乐器的辅助函数:
python
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:
Usage after loading MIDI with mido:
set_track_instrument(mid.tracks[2], 33) # Set track 2 to Electric Bass
set_track_instrument(mid.tracks[2], 33) # Set track 2 to Electric Bass
**Step 1: Compose with music21 (use placeholder or skip instrument)**
```python
from music21 import stream, note, chord, tempo
score = stream.Score()
**步骤1:使用music21作曲(使用占位符或跳过乐器设置)**
```python
from music21 import stream, note, chord, tempo
score = stream.Score()Create parts - don't worry about instrument assignment yet
Create parts - don't worry about instrument assignment yet
synth_lead = stream.Part()
synth_pad = stream.Part()
bass = stream.Part()
synth_lead = stream.Part()
synth_pad = stream.Part()
bass = stream.Part()
Add your notes/chords using .insert() with explicit timing
Add your notes/chords using .insert() with explicit timing
CRITICAL: Always use .insert(offset, note) not .append(note)
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))
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()
... compose your music with .insert()
score.append(synth_lead)
score.append(synth_pad)
score.append(bass)
score.append(synth_lead)
score.append(synth_pad)
score.append(bass)
Export to MIDI
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**
```python
from mido import MidiFile, Message
mid = MidiFile(midi_path)midi_path = # define a output path
score.write('midi', fp=midi_path)
**步骤2:查看MIDI结构,使用mido分配乐器**
```python
from mido import MidiFile, Message
mid = MidiFile(midi_path)CRITICAL: DO NOT assume track numbers! Inspect the MIDI file first.
CRITICAL: DO NOT assume track numbers! Inspect the MIDI file first.
Print track structure to see what tracks exist
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")
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
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))
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
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:
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)**
```pythonset_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)
**步骤3:如果是鼓组,还要将通道设置为9(1索引为10)**
```pythonIf track is drums, set ALL messages to channel 9
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**
```python
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')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
**步骤4:渲染为音频**
```python
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
常用和弦进行与风格
python
undefinedpython
undefinedStandard Progressions (Roman numerals)
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
"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
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
}
}
undefinedSTYLES = {
"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
}
}
undefinedmusic21 Instrument Classes
music21乐器类
python
from music21 import instrumentpython
from music21 import instrumentStrings
Strings
instrument.Violin()
instrument.Viola()
instrument.Violoncello() # Note: NOT Cello()
instrument.Contrabass()
instrument.Harp()
instrument.Violin()
instrument.Viola()
instrument.Violoncello() # Note: NOT Cello()
instrument.Contrabass()
instrument.Harp()
Piano
Piano
instrument.Piano()
instrument.Harpsichord()
instrument.Piano()
instrument.Harpsichord()
Brass
Brass
instrument.Trumpet()
instrument.Trombone()
instrument.Tuba()
instrument.Horn() # French horn
instrument.Trumpet()
instrument.Trombone()
instrument.Tuba()
instrument.Horn() # French horn
Woodwinds
Woodwinds
instrument.Flute()
instrument.Clarinet()
instrument.Oboe()
instrument.Bassoon()
instrument.SopranoSaxophone()
instrument.AltoSaxophone()
instrument.TenorSaxophone() # Most common for jazz
instrument.BaritoneSaxophone()
instrument.Flute()
instrument.Clarinet()
instrument.Oboe()
instrument.Bassoon()
instrument.SopranoSaxophone()
instrument.AltoSaxophone()
instrument.TenorSaxophone() # Most common for jazz
instrument.BaritoneSaxophone()
Other
Other
instrument.AcousticGuitar()
instrument.ElectricGuitar()
instrument.Bass()
instrument.Timpani()
instrument.AcousticGuitar()
instrument.ElectricGuitar()
instrument.Bass()
instrument.Timpani()
CRITICAL: music21 has LIMITED support for electronic instruments and drums
CRITICAL: music21 has LIMITED support for electronic instruments and drums
For synths, drums, and electronic sounds, the following steps must be taken:
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())
1. Create a Part without an instrument (or use a placeholder like Piano())
2. Use mido library to INSERT program_change messages after export
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
3. Set drums to MIDI channel 10 (channel 9 in 0-indexed) or they won't sound like drums
Common mistakes:
Common mistakes:
- instrument.Cello() doesn't exist - use Violoncello()
- instrument.Cello() doesn't exist - use Violoncello()
- instrument.FrenchHorn() doesn't exist - use Horn()
- instrument.FrenchHorn() doesn't exist - use Horn()
- Setting part.partName doesn't change the sound - MIDI program must be set with mido
- 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
- Drums on channel 0 will play as pitched notes, not drum sounds
undefinedundefinedNote Durations (Quarter Note = 1.0)
音符时长(四分音符=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
- 全音符:4.0
- 二分音符:2.0
- 四分音符:1.0
- 八分音符:0.5
- 十六分音符:0.25
- 附点四分音符:1.5
- 三连音四分音符:0.667
mido Quick Reference
mido快速参考
For electronic music and drums, use to set MIDI programs after music21 export:
midopython
from mido import MidiFile, Message
mid = MidiFile(midi_path)针对电子音乐和鼓组,导出music21文件后使用设置MIDI程序:
midopython
from mido import MidiFile, Message
mid = MidiFile(midi_path)Define helper function
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))
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
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 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 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 Bassfor 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)
**常用MIDI程序:**
- 38: Synth Bass 1
- 80: Square Lead
- 81: Sawtooth Lead
- 88: Pad 1 (New Age)
- 25: Acoustic Guitar (Steel) - 音量大,穿透力强
- 33: Acoustic BassCommon Techniques
常用技巧
Drum Programming (4-on-floor house beat)
鼓组编程(4/4拍house节拍)
CRITICAL: music21's adds notes sequentially (one after another), not simultaneously. For layered drums where kicks, snares, and hats play at the same time, MUST be used with explicit timing.
.append().insert(offset, note)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 is used for drums and 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.
.insert().append()Note: Using to add Parts to the Score is fine - the issue is specifically about adding notes/chords to Parts, not adding Parts to Scores.
score.append(part)Correct Pattern:
python
undefined关键提醒:music21的会按顺序依次添加音符,而非同时添加。对于底鼓、军鼓、踩镲同时演奏的分层鼓组,必须使用带明确时间参数的。
.append().insert(offset, note)所有音轨必须始终使用.insert()添加音符:
由于几乎所有优质音乐创作都需要分层,所有音轨(鼓组、贝斯、吉他、铺底等)必须始终使用添加音符,这可以避免时序bug,确保同步正确。
part.insert(offset, note)向音轨添加音符时切勿混合使用.insert()和.append()——如果鼓组用而其他乐器用,music21会错误计算音轨长度,生成5-10倍于预期长度的音轨(本该1.5分钟变成8分钟),且只有前20-25%有实际声音。
.insert().append()注意:使用向乐谱添加音轨是没问题的——问题特指向音轨添加音符/和弦的操作,而非向乐谱添加音轨的操作。
score.append(part)正确示例:
python
undefinedRIGHT: Use .insert() for notes, .append() for parts
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 timedrum_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 timeAdd parts to score (this .append() is fine)
Add parts to score (this .append() is fine)
score.append(drum_part)
score.append(bass_part)
undefinedscore.append(drum_part)
score.append(bass_part)
undefinedEnsuring 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):
python
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关键提醒:使用循环作曲时,请确保你的音符完全填满每个小节,没有空隙。常见错误是放置的音符没有覆盖整个小节时长,留下静音缺口。
错误示例(会产生缺口):
python
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 remainsResult: Guitar plays for 10 seconds, then silence for remaining duration
Result: Guitar plays for 10 seconds, then silence for remaining duration
**Solution Pattern** (complete coverage):
```python
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
**正确示例**(完整覆盖):
```python
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_lengthResult: Guitar plays continuously throughout the entire piece
Result: Guitar plays continuously throughout the entire piece
**Best 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 at `offset + 1.0` with `quarterLength=0.5` to reach `offset + 1.5`
- If `bar_length = 2.0`, your last note should start at `offset + 1.5` with `quarterLength=0.5` to reach `offset + 2.0`
**最佳实践**:在执行`offset += bar_length`之前,验证最后一个音符的结束位置恰好是`offset + bar_length`。例如:
- 如果`bar_length = 1.5`,最后一个音符应该从`offset + 1.0`开始,`quarterLength=0.5`,结束在`offset + 1.5`
- 如果`bar_length = 2.0`,最后一个音符应该从`offset + 1.5`开始,`quarterLength=0.5`,结束在`offset + 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
python
import random n = note.Note('C5', quarterLength=1.0 + random.uniform(-0.05, 0.05)) n.volume.velocity = 80 + random.randint(-5, 5)
- 生成多样性内容:不要整首曲子重复相同的4小节内容
- 遵循乐理规则:使用真实的和弦进行、合理的声部连接
- 尊重乐器音域:小提琴(G3-E7)、大提琴(C2-C6)、小号(E3-C6)
- 添加动态变化:使用p、mp、mf、f、ff标记和渐强效果
- 结构完整:前奏→发展→高潮→收尾
- 添加拟人化效果:调整时序和力度,避免机械感
python
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 to prevent clipping
audio.compress_dynamic_range() - Timing precision: Use quarterLength values carefully
- Cleanup: Remove temporary MIDI/WAV files after MP3 conversion
- SoundFont选择:使用FluidR3_GM.sf2获得最佳音质
- 比特率:最低192kbps,高品质使用320kbps
- 限幅:通过应用限幅,避免削波
audio.compress_dynamic_range() - 时序精度:谨慎使用quarterLength值
- 清理文件:MP3转换完成后删除临时MIDI/WAV文件
Common Pitfalls
常见陷阱
- CRITICAL: NEVER assume track numbers - DO NOT blindly use for guitar. ALWAYS inspect the MIDI file first with note counts to verify which track is which
if i == 3 - CRITICAL: Always use part.insert(offset, note) for adding notes to parts - Never use or mix
part.append(note)and.insert(). This causes timing bugs where tracks are 5-10x longer than intended with only 20-25% containing sound. See "Drum Programming" section for details.append() - CRITICAL: INSERT program_change messages - Use not
track.insert(pos, Message('program_change', ...)). See mido Quick Referencemsg.program = X - 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 to first part
tempo.MetronomeMark() - Drums not sounding like drums - Set channel to 9 with mido (see mido Quick Reference)
- 关键提醒:切勿假设音轨编号——不要盲目使用指定吉他音轨。始终先通过音符数量查看MIDI文件,确认每个音轨对应的内容
if i == 3 - 关键提醒:向音轨添加音符始终使用part.insert(offset, note)——不要使用,也不要混合使用
part.append(note)和.insert()。这会导致时序bug,生成5-10倍于预期长度的音轨,且只有20-25%有声音。参考「鼓组编程」章节了解详情.append() - 关键提醒:插入program_change消息——使用,而非
track.insert(pos, Message('program_change', ...))。参考mido快速参考msg.program = X - 关键提醒:设置力度:主音90-105,背景音50-65。参考「混音与平衡」章节
- 关键提醒:贝斯八度:使用A1-A2(55-110 Hz),不要使用A0-G0(大多数设备无法播放)
- **instrument.Cello()**不存在,请使用
Violoncello() - 忘记设置速度:在第一个音轨添加
tempo.MetronomeMark() - 鼓组音色不对:使用mido将通道设置为9(参考mido快速参考)
Mixing and Balance
混音与平衡
CRITICAL: Setting MIDI program numbers alone is not enough. Without explicit velocity control, some instruments will be completely inaudible.
关键提醒:仅设置MIDI程序编号是不够的。如果不明确控制力度,部分乐器会完全听不见。
Setting Velocities
设置力度
music21 uses default velocity 64 for all notes, which causes poor mixing. Use mido to set velocities after MIDI export:
python
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)music21对所有音符使用默认力度64,会导致混音效果差。导出MIDI后使用mido设置力度:
python
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
按角色划分:
- 主音乐器(旋律、独奏):90-105
- 节奏乐器(吉他扫弦、伴奏):85-100
- 贝斯:75-85
- 鼓组:70-90
- 背景音(铺底、风琴):50-65
按频率范围划分:
- 低频(20-250 Hz):贝斯、底鼓——仅保留一个 dominant 声部,力度75-85
- 中频(250-2000 Hz):最拥挤的频段——通过力度区分(主音90+,背景音50-65)
- 高频(2000+ Hz):踩镲、钹——力度70-85,清晰且不刺耳
Soundfont Level Issues
Soundfont电平问题
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)
FluidR3_GM的乐器录制电平不同,哪怕力度设置正确,部分乐器也可能听不见:
音量小的程序(避免用于主音/节奏声部):
- 程序27(电吉他-清音)——音量非常小
- 程序24(原声吉他-尼龙弦)
- 程序73(长笛)
更好的替代选项:
- 程序25(原声吉他-钢弦)——音量高8-10dB,穿透力强
- 程序28(电吉他-制音)——打击感强
- 程序30(失真吉他)——有冲击力
其他修复方案:
- 增加音符时长(最少0.4个四分音符,而非0.25)
- 使用八度分离(把冲突的乐器移到不同八度)
- 极端力度对比(音量小的乐器设为110,音量大的设为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
渲染前确认:
- ✅ 主音力度90-105
- ✅ 背景音力度50-65
- ✅ 贝斯力度75-85
- ✅ 检查音量小的乐器(程序24、27、73)并使用替代选项
- ✅ 节奏乐器最少0.4个四分音符时长
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!
- music21文档:https://web.mit.edu/music21/doc/
- General MIDI规范:https://www.midi.org/specifications-old/item/gm-level-1-sound-set
- 乐理学习:https://www.musictheory.net/
- IMSLP(免费乐谱):https://imslp.org/——可在此下载古典音乐MIDI文件!
Limitations
局限性
- Instrumental only - No lyrics/vocals
- MIDI-based synthesis - Not studio-quality recordings
- No real-time playback - Files must be rendered before playback
- 仅支持器乐——不支持歌词/声乐
- 基于MIDI合成——达不到录音室级录制质量
- 无实时播放——必须先渲染文件才能播放