kuruitsugizaki

Permite que un modelo escriba proyectos de FL Studio (notas, cualquier plugin y preset instalado, mezclador, sidechain, automatización), los abra en un FL en ejecución y los renderice.

Documentación

kuruitsugizaki

Un servidor MCP que permite a un modelo leer y escribir proyectos de FL Studio y controlar una instancia en ejecución de FL Studio, para que pueda componer música. FL Studio es la primera integración; el sistema está pensado para llegar a más software en el futuro.

El modelo no escucha. Un render vuelve como números e imágenes (espectrogramas, sonoridad, ancho estéreo), y la persona escucha y decide.

Factory Seance, dos minutos de witch house glitchy y breakcore, fue escrita de principio a fin por Claude Opus 5.5 a través de este sistema, en unos 35 minutos: cada nota, golpe de batería, elección de sonido, ruta de mezclador y clip de automatización, usando solo los sonidos de fábrica de FL. Nadie la editó; la persona que la pidió escuchó después y la dejó tal cual. Escucha: examples/seance.mp3, el clip con sonido: examples/demo.mp4, cómo se hizo: examples/seance.md.

https://github.com/user-attachments/assets/8ee80e6e-cf6b-459d-bbe1-a83a50da325c

Factory Seance playing in FL Studio Factory Seance: piano-roll spectrogram, chroma, spectrogram, level and stereo correlation

Solo Windows. Para instalarlo, sigue INSTALL.md. Todo lo que va más allá de la configuración y las herramientas (cómo escribir una pieza, el formato de archivo, trampas, decisiones) está en docs/, empezando por docs/INDEX.md.

Requisitos

  • Windows, FL Studio 2025. Construido y medido en FL 25.1.6, API de scripting 38.
  • loopMIDI con al menos un puerto. FL carga el script de controlador solo cuando su puerto de entrada MIDI existe.
  • Python 3.12 o más reciente y uv.
  • ffmpeg y ffprobe en PATH, para los scripts de análisis.
  • Serum 2 para usar el canal Serum 2 de la plantilla. FLEX viene con FL.

Configuración

La forma rápida: cierra FL y haz doble clic en setup.bat. Instala lo que falta (uv, loopMIDI, opcionalmente ffmpeg), construye el entorno, instala el script de controlador de FL, crea el puerto MIDI y el ajuste MIDI de FL, añade el servidor a la aplicación de escritorio de Claude, y termina con una comprobación de que FL responde. INSTALL.md lista cada paso y dónde termina.

A mano, lo mismo en cuatro movimientos (INSTALL.md, A mano, tiene el detalle):

uv sync

Registra el servidor con el cliente MCP, con la ruta a esta carpeta:

"kuruitsugizaki": { "command": "<this folder>\\.venv\\Scripts\\kuruitsugizaki.exe" }

Luego, en un chat con el modelo, con FL cerrado:

  1. Llama a config. En una instalación estándar, todas las rutas se encuentran solas (FL en Program Files\Image-Line, el resto en tu carpeta de Documentos) y se muestra como detected; para cualquier cosa que no encuentre, te pregunta dónde está y lo establece; no adivina. Las claves están en kuruitsugizaki.example.json; lo que establezcas se guarda en kuruitsugizaki.json aquí, que git ignora.
  2. Llama a fl_install, que escribe el script de controlador en la carpeta Settings\Hardware de FL con la ruta del puente de esta carpeta incluida, añade un puerto loopMIDI llamado kuruitsugizaki y asigna el script en los ajustes MIDI de FL. Ambos viven en el registro, y FL sobrescribe el suyo al cerrarse, por eso FL tiene que estar cerrado.
  3. Solo cuando fl_install responda que no pudo hacer el ajuste MIDI: en FL, Opciones, ajustes MIDI, selecciona un puerto de entrada loopMIDI, tipo de controlador kuruitsugizaki, y reinicia FL una vez.

La salida del script en FL muestra kuruitsugizaki {'api': 38, ...} al cargar. .venv\Scripts\python scripts\doctor.py imprime una línea por requisito, en cualquier momento.

El servidor inicia loopMIDI y FL mismo cuando una herramienta en vivo los encuentra cerrados.

Estructura

archivoqué es
scripts/server.pyel servidor MCP: herramientas, y el enlace en vivo con FL
scripts/config.pylas rutas de la máquina, leídas de kuruitsugizaki.json
setup.bat, setup.ps1la instalación en un paso (INSTALL.md, La forma rápida)
Dockerfilepara las comprobaciones automáticas de los directorios MCP: ejecuta el servidor en Linux, donde las herramientas de archivo funcionan y el enlace en vivo con FL responde que necesita Windows
scripts/doctor.pydoctor.py [--install] [--claude [config]] [--live]: una línea por requisito de la configuración de esta máquina, ok o MISSING con qué hacer; --install ejecuta fl_install, --claude añade el servidor a la configuración de la aplicación de escritorio de Claude, --live inicia FL y le pide su versión de API
scripts/midisetup.pyel puerto loopMIDI y el ajuste de entrada MIDI de FL, ambos en el registro: los lee, y los crea cuando ninguna entrada tiene el script
fl/device_kuruitsugizaki.pyscript de controlador de FL; fl_install lo coloca en su sitio
scripts/flp.pycódec de eventos .flp: analizar, serializar (byte exacto), notas, diff
scripts/project.py.flp -> JSON completo del proyecto (round-trip byte idéntico)
scripts/view.pyvistas compactas y de resumen, compacto -> completo
scripts/writer.pyJSON del proyecto -> .flp, basado en plantilla
scripts/automation.pypuntos de automatización (evento 234) y objetivos (evento 227)
scripts/levels.pyregistros de fader de mezclador y volumen principal en el evento 225
scripts/mixerfx.pybloques de efectos de mezclador: lee uno de cualquier proyecto guardado por FL, lo coloca en la ranura de una pista
scripts/presets.pypone un preset en el estado del canal de un plugin: .vital (Vital), .h2p (Diva), .fxp (Sylenth1), .SerumPreset (Serum 2), <pack>/<preset> (FLEX)
scripts/serum2.py, sylenth1.py, diva.py, flex.pyel formato de preset de cada sintetizador, y <script> catalog [out.tsv]: una fila TSV por preset en esa biblioteca
scripts/catalog.pycatalog.py <preset folder> <out.tsv>: presets de Vital por estilo, osciladores, unison, efectos activados, nombres de macros
scripts/render.pyrender.py <flp> [--close] [--reopen]: el render por línea de comandos de FL a wav y mp3 junto al proyecto, luego espectro y estadísticas estéreo. FL debe estar cerrado primero; --close se lo pide (nunca forzado)
scripts/spectro.pyrender -> PNG: espectrograma de piano roll en semitonos, croma, espectrograma de 20 Hz-20 kHz, RMS/pico, correlación L/R; --bpm añade líneas de compás. PNGs en tmp/spectro/
scripts/loudness.pyLUFS integrado y momentáneo, LRA, pico verdadero, RMS; --zwicker añade sonoridad ISO 532-1 en sones
scripts/scope.pymapas de calor de densidad estéreo por momento y banda, --timeline para ancho a lo largo del tiempo
scripts/waveshape.pyA/B de varios renders a varios zooms alrededor del segundo más fuerte de cada archivo
scripts/onsets.pyataques de un WAV, para cortar loops
scripts/chop.pycorta rangos de tiempo de un archivo en sus propios WAVs con fades cortos, opcionalmente invertidos
scripts/vox.pyvoz -> sílabas con un tono mediano cada una
scripts/samples.pysamples.py <folder>: por archivo duración, BPM y tonalidad desde el nombre, niveles, ancho, ataques, centroide -> tmp/samples/<folder>.tsv
scripts/melstats.pyvista general de notas por instrumento de un proyecto
scripts/fxdump_fl.pyenviado a través de fl_exec: cada efecto en cada insert del proyecto abierto con sus parámetros
scripts/nudgemap.py, profiles/serum2/mapeo de parámetros de host de Serum 2 (aparcado)
profiles/sylenth1/params.jsonlos 244 nombres de parámetros de Sylenth1 en orden de blob
projects/init/init.flpla plantilla desde la que se construyen los proyectos nuevos
projects/init/fx.flpuna biblioteca de efectos propios de FL para copiar
examples/siete documentos pequeños, uno por parte del formato (instrumentos, efectos, enrutamiento y sidechain, global, automatización de mezclador e instrumento), y una pieza completa de dos minutos con su script, notas y render; examples/build.py los construye todos. Empieza en examples/README.md
docs/params/mapas de parámetros, un TSV por plugin: 27 de los propios de FL y 22 VST3s, listados en docs/params/INDEX.md; la herramienta params los lee

Herramientas

herramientahace
config(key=None, value=None)sin argumentos: cada clave de ruta, su valor o la ruta detectada para ella, y si existe; con key y value: la establece
fl_install()instala el script de controlador en la carpeta Hardware de FL con la ruta del puente escrita y, con FL cerrado y salvo que una entrada en FL ya tenga el script, añade un puerto loopMIDI llamado kuruitsugizaki y asigna el script en los ajustes MIDI de FL (midisetup.py); la entrada midi de la respuesta dice qué se hizo o qué queda por hacer a mano
project_read(path, view="compact", patterns=None, arrangements=None, unused=False).flp -> JSON. compact: filas con punteros, la forma que toma project_write. summary: estadísticas por patrón y una línea de tiempo por pista. full: forma exacta de round-trip
project_write(doc, path, open=False, play=None)JSON (compacto o completo) -> .flp; open lo carga en FL (lanzando FL si es necesario), play = song o pattern
fl_render(path, reopen=True)guarda el proyecto abierto de FL, luego ejecuta render.py <path> --close [--reopen] en segundo plano; devuelve la ruta del log en tmp/render_logs/
fl_transport(action, mode=None, pattern=None)reproducir / detener, modo canción / patrón
plugins(query=None)cada plugin que FL tiene instalado, y cada instancia en el proyecto de biblioteca opcional (ver Plugins por nombre): nombre, instrumento o efecto, fabricante, formato, fuente, cuántos presets cargan por nombre, si tiene un mapa de parámetros
presets(plugin, query=None, limit=200)los ids de preset de un plugin de biblioteca, filtrados por palabras; el id va en project_write como preset
params(plugin, query=None)los parámetros de un plugin de biblioteca desde docs/params/: índice (para objetivos de automatización), nombre, valor guardado y display, y el display de FL en 0, .25, .5, .75 y 1 donde se barrió
fl_exec(code, out=None, timeout=10), fl_call(fn, args), fl_probe()acceso directo a la API de Python de FL dentro del FL en ejecución; fl_exec devuelve lo que el código asigna a result, o lo escribe como JSON en out (también cuando result es {'__out__': path, 'data': value})
test()devuelve ok

project_read, project_write, plugins, presets y params recargan los módulos del códec en cada llamada; un cambio en server.py, flex.py o diva.py necesita un reinicio de MCP, y una herramienta nueva llega a un cliente solo en una sesión iniciada después.

La plantilla

projects/init/init.flp, guardada por FL: un Sampler, Serum 2, FLEX, un canal de audio y un clip de automatización; master más inserts 1-17, sin efectos. Enruta a inserts 1-16: el insert 17 no toma fader en este mezclador (docs/NOTES.md). Un instrumento plugin se copia del canal de plantilla de ese plugin, así que con esta plantilla plugin toma serum2 o flex. Los cargadores de preset también manejan Vital, Diva y Sylenth1: para usarlos, guarda una plantilla propia en FL con esos plugins y establece template a ella, o toma un canal de cualquier proyecto con state: {from, channel}. Los plugins de plantilla se emparejan por nombre: Serum2, FLEX, Vital, Sylenth1, Diva(x64).

Efectos

El escritor no puede crear un efecto; copia uno, con sus ajustes, de un proyecto guardado por FL. projects/init/fx.flp contiene los propios de FL:

de insert/ranuraefecto
1/0Fruity Parametric EQ 2, guardado con una banda configurada (un shelf alto a -18 dB); mueve o pon a cero sus bandas antes de confiar en él
2/0Fruity Limiter
3/0Fruity Limiter en modo compresor, configurado para ducking de sidechain
4/0Fruity Reeverb 2
5/0Fruity Multiband Compressor
6/0Soundgoodizer
7/0Gross Beat

Soundgoodizer y Gross Beat cargan solo en una edición de FL que los incluya. Cualquier otro efecto instalado entra por nombre (Plugins por nombre).

Plugins por nombre

Cada plugin que FL tiene instalado puede entrar en un proyecto por nombre, sin nada que preparar: el gestor de plugins de FL mantiene una entrada por plugin en su base de datos de plugins (Documents\Image-Line\FL Studio\Presets\Plugin database, clave de configuración plugin_db, encontrada sola), y el escritor construye el plugin desde ella en los bloques de la plantilla, fresco, como al arrastrarlo desde el navegador de FL. plugins los lista; project_write toma un instrumento como {"kind": "plugin", "plugin": "<name>", "preset": "<id>"} y un efecto como mixer.effects: [{"track": t, "slot": s, "plugin": "<name>", "preset": "<id>"}]. Un plugin instalado en varios formatos es un nombre; añade "format": "vst3" (o native, clap, vst) para elegir uno. Un plugin que FL no ha escaneado no se lista: ejecuta primero el gestor de plugins de FL. Opcionalmente, un proyecto FL guardado puede contener instancias afinadas que quieras reutilizar tal como están, cada instrumento en su propio canal, cada efecto en una ranura de mezclador; establece su ruta como clave de configuración library. Una instancia de biblioteca gana sobre una nueva del mismo nombre.

Presets, listados por presets:

  • Archivos .fst, de las carpetas de presets de fábrica y de usuario de FL (claves de configuración fl_presets, fl_user_presets, encontradas por sí mismas). FL escribe uno para cualquier plugin desde su menú de plugins, incluidos los VST (los de un VST envuelto van a Fruity Wrapper - <name>), así que guardar un sonido allí lo hace utilizable por nombre.
  • Archivos de preset de proveedor para plugins JUCE (un preset XML) y los de Cableguys (#zip#), de las carpetas que la clave de configuración vst_presets nombra por plugin; un plugin JUCE no necesita una instancia guardada para esto: {"<plugin>": {"ext": ".vpreset", "dirs": {"": "<factory folder>", "user": "<user folder>"}}}.

Mapas de parámetros: abre una copia de la biblioteca en FL (el barrido deja cada perilla en 1) y envía el texto de scripts/params_fl.py a través de fl_exec una vez. Luego _kz_dump('tmp/params/pass1_all.json'); para cada v en 0, .25, .5, .75, 1, cuatro llamadas separadas de fl_exec, _kz_sweep(v, 'set'), _kz_sweep(v, 'nudge'), _kz_sweep(v, 'back'), _kz_read('tmp/params/sweep_<v>.json') (una lectura mostrada en la llamada que la estableció está desactualizada); luego python scripts/parammap.py escribe docs/params/. Cada función toma listas de only y skip de nombres de plugins tal como FL los reporta (docs/params/INDEX.md, columna as FL names it).

JSON de proyecto, forma compacta

{
 "format": "compact",
 "base": "projects/init/init.flp",
 "project": {"bpm": 175, "ppq": 96},
 "instruments": [{"id": "lead", "kind": "plugin", "plugin": "serum2", "preset": "C:/presets/lead.SerumPreset", "mixer": 1},
                 {"id": "keys", "kind": "plugin", "plugin": "flex", "preset": "Hard 808s/808 Clean Fuzz", "mixer": 2},
                 {"id": "pad", "kind": "plugin", "state": {"from": "projects/my_song.flp", "channel": 3}, "mixer": 3}],
 "samples": [{"id": "snare", "kind": "audio", "path": "C:/samples/snare.wav", "mixer": 8}],
 "automation": [{"id": "fade", "kind": "automation", "target": {"mixer": 0, "param": "vol"}, "points": [[0, 0.8], [32, 0.0]]}],
 "mixer": {"volume": {"1": 0.729, "7": 0.434}, "main_volume": 0.6625},
 "note_cols": ["bar", "beat", "tick", "len", "key", "vel", "inst"],
 "patterns": [{"id": "lead_a", "notes": [[1, 1, 4, 66, "A#4", 96, 0], [1, 2, 3, 18, "G#4", 80, 0]]}],
 "clip_cols": ["track", "bar", "beat", "tick", "len", "src", "trim?"],
 "arrangements": [{"name": "Arrangement", "clips": [[5, 17, 1, 0, 1536, "lead_a"], [4, 17, 3, 0, 31, "s0"], [20, 1, 1, 0, 27648, "a0"]]}]
}
  • Tiempo: [bar, beat, tick], compás y tiempo basados en 1, 96 ticks por tiempo, 4/4. Longitudes en ticks. Enteros exactos; no se impone una cuadrícula.
  • Notas: nombres de notas, MIDI 60 = C4 (la propia interfaz de FL llama MIDI 60 C5). Se aceptan bemoles.
  • Punteros: el inst de una nota es un índice en instruments. El src de un clip es un id de patrón, s<N> (muestra N) o a<N> (automatización N), así que ningún instrumento o id de patrón puede parecerse a s1 o a1.
  • Patrones contienen notas para cualquier número de instrumentos. El len de un clip establece cuánto de un patrón se reproduce.
  • Instrumentos: state: {from: file, channel: n} copia ese canal completo, preset y enrutamiento incluidos, de cualquier proyecto guardado por FL; o una plantilla plugin (ver La plantilla). preset carga un sonido en él: una ruta .SerumPreset, .vital, .h2p o .fxp, o <pack>/<preset> para FLEX. mixer: n lo enruta. Los .fxp de Serum 1 no son compatibles.
  • Canales de sampler: "kind": "sampler" con un path carga ese archivo en un sampler, así que las notas lo afinan (C4 = tono original). Un sampler reproduce su muestra hasta el final sin importar la longitud de la nota; corta los chops a sus propios archivos primero (chop.py).
  • Modo canción: los documentos compactos se guardan en modo canción para que FL y su renderizado por línea de comandos reproduzcan el arreglo; project.song_mode: false guarda el modo patrón.
  • Muestras: path, o state para copiar una. Un clip de audio sin trim reproduce toda la muestra; trim es [start_ms, end_ms] dentro de la muestra. Una ruta puede comenzar con %FLStudioFactoryData%, el propio token de FL para su carpeta de instalación, así que %FLStudioFactoryData%/Data/Patches/Packs/Drums/Kicks/808 Kick.wav encuentra el kick de fábrica de FL en cualquier máquina.
  • Información del proyecto: project toma title, author, genre y comment, escritos en la información del proyecto de FL (FL muestra el título en su ventana), y project_read los devuelve.
  • Automatización: points son [beat, value, tension?] en tiempos desde el inicio del clip. Objetivos: {"global": "tempo" | "main_pitch"}, {"mixer": t, "param": "vol" | "pan" | "sep"} (la inserción 0 es el maestro: su fader es el nivel general), {"mixer": t, "slot": s, "param": n} (parámetro de efecto n), {"mixer": t, "slot": s, "rec": "0x1f00"} (el mute de la ranura) o "0x1f01" (su mezcla), {"channel": id, "param": n} (parámetro de instrumento n) o {"channel": id, "param": "vol" | "pan" | "pitch" | "fcut" | "fres" | "mute"}. Valores: tempo = (bpm - 60) / 120; tono principal 0.5 afinado, +-12 semitonos; volumen del mezclador 0.8 = 0 dB; pan 0 izquierda, 0.5 centro, 1 derecha; sep 1 mono, 0 más ancho; tono de canal 0.5 afinado, +-48 semitonos; volumen de canal 0.78 es el predeterminado de FL. {"global": "main_vol"} se escribe pero FL 2025 lo ignora. examples/README.md lista las escalas medidas hasta ahora (fcut, fres y mute se escriben pero sus escalas no se midieron).
  • Efectos del mezclador: mixer.effects: [{"track": 0, "slot": 9, "from": {"file": "projects/init/fx.flp", "track": 2, "slot": 0}}] copia ese efecto con su configuración en la ranura dada, reemplazando lo que había allí.
  • Rutas del mezclador: mixer.routes: [{"from": 11, "to": 10, "level": 0.0}] enruta la inserción 11 a la inserción 10; level 0 es un envío solo de sidechain, que es lo que un limitador o compresor con sidechain escucha. "on": false elimina una ruta.
  • Volumen del mezclador: mixer.volume mapea inserción -> fader, 0.8 = 0 dB; main_volume es el volumen principal global de FL.
  • base: el archivo cuyos globales, secciones no mapeadas, plantillas de pista de arreglo y mezclador se conservan. Construir sobre uno de tus propios proyectos conserva sus cadenas de mezclador. Predeterminado: la clave template.

La forma compacta omite bytes que no están mapeados. Para editar un proyecto existente sin perder nada, usa view="full".

Aún no escrito

Panes y envíos del mezclador como configuraciones estáticas; un plugin que el administrador de plugins de FL no ha escaneado; nombres de patrones; firma de tiempo; archivos de preset de proveedor para VST cuyo estado no es ni XML JUCE ni #zip# (un .fst guardado desde FL es su ruta). Ver docs/FLP.md, Abrir.

Licencia

MIT, ver LICENSE. Cubre este código y estos documentos, no FL Studio, los plugins o sus presets.