Sync: Complete project state with all MEGA SPRINT V1-V3 features and Codex stubs
This commit is contained in:
531
docs/GRANULAR_SPRINT_PART2_T101_T200.md
Normal file
531
docs/GRANULAR_SPRINT_PART2_T101_T200.md
Normal file
@@ -0,0 +1,531 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user