名称: 生成音乐作曲家 描述: 使用过程生成、马尔可夫链、L系统和神经方法创建算法音乐作曲系统,用于环境音乐、适应性音乐和实验音乐。 许可证: MIT
生成音乐作曲家
本技能提供指导,用于创建自主或半自主生成作品的算法和程序化音乐系统。
核心能力
- 算法作曲:基于规则的音乐生成
- 随机方法:马尔可夫链、概率分布
- 形式文法:L系统、音乐生成文法
- 适应性系统:响应输入/上下文
- 神经方法:基于机器学习生成技术
生成音乐基础
生成范式
| 方法 | 描述 | 最佳用途 |
|---|---|---|
| 基于规则 | 明确的作曲规则 | 传统风格、受控输出 |
| 随机 | 概率驱动选择 | 自然变化、惊喜 |
| 基于文法 | 递归结构生成 | 复杂形式、自相似性 |
| 基于约束 | 满足音乐约束 | 和声、声部进行 |
| 基于学习 | 在语料库上训练 | 风格模仿、新颖性 |
生成音乐元素
┌─────────────────────────────────────────────────────────┐
│ 音乐生成层次 │
├─────────────────────────────────────────────────────────┤
│ │
│ 宏观结构 │ 形式、段落、调性区域 │
│ ──────────────────────────────────────────────────────│
│ 和声 │ 和弦进行、声部进行 │
│ ──────────────────────────────────────────────────────│
│ 旋律 │ 音高序列、轮廓、节奏 │
│ ──────────────────────────────────────────────────────│
│ 节奏 │ 持续时间模式、节拍、律动 │
│ ──────────────────────────────────────────────────────│
│ 音色/纹理 │ 乐器配置、动态 │
│ ──────────────────────────────────────────────────────│
│ 微观变化 │ 装饰音、表达、人性化 │
│ │
└─────────────────────────────────────────────────────────┘
随机生成
马尔可夫链旋律
import random
from collections import defaultdict
class MarkovMelodyGenerator:
"""使用马尔可夫链生成旋律"""
def __init__(self, order=2):
self.order = order
self.transitions = defaultdict(list)
def train(self, melodies):
"""从现有旋律学习(MIDI音符列表)"""
for melody in melodies:
for i in range(len(melody) - self.order):
state = tuple(melody[i:i + self.order])
next_note = melody[i + self.order]
self.transitions[state].append(next_note)
def generate(self, length, seed=None):
"""生成新旋律"""
if seed is None:
seed = random.choice(list(self.transitions.keys()))
melody = list(seed)
for _ in range(length - self.order):
state = tuple(melody[-self.order:])
if state in self.transitions:
next_note = random.choice(self.transitions[state])
else:
# 回退:从所有可能的下一个音符中随机选择
next_note = random.choice(
[n for notes in self.transitions.values() for n in notes]
)
melody.append(next_note)
return melody
加权随机选择
def weighted_choice(options, weights):
"""使用自定义概率分布选择"""
total = sum(weights)
r = random.uniform(0, total)
cumulative = 0
for option, weight in zip(options, weights):
cumulative += weight
if r <= cumulative:
return option
return options[-1]
# 音阶度概率(倾向音)
scale_weights = {
1: 0.20, # 主音 - 稳定,常见
2: 0.10, # 上主音 - 经过
3: 0.15, # 中音 - 稳定
4: 0.10, # 下属音 - 倾向到3
5: 0.20, # 属音 - 稳定
6: 0.10, # 下中音 - 相关大调/小调
7: 0.05, # 导音 - 强倾向
8: 0.10 # 八度
}
def generate_scale_melody(length, key='C', scale='major'):
degrees = list(scale_weights.keys())
weights = list(scale_weights.values())
melody = [weighted_choice(degrees, weights) for _ in range(length)]
return [degree_to_midi(d, key, scale) for d in melody]
基于文法的生成
音乐结构的L系统
class MusicalLSystem:
"""用于生成乐句的L系统"""
def __init__(self):
self.rules = {
'A': 'AB', # 前句扩展为前句 + 桥段
'B': 'CA', # 桥段扩展为后句 + 前句
'C': 'DC', # 后句扩展为发展 + 后句
'D': 'A' # 发展回到前句
}
self.interpretations = {
'A': self._phrase_a,
'B': self._phrase_b,
'C': self._phrase_c,
'D': self._phrase_d
}
def generate_structure(self, axiom='A', iterations=4):
"""生成形式结构"""
result = axiom
for _ in range(iterations):
result = ''.join(self.rules.get(c, c) for c in result)
return result
def realize(self, structure):
"""将结构转换为音乐乐句"""
phrases = []
for symbol in structure:
if symbol in self.interpretations:
phrases.append(self.interpretations[symbol]())
return phrases
def _phrase_a(self):
# 前句:紧张构建乐句
return generate_phrase(contour='ascending', cadence='half')
def _phrase_b(self):
# 桥段:过渡材料
return generate_phrase(contour='static', cadence='deceptive')
def _phrase_c(self):
# 后句:解决乐句
return generate_phrase(contour='descending', cadence='authentic')
def _phrase_d(self):
# 发展:变化材料
return generate_phrase(contour='varied', cadence='half')
节奏生成文法
class RhythmGrammar:
"""用于节奏生成的上下文无关文法"""
def __init__(self):
# 非终结符与产生规则
self.rules = {
'MEASURE': [
['HALF', 'HALF'],
['QUARTER', 'QUARTER', 'QUARTER', 'QUARTER'],
['DOTTED_HALF', 'QUARTER'],
['BEAT', 'BEAT', 'BEAT', 'BEAT']
],
'HALF': [
['QUARTER', 'QUARTER'],
['half']
],
'QUARTER': [
['EIGHTH', 'EIGHTH'],
['quarter'],
['SIXTEENTH', 'SIXTEENTH', 'EIGHTH']
],
'BEAT': [
['quarter'],
['EIGHTH', 'EIGHTH'],
['TRIPLET']
],
'EIGHTH': [
['eighth'],
['SIXTEENTH', 'SIXTEENTH']
],
'TRIPLET': [
['triplet', 'triplet', 'triplet']
],
'SIXTEENTH': [
['sixteenth']
]
}
def generate(self, symbol='MEASURE'):
"""递归扩展文法"""
if symbol not in self.rules:
return [symbol] # 终结符
# 选择随机产生规则
production = random.choice(self.rules[symbol])
result = []
for s in production:
result.extend(self.generate(s))
return result
基于约束的和声
声部进行规则
class HarmonyGenerator:
"""生成满足声部进行约束的和弦进行"""
def __init__(self, key='C', mode='major'):
self.key = key
self.mode = mode
self.chord_vocabulary = self._build_chords()
def generate_progression(self, length=8):
"""生成满足约束的进行"""
progression = [self._tonic_chord()] # 从主和弦开始
for _ in range(length - 2):
current = progression[-1]
candidates = self._valid_next_chords(current)
next_chord = self._select_chord(candidates, current)
progression.append(next_chord)
# 以终止式结束
progression.append(self._dominant_chord())
progression.append(self._tonic_chord())
return progression
def _valid_next_chords(self, current):
"""通过声部进行约束过滤和弦"""
candidates = []
for chord in self.chord_vocabulary:
if self._check_voice_leading(current, chord):
candidates.append(chord)
return candidates
def _check_voice_leading(self, chord1, chord2):
"""检查声部进行规则"""
# 无平行五度
if self._has_parallel_fifths(chord1, chord2):
return False
# 无平行八度
if self._has_parallel_octaves(chord1, chord2):
return False
# 解决倾向音
if not self._resolves_tendencies(chord1, chord2):
return False
# 限制声部移动
if self._excessive_movement(chord1, chord2):
return False
return True
功能和声
# 和弦功能概率
PROGRESSION_TENDENCIES = {
'I': {'IV': 0.3, 'V': 0.3, 'vi': 0.2, 'ii': 0.1, 'iii': 0.1},
'ii': {'V': 0.7, 'vii': 0.2, 'IV': 0.1},
'iii': {'vi': 0.4, 'IV': 0.3, 'ii': 0.2, 'I': 0.1},
'IV': {'V': 0.4, 'I': 0.2, 'ii': 0.2, 'vii': 0.1, 'vi': 0.1},
'V': {'I': 0.6, 'vi': 0.3, 'IV': 0.1},
'vi': {'IV': 0.3, 'ii': 0.3, 'V': 0.2, 'I': 0.1, 'iii': 0.1},
'vii': {'I': 0.5, 'iii': 0.3, 'vi': 0.2}
}
def generate_functional_progression(length):
progression = ['I']
for _ in range(length - 1):
current = progression[-1]
tendencies = PROGRESSION_TENDENCIES[current]
next_chord = weighted_choice(
list(tendencies.keys()),
list(tendencies.values())
)
progression.append(next_chord)
return progression
适应性和交互式音乐
参数驱动生成
class AdaptiveComposer:
"""响应外部参数的音乐"""
def __init__(self):
self.parameters = {
'energy': 0.5, # 0-1:平静到激烈
'tension': 0.5, # 0-1:协和到不协和
'density': 0.5, # 0-1:稀疏到密集
'tempo_factor': 1.0 # 节拍乘数
}
def update_parameter(self, name, value):
self.parameters[name] = max(0, min(1, value))
def generate_measure(self):
"""生成适应当前参数的音乐"""
energy = self.parameters['energy']
tension = self.parameters['tension']
density = self.parameters['density']
# 调整音乐元素
note_density = int(4 + density * 12) # 4-16音符
velocity_range = (40 + int(energy * 40), 80 + int(energy * 47))
# 张力影响和声
if tension < 0.3:
chord_pool = ['I', 'IV', 'V', 'vi'] # 协和
elif tension < 0.7:
chord_pool = ['ii', 'iii', 'IV', 'V', 'vi'] # 混合
else:
chord_pool = ['ii', 'vii', 'V7', 'bVII', 'iv'] # 紧张
# 使用调整参数生成
return self._generate_notes(
density=note_density,
velocity_range=velocity_range,
harmonic_pool=chord_pool
)
游戏音频适应系统
class GameMusicSystem:
"""游戏的分层适应性音乐"""
def __init__(self):
self.layers = {
'ambient': {'volume': 1.0, 'active': True},
'percussion': {'volume': 0.0, 'active': False},
'melody': {'volume': 0.0, 'active': False},
'intensity': {'volume': 0.0, 'active': False}
}
self.current_state = 'exploration'
def set_game_state(self, state, transition_time=2.0):
"""基于游戏状态交叉淡化层次"""
presets = {
'exploration': {
'ambient': 1.0, 'percussion': 0.0,
'melody': 0.3, 'intensity': 0.0
},
'tension': {
'ambient': 0.7, 'percussion': 0.3,
'melody': 0.5, 'intensity': 0.3
},
'combat': {
'ambient': 0.3, 'percussion': 1.0,
'melody': 0.7, 'intensity': 1.0
},
'victory': {
'ambient': 0.5, 'percussion': 0.5,
'melody': 1.0, 'intensity': 0.0
}
}
target = presets.get(state, presets['exploration'])
self._crossfade_to(target, transition_time)
self.current_state = state
输出格式
MIDI生成
from midiutil import MIDIFile
def create_midi(melody, filename='output.mid', tempo=120):
"""导出旋律到MIDI文件"""
midi = MIDIFile(1) # 一个轨道
track = 0
channel = 0
time = 0
volume = 100
midi.addTempo(track, 0, tempo)
for note in melody:
pitch = note['pitch']
duration = note['duration']
midi.addNote(track, channel, pitch, time, duration, volume)
time += duration
with open(filename, 'wb') as f:
midi.writeFile(f)
最佳实践
音乐连贯性
- 重复与变化:重复主题但变化
- 动机发展:转换小想法
- 分层结构:乐句 → 段落 → 乐章
- 紧张与释放:随时间构建和解决
避免常见陷阱
- 纯随机听起来混乱——添加约束
- 过多规则听起来机械——添加随机变化
- 用实际音频测试,不仅是数据
- 考虑表演/可演奏性
参考资料
references/music-theory-primer.md- 生成所需的基本音乐理论references/markov-music.md- 高级马尔可夫链技术references/midi-reference.md- MIDI规范和库