1581 lines
60 KiB
Python
1581 lines
60 KiB
Python
"""
|
||
作曲引擎
|
||
负责动态生成音乐内容
|
||
"""
|
||
|
||
import uuid
|
||
import math
|
||
import time
|
||
import numpy as np
|
||
from typing import Dict, List, Any, Optional, Callable
|
||
from scipy import signal
|
||
import threading
|
||
import queue
|
||
|
||
class CompositionEngine:
|
||
"""
|
||
作曲引擎
|
||
负责动态生成音乐内容
|
||
"""
|
||
|
||
def __init__(self, plugin):
|
||
"""
|
||
初始化作曲引擎
|
||
|
||
Args:
|
||
plugin: 动态音乐系统插件实例
|
||
"""
|
||
self.plugin = plugin
|
||
self.generators: Dict[str, Dict[str, Any]] = {}
|
||
self.active_generators: Dict[str, Dict[str, Any]] = {}
|
||
self.composed_segments: Dict[str, Dict[str, Any]] = {}
|
||
self.generator_threads: Dict[str, threading.Thread] = {}
|
||
self.generator_queues: Dict[str, queue.Queue] = {}
|
||
|
||
self.stats = {
|
||
'generators_created': 0,
|
||
'generators_active': 0,
|
||
'segments_composed': 0,
|
||
'composition_time': 0.0,
|
||
'memory_usage': 0,
|
||
'failed_compositions': 0
|
||
}
|
||
|
||
self.sample_rate = 44100
|
||
self.buffer_size = plugin.buffer_size if plugin else 4096
|
||
self.master_key = 'C'
|
||
self.master_scale = 'major'
|
||
self.master_bpm = 120
|
||
self.max_polyphony = 16
|
||
|
||
# 音乐理论数据
|
||
self._initialize_music_theory()
|
||
|
||
# 作曲参数
|
||
self.composition_settings = {
|
||
'max_segment_duration': 30.0, # 最大片段时长
|
||
'min_segment_duration': 5.0, # 最小片段时长
|
||
'segment_overlap': 0.5, # 片段重叠比例
|
||
'lookahead_time': 2.0, # 预生成时间
|
||
'complexity_range': (0.2, 0.8) # 复杂度范围
|
||
}
|
||
|
||
# 音色库
|
||
self.instrument_presets = {
|
||
'piano': {'attack': 0.01, 'decay': 0.1, 'sustain': 0.7, 'release': 0.3},
|
||
'strings': {'attack': 0.2, 'decay': 0.1, 'sustain': 0.8, 'release': 0.5},
|
||
'brass': {'attack': 0.05, 'decay': 0.1, 'sustain': 0.9, 'release': 0.2},
|
||
'woodwind': {'attack': 0.1, 'decay': 0.05, 'sustain': 0.6, 'release': 0.3},
|
||
'guitar': {'attack': 0.02, 'decay': 0.2, 'sustain': 0.5, 'release': 0.4},
|
||
'synth': {'attack': 0.005, 'decay': 0.05, 'sustain': 0.9, 'release': 0.1}
|
||
}
|
||
|
||
# 和声进行数据库
|
||
self.chord_progressions = {
|
||
'major': [
|
||
['I', 'V', 'vi', 'IV'], # 流行进行
|
||
['I', 'vi', 'IV', 'V'], # 古典进行
|
||
['I', 'IV', 'V', 'I'], # 终止进行
|
||
['I', 'iii', 'IV', 'V'], # 扩展进行
|
||
['I', 'vi', 'ii', 'V'] # 2-5-1进行
|
||
],
|
||
'minor': [
|
||
['i', 'V', 'VI', 'III'], # 小调流行进行
|
||
['i', 'bVII', 'VI', 'VII'], # 模态进行
|
||
['i', 'iv', 'V', 'i'], # 小调终止进行
|
||
['i', 'bVI', 'bIII', 'V'], # 情感进行
|
||
['i', 'bVII', 'bVI', 'V'] # 蓝调进行
|
||
]
|
||
}
|
||
|
||
# 初始化作曲引擎
|
||
self._initialize_composition_engine()
|
||
|
||
def _initialize_music_theory(self):
|
||
"""初始化音乐理论数据"""
|
||
# 音符频率表 (基于A4=440Hz)
|
||
self.note_frequencies = {}
|
||
a4_freq = 440.0
|
||
for i in range(-60, 60): # 支持多个八度
|
||
note_name = self._get_note_name(i)
|
||
self.note_frequencies[note_name] = a4_freq * (2 ** (i / 12))
|
||
|
||
# 音阶模式
|
||
self.scale_patterns = {
|
||
'major': [0, 2, 4, 5, 7, 9, 11],
|
||
'minor': [0, 2, 3, 5, 7, 8, 10],
|
||
'harmonic_minor': [0, 2, 3, 5, 7, 8, 11],
|
||
'melodic_minor': [0, 2, 3, 5, 7, 9, 11],
|
||
'dorian': [0, 2, 3, 5, 7, 9, 10],
|
||
'phrygian': [0, 1, 3, 5, 7, 8, 10],
|
||
'lydian': [0, 2, 4, 6, 7, 9, 11],
|
||
'mixolydian': [0, 2, 4, 5, 7, 9, 10],
|
||
'aeolian': [0, 2, 3, 5, 7, 8, 10],
|
||
'locrian': [0, 1, 3, 5, 6, 8, 10]
|
||
}
|
||
|
||
# 和弦模式
|
||
self.chord_patterns = {
|
||
'major': [0, 4, 7],
|
||
'minor': [0, 3, 7],
|
||
'diminished': [0, 3, 6],
|
||
'augmented': [0, 4, 8],
|
||
'sus2': [0, 2, 7],
|
||
'sus4': [0, 5, 7],
|
||
'7th': [0, 4, 7, 10],
|
||
'maj7': [0, 4, 7, 11],
|
||
'min7': [0, 3, 7, 10],
|
||
'dim7': [0, 3, 6, 9],
|
||
'm7b5': [0, 3, 6, 10]
|
||
}
|
||
|
||
# 罗马数字和弦映射
|
||
self.roman_numerals = {
|
||
'I': 0, 'II': 1, 'III': 2, 'IV': 3, 'V': 4, 'VI': 5, 'VII': 6,
|
||
'i': 0, 'ii': 1, 'iii': 2, 'iv': 3, 'v': 4, 'vi': 5, 'vii': 6,
|
||
'bII': 10, 'bIII': 11, 'bVI': 14, 'bVII': 15 # 用于小调
|
||
}
|
||
|
||
# 音符名称
|
||
self.note_names = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
|
||
|
||
# 节拍模式
|
||
self.rhythm_patterns = {
|
||
'4/4': [1.0, 0.5, 0.5, 1.0, 0.5, 0.5, 0.5, 0.5], # 基本4/4拍
|
||
'3/4': [1.0, 0.5, 0.5], # 华尔兹
|
||
'6/8': [1.5, 0.5, 0.5, 1.5, 0.5, 0.5], # 6/8拍
|
||
'syncopated': [0.5, 0.5, 1.0, 0.5, 0.5, 1.0], # 切分音
|
||
'swing': [1.33, 0.67, 1.33, 0.67] # 摇摆节奏
|
||
}
|
||
|
||
def _get_note_name(self, semitone_offset: int) -> str:
|
||
"""
|
||
根据半音偏移量获取音符名称
|
||
|
||
Args:
|
||
semitone_offset: 相对于A4的半音偏移量
|
||
|
||
Returns:
|
||
音符名称
|
||
"""
|
||
note_index = (9 + semitone_offset) % 12 # A是第9个音符
|
||
octave = 4 + (9 + semitone_offset) // 12
|
||
return f"{self.note_names[note_index]}{octave}"
|
||
|
||
def _initialize_composition_engine(self):
|
||
"""初始化作曲引擎"""
|
||
print("✓ 作曲引擎初始化完成")
|
||
|
||
def create_generator(self, generator_name: str, generator_params: Dict[str, Any]) -> bool:
|
||
"""
|
||
创建音乐生成器
|
||
|
||
Args:
|
||
generator_name: 生成器名称
|
||
generator_params: 生成器参数
|
||
|
||
Returns:
|
||
是否创建成功
|
||
"""
|
||
try:
|
||
# 设置默认参数
|
||
default_params = {
|
||
'type': 'melody', # melody, harmony, rhythm, bass
|
||
'key': self.master_key,
|
||
'scale': self.master_scale,
|
||
'bpm': self.master_bpm,
|
||
'octave': 4,
|
||
'complexity': 0.5, # 0.0-1.0
|
||
'variation': 0.3, # 0.0-1.0
|
||
'length': 8, # 小节数
|
||
'instrument': 'piano',
|
||
'arpeggiate': False,
|
||
'syncopation': 0.0, # 0.0-1.0
|
||
'polyphony': 1, # 同时发声数
|
||
'mode': 'loop', # loop, once, random
|
||
'tempo_sync': True, # 是否与主BPM同步
|
||
'dynamic_range': (0.6, 1.0) # 音量范围
|
||
}
|
||
|
||
# 合并参数
|
||
params = default_params.copy()
|
||
params.update(generator_params)
|
||
|
||
# 验证参数
|
||
if params['type'] not in ['melody', 'harmony', 'rhythm', 'bass']:
|
||
print(f"✗ 无效的生成器类型: {params['type']}")
|
||
return False
|
||
|
||
if params['scale'] not in self.scale_patterns:
|
||
print(f"✗ 无效的音阶: {params['scale']}")
|
||
return False
|
||
|
||
if params['instrument'] not in self.instrument_presets:
|
||
print(f"⚠ 未知乐器: {params['instrument']},使用默认钢琴音色")
|
||
params['instrument'] = 'piano'
|
||
|
||
# 验证复杂度和变化度范围
|
||
params['complexity'] = max(0.0, min(1.0, params['complexity']))
|
||
params['variation'] = max(0.0, min(1.0, params['variation']))
|
||
params['syncopation'] = max(0.0, min(1.0, params['syncopation']))
|
||
params['polyphony'] = max(1, min(self.max_polyphony, params['polyphony']))
|
||
|
||
# 创建生成器
|
||
generator = {
|
||
'name': generator_name,
|
||
'params': params,
|
||
'state': 'created',
|
||
'segments': [],
|
||
'current_segment': 0,
|
||
'playback_position': 0.0,
|
||
'last_generated_time': 0.0,
|
||
'is_generating': False,
|
||
'thread': None,
|
||
'queue': queue.Queue()
|
||
}
|
||
|
||
self.generators[generator_name] = generator
|
||
self.generator_queues[generator_name] = generator['queue']
|
||
self.stats['generators_created'] += 1
|
||
|
||
print(f"✓ 音乐生成器创建成功: {generator_name}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 创建音乐生成器失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def activate_generator(self, generator_name: str) -> bool:
|
||
"""
|
||
激活音乐生成器
|
||
|
||
Args:
|
||
generator_name: 生成器名称
|
||
|
||
Returns:
|
||
是否激活成功
|
||
"""
|
||
try:
|
||
if generator_name not in self.generators:
|
||
print(f"✗ 音乐生成器不存在: {generator_name}")
|
||
return False
|
||
|
||
generator = self.generators[generator_name]
|
||
|
||
# 启动生成器线程
|
||
if generator['thread'] is None or not generator['thread'].is_alive():
|
||
generator['thread'] = threading.Thread(
|
||
target=self._generator_worker,
|
||
args=(generator_name,),
|
||
daemon=True
|
||
)
|
||
generator['thread'].start()
|
||
|
||
# 生成初始音乐片段
|
||
if self._generate_segment(generator_name):
|
||
self.active_generators[generator_name] = self.generators[generator_name]
|
||
self.stats['generators_active'] = len(self.active_generators)
|
||
generator['state'] = 'active'
|
||
print(f"✓ 音乐生成器已激活: {generator_name}")
|
||
return True
|
||
else:
|
||
print(f"✗ 生成音乐片段失败: {generator_name}")
|
||
return False
|
||
|
||
except Exception as e:
|
||
print(f"✗ 激活音乐生成器失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def deactivate_generator(self, generator_name: str) -> bool:
|
||
"""
|
||
停用音乐生成器
|
||
|
||
Args:
|
||
generator_name: 生成器名称
|
||
|
||
Returns:
|
||
是否停用成功
|
||
"""
|
||
try:
|
||
if generator_name not in self.active_generators:
|
||
print(f"⚠ 音乐生成器未激活: {generator_name}")
|
||
return True
|
||
|
||
generator = self.generators[generator_name]
|
||
generator['state'] = 'inactive'
|
||
|
||
del self.active_generators[generator_name]
|
||
self.stats['generators_active'] = len(self.active_generators)
|
||
|
||
print(f"✓ 音乐生成器已停用: {generator_name}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 停用音乐生成器失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def _generator_worker(self, generator_name: str):
|
||
"""
|
||
生成器工作线程
|
||
|
||
Args:
|
||
generator_name: 生成器名称
|
||
"""
|
||
try:
|
||
while generator_name in self.generators:
|
||
generator = self.generators[generator_name]
|
||
if generator['state'] == 'active':
|
||
# 检查是否需要生成新片段
|
||
if self._should_generate_new_segment(generator):
|
||
self._generate_segment(generator_name)
|
||
|
||
# 处理队列中的请求
|
||
try:
|
||
request = generator['queue'].get(timeout=0.1)
|
||
if request['type'] == 'generate':
|
||
self._generate_segment(generator_name)
|
||
elif request['type'] == 'stop':
|
||
break
|
||
except queue.Empty:
|
||
pass
|
||
|
||
time.sleep(0.01) # 10ms延迟
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生成器工作线程错误: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
def _generate_segment(self, generator_name: str) -> bool:
|
||
"""
|
||
生成音乐片段
|
||
|
||
Args:
|
||
generator_name: 生成器名称
|
||
|
||
Returns:
|
||
是否生成成功
|
||
"""
|
||
try:
|
||
if generator_name not in self.generators:
|
||
return False
|
||
|
||
generator = self.generators[generator_name]
|
||
params = generator['params']
|
||
|
||
# 检查是否正在生成
|
||
if generator['is_generating']:
|
||
return True # 避免重复生成
|
||
|
||
generator['is_generating'] = True
|
||
|
||
# 生成唯一片段ID
|
||
segment_id = f"{generator_name}_{int(time.time() * 1000000)}"
|
||
|
||
# 根据生成器类型生成音乐数据
|
||
if params['type'] == 'melody':
|
||
segment_data = self._generate_melody_segment(params)
|
||
elif params['type'] == 'harmony':
|
||
segment_data = self._generate_harmony_segment(params)
|
||
elif params['type'] == 'rhythm':
|
||
segment_data = self._generate_rhythm_segment(params)
|
||
elif params['type'] == 'bass':
|
||
segment_data = self._generate_bass_segment(params)
|
||
else:
|
||
segment_data = self._generate_melody_segment(params) # 默认生成旋律
|
||
|
||
# 创建片段
|
||
segment = {
|
||
'id': segment_id,
|
||
'generator': generator_name,
|
||
'data': segment_data,
|
||
'created_time': time.time(),
|
||
'duration': params['length'] * (60.0 / params['bpm']) * 4, # 假设4/4拍
|
||
'bpm': params['bpm'],
|
||
'key': params['key'],
|
||
'scale': params['scale'],
|
||
'type': params['type']
|
||
}
|
||
|
||
# 存储片段
|
||
self.composed_segments[segment_id] = segment
|
||
generator['segments'].append(segment_id)
|
||
|
||
# 限制片段数量
|
||
if len(generator['segments']) > 10: # 最多保留10个片段
|
||
old_segment_id = generator['segments'].pop(0)
|
||
if old_segment_id in self.composed_segments:
|
||
del self.composed_segments[old_segment_id]
|
||
|
||
self.stats['segments_composed'] += 1
|
||
generator['last_generated_time'] = time.time()
|
||
generator['is_generating'] = False
|
||
|
||
print(f"✓ 音乐片段生成成功: {segment_id}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生成音乐片段失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
if generator_name in self.generators:
|
||
self.generators[generator_name]['is_generating'] = False
|
||
self.stats['failed_compositions'] += 1
|
||
return False
|
||
|
||
def _generate_melody_segment(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||
"""
|
||
生成旋律片段
|
||
|
||
Args:
|
||
params: 生成参数
|
||
|
||
Returns:
|
||
旋律片段数据
|
||
"""
|
||
try:
|
||
# 获取音阶音符
|
||
scale_notes = self._get_scale_notes(params['key'], params['scale'], params['octave'])
|
||
|
||
# 计算片段长度(以拍为单位)
|
||
beats_per_measure = 4 # 假设4/4拍
|
||
total_beats = params['length'] * beats_per_measure
|
||
|
||
# 选择节拍模式
|
||
time_signature = '4/4'
|
||
if params['syncopation'] > 0.7:
|
||
rhythm_pattern = self.rhythm_patterns['syncopated']
|
||
elif params['syncopation'] > 0.4:
|
||
rhythm_pattern = self.rhythm_patterns['swing']
|
||
else:
|
||
rhythm_pattern = self.rhythm_patterns[time_signature]
|
||
|
||
# 生成音符序列
|
||
notes = []
|
||
current_time = 0.0
|
||
beat_index = 0
|
||
|
||
while current_time < total_beats:
|
||
# 选择音符
|
||
note_index = self._choose_note(scale_notes, params['complexity'], params['variation'])
|
||
note_name = scale_notes[note_index]
|
||
note_freq = self.note_frequencies.get(note_name, 440.0)
|
||
|
||
# 选择时值
|
||
duration = rhythm_pattern[beat_index % len(rhythm_pattern)]
|
||
|
||
# 应用复杂度调整时值
|
||
if params['complexity'] > 0.7:
|
||
# 增加装饰音和复杂节奏
|
||
if np.random.random() < 0.3:
|
||
duration *= 0.5 # 八分音符
|
||
# 添加装饰音
|
||
if note_index > 0:
|
||
grace_note_index = max(0, note_index - 1)
|
||
grace_note_name = scale_notes[grace_note_index]
|
||
grace_note_freq = self.note_frequencies.get(grace_note_name, 440.0)
|
||
notes.append({
|
||
'frequency': grace_note_freq,
|
||
'duration': duration * 0.3,
|
||
'start_time': current_time,
|
||
'velocity': 0.4 + np.random.random() * 0.2,
|
||
'type': 'grace'
|
||
})
|
||
current_time += duration * 0.3
|
||
|
||
# 添加主要音符
|
||
velocity_range = params.get('dynamic_range', (0.6, 1.0))
|
||
velocity = velocity_range[0] + np.random.random() * (velocity_range[1] - velocity_range[0])
|
||
|
||
notes.append({
|
||
'frequency': note_freq,
|
||
'duration': duration,
|
||
'start_time': current_time,
|
||
'velocity': velocity,
|
||
'type': 'main'
|
||
})
|
||
|
||
current_time += duration
|
||
beat_index += 1
|
||
|
||
# 避免无限循环
|
||
if beat_index > total_beats * 4: # 限制最多4倍拍数
|
||
break
|
||
|
||
# 应用琶音效果
|
||
if params['arpeggiate']:
|
||
notes = self._apply_arpeggiation(notes, params['syncopation'])
|
||
|
||
return {
|
||
'type': 'melody',
|
||
'notes': notes,
|
||
'bpm': params['bpm'],
|
||
'key': params['key'],
|
||
'scale': params['scale'],
|
||
'instrument': params['instrument']
|
||
}
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生成旋律片段失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return {
|
||
'type': 'melody',
|
||
'notes': [],
|
||
'bpm': params['bpm'],
|
||
'key': params['key'],
|
||
'scale': params['scale'],
|
||
'instrument': params['instrument']
|
||
}
|
||
|
||
def _generate_harmony_segment(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||
"""
|
||
生成和声片段
|
||
|
||
Args:
|
||
params: 生成参数
|
||
|
||
Returns:
|
||
和声片段数据
|
||
"""
|
||
try:
|
||
# 选择和弦进行
|
||
progressions = self.chord_progressions.get(params['scale'], self.chord_progressions['major'])
|
||
chord_progression = np.random.choice(progressions)
|
||
|
||
# 计算片段长度
|
||
beats_per_measure = 4
|
||
total_measures = params['length']
|
||
|
||
# 生成和弦序列
|
||
chords = []
|
||
current_time = 0.0
|
||
measure_duration = beats_per_measure * (60.0 / params['bpm'])
|
||
|
||
for measure in range(total_measures):
|
||
# 获取和弦
|
||
chord_index = measure % len(chord_progression)
|
||
roman_numeral = chord_progression[chord_index]
|
||
|
||
# 转换罗马数字为和弦
|
||
scale_degree = self.roman_numerals.get(roman_numeral, 0)
|
||
chord_type = 'major' if roman_numeral.isupper() else 'minor'
|
||
|
||
# 特殊处理小调中的降号和弦
|
||
if roman_numeral.startswith('b'):
|
||
scale_degree = self.roman_numerals.get(roman_numeral, scale_degree)
|
||
if 'VII' in roman_numeral:
|
||
chord_type = 'major'
|
||
elif 'VI' in roman_numeral or 'III' in roman_numeral:
|
||
chord_type = 'major'
|
||
|
||
# 获取和弦音符
|
||
scale_notes = self._get_scale_notes(params['key'], params['scale'], params['octave'] - 1)
|
||
chord_root_index = scale_degree % len(scale_notes)
|
||
chord_root = scale_notes[chord_root_index]
|
||
chord_notes = self._get_chord_notes(chord_root, chord_type, params['octave'] - 1)
|
||
|
||
# 计算和弦频率
|
||
frequencies = [self.note_frequencies.get(note, 440.0) for note in chord_notes]
|
||
|
||
# 应用琶音效果
|
||
arpeggiated = params['arpeggiate'] and np.random.random() < 0.5
|
||
if arpeggiated:
|
||
# 生成琶音音符
|
||
arp_notes = []
|
||
arp_duration = measure_duration / len(chord_notes)
|
||
for i, freq in enumerate(frequencies):
|
||
arp_notes.append({
|
||
'frequency': freq,
|
||
'duration': arp_duration,
|
||
'start_time': current_time + i * arp_duration,
|
||
'velocity': 0.5 + np.random.random() * 0.3,
|
||
'type': 'arpeggio'
|
||
})
|
||
chords.extend(arp_notes)
|
||
else:
|
||
# 生成和弦
|
||
velocity_range = params.get('dynamic_range', (0.5, 0.9))
|
||
velocity = velocity_range[0] + np.random.random() * (velocity_range[1] - velocity_range[0])
|
||
|
||
chords.append({
|
||
'notes': chord_notes,
|
||
'frequencies': frequencies,
|
||
'duration': measure_duration,
|
||
'start_time': current_time,
|
||
'velocity': velocity,
|
||
'type': 'chord'
|
||
})
|
||
|
||
current_time += measure_duration
|
||
|
||
return {
|
||
'type': 'harmony',
|
||
'chords': chords,
|
||
'progression': chord_progression,
|
||
'bpm': params['bpm'],
|
||
'key': params['key'],
|
||
'scale': params['scale'],
|
||
'instrument': params['instrument']
|
||
}
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生成和声片段失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return {
|
||
'type': 'harmony',
|
||
'chords': [],
|
||
'progression': [],
|
||
'bpm': params['bpm'],
|
||
'key': params['key'],
|
||
'scale': params['scale'],
|
||
'instrument': params['instrument']
|
||
}
|
||
|
||
def _generate_rhythm_segment(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||
"""
|
||
生成节奏片段
|
||
|
||
Args:
|
||
params: 生成参数
|
||
|
||
Returns:
|
||
节奏片段数据
|
||
"""
|
||
try:
|
||
# 生成鼓组节奏
|
||
drums = ['kick', 'snare', 'hihat', 'crash', 'tom_low', 'tom_mid', 'tom_high']
|
||
drum_frequencies = {
|
||
'kick': 60.0,
|
||
'snare': 200.0,
|
||
'hihat': 8000.0,
|
||
'crash': 4000.0,
|
||
'tom_low': 120.0,
|
||
'tom_mid': 250.0,
|
||
'tom_high': 500.0
|
||
}
|
||
|
||
beats_per_measure = 4
|
||
total_beats = params['length'] * beats_per_measure
|
||
|
||
hits = []
|
||
current_time = 0.0
|
||
beat_duration = 60.0 / params['bpm']
|
||
|
||
# 基本节奏模式
|
||
basic_pattern = {
|
||
'kick': [0, 2], # 强拍
|
||
'snare': [1, 3], # 弱拍
|
||
'hihat': [0, 1, 2, 3] # 每拍
|
||
}
|
||
|
||
# 根据复杂度调整模式
|
||
if params['complexity'] > 0.7:
|
||
# 复杂节奏
|
||
basic_pattern['kick'] = [0, 0.5, 2, 2.5, 3.5]
|
||
basic_pattern['snare'] = [1, 3]
|
||
basic_pattern['hihat'] = [0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5]
|
||
basic_pattern['tom_low'] = [1.5]
|
||
basic_pattern['tom_mid'] = [2.5]
|
||
basic_pattern['tom_high'] = [3.5]
|
||
elif params['complexity'] > 0.4:
|
||
# 中等复杂度
|
||
basic_pattern['kick'] = [0, 2]
|
||
basic_pattern['snare'] = [1, 3]
|
||
basic_pattern['hihat'] = [0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5]
|
||
|
||
# 生成节奏
|
||
for measure in range(params['length']):
|
||
measure_start = measure * beats_per_measure
|
||
|
||
for drum_type, pattern in basic_pattern.items():
|
||
if drum_type in drum_frequencies:
|
||
for beat_offset in pattern:
|
||
hit_time = (measure_start + beat_offset) * beat_duration
|
||
velocity_range = params.get('dynamic_range', (0.6, 1.0))
|
||
velocity = velocity_range[0] + np.random.random() * (velocity_range[1] - velocity_range[0])
|
||
|
||
# 应用切分音效果
|
||
if params['syncopation'] > 0.5 and np.random.random() < 0.3:
|
||
# 偶尔偏移时间
|
||
hit_time += (np.random.random() - 0.5) * beat_duration * 0.2
|
||
|
||
hits.append({
|
||
'drum': drum_type,
|
||
'frequency': drum_frequencies[drum_type],
|
||
'time': hit_time,
|
||
'velocity': velocity,
|
||
'duration': beat_duration * 0.1 # 短促的打击乐
|
||
})
|
||
|
||
# 添加填充(fills)
|
||
if np.random.random() < 0.3: # 30%概率添加填充
|
||
fill_start = (params['length'] - 1) * beats_per_measure * beat_duration
|
||
for i in range(4): # 4个填充音符
|
||
drum_type = np.random.choice(['snare', 'tom_low', 'tom_mid', 'tom_high'])
|
||
hit_time = fill_start + i * beat_duration * 0.25
|
||
hits.append({
|
||
'drum': drum_type,
|
||
'frequency': drum_frequencies[drum_type],
|
||
'time': hit_time,
|
||
'velocity': 0.8 + np.random.random() * 0.2,
|
||
'duration': beat_duration * 0.05
|
||
})
|
||
|
||
return {
|
||
'type': 'rhythm',
|
||
'hits': hits,
|
||
'bpm': params['bpm'],
|
||
'pattern_complexity': params['complexity']
|
||
}
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生成节奏片段失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return {
|
||
'type': 'rhythm',
|
||
'hits': [],
|
||
'bpm': params['bpm'],
|
||
'pattern_complexity': params['complexity']
|
||
}
|
||
|
||
def _generate_bass_segment(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||
"""
|
||
生成贝斯片段
|
||
|
||
Args:
|
||
params: 生成参数
|
||
|
||
Returns:
|
||
贝斯片段数据
|
||
"""
|
||
try:
|
||
# 获取低音音阶音符
|
||
scale_notes = self._get_scale_notes(params['key'], params['scale'], params['octave'] - 1)
|
||
|
||
# 计算片段长度
|
||
beats_per_measure = 4
|
||
total_beats = params['length'] * beats_per_measure
|
||
beat_duration = 60.0 / params['bpm']
|
||
|
||
# 生成贝斯线
|
||
notes = []
|
||
current_time = 0.0
|
||
beat_count = 0
|
||
|
||
# 使用和弦进行作为贝斯线基础
|
||
progressions = self.chord_progressions.get(params['scale'], self.chord_progressions['major'])
|
||
chord_progression = np.random.choice(progressions)
|
||
|
||
while current_time < total_beats * beat_duration and beat_count < total_beats:
|
||
# 确定当前小节和和弦
|
||
measure = beat_count // beats_per_measure
|
||
chord_index = measure % len(chord_progression)
|
||
roman_numeral = chord_progression[chord_index]
|
||
|
||
# 获取和弦根音作为贝斯音符
|
||
scale_degree = self.roman_numerals.get(roman_numeral, 0)
|
||
bass_note_index = scale_degree % len(scale_notes)
|
||
bass_note = scale_notes[bass_note_index]
|
||
bass_freq = self.note_frequencies.get(bass_note, 65.41) # C2
|
||
|
||
# 确定音符时值
|
||
if np.random.random() < params['complexity'] * 0.5:
|
||
# 复杂节奏:使用八分音符
|
||
note_duration = beat_duration * 0.5
|
||
subdivisions = 2
|
||
else:
|
||
# 简单节奏:使用四分音符
|
||
note_duration = beat_duration
|
||
subdivisions = 1
|
||
|
||
# 生成音符
|
||
for sub in range(subdivisions):
|
||
if beat_count + sub * 0.5 < total_beats:
|
||
note_time = current_time + sub * note_duration
|
||
|
||
# 应用滑音和装饰
|
||
if params['complexity'] > 0.6 and np.random.random() < 0.2:
|
||
# 添加经过音
|
||
if bass_note_index < len(scale_notes) - 1:
|
||
passing_note_index = bass_note_index + 1
|
||
passing_note = scale_notes[passing_note_index]
|
||
passing_freq = self.note_frequencies.get(passing_note, 65.41)
|
||
|
||
notes.append({
|
||
'frequency': passing_freq,
|
||
'duration': note_duration * 0.3,
|
||
'start_time': note_time,
|
||
'velocity': 0.4 + np.random.random() * 0.2,
|
||
'type': 'passing'
|
||
})
|
||
note_time += note_duration * 0.3
|
||
|
||
# 主要贝斯音符
|
||
velocity_range = params.get('dynamic_range', (0.7, 1.0))
|
||
velocity = velocity_range[0] + np.random.random() * (velocity_range[1] - velocity_range[0])
|
||
|
||
notes.append({
|
||
'frequency': bass_freq,
|
||
'duration': note_duration * (1 - 0.3 * (sub > 0)), # 经过音后缩短
|
||
'start_time': note_time,
|
||
'velocity': velocity,
|
||
'type': 'bass'
|
||
})
|
||
|
||
current_time += beat_duration
|
||
beat_count += 1
|
||
|
||
return {
|
||
'type': 'bass',
|
||
'notes': notes,
|
||
'bpm': params['bpm'],
|
||
'key': params['key'],
|
||
'scale': params['scale'],
|
||
'instrument': params['instrument'],
|
||
'progression': chord_progression
|
||
}
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生成贝斯片段失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return {
|
||
'type': 'bass',
|
||
'notes': [],
|
||
'bpm': params['bpm'],
|
||
'key': params['key'],
|
||
'scale': params['scale'],
|
||
'instrument': params['instrument'],
|
||
'progression': []
|
||
}
|
||
|
||
def _get_scale_notes(self, key: str, scale: str, octave: int) -> List[str]:
|
||
"""
|
||
获取音阶中的音符
|
||
|
||
Args:
|
||
key: 调号
|
||
scale: 音阶类型
|
||
octave: 八度
|
||
|
||
Returns:
|
||
音符列表
|
||
"""
|
||
try:
|
||
# 找到调号在音符列表中的索引
|
||
key_index = self.note_names.index(key.upper())
|
||
|
||
# 获取音阶模式
|
||
pattern = self.scale_patterns[scale]
|
||
|
||
# 生成音符
|
||
notes = []
|
||
for semitone in pattern:
|
||
note_index = (key_index + semitone) % 12
|
||
note_octave = octave + (key_index + semitone) // 12
|
||
note_name = f"{self.note_names[note_index]}{note_octave}"
|
||
notes.append(note_name)
|
||
|
||
return notes
|
||
|
||
except Exception as e:
|
||
print(f"✗ 获取音阶音符失败: {e}")
|
||
# 返回默认C大调音阶
|
||
return [f"{note}{octave}" for note in ['C', 'D', 'E', 'F', 'G', 'A', 'B']]
|
||
|
||
def _get_chord_notes(self, root: str, chord_type: str, octave: int) -> List[str]:
|
||
"""
|
||
获取和弦音符
|
||
|
||
Args:
|
||
root: 根音
|
||
chord_type: 和弦类型
|
||
octave: 八度
|
||
|
||
Returns:
|
||
和弦音符列表
|
||
"""
|
||
try:
|
||
# 找到根音在音符列表中的索引
|
||
root_index = self.note_names.index(root[:-1]) # 去掉八度数字
|
||
root_octave = int(root[-1])
|
||
|
||
# 获取和弦模式
|
||
pattern = self.chord_patterns.get(chord_type, self.chord_patterns['major'])
|
||
|
||
# 生成和弦音符
|
||
notes = []
|
||
for i, semitone in enumerate(pattern):
|
||
note_index = (root_index + semitone) % 12
|
||
note_octave = root_octave + (root_index + semitone) // 12
|
||
# 保证和弦音符在合理的八度范围内
|
||
if i > 0 and note_octave <= root_octave:
|
||
note_octave += 1
|
||
note_name = f"{self.note_names[note_index]}{note_octave}"
|
||
notes.append(note_name)
|
||
|
||
return notes
|
||
|
||
except Exception as e:
|
||
print(f"✗ 获取和弦音符失败: {e}")
|
||
# 返回默认三和弦
|
||
return [root, f"{root[:-1]}{int(root[-1])+1}", f"{root[:-1]}{int(root[-1])+2}"]
|
||
|
||
def _choose_note(self, scale_notes: List[str], complexity: float, variation: float) -> int:
|
||
"""
|
||
选择音符
|
||
|
||
Args:
|
||
scale_notes: 音阶音符列表
|
||
complexity: 复杂度 (0.0-1.0)
|
||
variation: 变化度 (0.0-1.0)
|
||
|
||
Returns:
|
||
音符索引
|
||
"""
|
||
# 基于复杂度和变化度选择音符
|
||
if np.random.random() < complexity:
|
||
# 复杂选择:可以跳到任何音符
|
||
return np.random.randint(0, len(scale_notes))
|
||
else:
|
||
# 简单选择:倾向于选择相邻音符
|
||
if len(scale_notes) > 1:
|
||
current_index = np.random.randint(0, len(scale_notes))
|
||
if np.random.random() < 0.7: # 70%概率选择相邻音符
|
||
offset = np.random.choice([-1, 1])
|
||
new_index = current_index + offset
|
||
if 0 <= new_index < len(scale_notes):
|
||
return new_index
|
||
return current_index
|
||
else:
|
||
return 0
|
||
|
||
def _apply_arpeggiation(self, notes: List[Dict[str, Any]], syncopation: float) -> List[Dict[str, Any]]:
|
||
"""
|
||
应用琶音效果
|
||
|
||
Args:
|
||
notes: 音符列表
|
||
syncopation: 切分音程度
|
||
|
||
Returns:
|
||
处理后的音符列表
|
||
"""
|
||
try:
|
||
arpeggiated_notes = []
|
||
for note in notes:
|
||
if note['type'] == 'chord':
|
||
# 将和弦分解为琶音
|
||
frequencies = note.get('frequencies', [])
|
||
duration = note['duration'] / len(frequencies)
|
||
for i, freq in enumerate(frequencies):
|
||
arpeggiated_notes.append({
|
||
'frequency': freq,
|
||
'duration': duration,
|
||
'start_time': note['start_time'] + i * duration,
|
||
'velocity': note['velocity'] * (0.8 + 0.2 * (i % 2)),
|
||
'type': 'arpeggio'
|
||
})
|
||
else:
|
||
arpeggiated_notes.append(note)
|
||
return arpeggiated_notes
|
||
except Exception as e:
|
||
print(f"✗ 应用琶音效果失败: {e}")
|
||
return notes
|
||
|
||
def update(self, dt: float):
|
||
"""
|
||
更新作曲引擎状态
|
||
|
||
Args:
|
||
dt: 时间增量
|
||
"""
|
||
update_start_time = time.time()
|
||
|
||
# 更新活动生成器
|
||
for generator_name, generator in self.active_generators.items():
|
||
# 更新播放位置
|
||
generator['playback_position'] += dt
|
||
|
||
# 检查是否需要生成新片段
|
||
if self._should_generate_new_segment(generator):
|
||
# 异步请求生成新片段
|
||
if generator['queue']:
|
||
try:
|
||
generator['queue'].put({'type': 'generate'}, block=False)
|
||
except queue.Full:
|
||
pass # 队列满时跳过
|
||
|
||
# 更新待处理生成器请求
|
||
self._process_generator_queues()
|
||
|
||
# 更新性能统计
|
||
self.stats['composition_time'] += (time.time() - update_start_time)
|
||
|
||
def _should_generate_new_segment(self, generator: Dict[str, Any]) -> bool:
|
||
"""
|
||
判断是否需要生成新片段
|
||
|
||
Args:
|
||
generator: 生成器数据
|
||
|
||
Returns:
|
||
是否需要生成
|
||
"""
|
||
try:
|
||
# 检查生成器状态
|
||
if generator['state'] != 'active':
|
||
return False
|
||
|
||
# 检查是否正在生成
|
||
if generator['is_generating']:
|
||
return False
|
||
|
||
# 检查时间条件
|
||
current_time = time.time()
|
||
time_since_last_generation = current_time - generator['last_generated_time']
|
||
|
||
# 如果没有片段或距离上次生成时间较长,则需要生成
|
||
if len(generator['segments']) < 2 or time_since_last_generation > 5.0:
|
||
return True
|
||
|
||
return False
|
||
except Exception as e:
|
||
print(f"✗ 检查生成条件失败: {e}")
|
||
return False
|
||
|
||
def _process_generator_queues(self):
|
||
"""处理生成器队列"""
|
||
# 这里可以处理一些队列相关的逻辑
|
||
pass
|
||
|
||
def cleanup(self):
|
||
"""清理所有资源"""
|
||
# 停止所有生成器线程
|
||
for generator_name, generator in self.generators.items():
|
||
if generator['queue']:
|
||
try:
|
||
generator['queue'].put({'type': 'stop'}, block=False)
|
||
except queue.Full:
|
||
pass
|
||
|
||
# 清理数据结构
|
||
self.generators.clear()
|
||
self.active_generators.clear()
|
||
self.composed_segments.clear()
|
||
self.generator_threads.clear()
|
||
self.generator_queues.clear()
|
||
|
||
self.stats = {
|
||
'generators_created': 0,
|
||
'generators_active': 0,
|
||
'segments_composed': 0,
|
||
'composition_time': 0.0,
|
||
'memory_usage': 0,
|
||
'failed_compositions': 0
|
||
}
|
||
|
||
print("✓ 作曲引擎资源已清理")
|
||
|
||
def get_stats(self) -> Dict[str, int]:
|
||
"""
|
||
获取统计信息
|
||
|
||
Returns:
|
||
统计信息字典
|
||
"""
|
||
return self.stats.copy()
|
||
|
||
def get_active_generators(self) -> List[str]:
|
||
"""
|
||
获取活动生成器列表
|
||
|
||
Returns:
|
||
活动生成器名称列表
|
||
"""
|
||
return list(self.active_generators.keys())
|
||
|
||
def get_generator_status(self, generator_name: str) -> Dict[str, Any]:
|
||
"""
|
||
获取生成器状态
|
||
|
||
Args:
|
||
generator_name: 生成器名称
|
||
|
||
Returns:
|
||
生成器状态字典
|
||
"""
|
||
if generator_name in self.active_generators:
|
||
generator = self.active_generators[generator_name]
|
||
return {
|
||
'name': generator_name,
|
||
'state': generator['state'],
|
||
'type': generator['params']['type'],
|
||
'segments': len(generator['segments']),
|
||
'active': True,
|
||
'playback_position': generator['playback_position']
|
||
}
|
||
elif generator_name in self.generators:
|
||
generator = self.generators[generator_name]
|
||
return {
|
||
'name': generator_name,
|
||
'state': generator['state'],
|
||
'type': generator['params']['type'],
|
||
'segments': len(generator['segments']),
|
||
'active': False,
|
||
'playback_position': generator['playback_position']
|
||
}
|
||
else:
|
||
return {
|
||
'name': generator_name,
|
||
'state': 'unknown'
|
||
}
|
||
|
||
def set_master_key(self, key: str):
|
||
"""
|
||
设置主调
|
||
|
||
Args:
|
||
key: 调号
|
||
"""
|
||
if key.upper() in self.note_names:
|
||
self.master_key = key.upper()
|
||
print(f"✓ 主调设置为: {self.master_key}")
|
||
else:
|
||
print(f"✗ 无效的调号: {key}")
|
||
|
||
def set_master_scale(self, scale: str):
|
||
"""
|
||
设置主音阶
|
||
|
||
Args:
|
||
scale: 音阶类型
|
||
"""
|
||
if scale in self.scale_patterns:
|
||
self.master_scale = scale
|
||
print(f"✓ 主音阶设置为: {self.master_scale}")
|
||
else:
|
||
print(f"✗ 无效的音阶: {scale}")
|
||
|
||
def set_master_bpm(self, bpm: float):
|
||
"""
|
||
设置主BPM
|
||
|
||
Args:
|
||
bpm: 每分钟节拍数
|
||
"""
|
||
self.master_bpm = max(30, min(300, bpm)) # 限制在合理范围内
|
||
print(f"✓ 主BPM设置为: {self.master_bpm}")
|
||
|
||
def generate_theme(self, theme_name: str, theme_params: Dict[str, Any]) -> bool:
|
||
"""
|
||
生成完整音乐主题
|
||
|
||
Args:
|
||
theme_name: 主题名称
|
||
theme_params: 主题参数
|
||
|
||
Returns:
|
||
是否生成成功
|
||
"""
|
||
try:
|
||
# 创建多个生成器来组成完整主题
|
||
generators_to_create = theme_params.get('generators', ['melody', 'harmony', 'rhythm'])
|
||
|
||
for generator_type in generators_to_create:
|
||
generator_name = f"{theme_name}_{generator_type}"
|
||
generator_params = {
|
||
'type': generator_type,
|
||
'key': theme_params.get('key', self.master_key),
|
||
'scale': theme_params.get('scale', self.master_scale),
|
||
'bpm': theme_params.get('bpm', self.master_bpm),
|
||
'length': theme_params.get('length', 8),
|
||
'complexity': theme_params.get('complexity', 0.5),
|
||
'variation': theme_params.get('variation', 0.3),
|
||
'instrument': theme_params.get('instrument_' + generator_type, 'piano'),
|
||
'arpeggiate': theme_params.get('arpeggiate', False),
|
||
'syncopation': theme_params.get('syncopation', 0.0)
|
||
}
|
||
|
||
self.create_generator(generator_name, generator_params)
|
||
|
||
print(f"✓ 音乐主题生成成功: {theme_name}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生成音乐主题失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def get_segment_data(self, segment_id: str) -> Dict[str, Any]:
|
||
"""
|
||
获取片段数据
|
||
|
||
Args:
|
||
segment_id: 片段ID
|
||
|
||
Returns:
|
||
片段数据
|
||
"""
|
||
return self.composed_segments.get(segment_id, {}).get('data', {})
|
||
|
||
def set_sample_rate(self, sample_rate: int):
|
||
"""
|
||
设置采样率
|
||
|
||
Args:
|
||
sample_rate: 采样率 (Hz)
|
||
"""
|
||
self.sample_rate = sample_rate
|
||
print(f"✓ 作曲引擎采样率设置为: {self.sample_rate} Hz")
|
||
|
||
def create_adaptive_generator(self, generator_name: str,
|
||
game_state_callback: Callable[[], Dict[str, Any]]) -> bool:
|
||
"""
|
||
创建自适应音乐生成器
|
||
|
||
Args:
|
||
generator_name: 生成器名称
|
||
game_state_callback: 游戏状态回调函数
|
||
|
||
Returns:
|
||
是否创建成功
|
||
"""
|
||
try:
|
||
# 创建自适应生成器参数
|
||
adaptive_params = {
|
||
'type': 'melody',
|
||
'key': self.master_key,
|
||
'scale': self.master_scale,
|
||
'bpm': self.master_bpm,
|
||
'octave': 4,
|
||
'complexity': 0.5,
|
||
'variation': 0.3,
|
||
'length': 8,
|
||
'instrument': 'piano',
|
||
'arpeggiate': False,
|
||
'syncopation': 0.0,
|
||
'adaptive': True,
|
||
'game_state_callback': game_state_callback
|
||
}
|
||
|
||
# 创建生成器
|
||
generator = {
|
||
'name': generator_name,
|
||
'params': adaptive_params,
|
||
'state': 'created',
|
||
'segments': [],
|
||
'current_segment': 0,
|
||
'playback_position': 0.0,
|
||
'last_generated_time': 0.0,
|
||
'is_generating': False,
|
||
'thread': None,
|
||
'queue': queue.Queue(),
|
||
'adaptive_callback': game_state_callback
|
||
}
|
||
|
||
self.generators[generator_name] = generator
|
||
self.generator_queues[generator_name] = generator['queue']
|
||
self.stats['generators_created'] += 1
|
||
|
||
print(f"✓ 自适应音乐生成器创建成功: {generator_name}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 创建自适应音乐生成器失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def set_generator_parameter(self, generator_name: str, param_name: str, param_value: Any) -> bool:
|
||
"""
|
||
设置生成器参数
|
||
|
||
Args:
|
||
generator_name: 生成器名称
|
||
param_name: 参数名称
|
||
param_value: 参数值
|
||
|
||
Returns:
|
||
是否设置成功
|
||
"""
|
||
try:
|
||
if generator_name not in self.generators:
|
||
print(f"✗ 生成器不存在: {generator_name}")
|
||
return False
|
||
|
||
generator = self.generators[generator_name]
|
||
|
||
# 更新参数
|
||
generator['params'][param_name] = param_value
|
||
print(f"✓ 生成器 {generator_name} 参数 {param_name} 设置为: {param_value}")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 设置生成器参数失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def get_generator_parameters(self, generator_name: str) -> Dict[str, Any]:
|
||
"""
|
||
获取生成器参数
|
||
|
||
Args:
|
||
generator_name: 生成器名称
|
||
|
||
Returns:
|
||
参数字典
|
||
"""
|
||
if generator_name in self.generators:
|
||
return self.generators[generator_name]['params'].copy()
|
||
return {}
|
||
|
||
def regenerate_segment(self, generator_name: str) -> bool:
|
||
"""
|
||
重新生成片段
|
||
|
||
Args:
|
||
generator_name: 生成器名称
|
||
|
||
Returns:
|
||
是否重新生成成功
|
||
"""
|
||
try:
|
||
if generator_name not in self.generators:
|
||
print(f"✗ 生成器不存在: {generator_name}")
|
||
return False
|
||
|
||
return self._generate_segment(generator_name)
|
||
|
||
except Exception as e:
|
||
print(f"✗ 重新生成片段失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def set_instrument_preset(self, instrument_name: str, preset: Dict[str, float]):
|
||
"""
|
||
设置乐器预设
|
||
|
||
Args:
|
||
instrument_name: 乐器名称
|
||
preset: 预设参数字典 (attack, decay, sustain, release)
|
||
"""
|
||
required_params = ['attack', 'decay', 'sustain', 'release']
|
||
for param in required_params:
|
||
if param not in preset:
|
||
print(f"✗ 乐器预设缺少必需参数: {param}")
|
||
return
|
||
|
||
self.instrument_presets[instrument_name] = preset.copy()
|
||
print(f"✓ 乐器预设 {instrument_name} 已更新")
|
||
|
||
def get_instrument_preset(self, instrument_name: str) -> Dict[str, float]:
|
||
"""
|
||
获取乐器预设
|
||
|
||
Args:
|
||
instrument_name: 乐器名称
|
||
|
||
Returns:
|
||
预设参数字典
|
||
"""
|
||
return self.instrument_presets.get(instrument_name, self.instrument_presets['piano']).copy()
|
||
|
||
def set_composition_setting(self, setting_name: str, value: Any):
|
||
"""
|
||
设置作曲参数
|
||
|
||
Args:
|
||
setting_name: 参数名称
|
||
value: 参数值
|
||
"""
|
||
self.composition_settings[setting_name] = value
|
||
print(f"✓ 作曲参数 {setting_name} 设置为: {value}")
|
||
|
||
def get_composition_setting(self, setting_name: str) -> Any:
|
||
"""
|
||
获取作曲参数
|
||
|
||
Args:
|
||
setting_name: 参数名称
|
||
|
||
Returns:
|
||
参数值
|
||
"""
|
||
return self.composition_settings.get(setting_name, None)
|
||
|
||
def generate_audio_buffer(self, generator_name: str, buffer_size: int = None) -> np.ndarray:
|
||
"""
|
||
生成音频缓冲区数据
|
||
|
||
Args:
|
||
generator_name: 生成器名称
|
||
buffer_size: 缓冲区大小
|
||
|
||
Returns:
|
||
音频数据数组
|
||
"""
|
||
try:
|
||
if buffer_size is None:
|
||
buffer_size = self.buffer_size
|
||
|
||
if generator_name not in self.active_generators:
|
||
return np.zeros(buffer_size)
|
||
|
||
generator = self.active_generators[generator_name]
|
||
if not generator['segments']:
|
||
return np.zeros(buffer_size)
|
||
|
||
# 获取当前片段
|
||
current_segment_id = generator['segments'][generator['current_segment'] % len(generator['segments'])]
|
||
segment_data = self.get_segment_data(current_segment_id)
|
||
|
||
# 生成音频数据
|
||
audio_buffer = np.zeros(buffer_size)
|
||
t = np.linspace(0, buffer_size / self.sample_rate, buffer_size, False)
|
||
|
||
if segment_data.get('type') == 'melody':
|
||
notes = segment_data.get('notes', [])
|
||
for note in notes:
|
||
freq = note.get('frequency', 440.0)
|
||
duration = note.get('duration', 1.0)
|
||
start_time = note.get('start_time', 0.0)
|
||
velocity = note.get('velocity', 0.7)
|
||
|
||
# 计算样本索引
|
||
start_sample = int(start_time * self.sample_rate)
|
||
end_sample = min(buffer_size, int((start_time + duration) * self.sample_rate))
|
||
|
||
if start_sample < buffer_size and end_sample > start_sample:
|
||
# 生成正弦波
|
||
note_samples = end_sample - start_sample
|
||
note_t = np.linspace(0, duration, note_samples, False)
|
||
wave = velocity * np.sin(2 * np.pi * freq * note_t)
|
||
|
||
# 应用包络
|
||
envelope = self._create_envelope(note_samples, 'piano')
|
||
wave *= envelope
|
||
|
||
audio_buffer[start_sample:end_sample] += wave
|
||
|
||
elif segment_data.get('type') == 'rhythm':
|
||
hits = segment_data.get('hits', [])
|
||
for hit in hits:
|
||
freq = hit.get('frequency', 200.0)
|
||
hit_time = hit.get('time', 0.0)
|
||
duration = hit.get('duration', 0.1)
|
||
velocity = hit.get('velocity', 0.8)
|
||
|
||
# 计算样本索引
|
||
start_sample = int(hit_time * self.sample_rate)
|
||
end_sample = min(buffer_size, int((hit_time + duration) * self.sample_rate))
|
||
|
||
if start_sample < buffer_size and end_sample > start_sample:
|
||
# 生成打击乐声音
|
||
hit_samples = end_sample - start_sample
|
||
hit_t = np.linspace(0, duration, hit_samples, False)
|
||
|
||
# 生成噪声为基础的打击乐
|
||
noise = np.random.random(hit_samples) * 2 - 1
|
||
# 应用滤波器模拟不同打击乐器
|
||
b, a = signal.butter(4, freq / (self.sample_rate / 2), 'low')
|
||
filtered_noise = signal.lfilter(b, a, noise)
|
||
|
||
# 应用包络
|
||
envelope = self._create_envelope(hit_samples, 'percussion')
|
||
wave = velocity * filtered_noise * envelope
|
||
|
||
audio_buffer[start_sample:end_sample] += wave
|
||
|
||
# 限制幅度防止削波
|
||
audio_buffer = np.clip(audio_buffer, -1.0, 1.0)
|
||
|
||
return audio_buffer
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生成音频缓冲区失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return np.zeros(buffer_size if buffer_size else self.buffer_size)
|
||
|
||
def _create_envelope(self, sample_count: int, instrument_type: str) -> np.ndarray:
|
||
"""
|
||
创建包络
|
||
|
||
Args:
|
||
sample_count: 样本数量
|
||
instrument_type: 乐器类型
|
||
|
||
Returns:
|
||
包络数组
|
||
"""
|
||
try:
|
||
preset = self.instrument_presets.get(instrument_type, self.instrument_presets['piano'])
|
||
|
||
attack_samples = int(preset['attack'] * self.sample_rate)
|
||
decay_samples = int(preset['decay'] * self.sample_rate)
|
||
release_samples = int(preset['release'] * self.sample_rate)
|
||
sustain_level = preset['sustain']
|
||
|
||
envelope = np.ones(sample_count)
|
||
|
||
# 攻击阶段
|
||
if attack_samples > 0 and attack_samples < sample_count:
|
||
envelope[:attack_samples] = np.linspace(0, 1, attack_samples)
|
||
|
||
# 衰减阶段
|
||
decay_start = attack_samples
|
||
decay_end = attack_samples + decay_samples
|
||
if decay_end < sample_count:
|
||
envelope[decay_start:decay_end] = np.linspace(1, sustain_level, decay_samples)
|
||
elif decay_start < sample_count:
|
||
envelope[decay_start:] = np.linspace(1, sustain_level, sample_count - decay_start)
|
||
|
||
# 释放阶段(如果空间足够)
|
||
release_start = max(0, sample_count - release_samples)
|
||
if release_start < sample_count:
|
||
envelope[release_start:] = np.linspace(sustain_level, 0, sample_count - release_start)
|
||
|
||
return envelope
|
||
|
||
except Exception as e:
|
||
print(f"✗ 创建包络失败: {e}")
|
||
return np.ones(sample_count)
|
||
|
||
def set_max_polyphony(self, max_voices: int):
|
||
"""
|
||
设置最大复调数
|
||
|
||
Args:
|
||
max_voices: 最大同时发声数
|
||
"""
|
||
self.max_polyphony = max(1, max_voices)
|
||
print(f"✓ 最大复调数设置为: {self.max_polyphony}")
|
||
|
||
def get_active_segment(self, generator_name: str) -> str:
|
||
"""
|
||
获取生成器当前活动片段
|
||
|
||
Args:
|
||
generator_name: 生成器名称
|
||
|
||
Returns:
|
||
片段ID
|
||
"""
|
||
if generator_name in self.active_generators:
|
||
generator = self.active_generators[generator_name]
|
||
if generator['segments']:
|
||
return generator['segments'][generator['current_segment'] % len(generator['segments'])]
|
||
return "" |