Implement FASE 3, 4, 6 - 15 new MCP tools, 76/110 tasks complete

FASE 3 - Human Feel & Dynamics (10/11 tasks):
- apply_clip_fades() - T041: Fade automation per section
- write_volume_automation() - T042: Curves (linear, exp, s_curve, punch)
- apply_sidechain_pump() - T045: Sidechain by intensity/style
- inject_pattern_fills() - T048: Snare rolls, fills by density
- humanize_set() - T050: Timing + velocity + groove automation

FASE 4 - Key Compatibility & Tonal (9/12 tasks):
- audio_key_compatibility.py: Full KEY_COMPATIBILITY_MATRIX
- analyze_key_compatibility() - T053: Harmonic compatibility scoring
- suggest_key_change() - T054: Circle of fifths modulation
- validate_sample_key() - T055: Sample key validation
- analyze_spectral_fit() - T057/T062: Spectral role matching

FASE 6 - Mastering & QA (8/13 tasks):
- calibrate_gain_staging() - T079: Auto gain by bus targets
- run_mix_quality_check() - T085: LUFS, peaks, L/R balance
- export_stem_mixdown() - T087: 24-bit/44.1kHz stem export

New files:
- audio_key_compatibility.py (T052)
- bus_routing_fix.py (T101-T104)
- validation_system_fix.py (T105-T106)

Total: 76/110 tasks (69%), 71 MCP tools exposed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
renato97
2026-03-29 00:59:24 -03:00
parent ed6f75c49f
commit 4332ff65da
24 changed files with 6586 additions and 38 deletions

View File

@@ -2306,6 +2306,106 @@ class StyleConfig:
complexity: str # simple, moderate, complex
class HumanFeelEngine:
"""
T040-T050: Engine de humanizacion y dinamica.
Aplica variaciones de timing, velocity y groove a patrones MIDI.
"""
def __init__(self, seed: int = 42):
self.rng = random.Random(seed)
self._groove_templates = {
'straight': {'swing': 0.0, 'humanize': 0.0},
'shuffle': {'swing': 0.33, 'humanize': 0.02},
'triplet': {'swing': 0.66, 'humanize': 0.03},
'latin': {'swing': 0.25, 'humanize': 0.04},
}
def apply_timing_variation(self, notes: List[Dict], amount_ms: float = 5.0) -> List[Dict]:
"""T040: Micro-offsets de timing (-5ms a +5ms)."""
result = []
for note in notes:
offset = self.rng.uniform(-amount_ms, amount_ms) / 1000.0 # Convert to seconds
new_note = dict(note)
new_note['start'] = note.get('start', 0) + offset
result.append(new_note)
return result
def apply_velocity_humanize(self, notes: List[Dict], variance: float = 0.05) -> List[Dict]:
"""T041: Humanizacion de velocity (+-5% variacion)."""
result = []
for note in notes:
vel = note.get('velocity', 100)
variation = self.rng.uniform(-variance, variance)
new_vel = int(vel * (1 + variation))
new_vel = max(1, min(127, new_vel)) # Clamp to MIDI range
new_note = dict(note)
new_note['velocity'] = new_vel
result.append(new_note)
return result
def apply_note_skip_probability(self, notes: List[Dict], prob: float = 0.02) -> List[Dict]:
"""T042: Probabilidad de skip nota (2% ghost notes)."""
result = []
for note in notes:
if self.rng.random() > prob: # Keep note with probability (1-prob)
result.append(note)
return result
def apply_groove(self, notes: List[Dict], style: str = 'shuffle', amount: float = 0.5) -> List[Dict]:
"""T044-T046: Aplica groove template."""
template = self._groove_templates.get(style, self._groove_templates['straight'])
swing = template['swing'] * amount
result = []
for note in notes:
start = note.get('start', 0)
# Apply swing to off-beat notes
beat_pos = start % 1.0 # Position within beat
if 0.4 < beat_pos < 0.6: # Off-beat
delay = swing * 0.1 # Max 100ms delay
new_note = dict(note)
new_note['start'] = start + delay
result.append(new_note)
else:
result.append(note)
return result
def apply_section_dynamics(self, notes: List[Dict], section: str) -> List[Dict]:
"""T047-T050: Dinamica por seccion (intro 70%, drop 100%, etc)."""
section_scales = {
'intro': 0.70,
'build': 0.85,
'drop': 1.00,
'break': 0.75,
'outro': 0.60,
}
scale = section_scales.get(section.lower(), 1.0)
result = []
for note in notes:
vel = note.get('velocity', 100)
new_vel = int(vel * scale)
new_vel = max(1, min(127, new_vel))
new_note = dict(note)
new_note['velocity'] = new_vel
result.append(new_note)
return result
def process_notes(self, notes: List[Dict], section: str = 'drop',
humanize: bool = True, groove_style: str = 'shuffle') -> List[Dict]:
"""Procesamiento completo con todos los efectos."""
result = list(notes)
if humanize:
result = self.apply_timing_variation(result)
result = self.apply_velocity_humanize(result)
result = self.apply_note_skip_probability(result)
result = self.apply_groove(result, groove_style)
result = self.apply_section_dynamics(result, section)
return result
class SongGenerator:
"""Generador de configuraciones y patrones musicales"""
@@ -4936,7 +5036,8 @@ class SongGenerator:
}
def generate_config(self, genre: str, style: str = "", bpm: float = 0,
key: str = "", structure: str = "standard") -> Dict[str, Any]:
key: str = "", structure: str = "standard",
palette: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
"""
Genera una configuración completa de track
@@ -5013,6 +5114,7 @@ class SongGenerator:
'buses': self._build_mix_bus_blueprint(profile, genre, style, reference_resolution),
'returns': self._build_return_blueprint(profile, genre, style, reference_resolution),
'master': self._build_master_blueprint(profile, genre, style, reference_resolution),
'palette': palette or {},
'tracks': [],
}