# GRANULAR SPRINT PART 2 — Tareas T101–T200 ## 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 T001–T100 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 (T101–T120) ### 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 (0–1) 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. ### T108–T115 — Bus architecture verification **T108:** Llama a `get_track_info` para los tracks 1–5 (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 (A–D) 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 T108–T111. **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()`. ### T116–T120 — 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 (T121–T145) ### 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 2–4 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}" ``` ### T124–T135 — 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 128–160), 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 160–192), 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`. ### T136–T145 — 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 (T146–T165) ### 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) ``` ### T152–T160 — Send automation para builds **T152:** En el track 14 (AUDIO SYNTH PEAK), durante build_a (beats 32–64), 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 160–192), 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 T152–T154: 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`. ### T161–T165 — 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 128–160) 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 (T166–T180) ### 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. ### T171–T175 — 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. ### T176–T180 — 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 T176–T179. --- ## BLOQUE J — TESTING Y DOCUMENTACIÓN (T181–T200) ### 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 61–64 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 (T016–T045) - FASE 7.1: Scale-aware melody / Motif engine (T121–T135) - FASE 7.2: Chord progressions reggaeton (T048) - FASE 7.3: Bass line dembow (T050–T051) - FASE 2.3: Crash + snare roll (T147–T148) - FASE 1.1: LUFS estimator (T101–T103) ### 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 T001–T200 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 0–288, verifica que no se superponen con el harmónico (track 15). Si hay conflicto, documenta. ### T192 — Verificar que los 5 buses (tracks 1–5) están sin clips **Acción:** `get_track_info` para indices 1–5. 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