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

28 KiB
Raw Permalink Blame History

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:

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:

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:

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:

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:

@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:

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).

"""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

@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:

@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

@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:

'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

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

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:

# 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