- Add _cmd_create_arrangement_audio_pattern with 5-method fallback chain - Method 1: track.insert_arrangement_clip() [Live 12+] - Method 2: track.create_audio_clip() [Live 11+] - Method 3: arrangement_clips.add_new_clip() [Live 12+] - Method 4: Session->duplicate_clip_to_arrangement [Legacy] - Method 5: Session->Recording [Universal] - Add _cmd_duplicate_clip_to_arrangement for session-to-arrangement workflow - Update skills documentation - Verified: 3 clips created at positions [0, 4, 8] in Arrangement View Closes: Audio injection in Arrangement View
286 lines
13 KiB
Markdown
286 lines
13 KiB
Markdown
# SPRINT 4 — BLOQUE A: CARGA REAL, DIAGNÓSTICO Y ESTABILIZACIÓN (T001-T050)
|
|
|
|
> **Fecha**: 2026-04-11
|
|
> **Estado Sprint 3**: ✅ COMPLETO — 119 tools MCP, 64 handlers, 3 engines nuevos
|
|
> **Objetivo Sprint 4-A**: Que TODO lo que "dice" que hace, LO HAGA REALMENTE en Ableton
|
|
> **Revisión**: Qwen
|
|
|
|
---
|
|
|
|
## CONTEXTO
|
|
|
|
Sprint 3 entregó código que compila 100%. El problema: muchas acciones retornan
|
|
`"loaded": True` sin verificar que Ableton realmente las ejecutó. Este bloque se
|
|
enfoca en tres pilares:
|
|
|
|
1. **Verificación real** — cada handler confirma el estado POST-ejecución en Live
|
|
2. **Integración completa** — browser API ya implementada, ahora se usa en TODO el sistema
|
|
3. **Diagnóstico** — herramientas para que el usuario sepa exactamente qué funciona
|
|
|
|
---
|
|
|
|
## FASE A1: VERIFICACIÓN POST-EJECUCIÓN (T001-T010)
|
|
|
|
**T001** — `_cmd_load_sample_to_clip`: Agregar `_verify_clip_has_audio(slot)` que
|
|
inspecciona `slot.has_clip` y `clip.length > 0` DESPUÉS de la carga.
|
|
Retorna `verified: true/false` con `duration_beats` real si el clip existe.
|
|
|
|
**T002** — `_cmd_insert_device`: Agregar `_verify_device_on_track(track, device_name)`
|
|
que compara lista de devices ANTES y DESPUÉS. Retorna `verified: true` + `device_index`
|
|
real si el device apareció en `track.devices`.
|
|
|
|
**T003** — `_cmd_create_arrangement_midi_clip`: Verificar si `arrangement_clips` API
|
|
funcionó chequeando el clip existe en el track. Si Session fallback, marcar
|
|
`view: "session_fallback"` y retornar `clip_index` + URL del slot real.
|
|
|
|
**T004** — `_cmd_load_sample_to_drum_rack_pad`: Verificar que el pad tiene cadena
|
|
después del intento. Acceder a `pad.chains[0].devices[0].sample.file_path`
|
|
y comparar con el fname buscado. Retornar `verified_path`.
|
|
|
|
**T005** — `_cmd_generate_dembow_clip`: Verificar que las notas se escribieron
|
|
exactamente. Leer el clip con `clip.get_notes()` y comparar count.
|
|
Retornar `notes_written: N, notes_verified: M`.
|
|
|
|
**T006** — `_cmd_generate_midi_clip`: Agregar verificación de notas post-escritura.
|
|
Si `clip.get_notes()` retorna vacío cuando se enviaron notas, loguear el error
|
|
y reintentar con `replace_selected_notes` si disponible.
|
|
|
|
**T007** — `_cmd_create_drum_kit`: Después de crear el Drum Rack, verificar que
|
|
`track.devices` contiene el device. Acceder a `device.drum_pads` y contar pads
|
|
activos. Retornar `pads_active`, `drum_rack_index`.
|
|
|
|
**T008** — `_cmd_configure_eq`: Verificar que el EQ Eight está en la cadena.
|
|
Leer `device.parameters` y confirmar que se aplicaron los valores.
|
|
Retornar `parameters_verified: {band: value}`.
|
|
|
|
**T009** — `_cmd_setup_sidechain`: Verificar que el Compressor tiene `sidechain_active`.
|
|
Acceder a `device.sidechain` si existe. Retornar `sidechain_confirmed: true/false`.
|
|
|
|
**T010** — Crear handler `_cmd_verify_track_setup(track_index)`:
|
|
- Lista todos los devices del track
|
|
- Lista clips activos en Session View
|
|
- Informa volumen, pan actual
|
|
- Retorna snapshot completo del track para debugging
|
|
|
|
---
|
|
|
|
## FASE A2: BROWSER API — USAR EN TODO EL SISTEMA (T011-T020)
|
|
|
|
**T011** — `_cmd_load_samples_for_genre` (T008): Actualmente usa solo
|
|
`sample_selector.select_for_genre()` para paths. Integrar `_browser_load_audio()`
|
|
para cada sample, con fallback a `create_audio_clip`. Retornar qué método funcionó
|
|
por cada sample.
|
|
|
|
**T012** — `_cmd_create_drum_kit` (T009): Actualmente crea Drum Rack via
|
|
`create_midi_track()` pero no carga el Drum Rack device. Integrar
|
|
`_browser_load_device(t, "Drum Rack", "instruments")` antes de cargar samples.
|
|
Verificar que el Drum Rack apareció antes de intentar cargar pads.
|
|
|
|
**T013** — `_cmd_build_track_from_samples` (T010): Usar `_browser_load_audio()`
|
|
en lugar de confiar en `create_audio_clip`. Agregar lógica de fallback:
|
|
si browser falla, crear MIDI track con nota de instrucción.
|
|
|
|
**T014** — `_cmd_insert_device` → extender lookup: Actualmente busca solo en una
|
|
sección. Agregar búsqueda secundaria en TODAS las secciones si la primera falla.
|
|
Orden: `instruments → audio_effects → midi_effects → packs`.
|
|
|
|
**T015** — Nuevo handler `_cmd_scan_browser_section(section_name, depth=2)`:
|
|
- Escanea una sección del browser Live y retorna árbol de items
|
|
- Sections: "instruments", "audio_effects", "sounds", "user_folders", "packs"
|
|
- Útil para debug: saber exactamente qué ve el sistema en el browser
|
|
- Retorna lista de items con `name`, `is_loadable`, `is_folder`
|
|
|
|
**T016** — Nuevo tool MCP `scan_browser_section(section, depth)` en `server.py`:
|
|
- Llama a `_cmd_scan_browser_section`
|
|
- Permite al usuario descubrir qué devices/samples tiene disponibles
|
|
- Retorna JSON con árbol navegable
|
|
|
|
**T017** — `_cmd_configure_eq`: Si el device no existe en el track, PRIMERO
|
|
insertar EQ Eight via `_browser_load_device`, LUEGO configurar parámetros.
|
|
Secuencia: insert → verify → configure.
|
|
|
|
**T018** — `_cmd_configure_compressor`: Si no hay Compressor, insertar via
|
|
browser antes de configurar. Verificar la inserción. Mismo patrón que T017.
|
|
|
|
**T019** — `_cmd_setup_sidechain`: Insertar Compressor si no existe,
|
|
configurar la fuente de sidechain. Usar `device.sidechain_enabled = True` si disponible.
|
|
Retornar los parámetros realmente configurados.
|
|
|
|
**T020** — Nuevo handler `_cmd_add_libreria_to_browser()`:
|
|
- Lee path de `libreria/reggaeton` desde constante
|
|
- Intenta agregar el folder a Live's user library via `application().browser`
|
|
- Retorna `added: true/false` con instrucción manual si falla
|
|
|
|
---
|
|
|
|
## FASE A3: ARRANGEMENT VIEW — IMPLEMENTACIÓN COMPLETA (T021-T030)
|
|
|
|
**T021** — `_cmd_create_arrangement_midi_clip`: Agregar soporte para `song.record_mode`.
|
|
Si `song.record_mode` está disponible, configurar overdub antes de fire.
|
|
Retornar `arrangement_mode_set: true/false`.
|
|
|
|
**T022** — Nuevo handler `_cmd_set_arrangement_position(bar)`:
|
|
- `song.current_song_time = bar * beats_per_bar`
|
|
- `app.view.show_view("Arranger")`
|
|
- Retorna posición actual del playhead
|
|
|
|
**T023** — Nuevo handler `_cmd_fire_clip_to_arrangement(track_index, clip_index, target_bar)`:
|
|
- Pos playhead en `target_bar`
|
|
- Activa `song.arrangement_overdub = True`
|
|
- Dispara el clip: `track.clip_slots[clip_index].fire()`
|
|
- Espera `clip.length` beats en la queue de `_pending_tasks`
|
|
- Desactiva overdub: `song.arrangement_overdub = False`
|
|
- Retorna `recorded_to_bar: target_bar`
|
|
|
|
**T024** — `_cmd_duplicate_session_to_arrangement` (T014): Reescribir usando
|
|
`_cmd_fire_clip_to_arrangement` para cada clip+escena. Calcular posición en bars
|
|
basada en `scene_index * section_length`. Retorna clips colocados + posición.
|
|
|
|
**T025** — Nuevo handler `_cmd_get_arrangement_clips(track_index)`:
|
|
- Lee todos los clips de arrangement via `track.arrangement_clips` si disponible
|
|
- Retorna lista con `name`, `start_time`, `length`, `has_notes`
|
|
- Si no disponible, retorna vacío con `method: "not_available"`
|
|
|
|
**T026** — Nuevo handler `_cmd_show_arrangement_view()`:
|
|
- `app.view.show_view("Arranger")`
|
|
- `app.view.show_view("Detail/Clip")` para mostrar detalle
|
|
- Retorna `view: "arranger"`
|
|
|
|
**T027** — Nuevo handler `_cmd_show_session_view()`:
|
|
- `app.view.show_view("Session")`
|
|
- Retorna `view: "session"`
|
|
|
|
**T028** — `_cmd_build_arrangement_structure`: Usa `_cmd_fire_clip_to_arrangement`
|
|
para colocar clips reales en posiciones de la estructura (Intro, Verse, Drop, etc.)
|
|
en lugar de solo crear escenas en session view.
|
|
|
|
**T029** — Nuevo handler `_cmd_loop_arrangement_region(start_bar, end_bar)`:
|
|
- `song.loop_start = start_bar * beats_per_bar`
|
|
- `song.loop_length = (end_bar - start_bar) * beats_per_bar`
|
|
- `song.loop_on = True`
|
|
- Retorna `loop_set: true`
|
|
|
|
**T030** — Nuevo handler `_cmd_capture_to_arrangement()`:
|
|
- Equivalente a "Capture" de Live: `app.get_document().capture_midi()` si disponible
|
|
- Fallback: instrucción de cómo usar Capture manualmente
|
|
- Retorna `captured: true/false`
|
|
|
|
---
|
|
|
|
## FASE A4: DIAGNÓSTICO Y MONITOREO (T031-T040)
|
|
|
|
**T031** — Nuevo handler `_cmd_get_live_version()`:
|
|
- `Live.Application.get_application().get_major_version()`
|
|
- `Live.Application.get_application().get_minor_version()`
|
|
- Retorna `version: "12.x.x"`, `build: N`
|
|
|
|
**T032** — Nuevo handler `_cmd_get_track_details(track_index)`:
|
|
- Snapshot completo de un track: devices, clips, volumes, routing
|
|
- Para debugging: `has_input`, `has_output`, `arm`, `mute`, `solo`
|
|
- Lista cada device con parámetros accesibles
|
|
|
|
**T033** — Nuevo handler `_cmd_get_device_parameters(track_index, device_index)`:
|
|
- Lista todos los parámetros de un device
|
|
- `device.parameters` → `{name, value, min, max, is_quantized}`
|
|
- Útil para saber cómo configurar el device vía API
|
|
|
|
**T034** — Nuevo handler `_cmd_set_device_parameter(track_index, device_index, param_name, value)`:
|
|
- Busca parámetro por nombre en `device.parameters`
|
|
- Setea `param.value = value`
|
|
- Verifica que el cambio se aplicó
|
|
- Retorna `parameter`, `old_value`, `new_value`
|
|
|
|
**T035** — Nuevo handler `_cmd_get_clip_notes(track_index, clip_index)`:
|
|
- Lee las notas de un MIDI clip via `clip.get_notes()`
|
|
- Retorna lista de `{pitch, start, duration, velocity, mute}`
|
|
- Con estadísticas: `note_count`, `min_pitch`, `max_pitch`, `duration_bars`
|
|
|
|
**T036** — Nuevo handler `_cmd_test_browser_connection()`:
|
|
- Verifica que `application().browser` es accesible
|
|
- Lista las secciones disponibles: sounds, instruments, audio_effects, etc.
|
|
- Retorna `browser_ok: true/false`, `sections: [...]`
|
|
|
|
**T037** — Nuevo handler `_cmd_test_sample_loading(sample_path)`:
|
|
- Tests: `os.path.isfile()` → path OK
|
|
- Tests: `_browser_load_audio()` → browser OK
|
|
- Tests: `create_audio_clip()` si disponible
|
|
- Retorna `path_ok`, `browser_ok`, `direct_ok`, `recommended_method`
|
|
|
|
**T038** — Nuevo handler `_cmd_get_session_state()`:
|
|
- `song.current_song_time` → posición actual
|
|
- `song.is_playing`, `song.tempo`, `song.signature_numerator`
|
|
- Lista clips activos por track
|
|
- Retorna snapshot completo del estado de Session
|
|
|
|
**T039** — Nuevo tool MCP `get_system_diagnostics()` en `server.py`:
|
|
- Combina: get_live_version + test_browser_connection + get_session_state
|
|
- Retorna JSON con estado completo del sistema
|
|
- Primer tool que ejecutar para diagnosticar problemas
|
|
|
|
**T040** — Nuevo tool MCP `test_real_loading(sample_path)` en `server.py`:
|
|
- Llama a `_cmd_test_sample_loading`
|
|
- Retorna qué métodos de carga funcionan en el Live actual
|
|
- Guía al usuario sobre qué esperar
|
|
|
|
---
|
|
|
|
## FASE A5: ROBUSTEZ Y ESTABILIDAD (T041-T050)
|
|
|
|
**T041** — Agregar timeout global a `_cmd_*` handlers: Si un handler tarda
|
|
más de 3s (detectado via `time.time()`), retornar `timeout: true` y limpiar
|
|
`_pending_tasks` parcialmente. Previene bloqueos de Ableton.
|
|
|
|
**T042** — `_dispatch()`: Agregar manejo de `JSONDecodeError` y `KeyError`
|
|
explícitos. Retornar error descriptivo con el comando que falló.
|
|
Loguear en Ableton con `self.log_message`.
|
|
|
|
**T043** — Proteger `update_display()`: Atrapar excepciones dentro del loop
|
|
de `_pending_tasks`. Si una task lanza excepción, remover y continuar con la
|
|
siguiente. Nunca dejar que una task rota bloquee el drain.
|
|
|
|
**T044** — `_tcp_server_thread`: Si la conexión se cierra abruptamente,
|
|
cerrar el socket limpiamente. Agregar `socket.SO_REUSEADDR` si no está presente.
|
|
Reiniciar listener automáticamente tras error de conexión.
|
|
|
|
**T045** — Agregar límite a `_pending_tasks`: Si la queue supera 100 items,
|
|
droppear las tareas más viejas y loguear warning. Previene acumulación sin límite
|
|
cuando Ableton está bajo carga y `update_display()` no puede drenar rápido.
|
|
|
|
**T046** — `_cmd_get_tracks()`: Si un track da error al leer un atributo
|
|
(e.g., track sin nombre), continuar con el siguiente en lugar de fallar todo.
|
|
Agregar `try/except` granular por atributo.
|
|
|
|
**T047** — `_cmd_generate_full_song()`: Si un sub-handler falla durante
|
|
el pipeline, continuar con los siguientes tracks. Retornar lista de errores
|
|
al final pero no abortar. Comportamiento "best effort" para producción completa.
|
|
|
|
**T048** — Todos los handlers que crean tracks: Verificar que el índice
|
|
solicitado no excede `len(song.tracks)`. Si se intenta acceder a track[N]
|
|
y N>=len, retornar error claro en lugar de IndexError sin contexto.
|
|
|
|
**T049** — `_browser_search`: Agregar límite de tiempo: si la recursión
|
|
supera 5 segundos (verificar con `time.time()`), abortar y retornar `None`
|
|
en lugar de bloquear el thread de Ableton indefinidamente.
|
|
|
|
**T050** — Crear `_cmd_health_check()`:
|
|
- Ejecuta 5 checks: TCP OK, song accesible, tracks accesibles, browser accesible, update_display activo
|
|
- Retorna score 0-5 y descripción de cada check
|
|
- Tool MCP `health_check()` que llama a este handler
|
|
- Primero que ejecutar tras abrir Ableton
|
|
|
|
---
|
|
|
|
## ARCHIVOS A MODIFICAR (Bloque A)
|
|
|
|
| Archivo | Cambios |
|
|
|---------|---------|
|
|
| `__init__.py` | +25 handlers nuevos, robustez en handlers existentes |
|
|
| `mcp_server/server.py` | +10 tools MCP: scan_browser, health_check, get_system_diagnostics, test_real_loading, etc. |
|
|
|
|
## RESTRICCIONES
|
|
1. Compilar tras cada archivo: `python -m py_compile "<path>"`
|
|
2. `libreria/` → solo lectura
|
|
3. NO modificar engines del Sprint 1/2/3
|
|
4. Handlers de verificación son SOLO-LECTURA: no mutan estado
|
|
5. Retornar siempre JSON con `status` + `result` o `error`
|