kuruitsugizaki

Permite que um modelo escreva projetos do FL Studio (notas, qualquer plugin e preset instalado, mixer, sidechain, automação), abra-os em um FL em execução e os renderize.

Documentação

kuruitsugizaki

Um servidor MCP que permite a um modelo ler e escrever projetos do FL Studio e controlar um FL Studio em execução, para que ele possa compor música. O FL Studio é a primeira integração; a estrutura foi feita para alcançar mais softwares futuramente.

O modelo não escuta. Um render retorna como números e imagens (espectrogramas, loudness, largura estéreo), e a pessoa escuta e decide.

Factory Seance, dois minutos de witch house glitchy e breakcore, foi escrita do início ao fim pelo Claude Opus 5.5 através desta estrutura, em cerca de 35 minutos: cada nota, batida de bateria, escolha de som, rota de mixer e clipe de automação, usando apenas os sons de fábrica do FL. Ninguém editou; a pessoa que pediu escutou depois e manteve como estava. Ouça: examples/seance.mp3, o clipe com som: examples/demo.mp4, como foi feita: 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

Apenas Windows. Para instalar, siga INSTALL.md. Tudo além da configuração e das ferramentas (como escrever uma peça, o formato de arquivo, armadilhas, decisões) está em docs/, começando em docs/INDEX.md.

Requisitos

  • Windows, FL Studio 2025. Construído e medido no FL 25.1.6, API de script 38.
  • loopMIDI com pelo menos uma porta. O FL carrega o script de controle apenas quando sua porta de entrada MIDI existe.
  • Python 3.12 ou mais novo e uv.
  • ffmpeg e ffprobe no PATH, para os scripts de análise.
  • Serum 2 para usar o canal Serum 2 do template. O FLEX acompanha o FL.

Configuração

O caminho curto: feche o FL e clique duas vezes em setup.bat. Ele instala o que está faltando (uv, loopMIDI, opcionalmente ffmpeg), constrói o ambiente, instala o script de controle do FL, cria a porta MIDI e a configuração MIDI do FL, adiciona o servidor ao aplicativo de desktop do Claude e termina com uma verificação de que o FL responde. INSTALL.md lista cada etapa e onde ela termina.

Manual, o mesmo em quatro movimentos (INSTALL.md, Manual, tem o detalhe):

uv sync

Registre o servidor com o cliente MCP, com o caminho para esta pasta:

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

Então, em um chat com o modelo, com o FL fechado:

  1. Ele chama config. Em uma instalação padrão, todos os caminhos são encontrados sozinhos (FL em Program Files\Image-Line, o resto na sua pasta Documentos) e mostra como detected; para qualquer coisa não encontrada, ele pergunta onde está e define; não adivinha. As chaves estão em kuruitsugizaki.example.json; o que você define é armazenado em kuruitsugizaki.json aqui, que o git ignora.
  2. Ele chama fl_install, que escreve o script de controle na pasta Settings\Hardware do FL com o caminho da ponte desta pasta preenchido, adiciona uma porta loopMIDI chamada kuruitsugizaki e atribui o script a ela nas configurações MIDI do FL. Ambos vivem no registro, e o FL sobrescreve o próprio ao fechar, por isso o FL precisa estar fechado.
  3. Somente quando fl_install responder que não conseguiu fazer a configuração MIDI: no FL, Options, MIDI settings, selecione uma porta de entrada loopMIDI, tipo de controle kuruitsugizaki, e reinicie o FL uma vez.

A saída do script no FL mostra kuruitsugizaki {'api': 38, ...} ao carregar. .venv\Scripts\python scripts\doctor.py imprime uma linha por requisito, a qualquer momento.

O servidor inicia o loopMIDI e o próprio FL quando uma ferramenta ao vivo os encontra fechados.

Estrutura

arquivoo que é
scripts/server.pyo servidor MCP: ferramentas e o link ao vivo para o FL
scripts/config.pyos caminhos da máquina, lidos de kuruitsugizaki.json
setup.bat, setup.ps1a instalação em um passo (INSTALL.md, O caminho curto)
Dockerfilepara as verificações automatizadas dos diretórios MCP: executa o servidor no Linux, onde as ferramentas de arquivo funcionam e o link ao vivo para o FL responde que precisa de Windows
scripts/doctor.pydoctor.py [--install] [--claude [config]] [--live]: uma linha por requisito da configuração desta máquina, ok ou MISSING com o que fazer; --install executa fl_install, --claude adiciona o servidor à configuração do aplicativo de desktop do Claude, --live inicia o FL e pergunta sua versão da API
scripts/midisetup.pya porta loopMIDI e a configuração de entrada MIDI do FL, ambas no registro: lê-as e as cria quando nenhuma entrada tem o script
fl/device_kuruitsugizaki.pyscript de controle do FL; fl_install o coloca no lugar
scripts/flp.pycodec de eventos .flp: analisar, serializar (byte-exato), notas, diff
scripts/project.py.flp -> JSON completo do projeto (round-trip byte-idêntico)
scripts/view.pyvisões compactas e resumidas, compacto -> completo
scripts/writer.pyJSON do projeto -> .flp, baseado em template
scripts/automation.pypontos de automação (evento 234) e alvos (evento 227)
scripts/levels.pyregistros de fader do mixer e volume principal no evento 225
scripts/mixerfx.pyblocos de efeitos do mixer: leia um de qualquer projeto salvo pelo FL, coloque-o no slot de uma trilha
scripts/presets.pycoloca um preset no estado do canal de um plugin: .vital (Vital), .h2p (Diva), .fxp (Sylenth1), .SerumPreset (Serum 2), <pack>/<preset> (FLEX)
scripts/serum2.py, sylenth1.py, diva.py, flex.pyo formato de preset de cada sintetizador e <script> catalog [out.tsv]: uma linha TSV por preset nessa biblioteca
scripts/catalog.pycatalog.py <preset folder> <out.tsv>: presets do Vital por estilo, osciladores, unison, efeitos ativos, nomes de macros
scripts/render.pyrender.py <flp> [--close] [--reopen]: render por linha de comando do FL para wav e mp3 ao lado do projeto, depois estatísticas de espectro e estéreo. O FL deve estar fechado primeiro; --close pede a ele (nunca forçado)
scripts/spectro.pyrender -> PNG: espectrograma de piano roll em semitons, croma, espectrograma de 20 Hz-20 kHz, RMS/pico, correlação L/R; --bpm adiciona linhas de compasso. PNGs em tmp/spectro/
scripts/loudness.pyLUFS integrado e momentâneo, LRA, pico verdadeiro, RMS; --zwicker adiciona loudness ISO 532-1 em sones
scripts/scope.pymapas de calor de densidade estéreo por momento e banda, --timeline para largura ao longo do tempo
scripts/waveshape.pyA/B de vários renders em vários zooms ao redor do segundo mais alto de cada arquivo
scripts/onsets.pyonsets de batidas de um WAV, para cortar loops
scripts/chop.pycorta faixas de tempo de um arquivo em seus próprios WAVs com fades curtos, opcionalmente invertidos
scripts/vox.pyvocal -> sílabas com um pitch mediano cada
scripts/samples.pysamples.py <folder>: por arquivo, duração, BPM e tonalidade do nome, níveis, largura, onsets, centroide -> tmp/samples/<folder>.tsv
scripts/melstats.pyvisão geral de notas por instrumento de um projeto
scripts/fxdump_fl.pyenviado através de fl_exec: cada efeito em cada insert do projeto aberto com seus parâmetros
scripts/nudgemap.py, profiles/serum2/mapeamento de parâmetros de host do Serum 2 (estacionado)
profiles/sylenth1/params.jsonos 244 nomes de parâmetros do Sylenth1 em ordem de blob
projects/init/init.flpo template a partir do qual novos projetos são construídos
projects/init/fx.flpuma biblioteca dos próprios efeitos do FL para copiar
examples/sete documentos pequenos, um por parte do formato (instrumentos, efeitos, roteamento e sidechain, global, automação de mixer e instrumento), e uma peça completa de dois minutos com seu script, notas e render; examples/build.py constrói todos. Comece em examples/README.md
docs/params/mapas de parâmetros, um TSV por plugin: 27 dos próprios do FL e 22 VST3s, listados em docs/params/INDEX.md; a ferramenta params os lê

Ferramentas

ferramentafaz
config(key=None, value=None)sem argumentos: cada chave de caminho, seu valor ou o caminho detectado para ela, e se existe; com key e value: define
fl_install()instala o script de controle na pasta Hardware do FL com o caminho da ponte escrito e, com o FL fechado e a menos que uma entrada no FL já tenha o script, adiciona uma porta loopMIDI chamada kuruitsugizaki e atribui o script a ela nas configurações MIDI do FL (midisetup.py); a entrada midi da resposta diz o que foi feito ou o que resta fazer manualmente
project_read(path, view="compact", patterns=None, arrangements=None, unused=False).flp -> JSON. compact: linhas com ponteiros, a forma que project_write assume. summary: estatísticas por padrão e uma linha do tempo por trilha. full: forma exata de round-trip
project_write(doc, path, open=False, play=None)JSON (compacto ou completo) -> .flp; open o carrega no FL (iniciando o FL se necessário), play = song ou pattern
fl_render(path, reopen=True)salva o projeto aberto do FL, depois executa render.py <path> --close [--reopen] em separado; retorna o caminho do log em tmp/render_logs/
fl_transport(action, mode=None, pattern=None)tocar / parar, modo música / padrão
plugins(query=None)cada plugin que o FL tem instalado e cada instância no projeto de biblioteca opcional (veja Plugins por nome): nome, instrumento ou efeito, fornecedor, formato, fonte, quantos presets carregam por nome, se tem um mapa de parâmetros
presets(plugin, query=None, limit=200)os ids de preset de um plugin da biblioteca, filtrados por palavras; o id vai em project_write como preset
params(plugin, query=None)os parâmetros de um plugin da biblioteca de docs/params/: índice (para alvos de automação), nome, valor salvo e exibição, e a exibição do FL em 0, .25, .5, .75 e 1 onde varrido
fl_exec(code, out=None, timeout=10), fl_call(fn, args), fl_probe()acesso bruto à API Python do FL dentro do FL em execução; fl_exec retorna o que o código atribui a result, ou o escreve como JSON em out (também quando result é {'__out__': path, 'data': value})
test()retorna ok

project_read, project_write, plugins, presets e params recarregam os módulos de codec a cada chamada; uma mudança em server.py, flex.py ou diva.py precisa de um reinício do MCP, e uma nova ferramenta alcança um cliente apenas em uma sessão iniciada depois dela.

O template

projects/init/init.flp, salvo pelo FL: um Sampler, Serum 2, FLEX, um canal de áudio e um clipe de automação; master mais inserts 1-17, sem efeitos. Roteie para inserts 1-16: o insert 17 não recebe fader neste mixer (docs/NOTES.md). Um instrumento plugin é copiado do canal do template desse plugin, então com este template plugin assume serum2 ou flex. Os carregadores de preset também lidam com Vital, Diva e Sylenth1: para usá-los, salve um template próprio no FL segurando esses plugins e defina template para ele, ou pegue um canal de qualquer projeto com state: {from, channel}. Os plugins do template são correspondidos por nome: Serum2, FLEX, Vital, Sylenth1, Diva(x64).

Efeitos

O escritor não pode criar um efeito; ele copia um, com suas configurações, de um projeto salvo pelo FL. projects/init/fx.flp contém os próprios do FL:

do insert/slotefeito
1/0Fruity Parametric EQ 2, salvo com uma banda definida (um high shelf em -18 dB); mova ou zere suas bandas antes de confiar nele
2/0Fruity Limiter
3/0Fruity Limiter em modo compressor, configurado para sidechain ducking
4/0Fruity Reeverb 2
5/0Fruity Multiband Compressor
6/0Soundgoodizer
7/0Gross Beat

Soundgoodizer e Gross Beat carregam apenas em uma edição do FL que os inclui. Qualquer outro efeito instalado entra por nome (Plugins por nome).

Plugins por nome

Cada plugin que o FL tem instalado pode entrar em um projeto por nome, sem nada para preparar: o gerenciador de plugins do FL mantém uma entrada por plugin em seu banco de dados de plugins (Documents\Image-Line\FL Studio\Presets\Plugin database, chave de configuração plugin_db, encontrada sozinha), e o escritor constrói o plugin a partir dela nos blocos do template, novo, como arrastá-lo do navegador do FL faz. plugins os lista; project_write assume um instrumento como {"kind": "plugin", "plugin": "<name>", "preset": "<id>"} e um efeito como mixer.effects: [{"track": t, "slot": s, "plugin": "<name>", "preset": "<id>"}]. Um plugin instalado em vários formatos é um nome; adicione "format": "vst3" (ou native, clap, vst) para escolher um. Um plugin que o FL não escaneou não é listado: execute o gerenciador de plugins do FL primeiro. Opcionalmente, um projeto FL salvo pode conter instâncias ajustadas que você quer reutilizar como estão, cada instrumento em seu próprio canal, cada efeito em um slot do mixer; defina seu caminho como chave de configuração library. Uma instância de biblioteca vence uma nova instância do mesmo nome.

Presets, listados por presets:

  • Arquivos .fst, das pastas de presets de fábrica e de usuário do FL (chaves de configuração fl_presets, fl_user_presets, encontradas por si mesmas). O FL escreve um para qualquer plugin a partir do menu de plugins, incluindo VSTs (os de um VST encapsulado vão para Fruity Wrapper - <name>), então salvar um som lá o torna utilizável por nome.
  • Arquivos de preset de fornecedor para plugins JUCE (um preset XML) e para os da Cableguys (#zip#), das pastas que a chave de configuração vst_presets nomeia por plugin; um plugin JUCE não precisa de instância salva para isso: {"<plugin>": {"ext": ".vpreset", "dirs": {"": "<factory folder>", "user": "<user folder>"}}}.

Mapas de parâmetros: abra uma cópia da biblioteca no FL (a varredura deixa cada knob em 1) e envie o texto de scripts/params_fl.py através de fl_exec uma vez. Então _kz_dump('tmp/params/pass1_all.json'); para cada v em 0, .25, .5, .75, 1, quatro chamadas separadas de fl_exec, _kz_sweep(v, 'set'), _kz_sweep(v, 'nudge'), _kz_sweep(v, 'back'), _kz_read('tmp/params/sweep_<v>.json') (uma leitura de display na chamada que o definiu está desatualizada); então python scripts/parammap.py escreve docs/params/. Cada função recebe listas de only e skip de nomes de plugins como o FL os reporta (docs/params/INDEX.md, coluna as FL names it).

JSON de projeto, 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"]]}]
}
  • Tempo: [bar, beat, tick], compasso e batida baseados em 1, 96 ticks por batida, 4/4. Comprimentos em ticks. Inteiros exatos; nenhuma grade é imposta.
  • Notas: nomes de notas, MIDI 60 = C4 (a própria interface do FL chama MIDI 60 de C5). Bemóis aceitos.
  • Ponteiros: o inst de uma nota é um índice em instruments. O src de um clipe é um id de padrão, s<N> (amostra N) ou a<N> (automação N), então nenhum instrumento ou id de padrão pode parecer com s1 ou a1.
  • Padrões contêm notas para qualquer número de instrumentos. O len de um clipe define quanto de um padrão toca.
  • Instrumentos: state: {from: file, channel: n} copia aquele canal inteiro, preset e roteamento incluídos, de qualquer projeto salvo no FL; ou um template plugin (veja O template). preset carrega um som nele: um caminho de .SerumPreset, .vital, .h2p ou .fxp, ou <pack>/<preset> para FLEX. mixer: n o roteia. Os .fxp do Serum 1 não são suportados.
  • Canais de sampler: "kind": "sampler" com um path carrega aquele arquivo em um sampler, então notas o afinam (C4 = afinação original). Um sampler toca sua amostra até o fim, qualquer que seja o comprimento da nota; cortes são divididos em seus próprios arquivos primeiro (chop.py).
  • Modo de música: documentos compactos são salvos em modo de música para que o FL e sua renderização por linha de comando toquem o arranjo; project.song_mode: false salva em modo de padrão.
  • Amostras: path, ou state para copiar uma. Um clipe de áudio sem trim toca a amostra inteira; trim é [start_ms, end_ms] na amostra. Um caminho pode começar com %FLStudioFactoryData%, o token próprio do FL para sua pasta de instalação, então %FLStudioFactoryData%/Data/Patches/Packs/Drums/Kicks/808 Kick.wav encontra o kick de fábrica do FL em qualquer máquina.
  • Informações do projeto: project recebe title, author, genre e comment, escritos nas informações do projeto do FL (o FL mostra o título em sua janela), e project_read os retorna.
  • Automação: points são [beat, value, tension?] em batidas a partir do início do clipe. Alvos: {"global": "tempo" | "main_pitch"}, {"mixer": t, "param": "vol" | "pan" | "sep"} (o insert 0 é o master: seu fader é o nível geral), {"mixer": t, "slot": s, "param": n} (parâmetro de efeito n), {"mixer": t, "slot": s, "rec": "0x1f00"} (o mute do slot) ou "0x1f01" (seu mix), {"channel": id, "param": n} (parâmetro de instrumento n) ou {"channel": id, "param": "vol" | "pan" | "pitch" | "fcut" | "fres" | "mute"}. Valores: tempo = (bpm - 60) / 120; afinação principal 0.5 afinado, +-12 semitons; volume do mixer 0.8 = 0 dB; pan 0 esquerda, 0.5 centro, 1 direita; sep 1 mono, 0 mais largo; afinação do canal 0.5 afinado, +-48 semitons; volume do canal 0.78 é o padrão do FL. {"global": "main_vol"} é escrito, mas o FL 2025 o ignora. examples/README.md lista as escalas medidas até agora (fcut, fres e mute são escritos, mas suas escalas não foram medidas).
  • Efeitos do mixer: mixer.effects: [{"track": 0, "slot": 9, "from": {"file": "projects/init/fx.flp", "track": 2, "slot": 0}}] copia aquele efeito com suas configurações para o slot dado, substituindo o que estava lá.
  • Roteamentos do mixer: mixer.routes: [{"from": 11, "to": 10, "level": 0.0}] roteia o insert 11 para o insert 10; level 0 é um envio apenas de sidechain, que é o que um limitador ou compressor com sidechain escuta. "on": false remove um roteamento.
  • Volume do mixer: mixer.volume mapeia insert -> fader, 0.8 = 0 dB; main_volume é o volume principal global do FL.
  • base: o arquivo cujos globais, seções não mapeadas, templates de trilhas de arranjo e mixer são mantidos. Construir sobre um de seus próprios projetos mantém suas cadeias de mixer. Padrão: a chave template.

A forma compacta omite bytes que não são mapeados. Para editar um projeto existente sem perder nada, use view="full".

Ainda não escrito

Pans e envios do mixer como configurações estáticas; um plugin que o gerenciador de plugins do FL não escaneou; nomes de padrões; assinatura de tempo; arquivos de preset de fornecedor para VSTs cujo estado não é nem XML JUCE nem #zip# (um .fst salvo do FL é sua rota). Veja docs/FLP.md, Abrir.

Licença

MIT, veja LICENSE. Ela cobre este código e estas documentações, não o FL Studio, os plugins ou seus presets.