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
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. ffmpegeffprobeno 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:
- Ele chama
config. Em uma instalação padrão, todos os caminhos são encontrados sozinhos (FL emProgram Files\Image-Line, o resto na sua pasta Documentos) e mostra comodetected; para qualquer coisa não encontrada, ele pergunta onde está e define; não adivinha. As chaves estão emkuruitsugizaki.example.json; o que você define é armazenado emkuruitsugizaki.jsonaqui, que o git ignora. - Ele chama
fl_install, que escreve o script de controle na pastaSettings\Hardwaredo FL com o caminho da ponte desta pasta preenchido, adiciona uma porta loopMIDI chamadakuruitsugizakie 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. - Somente quando
fl_installresponder 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
| arquivo | o que é |
|---|---|
scripts/server.py | o servidor MCP: ferramentas e o link ao vivo para o FL |
scripts/config.py | os caminhos da máquina, lidos de kuruitsugizaki.json |
setup.bat, setup.ps1 | a instalação em um passo (INSTALL.md, O caminho curto) |
Dockerfile | para 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.py | doctor.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.py | a 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.py | script de controle do FL; fl_install o coloca no lugar |
scripts/flp.py | codec de eventos .flp: analisar, serializar (byte-exato), notas, diff |
scripts/project.py | .flp -> JSON completo do projeto (round-trip byte-idêntico) |
scripts/view.py | visões compactas e resumidas, compacto -> completo |
scripts/writer.py | JSON do projeto -> .flp, baseado em template |
scripts/automation.py | pontos de automação (evento 234) e alvos (evento 227) |
scripts/levels.py | registros de fader do mixer e volume principal no evento 225 |
scripts/mixerfx.py | blocos de efeitos do mixer: leia um de qualquer projeto salvo pelo FL, coloque-o no slot de uma trilha |
scripts/presets.py | coloca 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.py | o formato de preset de cada sintetizador e <script> catalog [out.tsv]: uma linha TSV por preset nessa biblioteca |
scripts/catalog.py | catalog.py <preset folder> <out.tsv>: presets do Vital por estilo, osciladores, unison, efeitos ativos, nomes de macros |
scripts/render.py | render.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.py | render -> 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.py | LUFS integrado e momentâneo, LRA, pico verdadeiro, RMS; --zwicker adiciona loudness ISO 532-1 em sones |
scripts/scope.py | mapas de calor de densidade estéreo por momento e banda, --timeline para largura ao longo do tempo |
scripts/waveshape.py | A/B de vários renders em vários zooms ao redor do segundo mais alto de cada arquivo |
scripts/onsets.py | onsets de batidas de um WAV, para cortar loops |
scripts/chop.py | corta faixas de tempo de um arquivo em seus próprios WAVs com fades curtos, opcionalmente invertidos |
scripts/vox.py | vocal -> sílabas com um pitch mediano cada |
scripts/samples.py | samples.py <folder>: por arquivo, duração, BPM e tonalidade do nome, níveis, largura, onsets, centroide -> tmp/samples/<folder>.tsv |
scripts/melstats.py | visão geral de notas por instrumento de um projeto |
scripts/fxdump_fl.py | enviado 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.json | os 244 nomes de parâmetros do Sylenth1 em ordem de blob |
projects/init/init.flp | o template a partir do qual novos projetos são construídos |
projects/init/fx.flp | uma 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
| ferramenta | faz |
|---|---|
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/slot | efeito |
|---|---|
| 1/0 | Fruity Parametric EQ 2, salvo com uma banda definida (um high shelf em -18 dB); mova ou zere suas bandas antes de confiar nele |
| 2/0 | Fruity Limiter |
| 3/0 | Fruity Limiter em modo compressor, configurado para sidechain ducking |
| 4/0 | Fruity Reeverb 2 |
| 5/0 | Fruity Multiband Compressor |
| 6/0 | Soundgoodizer |
| 7/0 | Gross 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çãofl_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 paraFruity 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çãovst_presetsnomeia 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
instde uma nota é um índice eminstruments. Osrcde um clipe é um id de padrão,s<N>(amostra N) oua<N>(automação N), então nenhum instrumento ou id de padrão pode parecer coms1oua1. - Padrões contêm notas para qualquer número de instrumentos. O
lende 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 templateplugin(veja O template).presetcarrega um som nele: um caminho de.SerumPreset,.vital,.h2pou.fxp, ou<pack>/<preset>para FLEX.mixer: no roteia. Os.fxpdo Serum 1 não são suportados. - Canais de sampler:
"kind": "sampler"com umpathcarrega 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: falsesalva em modo de padrão. - Amostras:
path, oustatepara copiar uma. Um clipe de áudio semtrimtoca 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.wavencontra o kick de fábrica do FL em qualquer máquina. - Informações do projeto:
projectrecebetitle,author,genreecomment, escritos nas informações do projeto do FL (o FL mostra o título em sua janela), eproject_reados retorna. - Automação:
pointssã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.mdlista 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;level0 é um envio apenas de sidechain, que é o que um limitador ou compressor com sidechain escuta."on": falseremove um roteamento. - Volume do mixer:
mixer.volumemapeia 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.