Files
ableton-mcp-ai/docs/GRANULAR_SPRINT_PART2_T101_T200.md

532 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GRANULAR SPRINT PART 2 — Tareas T101T200
## Enfoque: Pro DJ Level — Mastering, Transiciones, Melodía Generativa, Validación
> **Contexto obligatorio para GLM-5:**
> - Continúa desde GRANULAR_SPRINT_PART1_T001_T100.md. Haz T001T100 antes de empezar aquí.
> - MCP corre en WSL. Remote Script corre en Windows. No uses time.sleep() en abletonmcp_init.py.
> - Proyecto: `C:\Users\ren\Desktop\song Project\song.als` 95 BPM, Am, 16 tracks.
> - Si una herramienta MCP falla con "Arrangement clip was not materialized", documenta y continúa con audio patterns.
---
## BLOQUE F — GAIN STAGING PROFESIONAL (T101T120)
### T101 — Implementar LUFS normalizer en sample_selector.py
**Archivo:** `sample_selector.py`
**Acción:** Añade función `estimate_lufs_from_rms(rms: float) -> float` que convierte RMS (01) a LUFS aproximado:
```python
def estimate_lufs_from_rms(rms: float) -> float:
if rms <= 0: return -70.0
import math
return 20 * math.log10(max(rms, 1e-10)) - 3.0 # -3 dB offset para mono→stereo
```
Úsala en el scoring para dar bonus a samples que estén cerca de los targets de LUFS por rol.
### T102 — Definir LUFS targets por rol para reggaeton
**Archivo:** `sample_selector.py`
**Acción:** Añade dict de targets:
```python
REGGAETON_LUFS_TARGETS = {
'kick': -12.0, # golpe fuerte, kick dembow
'snare': -14.0,
'clap': -14.0,
'hat': -20.0, # hats suaves, percusión latina
'bass_loop': -10.0, # reese bajo muy prominente
'perc_loop': -16.0,
'top_loop': -18.0,
'synth_loop':-14.0, # armónico principal
}
```
### T103 — Añadir LUFS bonus al score de selección
**Archivo:** `sample_selector.py`
**Acción:** En el scorer, para reggaeton, calcula:
```python
lufs_estimated = estimate_lufs_from_rms(profile.rms_energy)
lufs_target = REGGAETON_LUFS_TARGETS.get(role, -16.0)
lufs_delta = abs(lufs_estimated - lufs_target)
lufs_bonus = max(0.0, 1.0 - lufs_delta / 12.0) # 0→1, 0 si difiere > 12 LUFS
score = score * 0.85 + lufs_bonus * 0.15
```
### T104 — Implementar side-chain kick→bass como metadata en manifest
**Archivo:** `server.py`
**Acción:** Al generar el manifest, añade sección `sidechain_config`:
```python
manifest['sidechain_config'] = {
'kick_to_bass': {'source': 'kick_track_index', 'amount_db': -8, 'attack_ms': 5, 'release_ms': 100},
'kick_to_pad': {'source': 'kick_track_index', 'amount_db': -4, 'attack_ms': 10, 'release_ms': 150},
}
```
Esto no activa el sidechain automáticamente (requiere Live API que no está disponible), pero lo documenta para el operador.
### T105 — Añadir MCP tool: get_gain_staging_report
**Archivo:** `server.py`
**Acción:** Tool que hace `get_track_info` para todos los tracks y reporta el volumen de cada uno vs el target por rol, sugiriendo ajustes:
```python
@mcp.tool()
async def get_gain_staging_report() -> str:
"""Reporta el gain staging actual de todos los tracks vs targets pro."""
...
```
### T106 — Implementar set_track_volume_by_role en abletonmcp_init.py
**Archivo:** `abletonmcp_init.py`
**Acción:** Añade método que, dado un track_index y su rol, setea el volumen al valor calibrado:
```python
ROLE_VOLUME_TARGETS = {
'kick': 0.85, 'clap': 0.80, 'hat': 0.65,
'bass': 0.90, 'perc': 0.75, 'synth': 0.78,
'top_loop': 0.70, 'harmony': 0.72,
}
```
### T107 — Verificar mono sub en AUDIO BASS (track 9)
**Acción:** `get_track_info(track_index=9)`. Si el track tiene ancho estéreo > 0, añade una nota en el manifest que el operador debe insertar un `Utility` plugin con Mono activado abajo de 200 Hz.
### T108T115 — Bus architecture verification
**T108:** Llama a `get_track_info` para los tracks 15 (DRUM BUS, BASS BUS, MUSIC BUS, VOCAL BUS, FX BUS). Reporta si tienen clips o dispositivos configurados.
**T109:** Si DRUM BUS no tiene dispositivos, registra en el manifest `bus_status: requires_manual_setup`.
**T110:** Verifica que los returns (AD) tienen al menos 1 dispositivo cada uno.
**T111:** Si A-MCP SPACE (return 0) no tiene reverb, documenta en el reporte qué falta.
**T112:** Añade MCP tool `audit_bus_architecture() -> str` que consolida T108T111.
**T113:** En `server.py`, después de crear buses en generate_song, ejecuta `audit_bus_architecture` y añade el resultado al manifest.
**T114:** En `coherence_analyzer.py`, añade `BusArchitectureMetric` que lee el manifest y verifica si los buses están presentes y configurados.
**T115:** Añade `bus_architecture` al `CoherenceReport.to_dict()`.
### T116T120 — Volume automation básica
**T116:** Añade MCP tool `set_section_volume_automation(track_index, section_start, section_end, volume_start, volume_end)` que crea una automación de volumen básica en el track.
**T117:** Para el track synth_loop (13), aplica: intro 0.5, build ramp 0.5→0.85, drop 0.85, break 0.4, drop_b 0.9, outro 0.4.
**T118:** Para top_loop (12): intro 0, build ramp 0→0.7, drop 0.75, break 0, drop_b 0.8, outro 0.
**T119:** Para perc_alt (11): intro 0.5, build 0.7, drop 0.85, break 0.3, drop_b 0.9.
**T120:** Documenta las automatizaciones aplicadas en el reporte de validación.
---
## BLOQUE G — GENERACIÓN MELÓDICA PROCEDURAL (T121T145)
### T121 — Crear módulo melody_generator.py
**Archivo nuevo:** `melody_generator.py`
**Propósito:** Generación de melodías MIDI procedurales para el track harmónico (track 15).
```python
"""melody_generator.py — Generación melódica procedural para reggaeton."""
from typing import List, Dict, Tuple, Optional
import random
# Escala Am natural (semitones relativos a A)
AM_SCALE = [0, 2, 3, 5, 7, 8, 10] # A B C D E F G
AM_ROOT = 57 # A3 en MIDI
def scale_notes(root_midi: int = AM_ROOT, octaves: int = 2) -> List[int]:
notes = []
for oct in range(octaves):
for interval in AM_SCALE:
notes.append(root_midi + interval + oct*12)
return notes
CHORD_TONES = {
'Am': [57, 60, 64], # A C E
'F': [53, 57, 60], # F A C
'G': [55, 59, 62], # G B D
'Em': [52, 55, 59], # E G B
'Dm': [50, 53, 57], # D F A
'C': [48, 52, 55], # C E G
}
@dataclass
class MidiNote:
pitch: int
start_beat: float
duration_beats: float
velocity: int = 80
def generate_chord_block(chord: str, start_beat: float, length_beats: float,
style: str = 'block') -> List[MidiNote]:
"""Genera un bloque de acordes MIDI."""
tones = CHORD_TONES.get(chord, CHORD_TONES['Am'])
notes = []
if style == 'block':
for tone in tones:
notes.append(MidiNote(pitch=tone, start_beat=start_beat,
duration_beats=length_beats*0.9, velocity=75))
elif style == 'arpegio_up':
step = length_beats / len(tones)
for i, tone in enumerate(tones):
notes.append(MidiNote(pitch=tone, start_beat=start_beat + i*step,
duration_beats=step*0.8, velocity=70+i*3))
elif style == 'arpegio_down':
step = length_beats / len(tones)
for i, tone in enumerate(reversed(tones)):
notes.append(MidiNote(pitch=tone, start_beat=start_beat + i*step,
duration_beats=step*0.8, velocity=80-i*3))
return notes
def generate_motif(scale: List[int], start_beat: float, bars: int = 2,
seed: int = 42) -> List[MidiNote]:
"""Genera un motivo melódico de 24 notas que se repite."""
rng = random.Random(seed)
notes = []
motif_notes = rng.choices(scale[:7], k=3) # 3 notas del motif
durations = [0.5, 1.0, 0.5]
for bar in range(bars):
pos = start_beat + bar * 4.0
for note, dur in zip(motif_notes, durations):
notes.append(MidiNote(pitch=note, start_beat=pos,
duration_beats=dur, velocity=rng.randint(70,90)))
pos += dur
return notes
def generate_reggaeton_harmony(bpm: float = 95.0, total_beats: float = 288.0) -> Dict[str, List[MidiNote]]:
"""Genera el mapa completo de armonía reggaeton para song.als."""
scale = scale_notes()
progression = [
(0, 32, 'Am', 'block'),
(32, 32, 'F', 'arpegio_up'),
(64, 32, 'G', 'block'),
(96, 32, 'Em', 'arpegio_down'),
(128, 16, 'Am', 'block'),
(144, 16, 'F', 'block'),
(160, 32, 'G', 'arpegio_up'),
(192, 32, 'Am', 'block'),
(224, 32, 'F', 'block'),
(256, 32, 'Am', 'block'),
]
result = {}
for start, length, chord, style in progression:
if start >= total_beats:
break
clip_key = f"clip_{start}"
result[clip_key] = {
'start_beat': start,
'length_beats': length,
'chord': chord,
'notes': generate_chord_block(chord, 0, length, style)
}
return result
```
### T122 — Integrar melody_generator con populate_harmony_track
**Archivo:** `server.py`
**Acción:** Modifica `populate_harmony_track` (T055) para usar `melody_generator.generate_reggaeton_harmony()` en vez de la progresión hardcodeada. Esto hace que la herramienta sea configurable por estilo.
### T123 — Añadir MCP tool: generate_motif_sequence
**Archivo:** `server.py`
```python
@mcp.tool()
async def generate_motif_sequence(track_index: int, start_beat: float,
bars: int = 4, seed: int = 42) -> str:
"""Genera y coloca un motivo melódico Am de 2-4 notas en el track MIDI."""
from melody_generator import generate_motif, scale_notes, AM_ROOT
scale = scale_notes(AM_ROOT)
notes = generate_motif(scale, 0, bars, seed)
r = ableton.send_command("create_arrangement_clip",
{"track_index": track_index, "start_time": start_beat, "length": bars*4})
if _is_error_response(r):
return f"[ERROR] {r.get('message','')}"
midi_notes = [{"pitch": n.pitch, "start_time": n.start_beat, "duration": n.duration_beats, "velocity": n.velocity} for n in notes]
ableton.send_command("add_notes_to_arrangement_clip",
{"track_index": track_index, "start_time": start_beat, "notes": midi_notes})
return f"OK: {len(notes)} notes placed at beat {start_beat}"
```
### T124T135 — Melodía y acordes avanzados
**T124:** Añade a `melody_generator.py` la función `generate_call_response(chord_a, chord_b, start, length)` que genera un patrón "pregunta" (arpegio ascendente) y "respuesta" (nota pedal).
**T125:** Añade `generate_bass_motif(style='dembow', root=A2, bars=2)` que retorna notas MIDI de bajo con el patrón tumbao/dembow.
**T126:** Expón `generate_bass_motif` como MCP tool `place_bass_pattern(track_index, start_beat, bars, style)`.
**T127:** Para la sección break (beats 128160), genera un motivo especial: notas largas y sostenidas (whole notes) de Am, F, para crear tensión antes del build_b.
**T128:** Para build_b (beats 160192), genera arpegios ascendentes acelerando: empieza con quarter notes, termina con 16th notes.
**T129:** Añade función `quantize_to_scale(pitch: int, scale: List[int]) -> int` que redondea un pitch al grado de escala más cercano.
**T130:** Añade función `add_passing_tones(notes: List[MidiNote], scale: List[int]) -> List[MidiNote]` que inserta notas de paso cromáticas entre saltos de más de 2 semitones.
**T131:** Valida que `melody_generator.py` compila: `python -m py_compile melody_generator.py`.
**T132:** Escribe tests unitarios `test_melody_generator.py`: test de generate_chord_block (4 casos), test de generate_motif (retorna > 0 notas), test de quantize_to_scale.
**T133:** Añade `melody_generator` al `AGENTS.md` en la sección de módulos.
**T134:** Documenta la API de `melody_generator.py` en `docs/MELODY_GENERATOR_README.md`.
**T135:** Ejecuta `populate_harmony_track` usando la nueva lógica y verifica con `get_track_info(15)` que `arrangement_clip_count >= 5`.
### T136T145 — Síntesis granular desde samples existentes
**T136:** En `spectral_engine.py`, añade método `extract_grain(path: str, position_ratio: float, grain_ms: int = 50) -> np.ndarray` que extrae un fragmento de audio de `grain_ms` milisegundos desde la posición dada.
**T137:** Añade método `stretch_grain(grain: np.ndarray, target_duration_ms: int, sr: int = 44100) -> np.ndarray` que usa interpolación para estirar o comprimir un grano de audio.
**T138:** Añade `create_granular_texture(path: str, duration_s: float, density: float = 0.5) -> np.ndarray` que construye una textura granular mezclando granos aleatorios del sample.
**T139:** Expón `create_granular_texture` como MCP tool `generate_granular_pad(source_path, output_path, duration_s, density)`.
**T140:** Si `librosa` no está disponible en WSL, estas herramientas deben retornar gracefully: `[INFO] librosa no disponible, granular synthesis deshabilitada`.
**T141:** Documenta limitaciones de la síntesis granular en la cabecera del módulo.
**T142:** Añade test: si librosa no está disponible, `create_granular_texture` retorna None sin crash.
**T143:** Si `generate_granular_pad` genera un archivo, copiarlo a la carpeta `libreria/reggaeton/textures/` y reportar el path resultante.
**T144:** Añade el nuevo sample generado al índice espectral (`build_spectral_index.py` puede re-ejecutarse sobre la carpeta textures/).
**T145:** Reporta en `docs/GRANULAR_SYNTHESIS_RESULTS.md` qué muestras fue posible generar y qué se usaron en el proyecto.
---
## BLOQUE H — TRANSICIONES Y FX AUTOMATION (T146T165)
### T146 — Implementar EQ automation tool
**Archivo:** `server.py`
**Acción:** Añade tool que configura parámetros de un EQ Three o AutoFilter de Ableton:
```python
@mcp.tool()
async def automate_filter_sweep(track_index: int, start_beat: float,
duration_beats: float, filter_type: str = 'highpass',
freq_start: float = 20.0, freq_end: float = 20000.0) -> str:
"""Automatiza un sweep de filtro en el track dado."""
# Encuentra el dispositivo AutoFilter en el track
# Setea los parámetros usando set_device_parameter
...
```
### T147 — Implementar crash on first beat of drop
**Archivo:** `arrangement_intelligence.py`
**Acción:** Método `place_crash_at_drop(drop_beat: float, crash_sample_path: str)` que usa `create_arrangement_audio_pattern` para colocar el crash exactamente en el beat especificado con duración 4 beats.
### T148 — Implementar snare roll antes del drop
**Archivo:** `arrangement_intelligence.py`
**Acción:** Método `place_snare_roll(pre_drop_beat: float, roll_bars: int = 2, snare_sample: str)` que genera una secuencia de clips de snare con densidad creciente: 1 snare por beat → 2 por beat → 4 por beat → 8 por beat en los últimos 2 bars.
### T149 — Implementar riser placement
**Archivo:** `arrangement_intelligence.py`
**Acción:** Método `place_riser(start_beat: float, drop_beat: float, riser_sample: str)` que coloca el riser desde `start_beat` hasta `drop_beat` con duración exacta.
### T150 — Añadir downlifter al final de drops
**Archivo:** `arrangement_intelligence.py`
**Acción:** Al final del drop_a (beat 128) y drop_b (beat 256), coloca un downlifter de 4 beats para marcar la salida del drop. Usa samples de la carpeta `libreria/reggaeton/fx/` si existen, o la carpeta más cercana.
### T151 — Añadir MCP tool: apply_transition_fx
**Archivo:** `server.py`
```python
@mcp.tool()
async def apply_transition_fx(section: str = 'drop_a') -> str:
"""Aplica FX de transición (crash, riser, snare roll) al section especificado."""
from arrangement_intelligence import place_crash_at_drop, place_snare_roll, place_riser
SECTION_BEATS = {'drop_a': 64, 'drop_b': 192}
drop_beat = SECTION_BEATS.get(section, 64)
results = []
# snare roll (8 beats antes)
results.append(place_snare_roll(drop_beat - 8, 2, snare_sample=_find_sample('snare')))
# riser (32 beats antes)
results.append(place_riser(drop_beat - 32, drop_beat, riser_sample=_find_sample('riser')))
# crash on beat 1
results.append(place_crash_at_drop(drop_beat, crash_sample=_find_sample('crash')))
return "\n".join(str(r) for r in results)
```
### T152T160 — Send automation para builds
**T152:** En el track 14 (AUDIO SYNTH PEAK), durante build_a (beats 3264), automatiza el send al A-MCP SPACE de 0% a 80% (crea tensión de espacio).
**T153:** En el track 12 (AUDIO TOP LOOP), durante build_b (beats 160192), automatiza el send al B-MCP ECHO de 0% a 60%.
**T154:** Corta abruptamente todos los sends (a 0%) exactamente en el beat 64 (drop_a) y beat 192 (drop_b).
**T155:** Para las automatizaciones de T152T154: en `abletonmcp_init.py`, añade método `_create_send_automation(track_index, send_index, beats, values)` que crea puntos de automatización en el send.
**T156:** Verifica que `_create_send_automation` funciona llamándola sobre track 14, send 0, con 3 puntos de automatización.
**T157:** Añade MCP tool `automate_send(track_index, send_index, beats_values)` que usa `_create_send_automation`.
**T158:** Compila `abletonmcp_init.py` y reinicia Ableton.
**T159:** Verifica con `get_track_info(14)` que el track sigue teniendo sus clips intactos tras el reinicio.
**T160:** Documenta las automatizaciones en `docs/FX_AUTOMATION_APPLIED.md`.
### T161T165 — Verbs/delays send routing
**T161:** Para A-MCP SPACE: el send level de perc_main (track 10) debe estar a 35% en el drop para dar espacio a las percusiones latinas.
**T162:** Para B-MCP ECHO: el send level de AUDIO SYNTH LOOP (track 13) debe estar a 20% en el drop (delay mínimo para no saturar).
**T163:** Para C-MCP HEAT: solo activar en el break (beats 128160) para el track de bass.
**T164:** Para D-MCP GLUE: siempre activo en todos los tracks de audio, nivel fijo en 40%.
**T165:** Documenta en `docs/SENDS_ROUTING_GUIDE.md` qué send va a qué track y cuánto.
---
## BLOQUE I — MASTERING Y QA (T166T180)
### T166 — Implementar loudness estimator en audio_mastering.py
**Archivo:** `audio_mastering.py`
**Acción:** Añade función `estimate_integrated_lufs(rms_array: List[float]) -> float` que dado un array de valores RMS por frame, estima el LUFS integrado usando la fórmula K-weighted simplificada.
### T167 — Añadir MCP tool: get_mix_lufs_estimate
**Archivo:** `server.py`
**Acción:** Tool que hace `get_track_info` para todos los tracks, extrae los valores de volumen, y calcula un estimado de LUFS total del mix usando los volumes de Ableton como proxy.
### T168 — Verificar headroom antes del master
**Acción:** Si el estimado de LUFS > -6 dBFS, emite warning `[MASTERING_WARNING] Mix demasiado alto, reducir gain antes de masterizar`.
### T169 — Implementar mastering preset 'reggaeton_club'
**Archivo:** `audio_mastering.py`
**Acción:** Añade a `MasteringPreset`:
```python
'reggaeton_club': MasteringPreset(
name='Reggaeton Club',
target_lufs=-9.0, # nivel club
target_true_peak=-0.3,
low_end_mono_hz=200.0,
multiband_compression=True,
tape_saturation=True,
high_shelf_boost_hz=10000,
high_shelf_boost_db=0.5,
style_notes='Kick prominente, bajo en mono, énfasis en graves, ideal para sistemas PA clubs'
)
```
### T170 — Documentar cadena de mastering en manifest
**Archivo:** `server.py`
**Acción:** Al finalizar la generación, añade al manifest `mastering_preset: 'reggaeton_club'` y `mastering_notes: [...]` con las recomendaciones del preset.
### T171T175 — QA automático post-generación
**T171:** En `server.py`, al completar `generate_song_async`, ejecuta automáticamente `audit_project_coherence()` y adjunta el resultado al manifest.
**T172:** Si el coherence score < 5.0, escribe un warning en el manifest `quality_warning: coherence_below_threshold`.
**T173:** Si `drum_coverage_ratio < 0.55`, ejecuta automáticamente `fill_arrangement_gaps()` como post-proceso.
**T174:** Si `harmonic_coverage_ratio < 0.60`, ejecuta automáticamente `populate_harmony_track()` como post-proceso.
**T175:** Documenta en el manifest qué post-procesos automáticos se ejecutaron.
### T176T180 — Validación final del proyecto
**T176:** Llama a `get_session_info`. Verifica BPM=95, tracks >= 16.
**T177:** Llama a `get_track_info(15)`. Verifica `arrangement_clip_count >= 5`.
**T178:** Llama a `audit_project_coherence()`. Verifica que el score > 4.0.
**T179:** Llama a `find_similar_samples` sobre el sample de perc principal vs la carpeta `libreria/reggaeton/perc loop/`. Verifica que retorna al menos 3 resultados.
**T180:** Escribe `docs/SPRINT_GRANULAR_PART2_VALIDATION.md` con los outputs exactos de T176T179.
---
## BLOQUE J — TESTING Y DOCUMENTACIÓN (T181T200)
### T181 — Crear test_spectral_integration.py
**Archivo:** `AbletonMCP_AI/AbletonMCP_AI/MCP_Server/tests/test_spectral_integration.py`
**Tests:**
- `test_spectral_engine_imports_without_crash`
- `test_similarity_identity` (mismo sample → similitud 1.0)
- `test_similarity_range` (resultado entre 0 y 1)
- `test_basic_analysis_fallback` (sin librosa → devuelve SpectralProfile básico)
### T182 — Crear test_melody_generator.py
**Tests:**
- `test_chord_block_am` → verifica que retorna notas en pitches Am
- `test_motif_seeds` → mismo seed → mismas notas
- `test_generate_reggaeton_harmony` → retorna al menos 5 clips
### T183 — Crear test_arrangement_intelligence.py
**Tests:**
- `test_structure_beats` → verifica que la estructura de T086 no se superpone
- `test_energy_curve` → drop tiene energía > break
- `test_mute_throw_beats` → los beats 6164 están correctamente marcados
### T184 — Crear test_gain_staging.py
**Tests:**
- `test_estimate_lufs_from_rms` → rms=0.5 da ≈ -9.0 LUFS
- `test_lufs_targets_defined_for_all_roles`
- `test_lufs_bonus_within_range` → bonus entre 0 y 1
### T185 — Ejecutar todos los tests
```powershell
python -m pytest "C:\ProgramData\Ableton\Live 12 Suite\Resources\MIDI Remote Scripts\AbletonMCP_AI\AbletonMCP_AI\MCP_Server\tests\" -v --tb=short 2>&1 | Tee-Object -FilePath "docs\TEST_RESULTS_GRANULAR.txt"
```
Objetivo: todos los tests pasan. Documenta los que fallan.
### T186 — Actualizar AGENTS.md con nuevos módulos
**Acción:** Añade tabla de módulos nuevos al AGENTS.md:
| Módulo | Propósito | Estado |
|---|---|---|
| `spectral_engine.py` | Análisis espectral y similitud tímbrica | Nuevo en Granular Sprint |
| `melody_generator.py` | Generación melódica procedural Am reggaeton | Nuevo en Granular Sprint |
| `arrangement_intelligence.py` | Estructura de arrangement y transiciones DJ | Nuevo en Granular Sprint |
| `build_spectral_index.py` | Indexado offline de la librería de samples | Nuevo en Granular Sprint |
### T187 — Actualizar roadmap.md marcando completados
**Acción:** Marca como `[x]` los ítems del roadmap que estos sprints completaron:
- FASE 4.3: Spectral Fingerprinting (T016T045)
- FASE 7.1: Scale-aware melody / Motif engine (T121T135)
- FASE 7.2: Chord progressions reggaeton (T048)
- FASE 7.3: Bass line dembow (T050T051)
- FASE 2.3: Crash + snare roll (T147T148)
- FASE 1.1: LUFS estimator (T101T103)
### T188 — Actualizar KIMI_K2_ACTIVE_HANDOFF.md
**Acción:** Reemplaza "Sprint activo" con referencia a los archivos `GRANULAR_SPRINT_PART1` y `GRANULAR_SPRINT_PART2`. Actualiza "Estado real verificado" con la fecha actual y los módulos creados en este sprint.
### T189 — Crear SPRINT_GRANULAR_VALIDATION_REPORT.md
**Archivo nuevo:** `docs/SPRINT_GRANULAR_VALIDATION_REPORT.md`
**Contenido mínimo:**
- Fecha y sesión
- Lista de tareas T001T200 con status: ✅ OK / ❌ FAIL / ⚠️ PARTIAL
- Outputs exactos de `get_session_info`, `get_track_info(15)`, `audit_project_coherence()`
- Tests que pasaron y fallaron
- Problemas encontrados y su causa raíz
- Estado del índice espectral (cuántos samples indexados)
- Estado del proyecto musical (clips en arrangement, cobertura, score)
### T190 — Fix final: verificar que ProxyClip no crashea callers
**Archivo:** `abletonmcp_init.py`
**Acción:** La clase `ProxyClip` local debe exponer todos los atributos que el caller de `_record_session_clip_to_arrangement` podría acceder: `name`, `length`, `start_time`, `looping=False`, `is_midi_clip=True`. Añade estos atributos al `__init__` del ProxyClip.
### T191 — Asegurar que el track 0 (1-MIDI) no interfiere
**Acción:** `get_track_info(track_index=0)`. Si tiene clips en beats 0288, verifica que no se superponen con el harmónico (track 15). Si hay conflicto, documenta.
### T192 — Verificar que los 5 buses (tracks 15) están sin clips
**Acción:** `get_track_info` para indices 15. Los buses no deben tener arrangement clips. Si los tienen, es un error de generación previo. Documenta.
### T193 — Run compileall completo
```powershell
python -m compileall "C:\ProgramData\Ableton\Live 12 Suite\Resources\MIDI Remote Scripts\AbletonMCP_AI" 2>&1 | Tee-Object "docs\COMPILE_RESULTS.txt"
```
Objetivo: 0 errores de compilación.
### T194 — Verificar conectividad MCP tras todos los cambios
**Acción:** Si hay cambios en `server.py`, reinicia el MCP server. Llama a `get_session_info`. Si falla, revisa `Log.txt` y documenta el error exacto.
### T195 — Crear checklist de "listo para usar" para el usuario
**Archivo:** `docs/READY_CHECKLIST.md`
**Contenido:**
```markdown
# Checklist: AbletonMCP-AI Granular Sprint Listo
## Técnico
- [ ] spectral_engine.py compila
- [ ] melody_generator.py compila
- [ ] arrangement_intelligence.py compila
- [ ] spectral_index.json generado (≥ 50 samples)
- [ ] Tests pasan (ver TEST_RESULTS_GRANULAR.txt)
## Musical (verificar en Ableton)
- [ ] Track 15 (HARMONY_PIANO_MIDI) tiene ≥ 5 clips en arrangement
- [ ] Track 12 (AUDIO TOP LOOP) no tiene gaps > 32 beats
- [ ] Track 11 (AUDIO PERC ALT) no tiene gaps > 32 beats
- [ ] audit_project_coherence() score ≥ 4.0
- [ ] drum_coverage_ratio ≥ 0.55
- [ ] harmonic_coverage_ratio ≥ 0.60
## Herramientas MCP disponibles
- [ ] analyze_sample_spectrum funciona
- [ ] find_similar_samples funciona
- [ ] populate_harmony_track funciona
- [ ] fill_arrangement_gaps funciona
- [ ] apply_transition_fx funciona
- [ ] audit_bus_architecture funciona
```
### T196 — Bonus: Crear smoke test reggaeton granular
**Archivo:** `temp/smoke_test_granular_reggaeton.py`
**Acción:** Script que verifica el pipeline completo del sprint en 5 pasos:
1. Conectar al MCP
2. `analyze_sample_spectrum(perc_sample_path)` → ok
3. `find_similar_samples(perc_sample, reggaeton_folder)` → retorna ≥ 3
4. `populate_harmony_track(15, 'Am', 95.0)` → ok
5. `audit_project_coherence()` → score > 3.0
### T197 — Bonus: Añadir modo verbose a spectral_engine
**Archivo:** `spectral_engine.py`
**Acción:** Añade `SpectralEngine(verbose=True)` que loggea el perfil completo de cada sample analizado en modo `[SPECTRAL_VERBOSE]`.
### T198 — Bonus: Añadir cache invalidation a SpectralEngine
**Archivo:** `spectral_engine.py`
**Acción:** Añade `invalidate_cache(path: str)` y `clear_cache()` para que cuando un sample se modifica, su perfil se recalcula.
### T199 — Bonus: Conectar melody_generator con reference_listener
**Archivo:** `reference_listener.py` + `melody_generator.py`
**Acción:** Si `reference_listener` detecta que la referencia está en Am, pasa `root_midi=57` al `melody_generator`. Si detecta Dm, pasa `root_midi=50`. Esto hace que la melodía generada esté en la misma clave que la referencia.
### T200 — ENTREGA FINAL
**Acción:** Escribe el reporte final `docs/SPRINT_GRANULAR_ENTREGA_FINAL.md` con:
1. Resumen ejecutivo (3 líneas): qué se logró, qué score de coherencia, qué faltó.
2. Tabla de módulos nuevos con tamaño en KB.
3. Outputs de los 5 pasos del smoke test (T196).
4. Recomendaciones para el siguiente sprint.
5. Una línea honesta: si el proyecto `song.als` suena mejor aún, peor o igual, y por qué.
**Criterio de éxito del sprint completo:**
- `audit_project_coherence()` score ≥ 5.0
- `harmonic_coverage_ratio ≥ 0.70`
- `drum_coverage_ratio ≥ 0.60`
- Al menos 3 herramientas MCP nuevas funcionando
- `spectral_index.json` con ≥ 30 samples
- 0 errores de compilación en todos los archivos modificados