name: code-to-music description: 用于通过代码创作音乐的工具、模式和实用程序。输出为带有真实乐器声音的.mp3文件。编写自定义作品,通过音乐将创造力变为现实。此技能应用于用户要求创作音乐时。绝不用于复制歌曲、节奏、旋律或其他敏感作品。此技能不适用于声乐/歌词音乐、音频混音/母带处理(混响、均衡、压缩)、实时MIDI播放或专业录音室录制质量。
此技能提供工具和模式用于音乐创作,而不是预制解决方案。应使用智能和music21库根据用户请求动态创作音乐。
核心原则:编写自定义代码以算法方式创作音乐,而不是调用带有硬编码旋律的函数。
安装与设置
快速安装
运行自动化安装程序进行完整设置:install.sh。这将安装所有系统依赖项、Python包并验证安装。
注意:安装脚本最后可能显示“错误:外部管理环境”消息。这些是预期的,可以安全忽略 - 依赖项已经安装。如果出现这些消息,安装成功。
可用SoundFonts
传统管道(管弦乐/原声):
/usr/share/sounds/sf2/FluidR3_GM.sf2(141MB,通用MIDI声音字体 - 用于高质量的管弦乐/原声样本)/usr/share/sounds/sf2/default.sf2(指向最佳可用的symlink)- ⚠️ 不要使用TimGM6mb.sf2 - 质量较差,吉他听起来电子/类似钢琴
电子管道:
- 使用与传统音乐相同的FluidR3_GM.sf2声音字体
- 不同之处在于你选择的GM程序(合成器程序38-39, 80-95而不是管弦乐)
关键概念
- 始终创建可下载的MP3文件(而不是HTML播放器)
- 可以使用music21.instrument类以方便:
instrument.Violin(),instrument.Violoncello(),instrument.Piano(),instrument.Trumpet()等。 - 关键:始终使用mido设置MIDI程序编号后导出 - music21的instrument类不可靠地导出program_change消息。见下面的mido工作流程。
- 程序性生成音符 - 避免硬编码序列
选择您的音乐生成管道
此技能支持两种针对不同音乐风格的优化渲染管道。仔细阅读用户请求并根据读取正确的下一个文件选择正确的管道。
决策过程
从用户请求中识别:
- 流派关键词(house, techno, classical, orchestral, reggae)
- 乐器参考(合成器与原声乐器)
- 音乐风格描述(电子/DJ与传统/原声)
- 艺术家提及(DJs与古典作曲家)
电子管道
当请求包括时使用electronic-music-pipeline.md:
- 流派:House, techno, trance, EDM, electronic dance music, ambient electronic, acid house, deep house, club music, DJ sets
- 乐器:Synthesizers, synth bass/pads/leads, 808 drums, electronic drums, supersaw leads, sub-bass
- 上下文:提到DJ(Keinemusik, Black Coffee, Avicii, Swedish House Mafia), BPM 120-140, “for a club”, “for dancing”
- 声音描述:“Fat synth sound”, “buzzy leads”, “electronic”, “synth-heavy”
工作原理:
- 使用实时合成(无声音字体)
- 实时合成鼓,低音,垫子,引导DSP
- 流派预设优化合成参数(deep_house, techno, trance, ambient, acid_house)
- 频率感知混音自动平衡低/中/高频率
- 最适合:正宗电子声音,现代EDM制作
→ 见electronic-music-pipeline.md了解详细说明
传统管道
当请求包括时使用traditional-music-pipeline.md:
- 流派:Classical, orchestral, jazz, blues, rock, country, folk, reggae, ska, symphonic, chamber music
- 乐器:Violin, cello, trumpet, flute, piano, acoustic guitar, acoustic bass, brass ensembles, string quartets, organ (reggae/gospel)
- 上下文:提到作曲家(Mozart, Beethoven, Bach), 古典时期(Baroque, Romantic), “unplugged”, “acoustic version”
- 声音描述:“Traditional”, “acoustic”, “orchestral”
工作原理:
- 使用预录音的管弦乐样本(声音字体)
- FluidR3_GM.sf2声音字体与128种通用MIDI乐器
- 适用于逼真的管弦乐/原声乐器
- 最适合:古典,爵士,雷鬼,摇滚,传统音乐
→ 见traditional-music-pipeline.md了解详细说明
如果不清楚
- 默认为TRADITIONAL用于混合请求或模糊流派
- 如果请求可以两者兼而有之,请向用户询问澄清
为什么这很重要
- 使用错误的管道 = 音频质量差(技术上可行,听起来不好)
- 电子管道:实时合成以获得正宗的电子声音
- 传统管道:预录音样本以获得逼真的管弦乐/原声
可用脚本
所有脚本位于./scripts:
传统渲染:
midi_inventory.py- 从任何MIDI文件中提取完整结构到JSON格式midi_render.py- 使用FluidSynth和动态范围压缩将JSON音乐结构渲染为MP3midi_utils.py- MIDI实用程序函数(提取鼓,获取BPM等)
实用工具:
audio_validate.py- 验证音频文件质量和格式
注意:电子和传统音乐都使用相同的渲染管道(FluidSynth + FluidR3_GM.sf2)。不同之处在于你选择的GM程序。
音乐理论参考
完整的通用MIDI乐器地图(程序0-127)
关键:music21不可靠地导出程序更改消息。即使使用instrument.TenorSaxophone()或instrument.AcousticGuitar()等乐器类,也必须始终使用mido在导出后设置程序编号。没有这一步,所有曲目将默认为钢琴(程序0)。
合成乐器分类: 当创作电子音乐时,根据其声音特征对乐器进行分类,以进行适当的合成:
- 吉他(24-31):弹拨攻击,明亮的音色,中等持续时间 - 需要与垫子不同的合成
- 低音(32-39):低频基频,次低音能量,短攻击
- 弦乐(40-47):持续的弓法,丰富的和声,平滑的连奏
- 铜管(56-63):明亮的攻击,持续的音色,强大的投射
- 簧片/木管(64-79):呼吸攻击,有机的音色,表现力强的动态
- 合成主音(80-87):明亮,切割,激进 - 专为旋律而设计
- 合成垫(88-95):软攻击,长持续时间,大气 - 专为背景而设计
- 民族/打击乐(104-119):具有独特音色的弹拨或敲击声音
# 钢琴(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"
# 色彩打击乐(8-15)
8: "Celesta"
9: "Glockenspiel"
10: "Music Box"
11: "Vibraphone"
12: "Marimba"
13: "Xylophone"
14: "Tubular Bells"
15: "Dulcimer"
# 风琴(16-23)
16: "Drawbar Organ"
17: "Percussive Organ"
18: "Rock Organ"
19: "Church Organ"
20: "Reed Organ"
21: "Accordion"
22: "Harmonica"
23: "Tango Accordion"
# 吉他(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"
# 低音(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"
# 弦乐(40-47)
40: "Violin"
41: "Viola"
42: "Cello"
43: "Contrabass"
44: "Tremolo Strings"
45: "Pizzicato Strings"
46: "Orchestral Harp"
47: "Timpani"
# 合奏(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"
# 铜管(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"
# 簧片(64-71)
64: "Soprano Sax"
65: "Alto Sax"
66: "Tenor Sax"
67: "Baritone Sax"
68: "Oboe"
69: "English Horn"
70: "Bassoon"
71: "Clarinet"
# 管乐(72-79)
72: "Piccolo"
73: "Flute"
74: "Recorder"
75: "Pan Flute"
76: "Blown Bottle"
77: "Shakuhachi"
78: "Whistle"
79: "Ocarina"
# 合成主音(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)"
# 合成垫(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)"
# 合成效果(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)"
# 民族(104-111)
104: "Sitar"
105: "Banjo"
106: "Shamisen"
107: "Koto"
108: "Kalimba"
109: "Bag pipe"
110: "Fiddle"
111: "Shanai"
# 打击乐(112-119)
112: "Tinkle Bell"
113: "Agogo"
114: "Steel Drums"
115: "Woodblock"
116: "Taiko Drum"
117: "Melodic Tom"
118: "Synth Drum"
119: "Reverse Cymbal"
# 音效(120-127)
120: "Guitar Fret Noise"
121: "Breath Noise"
122: "Seashore"
123: "Bird Tweet"
124: "Telephone Ring"
125: "Helicopter"
126: "Applause"
127: "Gunshot"
完整的鼓映射(MIDI通道10,音符35-81)
鼓使用音符编号表示不同的声音,而不是音高。必须在通道10(0索引为9)上。
# 低音鼓
35: "Acoustic Bass Drum"
36: "Bass Drum 1" # 最常见的低音鼓
# 军鼓
38: "Acoustic Snare" # 标准军鼓
40: "Electric Snare"
# 通鼓
41: "Low Floor Tom"
43: "High Floor Tom"
45: "Low Tom"
47: "Low-Mid Tom"
48: "Hi-Mid Tom"
50: "High Tom"
# 踩镲
42: "Closed Hi-Hat" # 最常用
44: "Pedal Hi-Hat"
46: "Open Hi-Hat"
# 钹
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"
# 打击乐
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"
如何使用任何乐器(mido工作流程)
关键规则:music21不可靠地导出program_change消息,即使使用instrument.TenorSaxophone()或instrument.AcousticGuitar()等乐器类。导出到MIDI后,必须始终使用mido手动插入程序更改消息。没有这一步,所有曲目听起来都会像钢琴(程序0)。
常见错误:假设sax_part.insert(0, instrument.TenorSaxophone())会自动使曲目听起来像萨克斯。它不会!仍然必须使用mido设置程序编号。
设置乐器的辅助函数:
from mido import Message
def set_track_instrument(track, program):
"""在MIDI轨道的开头插入程序更改消息。"""
# 查找所有元消息之后的位置(track_name等)
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))
# 使用mido加载MIDI后的用法:
# set_track_instrument(mid.tracks[2], 33) # 将第二轨设置为电贝斯
第1步:使用music21进行创作(使用占位符或跳过乐器)
from music21 import stream, note, chord, tempo
score = stream.Score()
# 创建部分 - 现在不用担心乐器分配
synth_lead = stream.Part()
synth_pad = stream.Part()
bass = stream.Part()
# 使用.insert()添加你的音符/和弦,使用显式定时
# 关键:始终使用.insert(offset, note)而不是.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))
# ... 使用.insert()创作你的音乐
score.append(synth_lead)
score.append(synth_pad)
score.append(bass)
# 导出到MIDI
midi_path = # 定义一个输出路径
score.write('midi', fp=midi_path)
第2步:检查MIDI结构并使用mido分配乐器
from mido import MidiFile, Message
mid = MidiFile(midi_path)
# 关键:不要假设轨道号码!首先检查MIDI文件。
# 打印轨道结构,看看存在哪些轨道
print(f"总轨道数:{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"轨道 {i}: {note_count} 音符")
# 辅助函数设置乐器def set_track_instrument(track, program):
"""在MIDI轨道的开头插入程序更改消息。"""
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))
# 根据检查,为正确的轨道分配乐器
# 示例:如果[鼓,低音,吉他]被追加,检查显示轨道1, 2, 3有音符:
set_track_instrument(mid.tracks[1], 80) # 第一部分 - 正方形主音
set_track_instrument(mid.tracks[2], 88) # 第二部分 - 垫子
set_track_instrument(mid.tracks[3], 38) # 第三部分 - 低音
mid.save(midi_path)
第3步:对于鼓,还要将通道设置为9(通道10)
# 如果轨道是鼓,将所有消息设置为通道9
for i, track in enumerate(mid.tracks):
if i == 1: # 这是鼓轨道
for msg in track:
if hasattr(msg, 'channel'):
msg.channel = 9 # 1索引中的通道10
第4步:渲染为音频
from midi2audio import FluidSynth
from pydub import AudioSegment
fs = FluidSynth('/usr/share/sounds/sf2/FluidR3_GM.sf2')
wav_path = # 定义一个路径
fs.midi_to_audio(midi_path, wav_path)
audio = AudioSegment.from_wav(wav_path)
mp3_path = # 定义一个路径
audio.export(mp3_path, format='mp3', bitrate='192k')
常见和弦进程与风格
# 标准进程(罗马数字)
"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
# 流派特定特征
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乐器类
from music21 import instrument
# 弦乐
instrument.Violin()
instrument.Viola()
instrument.Violoncello() # 注意:不是Cello()
instrument.Contrabass()
instrument.Harp()
# 钢琴
instrument.Piano()
instrument.Harpsichord()
# 铜管
instrument.Trumpet()
instrument.Trombone()
instrument.Tuba()
instrument.Horn() # 法国号
# 木管
instrument.Flute()
instrument.Clarinet()
instrument.Oboe()
instrument.Bassoon()
instrument.SopranoSaxophone()
instrument.AltoSaxophone()
instrument.TenorSaxophone() # 最常用于爵士乐
instrument.BaritoneSaxophone()
# 其他
instrument.AcousticGuitar()
instrument.ElectricGuitar()
instrument.Bass()
instrument.Timpani()
# 关键:music21对电子乐器和鼓的支持有限
# 对于合成器,鼓和电子声音,必须采取以下步骤:
# 1. 创建一个没有乐器的部分(或使用占位符如Piano())
# 2. 使用mido库在导出后插入程序更改消息
# 3. 将鼓设置为MIDI通道10(0索引为9)否则它们不会听起来像鼓
#
# 常见错误:
# - instrument.Cello()不存在 - 使用Violoncello()
# - instrument.FrenchHorn()不存在 - 使用Horn()
# - 设置part.partName不会改变声音 - 必须使用mido设置MIDI程序
# - 鼓在通道0上将播放为有音高音符,而不是鼓声
音符持续时间(四分音符=1.0)
- 整音符:4.0
- 半音符:2.0
- 四分音符:1.0
- 八分音符:0.5
- 十六分音符:0.25
- 点四分音符:1.5
- 三连音四分音符:0.667
mido快速参考
对于电子音乐和鼓,使用mido在music21导出后设置MIDI程序:
from mido import MidiFile, Message
mid = MidiFile(midi_path)
# 定义辅助函数def set_track_instrument(track, program):
"""在MIDI轨道的开头插入程序更改消息。"""
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))
# 插入程序更改消息
for i, track in enumerate(mid.tracks):
if i == 1: # 你的轨道(轨道从1开始,不是0)
set_track_instrument(track, 38) # 合成贝斯1
# 对于鼓:将通道设置为9(1索引中的通道10)
for i, track in enumerate(mid.tracks):
if i == 1: # 鼓轨道
for msg in track:
if hasattr(msg, 'channel'):
msg.channel = 9
mid.save(midi_path)
常见MIDI程序:
- 38:合成贝斯1
- 80:正方形主音
- 81:锯形主音
- 88:垫1(新时代)
- 25:吉他(钢弦) - 响亮,穿透力强
- 33:原声贝斯
常见技巧
鼓编程(4-on-floor house节奏)
关键:music21的.append()添加音符顺序(一个接一个),不是同时。对于需要叠加的鼓,其中kick,snare和hats同时播放,.insert(offset, note)必须与显式定时一起使用。
始终对部分使用.insert()添加音符:
由于几乎所有优秀的音乐创作都需要叠加,对于所有轨道 - 鼓,低音,吉他,pads等,必须始终使用part.insert(offset, note)。这可以防止定时错误,并确保正确同步。
从不混合.insert()和.append()向部分添加音符 - 如果对鼓使用.insert(),对其他乐器使用.append(),music21会错误地计算轨道长度,创建比预期长5-10倍的轨道(8分钟而不是1.5分钟),其中只有前20-25%包含实际声音。
正确模式:
# 对:对音符使用.insert(),对部分使用.append()
drum_part = stream.Part()
bass_part = stream.Part()
offset = 0.0
for beat in range(16):
# 同时(叠加)的kick和hi-hat
drum_part.insert(offset, note.Note(36, quarterLength=0.5)) # Kick
drum_part.insert(offset, note.Note(42, quarterLength=0.5)) # Hi-hat
# 低音音符
bass_part.insert(offset, note.Note('A2', quarterLength=0.5))
offset += 0.5 # 提升时间
# 将部分添加到得分(这个.append()可以)
score.append(drum_part)
score.append(bass_part)
确保完整的小节覆盖
关键:在循环创作时,确保你的音符完全填满每个小节,没有间隙。一个常见错误是放置音符没有跨越整个小节的持续时间,留下静音间隙。
问题模式(创建间隙):
bar_length = 1.5 # 6/8时间签名
offset = 0.0
for bar in range(8):
# 只填充了第一个1.0四分之一长度,留下了0.5的静音!
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))
# 缺少:offset + 1.0到offset + 1.5
offset += bar_length # 推进到下一个小节,但间隙仍然存在
# 结果:吉他播放了10秒钟,然后剩余时间是静音
解决方案模式(完全覆盖):
bar_length = 1.5 # 6/8时间签名
offset = 0.0
for bar in range(8):
# 用音符填满整个小节
guitar_part.insert(offset, note.Note('D3', quarterLength=0.5)) # 0.0到0.5
guitar_part.insert(offset + 0.5, chord.Chord(['D3', 'F#3', 'A3'], quarterLength=0.5)) # 0.5到1.0
guitar_part.insert(offset + 1.0, note.Note('D3', quarterLength=0.5)) # 1.0到1.5 ✓ 完整!
offset += bar_length
# 结果:吉他在整个作品中持续播放
最佳实践:在提升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
最佳实践
作曲质量
- 生成多样性:不要为整个作品重复相同的4个小节
- 使用音乐理论:真正的和弦进程,适当的声音引导
- 尊重乐器范围:小提琴(G3-E7),大提琴(C2-C6),小号(E3-C6)
- 添加动态:使用p, mp, mf, f, ff标记和渐强
- 结构:引言 → 发展 → 高潮 → 解决
- 添加人性化:变化时间和速度,避免机器人声音
import random n = note.Note('C5', quarterLength=1.0 + random.uniform(-0.05, 0.05)) n.volume.velocity = 80 + random.randint(-5, 5)
技术质量
- 声音字体:使用FluidR3_GM.sf2获得最佳质量
- 比特率:最低192kbps,高质量320kbps
- 限制:通过
audio.compress_dynamic_range()防止剪辑 - 定时精度:仔细使用quarterLength值
- 清理:在MP3转换后删除临时MIDI/WAV文件
常见陷阱
- 关键:绝不假设轨道号码 - 不要盲目使用
if i == 3用于吉他。始终首先使用音符计数验证哪个轨道是哪个 - 关键:始终对部分使用part.insert(offset, note)添加音符 - 永远不要使用
part.append(note)或混合使用.insert()和.append()。这会导致定时错误,其中轨道比预期长5-10倍,只有20-25%包含声音。见“鼓编程”部分详情 - 关键:插入程序更改消息 - 使用
track.insert(pos, Message('program_change', ...))而不是msg.program = X。见mido快速参考 - 关键:设置速度 - 主音90-105,背景50-65。见“混合和平衡”部分
- 关键:低音八度 - 使用A1-A2(55-110 Hz),绝不使用A0-G0(在大多数系统上听不见)
- **instrument.Cello()**不存在 - 使用
Violoncello() - 忘记节奏 - 在第一部分添加
tempo.MetronomeMark() - 鼓听起来不像鼓 - 使用mido将通道设置为9(见mido快速参考)
混合和平衡
关键:仅设置MIDI程序编号是不够的。没有显式速度控制,一些乐器将完全听不见。
设置速度
music21对所有音符使用默认速度64,导致混合不良。使用mido在MIDI导出后设置速度:
from mido import MidiFile
def set_track_velocity(track, velocity):
"""为轨道中的所有note_on消息设置速度。"""
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: # 鼓
set_track_velocity(track, 75)
elif i == 2: # 低音
set_track_velocity(track, 80)
elif i == 3: # 主乐器(萨克斯,吉他,小号)
set_track_velocity(track, 95)
elif i == 4: # 背景(风琴,垫子)
set_track_velocity(track, 55)
mid.save(midi_path)
速度指南
按角色:
- 主乐器(旋律,独奏):90-105
- 节奏乐器(吉他弹奏,伴奏):85-100
- 低音:75-85
- 鼓:70-90
- 背景(垫子,风琴):50-65
按频率范围:
- 低(20-250 Hz):低音,kick - 只有一个在75-85处占主导
- 中(250-2000 Hz):最拥挤 - 使用速度分离(主音90+,背景50-65)
- 高(2000+ Hz):Hi-hats, cymbals - 70-85清晰而不刺耳
声音字体级别问题
FluidR3_GM乐器以不同级别录制。即使速度正确,一些乐器可能听不见:
安静程序(避免用于主音/节奏):
- 程序27(电吉他 - 干净)- 非常安静
- 程序24(吉他 - 尼龙弦)
- 程序73(长笛)
更好的选择:
- 程序25(吉他 - 钢弦)- 8-10dB更响亮,穿透力强
- 程序28(电吉他 - 闷音)- 打击乐
- 程序30(失真吉他)- 激进
额外修复:
- 增加音符持续时间(0.4 quarterLength最低vs 0.25)
- 使用八度分隔(将竞争乐器移动到不同的八度)
- 极端速度对比(安静乐器110,响亮40)
混合检查表
在渲染之前:
- ✅ 主音在速度90-105
- ✅ 背景在速度50-65
- ✅ 低音在速度75-85
- ✅ 检查安静乐器(程序24, 27, 73)并使用替代品
- ✅ 节奏乐器的最低0.4 quarterLength
资源
- music21文档:https://web.mit.edu/music21/doc/
- 通用MIDI规范:https://www.midi.org/specifications-old/item/gm-level-1-sound-set
- 音乐理论:https://www.musictheory.net/
- IMSLP(免费乐谱):https://imslp.org/ - 在这里下载古典MIDI!
限制
- 仅限器乐 - 无歌词/声乐
- 基于MIDI的合成 - 不是录音室质量录音
- 无实时播放 - 文件必须在播放前渲染