diff --git a/AbletonMCP_AI/MCP_Server/song_generator.py b/AbletonMCP_AI/MCP_Server/song_generator.py index 07a5c7d..d8b60ff 100644 --- a/AbletonMCP_AI/MCP_Server/song_generator.py +++ b/AbletonMCP_AI/MCP_Server/song_generator.py @@ -751,6 +751,50 @@ ARRANGEMENT_PROFILES = ( 'glue': 'GLUE SWING', }, }, + { + 'name': 'dembow', + 'genres': {'reggaeton'}, + 'drum_tightness': 0.92, + 'bass_motion': 'bouncy', + 'melodic_motion': 'hooky', + 'pan_width': 0.16, + 'fx_bias': 0.95, + 'bus_names': { + 'drums': 'DRUM DEMBOW', + 'bass': 'BASS TUBE', + 'music': 'SYNTH PLUCK', + 'vocal': 'VOCAL CHOP', + 'fx': 'FX LATIN', + }, + 'return_names': { + 'space': 'REVERB SHORT', + 'echo': 'DELAY PING', + 'heat': 'DRIVE HOT', + 'glue': 'GLUE BUS', + }, + }, + { + 'name': 'moombahton', + 'genres': {'reggaeton'}, + 'drum_tightness': 0.88, + 'bass_motion': 'heavy', + 'melodic_motion': 'anthemic', + 'pan_width': 0.20, + 'fx_bias': 1.05, + 'bus_names': { + 'drums': 'DRUM MOOMBAH', + 'bass': 'BASS HEAVY', + 'music': 'SYNTH BIG', + 'vocal': 'VOCAL LEAD', + 'fx': 'FX FESTIVAL', + }, + 'return_names': { + 'space': 'REVERB BIG', + 'echo': 'DELAY WIDE', + 'heat': 'DRIVE HEAVY', + 'glue': 'GLUE FAT', + }, + }, ) ROLE_FX_CHAINS = { @@ -1693,6 +1737,39 @@ DRUM_SECTION_VARIANTS = { }, } +# TODO-008: REGGAETON_SECTION_VARIANTS - Variantes especificas para reggaeton +# Mapeo de roles a variantes por tipo de seccion para reggaeton +REGGAETON_SECTION_VARIANTS = { + 'bass': { + 'intro': {'variant': 'smooth deep', 'intensity': 0.6}, + 'build': {'variant': 'rising', 'intensity': 0.8}, + 'drop': {'variant': 'full punchy dembow', 'intensity': 1.0}, + 'break': {'variant': 'minimal rolling', 'intensity': 0.5}, + 'outro': {'variant': 'atmospheric filtered', 'intensity': 0.4}, + }, + 'perc': { + 'intro': {'variant': 'minimal', 'intensity': 0.3}, + 'build': {'variant': 'layering', 'intensity': 0.7}, + 'drop': {'variant': 'full dembow latin percussion', 'intensity': 1.0}, + 'break': {'variant': 'sparse congas bongos', 'intensity': 0.4}, + 'outro': {'variant': 'minimal', 'intensity': 0.2}, + }, + 'vocal': { + 'intro': {'variant': 'absent', 'intensity': 0.0}, + 'build': {'variant': 'tease', 'intensity': 0.5}, + 'drop': {'variant': 'full chop', 'intensity': 1.0}, + 'break': {'variant': 'phrase', 'intensity': 0.7}, + 'outro': {'variant': 'fade', 'intensity': 0.3}, + }, + 'synth': { + 'intro': {'variant': 'filtered', 'intensity': 0.4}, + 'build': {'variant': 'rising', 'intensity': 0.8}, + 'drop': {'variant': 'pluck hooky', 'intensity': 1.0}, + 'break': {'variant': 'pad atmospheric', 'intensity': 0.5}, + 'outro': {'variant': 'fade', 'intensity': 0.3}, + }, +} + # Expanded drum pattern generators for section variation DRUM_PATTERN_BANKS = { 'kick': { @@ -2593,29 +2670,34 @@ class SongGenerator: def _resolve_bus_for_role(self, role: str) -> Optional[str]: return ROLE_BUS_ASSIGNMENTS.get(str(role or '').strip().lower(), 'music') - def _get_section_variation(self, role: str, section_kind: str) -> Dict[str, Any]: + def _get_section_variation(self, role: str, section_kind: str, genre: str = "") -> Dict[str, Any]: """ - Obtiene configuración de variación para un rol y sección. + Obtiene configuracion de variacion para un rol y seccion. Retorna dict con: - - use: bool - si el rol debe usarse en esta sección + - use: bool - si el rol debe usarse en esta seccion - sparse: bool - si usar variante sparse - full: bool - si usar variante completa - intensity: float - intensidad de 0 a 1 - etc. """ + # TODO-008: Usar variantes especificas de reggaeton si aplica + if genre.lower() == "reggaeton" and role in REGGAETON_SECTION_VARIANTS: + reggaeton_config = REGGAETON_SECTION_VARIANTS[role] + return reggaeton_config.get(section_kind.lower(), {"use": True, "intensity": 1.0}) + if role not in SECTION_VARIATION_CONFIG: - return {'use': True, 'intensity': 1.0} + return {"use": True, "intensity": 1.0} role_config = SECTION_VARIATION_CONFIG[role] - return role_config.get(section_kind.lower(), {'use': True, 'intensity': 1.0}) + return role_config.get(section_kind.lower(), {"use": True, "intensity": 1.0}) - def _should_vary_role_in_section(self, role: str, section_kind: str) -> bool: - """Determina si un rol debe variar en una sección dada.""" - if role not in SECTION_VARIATION_CONFIG: + def _should_vary_role_in_section(self, role: str, section_kind: str, genre: str = "") -> bool: + """Determina si un rol debe variar en una seccion dada.""" + if role not in SECTION_VARIATION_CONFIG and role not in REGGAETON_SECTION_VARIANTS: return False - config = self._get_section_variation(role, section_kind) + config = self._get_section_variation(role, section_kind, genre) # Si tiene clave 'use' explícita if 'use' in config: @@ -6101,6 +6183,26 @@ class SongGenerator: vel = 90 + random.randint(-20, 20) notes.append({'pitch': pitch, 'start': time, 'duration': 0.4, 'velocity': min(127, max(60, vel))}) + elif style == 'bouncy': + # Estilo bouncy para reggaeton - notas cortas con "bounce" + for bar in range(bars): + # Pattern: bump en 1, silencio, nota de apoyo en el "3" + notes.append({'pitch': root_midi, 'start': bar * 4.0 + 0.0, 'duration': 0.3, 'velocity': 120}) # Bump fuerte + notes.append({'pitch': root_midi, 'start': bar * 4.0 + 1.75, 'duration': 0.15, 'velocity': 90}) # Ghost note + notes.append({'pitch': scale_notes[4] if len(scale_notes) > 4 else root_midi + 7, + 'start': bar * 4.0 + 2.5, 'duration': 0.4, 'velocity': 100}) # Nota de apoyo + notes.append({'pitch': root_midi, 'start': bar * 4.0 + 3.5, 'duration': 0.25, 'velocity': 110}) # Cierre + + elif style == 'dembow': + # Linea de bajo dembow caracteristica - sigue el patron del kick + for bar in range(bars): + # Nota raiz en tiempos fuertes con slide/portamento + notes.append({'pitch': root_midi, 'start': bar * 4.0 + 0.0, 'duration': 0.5, 'velocity': 125}) # Beat 1 - fuerte + notes.append({'pitch': root_midi, 'start': bar * 4.0 + 1.75, 'duration': 0.3, 'velocity': 100}) # Ghost con kick + notes.append({'pitch': root_midi, 'start': bar * 4.0 + 3.0, 'duration': 0.5, 'velocity': 120}) # Beat 3 - fuerte + # Slide a octava superior en el anticipo + notes.append({'pitch': root_midi + 12, 'start': bar * 4.0 + 3.75, 'duration': 0.2, 'velocity': 90}) # Slide up + else: # walking for bar in range(bars): for beat in range(4):