Koskamo

Doseedo Film Score MCP

An official MCP server by Doseedo that enables AI agents to score video content. It spots video cuts, generates cue sheets with SMPTE hit points, renders MIDI scores using 50+ AI instrument models, mixes audio, and lays the final score onto the picture with dialogue ducking and loudness normalization.

Endpoint and access

No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.

Doseedo Film Score MCP logo, by DoseedoVendor evidenceStreamable HTTPNo sign-inEndpoint checked 2026-11-01Operated by Doseedo
You need:OAuth on first use, or `--header "X-API-Key: dsk_live_..."`
MCP endpoint (Streamable HTTP)
https://api.doseedo.com/mcp/film
Vendor documentation

What you can do with it

  • Spot video cuts and generate frame-exact cue sheets
  • Render MIDI scores using AI instrument models
  • Mix cues and normalize loudness for delivery
  • Lay scored audio onto video with dialogue ducking

Source and maintenance

Vendor evidenceRegistry 0.21.0

Protocol and capabilities

Server
doseedo-film-score 0.21.0
MCP protocol
2025-06-18
Tools
24 advertised
Resources
Not advertised
Resource templates
Not advertised
Prompts
Not advertised
Server-advertised capabilities
{
  "tools": {}
}

Server-provided instructions

Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.

doseedo film-score = SCORE A VIDEO AS AN AGENT: doseedo watches the film, YOU write the music, doseedo plays it on 64 real instruments and lays it on the picture. THE LOOP: 1) doseedo_get_upload_url {filename:"film.mp4"} → curl PUT the file → video_key. 2) doseedo_spot_video {video_key} → job_id → doseedo_wait_task → the cue sheet: every cut frame-exact, each shot with a keyframe JPEG (LOOK at them — it sees light, motion and editing, not story), the dialogue spans, and cues 1M1, 1M2 … with SMPTE start/end, emotion, energy, hit points and a suggested bpm. 3) Per cue: doseedo_tempo_map {…cue.tempo_map_input} → tempo_map + the beat each hit lands on (beat 0 = the cue's first frame). 4) WRITE every part as notes on that grid [{pitch, start_beats, duration_beats, velocity}] — accents on the hit beats, thinner under dialogue; instruments from doseedo_instruments (copy an entry's call; ranges in concert pitch; a section is ONE part with players: N). 5) doseedo_render_score {parts, tempo_map} → a stem per part with a fit receipt. 6) doseedo_mix {from_job} → the cue mixed (seated, a hall, the written dynamics as the balance) + its mixgraph. 7) doseedo_score_to_picture {video_key, tracks:[{audio_url: <mix_url>, start_timecode: <the cue's>, name:"1M1"}…]} → the scored film (picture untouched), printmaster and M&E, with what it MEASURED: where each cue landed, the music under the lines in dB, the final LUFS and true peak. 8) Optional: doseedo_session_from_stems {from_job, mix_job} → a Logic / Ableton project per cue. A FILM WITH MUSIC ALREADY ON IT: doseedo_split_soundtrack {video_key} → its dialogue / music / effects stems + where the music plays. Keeping the production audio would mix the old music under yours. REPLACE: lay stems.dialogue + stems.effects (start_s 0, role "dialogue" / "effects") with your cues and production_audio:"drop". REMIX / RE-ORCHESTRATE: stems.music → doseedo_analyze_song (key, tempo, chords) / doseedo_separate_stems / doseedo_transcribe (its notes) → write it for your players. KEEP and score around it: write cues only where soundtrack.stems.music.spans shows none, or lay stems.music back with duck:false. RULES: (1) There is no prompt-to-score — you write the notes, doseedo performs them. (2) An AI render may miss a note: read each part's fit receipt and re-render a failed part (doseedo_render_midi revise_take_url redoes one region). (3) Async tools return job_id → doseedo_wait_task {job_id} (≤ 50 s a call; call again while still_running). (4) Result URLs are pre-signed for 24 h: plain curl, and pass one as another tool's audio_url as is. (5) Errors carry code / retryable / next — act on next; service_unavailable = wait 30 s, retry ONCE, then tell the user. (6) doseedo_quote prices a call before you make it: spotting and delivery spend no credits (a daily fair-use cap), rendering spends credits. SOUND DESIGN: doseedo_generate_sound {prompt, kind:"hit"} — impacts, whooshes, risers — laid with role:"sfx". YOUR OWN SOUND: doseedo_train_instrument (30 s–3 min of one instrument → lora_id). The steps with cost, time and how to verify: doseedo_recipes {name:"score-a-film"}. Every other doseedo tool (stems, transcription, DAW sessions, conversion) is on the full server, https://api.doseedo.com/mcp. AUTH: an X-API-Key header on this connection, or an OAuth sign-in (mint a key at https://doseedo.com/settings/api-keys).

Tools

24 advertised

doseedo_generate_sound — Generate sound effects (SFX & foley)
Advertised by the server during the last successful anonymous check.
Open-world

SOUND EFFECTS (SFX) from words with Stable Audio Open — SOUND DESIGN and FOLEY for game audio, apps and video: impacts, whooshes, door slams, footsteps, UI blips, creature and sci-fi sounds, ambiences ("rain on a tin roof", "busy café"), risers, drum one-shots. A one-shot hit ("punchy house kick", "snare with a short room", "a riser into the drop"), a loop on the tempo grid ("glitchy percussion"), or a texture ("vinyl crackle", "rain"). kind picks which (hit | loop | texture); omitted, the server reads it off the words. Don't put a tempo in a hit's prompt — tempo words make it write a loop. Wrong tool for a part with notes (doseedo_render_midi) or a piece of music (you write it; doseedo_render_score plays it). Async: job_id → doseedo_wait_task (typically 15–40 s) → audio URLs (pre-signed: curl -OJ <url>, no auth header, valid 24 h). Cancel: a render that has not reached the GPU yet is stopped and its credits refunded; one already rendering finishes on the GPU (its credits are spent), the job ends as canceled and the output is discarded.

doseedo_generate_sound input schema
{
  "type": "object",
  "required": [
    "prompt"
  ],
  "properties": {
    "bpm": {
      "type": "number",
      "description": "Tempo a loop is cut to (default 120)."
    },
    "kind": {
      "enum": [
        "hit",
        "loop",
        "texture"
      ],
      "type": "string",
      "description": "hit = one sound (0.5–10 s), loop = 1–4 bars tiled at bpm, texture = a bed. Default: read off the prompt."
    },
    "seed": {
      "type": "number",
      "description": "Optional random seed for reproducibility."
    },
    "prompt": {
      "type": "string",
      "description": "What it sounds like, e.g. \"punchy house kick drum, single hit\" or \"dusty vinyl crackle\". ≤ 300 characters."
    },
    "time_signature": {
      "type": "string",
      "description": "Meter a loop is cut to, e.g. \"4/4\" (default)."
    },
    "idempotency_key": {
      "type": "string",
      "description": "Optional. Any unique string for THIS request (e.g. a UUID). Re-sending the same call with the same key after a timeout returns the same job instead of starting a second one."
    },
    "duration_seconds": {
      "type": "number",
      "description": "Length in seconds. Default: 2 s for a hit, 4 bars at bpm for a loop, 8 s for a texture."
    }
  },
  "additionalProperties": false
}
doseedo_generate_sound annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
doseedo_render_midi — Render MIDI on AI instruments (MIDI to audio)
Advertised by the server during the last successful anonymous check.
Open-world

MIDI TO AUDIO, the virtual instrument for agents — THE tool for "I wrote the part, now play it": an AI instrument model (orchestral strings, brass, woodwinds, piano, guitar, bass, drums, World) performs your exact notes, in your key, at your bpm, and returns audio takes (doseedo_generate_music takes no notes or key). Input ONE of: notes[] ({pitch, start_beats, duration_beats, velocity} — beats from bar 1, the same rows as doseedo_edit_session set_midi_notes), midi_url (a .mid ≤2 MiB: a doseedo result link or a public https URL), or session_id + track_id (that track's notes, read from the session). Required: instrument (an entry's `call` from doseedo_instruments, no key — stay inside its range) and bpm. The notes are sent AS GIVEN — no pitch snapping, no quantizing. One voice per part: a chord part is rendered one voice per output with voice_split:true (result.voices maps each voice to its files and notes). Takes come back best-first (takes[0] = the scorer's winner). Async: job_id → doseedo_wait_task (~1–3 min). Result: takes[{url, rank, score}] (pre-signed: curl -OJ, no auth header, 24 h), seed, duration, voices?, refine?, chain. A take may miss or change a note — read takes[].check (measured against your notes) or listen; offer the takes, never promise note-perfection. Free harmony check first: doseedo_analyze_midi. DRUMS (GM notes): drum_engine:"oneshot" = a sampler kit made for drum_genre (default for "electronic"/"808"), every hit as written — use it on ANY kit when every hit must land; the drum model PERFORMS a kit and can drop hits (takes[].fit.missing). "synth" = a one-shot patch (synth_role, synth_genre; "performed" = played, less 1-shot); result.oneshots = the samples. SOUND: a preset (timbre_preset), a recording's tone (timbre_audio_url = timbre transfer), or YOUR OWN instrument (lora_id from doseedo_train_instrument; result.lora.applied). Cancel: a render that has not reached the GPU yet is stopped and its credits refunded; one already rendering finishes on the GPU (its credits are spent), the job ends as canceled and the output is discarded.

doseedo_render_midi input schema
{
  "type": "object",
  "required": [
    "instrument"
  ],
  "properties": {
    "bpm": {
      "type": "number",
      "description": "REQUIRED (or tempo_map). Tempo of the notes (10–400). Written into the MIDI's tempo meta, so start_beats/duration_beats land at exactly this tempo."
    },
    "seed": {
      "type": "number",
      "description": "Random seed (−1 or 0–4294967295). Reuse a result's seed to re-render close to the same take."
    },
    "tune": {
      "type": "boolean",
      "description": "Light intonation (default true): a held, clearly pitched note the model rendered 15–85 cents off its written pitch is moved toward it, to a small natural residual — attack and vibrato as played, fast notes left. Winds, brass and strings. false = the take exactly as rendered."
    },
    "notes": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "pitch",
          "start_beats",
          "duration_beats"
        ],
        "properties": {
          "pitch": {
            "type": "number",
            "description": "MIDI note number 0–127 (60 = middle C)."
          },
          "velocity": {
            "type": "number",
            "description": "1–127 (default 96)."
          },
          "start_beats": {
            "type": "number",
            "description": "Onset in beats from bar 1."
          },
          "duration_beats": {
            "type": "number",
            "description": "Length in beats (> 0)."
          }
        },
        "additionalProperties": false
      },
      "description": "The part, as note rows in beats from bar 1 (beat 0 = bar 1 beat 1, quarter-note beats at `bpm`). Monophonic for one voice; chords need voice_split:true. Up to 4096 notes."
    },
    "takes": {
      "type": "number",
      "description": "How many takes to render, 1–4 (one GPU batch; no extra credits). Omit to let the server choose best-of-N for the part length."
    },
    "prompt": {
      "type": "string",
      "description": "Optional caption override (default: the instrument id). Style words help a little; the notes are the control."
    },
    "refine": {
      "enum": [
        "light",
        "shred"
      ],
      "type": "string",
      "description": "Opt-in post-render correction. \"light\": attacks re-timed onto the score, exposed wrong pitch classes re-pitched inside the played octave, misfires muted (winds/brass/strings). \"shred\": play exactly the written line, octave included, mute anything the score never wrote (any pitched mono part). The raw render stays downloadable (result.refine.raw_url)."
    },
    "vaefix": {
      "type": "boolean",
      "description": "VAE spectral corrector (default true): trims the low-mid fog the decoder adds under the instrument's register and removes mic hiss only when the take carries it. Winds, brass and strings. Receipt: vaefix. false = the decode exactly as rendered."
    },
    "lora_id": {
      "type": "string",
      "description": "A CUSTOM INSTRUMENT: the lora_id doseedo_train_instrument returned (16 hex). The render is played by that trained instrument; pass the same instrument it was trained as. Owner-only."
    },
    "dynamics": {
      "type": "string",
      "description": "One target dynamic for the whole part's tone: \"pp\", \"p\", \"mp\", \"mf\", \"f\", \"ff\", \"fff\", a 0–1 number, or a MIDI velocity as text. Omit it to let the notes' velocities shape the tone (dynamics_curve)."
    },
    "midi_url": {
      "type": "string",
      "description": "Alternative to notes: https URL of a Standard MIDI File (≤2 MiB) — a doseedo result link (a stem's MIDI) or a public URL. Its own tempo map is used by the model; bpm still tags the caption and sizes the default duration."
    },
    "track_id": {
      "type": "string",
      "description": "With session_id: the t_… id of the MIDI/instrument track to render."
    },
    "tempo_map": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "beat",
          "bpm"
        ],
        "properties": {
          "bpm": {
            "type": "number"
          },
          "beat": {
            "type": "number"
          }
        },
        "additionalProperties": false
      },
      "description": "Tempo CHANGES for the notes: [{beat, bpm}] — each point holds from its beat (beats from bar 1) until the next; the first point is beat 0 (or bpm covers the beats before it). Written into the MIDI, so a ritardando or a tempo that lands a cue on a film hit plays exactly. doseedo_tempo_map builds one from hit times."
    },
    "drum_genre": {
      "type": "string",
      "description": "Electronic drums (instrument \"electronic\" or \"808\"): the genre whose one-shot kit plays the part — \"house\", \"techno\", \"trap\", \"drill\", \"lo-fi\", \"dnb\", \"amapiano\", \"synthwave\"… or free words. Default: the instrument."
    },
    "instrument": {
      "type": "string",
      "description": "REQUIRED. The instrument from doseedo_instruments — its `call.instrument` (e.g. \"violin\", \"tenor_sax\", \"electric_guitar\", \"piano\"; a World name such as \"koto\" is resolved to its call). Keep the notes inside its range. An unknown name close to a catalog one is refused with did_you_mean before anything is spent."
    },
    "session_id": {
      "type": "string",
      "description": "Alternative to notes: with track_id, render THAT session track's notes (read from doseedo_get_session's summary)."
    },
    "synth_role": {
      "enum": [
        "bass",
        "808",
        "pad",
        "lead",
        "pluck"
      ],
      "type": "string",
      "description": "instrument \"synth\": the patch Stable Audio Open makes for the part — bass (an 808 where the genre's bass is one), 808, pad, lead, pluck. Default: from the notes (chords = pad, a low line = bass, else lead)."
    },
    "drum_engine": {
      "enum": [
        "oneshot",
        "model"
      ],
      "type": "string",
      "description": "Who plays a drum part: \"oneshot\" = a sampler kit made for drum_genre (the default for electronic / 808; congas and bongos are recordings of the skins) — every note lands exactly as written; use it on ANY kit when every hit must land. \"model\" = the drum model (the default for every other kit): a performance, which can drop or smear hits — read takes[].fit. Its pads (GM): kick 35/36, snare 38/40, rim 37, clap 39, hats 42/44/46, toms 41–50, crash 49/57, ride 51/59, tambourine 54, cowbell 56, bongos 60/61, congas 62–64, shaker 69/70/82, guiro 73/74, claves 75, woodblock 76/77, triangle 80/81."
    },
    "synth_genre": {
      "type": "string",
      "description": "instrument \"synth\": the genre the patch is made for — \"house\", \"techno\", \"trap\", \"synthwave\", \"lo-fi\", \"dnb\"… or free words."
    },
    "synth_words": {
      "type": "string",
      "description": "instrument \"synth\": extra sound words for the patch (\"reese\", \"warm analog\", \"acid\"), ≤80 chars."
    },
    "voice_split": {
      "type": "boolean",
      "description": "true = the notes hold chords: split them into the voices one player of this instrument can hold and render every voice (result.voices). Default false (a chord in a mono part is an error the model will fudge)."
    },
    "synth_engine": {
      "enum": [
        "oneshot",
        "performed",
        "model"
      ],
      "type": "string",
      "description": "Who plays a synth part: \"oneshot\" = a patch of one-shots made for it (the default for \"synth\"; every note the same sample), \"performed\" = that same patch (synth_role / synth_genre / synth_words pick it) played by the synth model trained on real synth loops — each note its own, the tone moving, the written notes kept (lora_strength 0.5 default; seed = another performance), \"model\" = the instrument model alone."
    },
    "lora_strength": {
      "type": "number",
      "description": "With lora_id (or synth_engine \"performed\"): how strongly the custom instrument applies, 0–2 (default 1.0, or a tuned per-adapter default; ~0.6 when a small training set overshoots; performed synth 0.5 — up to ~0.75 plays it looser, past that it starts leaving the notes)."
    },
    "timbre_preset": {
      "type": "string",
      "description": "A preset from doseedo_instruments — an entry's call.timbre_preset or presets[].call.timbre_preset (e.g. \"violin:pizz\", \"sax:alto\", \"koto:preset1\"). Omit for the instrument default."
    },
    "dynamics_curve": {
      "type": "string",
      "description": "What colours the TONE over time: omitted/\"auto\" = the note velocities when they vary (soft notes darker, loud brighter — every instrument but drums, LoRAs included); \"off\" = one colour; \"cc11\"/\"cc1\" = that CC lane of a midi_url; or \"pos:level,…\" (pos and level 0–1, e.g. \"0:0.1,1:0.9\" = a crescendo). Receipt: result.dynamics."
    },
    "time_signature": {
      "type": "string",
      "description": "Meter of the part, e.g. \"4/4\", \"3/4\", \"6/8\" (default 4/4). Written into the MIDI and the caption."
    },
    "idempotency_key": {
      "type": "string",
      "description": "Optional. Any unique string for THIS request (e.g. a UUID). Re-sending the same call with the same key after a timeout returns the same job instead of starting a second one."
    },
    "revise_take_url": {
      "type": "string",
      "description": "REVISE an earlier take instead of rendering a new one: the take's URL (as a render returned it). Only [revise_start_beats, revise_end_beats) is regenerated — steered by `notes` (send the whole part, with your corrections) — and spliced back with short crossfades; the rest of the take is kept as it was."
    },
    "duration_seconds": {
      "type": "number",
      "description": "Length of the render in seconds (0.01–600). Default: the last note's end + 2 s of release. Each 30 s window costs 10 credits."
    },
    "revise_end_beats": {
      "type": "number",
      "description": "With revise_take_url: end of the region to redo (exclusive), beats from bar 1."
    },
    "timbre_audio_url": {
      "type": "string",
      "description": "TIMBRE TRANSFER: https URL of a short recording (≤24 MB; its first 30 s are used) whose SOUND the render should take on — a doseedo result link or a public URL. The notes stay yours; the tone comes from the recording."
    },
    "revise_start_beats": {
      "type": "number",
      "description": "With revise_take_url: start of the region to redo, beats from bar 1."
    }
  },
  "additionalProperties": false
}
doseedo_render_midi annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
doseedo_train_instrument — Train a custom instrument
Advertised by the server during the last successful anonymous check.
Open-world

Make a CUSTOM INSTRUMENT in your own sound: upload 30 s–3 min of ONE instrument playing (your own player, a rare or folk instrument, a prepared/invented instrument, a vintage synth) and doseedo trains a private adapter (LoRA) on its serving instrument model in about 1–2 minutes. After that, doseedo_render_midi {instrument: <the same instrument>, lora_id} plays ANY notes you write in that sound. Several takes help (up to 8 files; the first 3 min are used, ≥8 s required); a clean, solo, dry recording works best. instrument = the closest catalog instrument — it picks the model voice the adapter trains on (a shamisen trains as acoustic_guitar, a whistle as flute: doseedo_instruments lists each one's call.instrument). The notes are transcribed for you (pass notes to skip that). 20 credits. Result: lora_id, name, the windows it trained on, loss first→last, and `use` (the render args). Earlier ones: doseedo_list_jobs {kind:"train_instrument"}. Owner-only: no one else can render with it. Async: job_id → doseedo_wait_task (~1–2 min). Cancel: the plane has no stop for a running training — it runs to completion (credits spent), the job ends as canceled and its output is discarded. Only a job still queued at the plane is refunded by the plane's own failure refund.

doseedo_train_instrument input schema
{
  "type": "object",
  "required": [
    "instrument"
  ],
  "properties": {
    "bpm": {
      "type": "number",
      "description": "Optional tempo of the recording (30–300)."
    },
    "name": {
      "type": "string",
      "description": "A name for it, e.g. \"Grandpa's banjo\" (≤80 chars)."
    },
    "seed": {
      "type": "number",
      "description": "Random seed."
    },
    "notes": {
      "type": "array",
      "description": "Optional: the notes played, one list per file — [[{pitch, start, end, velocity?}…]] in SECONDS. Omitted, they are transcribed."
    },
    "steps": {
      "type": "number",
      "description": "Training steps 50–800. Default 300 for ≤60 s of audio, 400 above."
    },
    "audio_key": {
      "type": "string",
      "description": "R2 ingest key of an uploaded audio file (from doseedo_get_upload_url / POST /api/upload/r2/ingest-url; starts with \"ingest/\"). One recording; or audio_keys for several."
    },
    "audio_url": {
      "type": "string",
      "description": "https URL of a recording (a doseedo result link — a separated stem works — or any public URL). Alternative to audio_key."
    },
    "drum_mode": {
      "type": "boolean",
      "description": "true for an unpitched percussion recording."
    },
    "audio_keys": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Several uploaded takes of the SAME instrument (≤8)."
    },
    "audio_urls": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Several recording URLs of the same instrument (≤8)."
    },
    "instrument": {
      "type": "string",
      "description": "REQUIRED. The catalog instrument it is closest to (call.instrument from doseedo_instruments, e.g. \"violin\", \"acoustic_guitar\", \"flute\", \"percussion\")."
    },
    "idempotency_key": {
      "type": "string",
      "description": "Optional. Any unique string for THIS request (e.g. a UUID). Re-sending the same call with the same key after a timeout returns the same job instead of starting a second one."
    }
  },
  "additionalProperties": false
}
doseedo_train_instrument annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
doseedo_render_score — Render a whole score
Advertised by the server during the last successful anonymous check.
Open-world

FILM SCORING, orchestral arrangement, background music, a song or an instrumental YOU wrote: a WHOLE ARRANGEMENT in one call (a film cue, a string quartet, a big-band chart): every part played by its doseedo instrument, one stem per part from bar 1 at your tempo, plus a summed mix. Input ONE of: parts[] ({name, instrument, timbre_preset?, lora_id?, players?, notes[]} — doseedo_instruments calls), midi_url (multi-track SMF), or musicxml_url (notation played as marked, at concert pitch; what isn't: prepared.score.warnings). A file's parts are matched by name, then GM program; name any part with instruments {"<part name or index>": {instrument, timbre_preset?, players?}}. A chord on a one-line instrument is split into voices (one stem each). ★ SECTIONS: an instrument is ONE player; give a part players: N (2–8) and its line is played by N different players (each its own performance), summed into ONE section stem — that is how you write an orchestra: violins I / violins II / violas / cellos / basses as five parts with players 6 / 6 / 4 / 4 / 3, horns 2–4 in unison, a woodwind or brass line doubled; leave players out for a solo. The WHOLE orchestra in one call: ≤ 32 lines and ≤ 96 player rows (players: N = N rows), plain, sampled and World / LoRA instruments together (~1 min groups), ≤ 600 s. Drums where every hit must land: parts[].drum_engine "oneshot" (+ drum_genre). Synth parts: doseedo_render_midi. Timing: bpm and/or tempo_map ([{beat, bpm}]; doseedo_tempo_map lands film hits on downbeats). Result: parts[{name, instrument, url, fit, timbre, lora, players}] — fit.verdict / coverage = did it PLAY its notes (a failed part is re-rolled once; fit.rolls.missed = a roll not played as one), lora.applied = its adapter played — plus mix_url. Next: doseedo_mix {from_job}, then doseedo_session_from_stems {from_job, mix_job}. Async: job_id → doseedo_wait_task (~1–4 min). Cancel: a render that has not reached the GPU yet is stopped and its credits refunded; one already rendering finishes on the GPU (its credits are spent), the job ends as canceled and the output is discarded.

doseedo_render_score input schema
{
  "type": "object",
  "properties": {
    "bpm": {
      "type": "number",
      "description": "Tempo of the parts (10–400). Required with parts[] unless tempo_map is given; on a file it sets the speed and keeps the score's fermatas / rit. / tempo changes."
    },
    "mix": {
      "type": "boolean",
      "description": "Also bounce a summed mix of the stems (default true)."
    },
    "seed": {
      "type": "number",
      "description": "Random seed (reuse to re-render close to the same performance)."
    },
    "tune": {
      "type": "boolean",
      "description": "Light intonation on every winds / brass / strings part (default true): held, clearly pitched notes the model rendered 15–85 cents off are moved toward their written pitch, to a small natural residual — attacks, vibrato and fast notes as played. Receipt: parts[].tune. false = every part as rendered (or parts[].tune false for one)."
    },
    "parts": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "notes"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "tune": {
            "type": "boolean",
            "description": "false = this part exactly as rendered (no light intonation pass; default true)."
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "pitch",
                "start_beats",
                "duration_beats"
              ],
              "properties": {
                "pitch": {
                  "type": "number"
                },
                "velocity": {
                  "type": "number"
                },
                "start_beats": {
                  "type": "number"
                },
                "duration_beats": {
                  "type": "number"
                }
              },
              "additionalProperties": false
            }
          },
          "vaefix": {
            "type": "boolean",
            "description": "false = this part's decode exactly as rendered (no VAE spectral corrector; default true)."
          },
          "lora_id": {
            "type": "string"
          },
          "players": {
            "type": "number",
            "description": "How many players play this line: 1 (default) = a soloist; 2–8 = a SECTION — the line played by that many different players (each its own recording and performance), summed into ONE section stem. A string section = violins I, violins II, violas, cellos, basses with players 4–8 each. Each player renders (and costs) like a part."
          },
          "dynamics": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "beat",
                "velocity"
              ],
              "properties": {
                "beat": {
                  "type": "number"
                },
                "velocity": {
                  "type": "number"
                }
              },
              "additionalProperties": false
            },
            "description": "The part's written LEVEL over time, as a DAW automation lane: [{beat, velocity 1–127}] in beat order, linear between points, a step = two points on one beat, held after the last (p 52, mf 80, f 96, ff 112). A velocity says how a note STARTS; this says how it goes on — a crescendo on a held note: [{beat: 0, velocity: 52}, {beat: 8, velocity: 96}]. Its tone follows it, through a held note too, and doseedo_mix {from_job} rides the level to it. A MusicXML score's dynamics + hairpins arrive as this lane on their own."
          },
          "drum_genre": {
            "type": "string",
            "description": "With drum_engine \"oneshot\": the kit's genre / words, e.g. \"country rock, acoustic kit\", \"jazz brushes\", \"house\", \"trap\". An acoustic genre gets an acoustic kit in a small room; an electronic one a dry drum-machine kit. Default: the instrument."
          },
          "instrument": {
            "type": "string"
          },
          "drum_engine": {
            "enum": [
              "model",
              "oneshot"
            ],
            "type": "string",
            "description": "A drum / percussion part: \"oneshot\" = played through a one-shot kit generated for drum_genre — every written hit lands exactly as written (a half-time bar stays half-time, a stop-time hit stays a hit); \"model\" (default) = the drum model, a live performance that may drop or add hits (check parts[].fit.voices: missing / extra). Its pads (GM): kick 35/36, snare 38/40, rim 37, clap 39, hats 42/44/46, toms 41–50, crash 49/57, ride 51/59, tambourine 54, cowbell 56, bongos 60/61, congas 62–64, shaker 69/70/82, guiro 73/74, claves 75, woodblock 76/77, triangle 80/81."
          },
          "timbre_preset": {
            "type": "string"
          },
          "dynamics_curve": {
            "enum": [
              "auto",
              "off",
              "velocity",
              "cc11",
              "cc1"
            ],
            "type": "string",
            "description": "What colours this part's TONE over time: auto (default) = its dynamics lane when that moves, else its note velocities when they vary — soft darker, loud brighter, on every instrument but drums; off = one colour throughout; velocity = the velocities even with a lane; cc11/cc1 = that MIDI CC lane (a midi_url part). Receipt: parts[].dynamics."
          }
        },
        "additionalProperties": false
      },
      "description": "The parts you wrote: [{name?, instrument, timbre_preset?, lora_id?, players?, notes:[{pitch, start_beats, duration_beats, velocity?}], dynamics?}] — beats from bar 1, concert pitch. instrument/timbre_preset = a doseedo_instruments call. players = how many play the line: 1 (default) a soloist, 2–8 a SECTION (e.g. Violins I players 6)."
    },
    "vaefix": {
      "type": "boolean",
      "description": "VAE spectral corrector on every winds / brass / strings part (default true): the decoder's low-mid fog under each instrument's register is trimmed, mic hiss removed only where present. Receipt: parts[].vaefix. false = every decode as rendered (or parts[].vaefix false for one)."
    },
    "midi_url": {
      "type": "string",
      "description": "Alternative to parts: a multi-track Standard MIDI File (≤ 2 MiB) — a doseedo result link or a public https URL. Channel 10 = drums: the kit, or the one percussion instrument a part is named for (Bass Drum, Cymbals, Bass Drum & Cymbals, Triangle…). A track's CC11 lane (CC1 on a wind / brass / string / bass part) is its written dynamics: a held note swells as it says."
    },
    "score_key": {
      "type": "string",
      "description": "Alternative to parts: a score file on YOUR machine — doseedo_get_upload_url {filename: \"cue.mid\" | \"cue.musicxml\"} → PUT the file → its score_key (kept 24 h). A big score sent as a file, not as notes in this call."
    },
    "score_url": {
      "type": "string",
      "description": "Alternative to parts: a score file URL — MIDI or MusicXML, told apart by its content."
    },
    "tempo_map": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "beat",
          "bpm"
        ],
        "properties": {
          "bpm": {
            "type": "number"
          },
          "beat": {
            "type": "number"
          }
        },
        "additionalProperties": false
      },
      "description": "Tempo changes [{beat, bpm}] (first point at beat 0). Overrides a file's own tempo."
    },
    "instruments": {
      "type": "object",
      "description": "Name the instrument for any part: {\"Violin I\": {\"instrument\": \"violin\", \"players\": 6}, \"2\": {\"instrument\": \"acoustic_guitar\", \"timbre_preset\": \"koto:preset1\"}} — key = the part name or its 0-based index; players makes a MIDI / MusicXML part a section, drum_engine \"oneshot\" (+ drum_genre) plays a file's drum part on a one-shot kit ({\"Drums\": {\"drum_engine\": \"oneshot\"}} — no instrument keeps the part's own). Wins over the name/program match."
    },
    "musicxml_url": {
      "type": "string",
      "description": "Alternative to parts: a MusicXML score (.musicxml / .xml / .mxl, ≤ 8 MiB) exported from Dorico, Sibelius, Finale, MuseScore, or written by you."
    },
    "time_signature": {
      "type": "string",
      "description": "Meter, e.g. \"4/4\" (default, or the file's)."
    },
    "idempotency_key": {
      "type": "string",
      "description": "Optional. Any unique string for THIS request (e.g. a UUID). Re-sending the same call with the same key after a timeout returns the same job instead of starting a second one."
    },
    "duration_seconds": {
      "type": "number",
      "description": "Render length (default: the last note + 2 s; ≤ 600)."
    }
  },
  "additionalProperties": false
}
doseedo_render_score annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
doseedo_mix — Mix and master stems
Advertised by the server during the last successful anonymous check.
Open-world

MIXING and MASTERING: each part levelled, panned, high-passed, given its depth on a shared reverb, and mastered to a loudness target — a WAV AND `mixgraph`, the mix as a document you edit. After doseedo_render_score pass from_job (its parts and the instruments they play are the stems); or stems: [{name, audio_url, instrument?}] (result links work as-is). An orchestra (strings / winds / brass / timpani / harp…) is SEATED by default (film-orchestral: violins left, low strings right, brass behind, each seat at its depth in a 2.3 s hall, written dynamics riding the faders); other stems get the blind default. style: film-orchestral, film-scoring-stage, concert-hall, film-epic-hybrid, cathedral-choral, classical-chamber, cartoon-studio, golden-age-cartoon, jazz-combo, ambient, country-pop, metal-modern, 1950s-mono, 1960s-lcr-pop, 1970s-analog-rock, 1970s-dub, 1980s-pop-gated, 1990s-rock-dry, 1990s-hiphop, 2000s-loudness-rock, 2010s-edm, 2010s-trap, 2020s-pop, 2020s-hyperpop — an id or words ("dry 90s rock"). balance {part: dB} moves parts on top of the levelling (+3 brings a melody forward). Change the mix: edit the returned mixgraph — strips[] {stem, gain_db, pan, seat, chain, sends}, buses[] (verb = the hall), master.chain, automation [{target: "strips.<stem>.gain_db", points: [[seconds, dB]…]}] for rides (dB off the strip's gain_db: 0 = as set, -60 mutes) — and call again with mix_job (this job_id) + only your edits (or the whole mixgraph + the same stems): rendered as written, rails reported, no credits. Then doseedo_session_from_stems {from_job, mix_job: <this job_id>} → the DAW project at this mix's levels and pans. TO PICTURE: dialogue (the VO; the music ducks under each line), music_offset_s (bar 1 on the picture), length_s (cut + end fade). Async: job_id → doseedo_wait_task (~15–90 s). Cancel: the plane has no stop for a running mix — it runs to completion (credits spent), the job ends as canceled and its output is discarded. Only a job still queued at the plane is refunded by the plane's own failure refund.

doseedo_mix input schema
{
  "type": "object",
  "properties": {
    "duck": {
      "type": "object",
      "properties": {
        "max_db": {
          "type": "number"
        },
        "include": {
          "type": "boolean"
        },
        "carve_db": {
          "type": "number"
        },
        "vo_over_db": {
          "type": "number"
        }
      },
      "description": "How the music sits under the dialogue: {vo_over_db (default 7 dB — a broadcast mix's 6–10), carve_db (default 3, at 1–4 kHz), max_db (default 12), include (default true; false = the music bed alone, ducked as if the voice played)}.",
      "additionalProperties": false
    },
    "room": {
      "anyOf": [
        {
          "enum": [
            "studio",
            "stage",
            "hall",
            "cathedral"
          ],
          "type": "string"
        },
        {
          "type": "number",
          "maximum": 1,
          "minimum": 0
        }
      ],
      "description": "An orchestra's room: \"studio\" (intimate scoring stage, ~1.2 s, close), \"stage\" (~1.8 s), \"hall\" (the default scoring hall, ~2.3 s), \"cathedral\" (~4 s, wet) — or 0..1 between them. Sets the decay, size and how wet every seat sits."
    },
    "stems": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "name",
          "audio_url"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "audio_url": {
            "type": "string"
          },
          "instrument": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "description": "[{name, audio_url, instrument?}] — the stems (≤ 32). instrument = the sound it plays (a doseedo_instruments id: \"violin\", \"timpani\", \"harp\"), which decides how it is mixed; without it the name decides."
    },
    "style": {
      "type": "string",
      "description": "A mix style: an id or words (\"dry 90s rock\"). Pick it for the picture — the default (film-orchestral, a 2.3 s scoring hall) suits film drama, not every orchestra: cartoon-studio = a cartoon / animation orchestra (a close, dry, wide scoring stage, punchy rides); golden-age-cartoon = a 1940s–50s cartoon (mono, small studio, tape); film-scoring-stage = intimate TV / documentary; concert-hall; film-epic-hybrid = trailer / action; cathedral-choral; classical-chamber; or jazz-combo, ambient, 1970s-analog-rock, 2020s-pop …"
    },
    "balance": {
      "type": "object",
      "description": "{part name: dB in -12..12} — your balance on top of the levelling (and of the written dynamics a from_job reads).",
      "additionalProperties": {
        "type": "number"
      }
    },
    "mix_job": {
      "type": "string",
      "description": "MCP: a doseedo_mix job_id to edit — its whole stored mix (every ride) is the base, mixgraph is applied on top, and its stems are reused when you pass no from_job / stems."
    },
    "vintage": {
      "enum": [
        "tape",
        "shellac",
        "none"
      ],
      "type": "string",
      "description": "A period medium over any style, after the mix: \"tape\" = 7.5 ips tape (saturation, wow and flutter, a low hiss); \"shellac\" = a 78 played back (mono, ~90 Hz–5.5 kHz with the reproducer's presence, a 1.3 Hz wow, surface noise that swishes once a turn, crackle). \"none\" takes a style's own medium off (golden-age-cartoon carries tape). Its node (master.chain type vintage: noise_db, crackle, hi_hz …) is editable in the returned mixgraph."
    },
    "dialogue": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "audio_url"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "audio_url": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "description": "The VO / dialogue on the picture's timeline (≤ 4): [{name?, audio_url}]. The music ducks under each line until the voice sits duck.vo_over_db over it, with a 1–4 kHz carve while it talks; the voice joins after the music's master chain. Report: output.dialogue.lines [{start_s, end_s, duck_db, vo_over_music_db}] as mixed."
    },
    "dynamics": {
      "type": "object",
      "description": "Fader rides from written dynamics: {part name: [[onset_seconds, velocity 0-127], …]}. from_job fills it from the score (pp to ff ≈ 17 dB); pass {} for no rides.",
      "additionalProperties": {
        "type": "array"
      }
    },
    "from_job": {
      "type": "string",
      "description": "MCP: a doseedo_render_score job_id — its parts are the stems, each with the instrument it plays, and each part's written dynamics (its mean velocity) set its balance."
    },
    "length_s": {
      "type": "number",
      "maximum": 600,
      "minimum": 0.5,
      "description": "The program's length (a :30 = 30; ≤ 600 s): the mix is cut there with end_fade_s (default 0.5 s); the room's tail never runs past it."
    },
    "mixgraph": {
      "type": "object",
      "description": "An edited mix — the mixgraph a doseedo_mix returned, changed — rendered against the same stems (from_job or stems). With mix_job: ONLY what you change (strips[] by stem, automation[] rides by target — points: [] removes one, buses[] by id, master) — the rest is that mix as stored. A mixgraph marked `partial` (its rides summarized) is refused without mix_job. Omit for a fresh mix."
    },
    "end_fade_s": {
      "type": "number",
      "description": "With length_s: the fade at the program's end (0–10 s, default 0.5)."
    },
    "target_lufs": {
      "type": "number",
      "description": "Master loudness, -24..-6 LUFS (default: the style's — -18 for film-orchestral, -14 for the blind default). Met within ~0.3 LU (a loud target clips the shortest transients first); output.loudness says what landed, and why when short. With mix_job it re-masters the stored mix."
    },
    "tonal_match": {
      "type": "boolean",
      "description": "Nudge the master toward professionally mixed orchestral references (default on for the orchestral styles). Its presence and air are judged only where the score is written loud (written_level), so a pp passage keeps its darkness."
    },
    "dynamics_ride": {
      "type": "number",
      "description": "How hard the written dynamics move the faders: 1 = default (pp to ff ≈ 17 dB), 0 = no rides, up to 1.5."
    },
    "written_level": {
      "type": "array",
      "items": {
        "type": "array",
        "items": {
          "type": "number"
        },
        "maxItems": 2,
        "minItems": 2
      },
      "maxItems": 4096,
      "description": "[[seconds, velocity], …] — the whole score's written level, the loudest part sounding, held until the next row (0 = nothing plays). The tonal match reads only the passages it writes loud (f and louder), so a pp passage keeps its darkness. from_job fills it."
    },
    "music_offset_s": {
      "type": "number",
      "maximum": 600,
      "minimum": 0,
      "description": "Where bar 1 of the music sits on the picture (seconds, ≤ 600; the program ≤ 600 s): every music stem — and its rides — starts this late; the dialogue does not move."
    },
    "idempotency_key": {
      "type": "string",
      "description": "Optional. Any unique string for THIS request (e.g. a UUID). Re-sending the same call with the same key after a timeout returns the same job instead of starting a second one."
    },
    "dynamics_carried": {
      "type": "object",
      "description": "{part name: top velocity} — parts whose stems already play their written level (a render part's dynamics_level receipt, up to its top_vel). Their rides count it: no doubled swing. from_job fills it.",
      "additionalProperties": {
        "type": "number"
      }
    }
  },
  "additionalProperties": false
}
doseedo_mix annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
doseedo_session_from_stems — Stems → a DAW session
Advertised by the server during the last successful anonymous check.
Open-world

Build a native DAW PROJECT from stems: each stem on its own audio track from bar 1, optional MIDI tracks (the written parts) beside them, on your tempo (bpm or tempo_map) — Logic Pro (.logicx) or Ableton Live (.als), zipped. The deliverable after doseedo_render_score (pass from_job: its job_id and the stems, MIDI and tempo come from that job) or after any renders / separations you made (stems: [{name, audio_url}] — result links work as-is). After doseedo_mix add mix_job: each track at that mix's level and pan, the mix as a muted reference track. Async: job_id → doseedo_wait_task (~30–90 s) → file (pre-signed zip, curl -o session.zip <url>, 24 h). Cancel: the plane has no stop for a running session build — it runs to completion (credits spent), the job ends as canceled and its output is discarded. Only a job still queued at the plane is refunded by the plane's own failure refund.

doseedo_session_from_stems input schema
{
  "type": "object",
  "properties": {
    "bpm": {
      "type": "number",
      "description": "Project tempo (default 120)."
    },
    "name": {
      "type": "string",
      "description": "Project name."
    },
    "stems": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "pan": {
            "type": "number"
          },
          "mute": {
            "type": "boolean"
          },
          "name": {
            "type": "string"
          },
          "gain_db": {
            "type": "number"
          },
          "audio_key": {
            "type": "string"
          },
          "audio_url": {
            "type": "string"
          },
          "position_beats": {
            "type": "number"
          }
        },
        "additionalProperties": false
      },
      "description": "[{name, audio_url | audio_key, gain_db?, pan?, mute?, position_beats?}] — the audio tracks, in order."
    },
    "target": {
      "enum": [
        "logic",
        "ableton"
      ],
      "type": "string",
      "description": "Which DAW (default logic)."
    },
    "mix_job": {
      "type": "string",
      "description": "MCP: a doseedo_mix job_id — every track takes that mix's level and pan, and the mix itself rides along as a muted reference track \"Mix (doseedo)\"."
    },
    "from_job": {
      "type": "string",
      "description": "MCP: a doseedo_render_score job_id — its stems, parts' MIDI and tempo are used (no other input needed)."
    },
    "mixgraph": {
      "type": "object",
      "description": "A mix document (doseedo_mix → mixgraph): each stem named in it takes its strip's gain_db and pan. The EQ, compression, reverb and rides are not written into the project."
    },
    "tempo_map": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "beat",
          "bpm"
        ],
        "properties": {
          "bpm": {
            "type": "number"
          },
          "beat": {
            "type": "number"
          }
        },
        "additionalProperties": false
      },
      "description": "Tempo changes [{beat, bpm}] for the project."
    },
    "midi_tracks": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        },
        "additionalProperties": false
      },
      "description": "Optional MIDI tracks: [{name, notes:[{pitch, start_beats, duration_beats, velocity?}]}]."
    },
    "time_signature": {
      "type": "string",
      "description": "Meter (default 4/4)."
    },
    "idempotency_key": {
      "type": "string",
      "description": "Optional. Any unique string for THIS request (e.g. a UUID). Re-sending the same call with the same key after a timeout returns the same job instead of starting a second one."
    }
  },
  "additionalProperties": false
}
doseedo_session_from_stems annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
doseedo_spot_video — Spot a video → cue sheet
Advertised by the server during the last successful anonymous check.
Open-world

SPOT A FILM: a video in, the music editor's cue sheet out — the first step of scoring to picture. Scene detection on EVERY frame, and it listens to the soundtrack (CPU, no credits): hard cuts frame-exact (dark, low-contrast footage included), fades, flashes and visual impacts; each shot's brightness, colour, warmth, contrast, motion and mood, with a keyframe JPEG per shot so you can SEE what it shows; where dialogue is (Silero VAD) and loud audio hits; then cues placed where the picture wants music and the dialogue lets it (1M1, 1M2 …: start/end SMPTE timecode, emotion, energy, tension arc, hit points ranked by strength, a suggested bpm that lands them, and tempo_map_input — pass it to doseedo_tempo_map as is). It sees light, movement and editing, not meaning: read the keyframes for what each scene is about. Input: video_key (doseedo_get_upload_url accepts mp4/mov/m4v/webm/mkv) or video_url (public https), ≤ 2 GB and ≤ 3 h. Optional start_timecode (the first frame's, e.g. 01:00:00:00; default the file's own), coverage (0.05–0.98, default 0.55: how much of the film gets music), reel (cue ids <reel>M<n>), keyframes (0–60). Async: job_id → doseedo_wait_task (~1 min for a 10-min 1080p reel). Next: doseedo_tempo_map per cue → write the notes → doseedo_render_score → doseedo_mix → doseedo_score_to_picture. Cancel: the plane has no stop for a running spotting pass — it runs to completion (credits spent), the job ends as canceled and its output is discarded. Only a job still queued at the plane is refunded by the plane's own failure refund.

doseedo_spot_video input schema
{
  "type": "object",
  "properties": {
    "reel": {
      "type": "integer",
      "maximum": 99,
      "minimum": 1,
      "description": "Reel number for the cue ids (default 1 → 1M1, 1M2 …)."
    },
    "coverage": {
      "type": "number",
      "maximum": 0.98,
      "minimum": 0.05,
      "description": "How much of the film gets music (default 0.55). Raise it for wall-to-wall scoring (a trailer, an animation), lower it for a dialogue drama."
    },
    "keyframes": {
      "type": "integer",
      "maximum": 60,
      "minimum": 0,
      "description": "How many shots get a keyframe JPEG (default 24; the longest shots first)."
    },
    "video_key": {
      "type": "string",
      "description": "R2 ingest key of the uploaded video (doseedo_get_upload_url with the file name, e.g. reel1.mp4; starts with \"ingest/\")."
    },
    "video_url": {
      "type": "string",
      "description": "https URL of the video, as an alternative to video_key (a public file URL, fetched directly; ≤ 2 GB)."
    },
    "cut_threshold": {
      "type": "number",
      "maximum": 0.9,
      "minimum": 0.05,
      "description": "Scene-change threshold at full contrast (default 0.3). Lower finds softer cuts, higher only the hardest."
    },
    "start_timecode": {
      "type": "string",
      "description": "SMPTE timecode of the first frame, HH:MM:SS:FF (\";\" before the frames = drop-frame at 29.97/59.94). Default: the file's embedded timecode, else 00:00:00:00."
    },
    "idempotency_key": {
      "type": "string",
      "description": "Optional. Any unique string for THIS request (e.g. a UUID). Re-sending the same call with the same key after a timeout returns the same job instead of starting a second one."
    }
  },
  "additionalProperties": false
}
doseedo_spot_video annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
doseedo_score_to_picture — Lay the score on the picture
Advertised by the server during the last successful anonymous check.
Open-world

SCORE TO PICTURE: the last step of scoring a film. Lays each mixed cue (and any sound effect) on the video at its SMPTE timecode, with dialogue ducking (the lines found on the video's own soundtrack by VAD, or the spans you pass; the music dips ahead of each line and comes back after it), normalises the program to a delivery loudness (web −16, streaming −14, broadcast/ebu −23, atsc −24 LUFS, or a number; true peak under the ceiling) and muxes it onto the picture untouched (no re-encode). Returns the scored video, the printmaster WAV and the music-and-effects stem, plus a report of what it MEASURED: where each track landed (and what was cut at the film's edges), how far the music sits under the lines, and the final loudness and true peak. CPU, no credits. tracks: [{audio_url (a doseedo_mix mix_url as returned, or any public https URL) | audio_key, start_timecode (in the film's timecode) | start_s, gain_db?, role?: music|dialogue|effects, duck?, name?}], ≤ 64. Keep the production audio (default) or drop it (production_audio:"drop"). A FILM THAT ALREADY HAS MUSIC: keeping its audio mixes the old music under yours — split it first (doseedo_split_soundtrack) and lay its dialogue and effects stems back (start_s 0, role dialogue / effects) with production_audio:"drop": the old music is gone, the lines are kept, and the duck reads the lines off the dialogue stem; lay the old music stem back too with duck:false to keep it (or a part of it). With dialogue tracks the delivery also returns the dialogue / music / effects stems. Up to 25 minutes of film per call (a reel). Async: job_id → doseedo_wait_task. Cancel: the plane has no stop for a running delivery — it runs to completion (credits spent), the job ends as canceled and its output is discarded. Only a job still queued at the plane is refunded by the plane's own failure refund.

doseedo_score_to_picture input schema
{
  "type": "object",
  "required": [
    "tracks"
  ],
  "properties": {
    "duck": {
      "type": "object",
      "properties": {
        "sfx": {
          "type": "boolean"
        },
        "enabled": {
          "type": "boolean"
        },
        "depth_db": {
          "type": "number",
          "maximum": 40,
          "minimum": 0
        },
        "attack_ms": {
          "type": "number",
          "maximum": 3000,
          "minimum": 0
        },
        "release_ms": {
          "type": "number",
          "maximum": 5000,
          "minimum": 0
        }
      },
      "description": "Dialogue ducking (on by default): depth_db (default 10), attack_ms (250: how early before a line the music is down), release_ms (700), sfx (false: duck effects too), enabled.",
      "additionalProperties": false
    },
    "tracks": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "duck": {
            "type": "boolean",
            "description": "false = this music track is not ducked (the film's own music laid back from doseedo_split_soundtrack was mixed under its lines already). Default true."
          },
          "name": {
            "type": "string",
            "description": "A label for the report, e.g. 1M3."
          },
          "role": {
            "enum": [
              "music",
              "dialogue",
              "effects",
              "sfx"
            ],
            "type": "string",
            "description": "music (default; ducked under the dialogue), dialogue (never ducked — the lines the rest ducks under; a split soundtrack's dialogue stem), effects / sfx (not ducked unless duck.sfx)."
          },
          "gain_db": {
            "type": "number",
            "maximum": 24,
            "minimum": -60,
            "description": "Level trim (default 0)."
          },
          "start_s": {
            "type": "number",
            "description": "Or where it starts in seconds from the first frame."
          },
          "audio_key": {
            "type": "string",
            "description": "An ingest key instead of audio_url."
          },
          "audio_url": {
            "type": "string",
            "description": "The cue's mixed audio (doseedo_mix mix_url as returned) or any public https URL."
          },
          "start_timecode": {
            "type": "string",
            "description": "Where it starts, in the film's timecode (HH:MM:SS:FF) — the cue sheet's start_timecode."
          }
        },
        "additionalProperties": false
      },
      "maxItems": 64,
      "minItems": 1,
      "description": "What to lay on the film, in any order."
    },
    "dialogue": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "start_s",
          "end_s"
        ],
        "properties": {
          "end_s": {
            "type": "number"
          },
          "start_s": {
            "type": "number"
          }
        },
        "additionalProperties": false
      },
      "description": "Dialogue spans [{start_s, end_s}] to duck under, instead of detecting them (use the cue sheet's dialogue, or your own)."
    },
    "loudness": {
      "anyOf": [
        {
          "enum": [
            "web",
            "streaming",
            "broadcast",
            "ebu",
            "atsc"
          ],
          "type": "string"
        },
        {
          "type": "number"
        }
      ],
      "description": "Delivery loudness: \"web\" (−16 LUFS, default), \"streaming\" (−14), \"broadcast\" / \"ebu\" (−23), \"atsc\" (−24), or a LUFS number (−36…−8)."
    },
    "video_key": {
      "type": "string",
      "description": "R2 ingest key of the video (the same upload doseedo_spot_video used)."
    },
    "video_url": {
      "type": "string",
      "description": "https URL of the video, as an alternative to video_key."
    },
    "start_timecode": {
      "type": "string",
      "description": "SMPTE timecode of the film's first frame (default: the file's own, else 00:00:00:00) — the clock the tracks' start_timecode is read on."
    },
    "idempotency_key": {
      "type": "string",
      "description": "Optional. Any unique string for THIS request (e.g. a UUID). Re-sending the same call with the same key after a timeout returns the same job instead of starting a second one."
    },
    "production_audio": {
      "enum": [
        "keep",
        "drop"
      ],
      "type": "string",
      "description": "keep (default) mixes the video's own soundtrack — all of it, its music included — under the score; drop replaces it (lay a doseedo_split_soundtrack's dialogue + effects stems to keep the lines)."
    },
    "production_gain_db": {
      "type": "number",
      "maximum": 12,
      "minimum": -60,
      "description": "Trim on the production audio when kept (default 0)."
    }
  },
  "additionalProperties": false
}
doseedo_score_to_picture annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
doseedo_separate_stems — Separate stems (vocal remover)
Advertised by the server during the last successful anonymous check.
Open-world

STEM SEPARATION and VOCAL REMOVER: split an uploaded mix into stems (vocals, drums, bass, guitar, piano, other — or orchestral/auto-detected instruments) to isolate vocals (the vocals stem is the acapella; the other stems are the karaoke backing), with optional per-stem MIDI transcription and DAW-session export. Async: returns a job_id — poll with doseedo_wait_task (~2 minutes). Result: stems{name: url}, midi{name: url}, drum_substems{kick, snare, …} (drum_split), vocals_lyrics / vocals_notes, session_tracks + session_tempo_bpm when session_id was given, models_effective (what the auto family gate actually ran), other_classification (what the "other" stem was heard as), guitar_split (outcome). Cancel: the plane has no stop for a running separation — it runs to completion (credits spent), the job ends as canceled and its output is discarded. Only a job still queued at the plane is refunded by the plane's own failure refund.

doseedo_separate_stems input schema
{
  "type": "object",
  "properties": {
    "export": {
      "enum": [
        "logic",
        "ableton"
      ],
      "type": "string",
      "description": "Optionally also build a DAW session from the stems."
    },
    "models": {
      "enum": [
        "6stem",
        "auto",
        "orchestra",
        "detect"
      ],
      "type": "string",
      "description": "Separation model set. Default 6stem (vocals/drums/bass/guitar/piano/other); auto/orchestra/detect for wider instrument sets."
    },
    "audio_key": {
      "type": "string",
      "description": "R2 ingest key of an uploaded audio file (from doseedo_get_upload_url / POST /api/upload/r2/ingest-url; starts with \"ingest/\"). Provide this or audio_url."
    },
    "audio_url": {
      "type": "string",
      "description": "https URL of the audio, as an alternative to audio_key: a doseedo result URL exactly as a job returned it (a stem, a take, a bounce), or any public URL. The gateway fetches it (≤256 MB) into your account's ingest storage before the job starts. Pass audio_key OR audio_url."
    },
    "drum_split": {
      "type": "boolean",
      "description": "true = also split the drum stem into isolated kick / snare / hi-hat / toms / ride / crash (+ perc) sub-stems (result.drum_substems)."
    },
    "session_id": {
      "type": "string",
      "description": "Register the stems INTO this doseedo session (from doseedo_list_sessions) as audio tracks — result.session_tracks lists them. Needs parent_track_id."
    },
    "stems_only": {
      "type": "boolean",
      "description": "true = the stems and NOTHING derived from them (no MIDI, no lyrics, no latents) — the fastest, cheapest separation. Implies include_midi:false."
    },
    "instruments": {
      "type": "string",
      "description": "models=orchestra only: comma-separated extractor targets, e.g. \"saxophone,trumpet,trombone\". Available: drums, percussion, bass, double_bass, guitar, electric_guitar, acoustic_guitar, piano, organ, harp, brass, trumpet, trombone, french_horn, tuba, saxophone, clarinet, oboe, bassoon, flute, woodwind, strings, violin, viola, cello. Omit for the curated default set."
    },
    "guitar_split": {
      "type": "string",
      "description": "Deal the guitar stem into N players (same-instrument separation): \"2\"–\"4\", or \"auto\" (a one-vs-two probe decides; no split when it hears one). Outcome in result.guitar_split."
    },
    "include_midi": {
      "type": "boolean",
      "description": "Also transcribe each stem to MIDI (default true)."
    },
    "idempotency_key": {
      "type": "string",
      "description": "Optional. Any unique string for THIS request (e.g. a UUID). Re-sending the same call with the same key after a timeout returns the same job instead of starting a second one."
    },
    "parent_track_id": {
      "type": "string",
      "description": "With session_id: the t_… track the mix sits on; the stems are added as its children."
    }
  },
  "additionalProperties": false
}
doseedo_separate_stems annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
doseedo_split_soundtrack — Split a film's soundtrack (dialogue / music / effects)
Advertised by the server during the last successful anonymous check.
Open-world

SPLIT A FILM'S SOUNDTRACK (dialogue / music / effects — "DME" — separation): the film's own audio into three stems that sum back to it, plus a report of what it holds: whether there is music already, where it plays (spans in seconds) and how much of the film it covers, where the dialogue is (cleaner than VAD on the full mix: a song's singer stays in the music stem), each part's level, and how far the music sits under the lines. Use it before scoring a film that already has music (soundtrack.stems.music.present): to REPLACE that music, lay the dialogue and effects stems under your score with doseedo_score_to_picture (production_audio:"drop", tracks role "dialogue" / "effects"); to REMIX or RE-ORCHESTRATE it, the music stem goes to doseedo_separate_stems / doseedo_transcribe / doseedo_analyze_song; to KEEP it, score where the report shows no music. Input: video_key (the upload doseedo_spot_video read) or video_url; audio_key / audio_url for a soundtrack already on its own. One reel per call (≤ 30 min). The split is good, not perfect: the dialogue comes out clean, while some of the old music (its percussion most) reads as effects — about −10 dB of it stays in the dialogue + effects bed, which the new score masks. Async: job_id → doseedo_wait_task (about a third of the soundtrack's length: a 3-min scene ≈ 1 min, a 20-min reel ≈ 6 min). Cancel: the plane has no stop for a running split — it runs to completion (credits spent), the job ends as canceled and its output is discarded. Only a job still queued at the plane is refunded by the plane's own failure refund.

doseedo_split_soundtrack input schema
{
  "type": "object",
  "properties": {
    "audio_key": {
      "type": "string",
      "description": "An ingest key of the soundtrack on its own (WAV/FLAC/MP3/M4A), instead of the film."
    },
    "audio_url": {
      "type": "string",
      "description": "https URL of the soundtrack on its own, instead of the film."
    },
    "video_key": {
      "type": "string",
      "description": "R2 ingest key of the film (the same upload doseedo_spot_video read; starts with \"ingest/\")."
    },
    "video_url": {
      "type": "string",
      "description": "https URL of the film, as an alternative to video_key (fetched through the URL up to 256 MB; upload a bigger film)."
    },
    "idempotency_key": {
      "type": "string",
      "description": "Optional. Any unique string for THIS request (e.g. a UUID). Re-sending the same call with the same key after a timeout returns the same job instead of starting a second one."
    }
  },
  "additionalProperties": false
}
doseedo_split_soundtrack annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
doseedo_transcribe — Audio to MIDI (transcribe notes)
Advertised by the server during the last successful anonymous check.
Open-world

AUDIO TO MIDI transcription: a melody, a bass line or a part as notes (pitch, onset/offset seconds, velocity, confidence as JSON — not a MIDI file; for .mid files per stem use doseedo_separate_stems with transcription or doseedo_audio_to_session). Works from an uploaded audio_key or an audio_url — any public https URL, including a result URL from another doseedo job (a generate take, a stem) as it was returned. Async: returns a job_id — poll with doseedo_wait_task (usually ~15–60s). For the KEY / tempo / chords of a whole mix use doseedo_analyze_song instead. Cancel: the plane has no stop for a running transcription — it runs to completion (credits spent), the job ends as canceled and its output is discarded. Only a job still queued at the plane is refunded by the plane's own failure refund.

doseedo_transcribe input schema
{
  "type": "object",
  "properties": {
    "want": {
      "enum": [
        "notes",
        "f0",
        "notes,f0"
      ],
      "type": "string",
      "description": "What to return: \"notes\" (default), \"f0\" (the continuous pitch contour — frame-rate Hz values, for glides/vibrato/intonation), or both."
    },
    "chairs": {
      "type": "string",
      "description": "polyphony=\"section\": register codes per chair, as an alternative to roster."
    },
    "family": {
      "type": "string",
      "description": "Optional instrument family (e.g. \"brass\", \"reeds\", \"strings\") when the exact instrument is unknown."
    },
    "roster": {
      "type": "string",
      "description": "polyphony=\"section\": the chairs on the stem, e.g. \"trumpet,trumpet,trombone,tenor_sax\". Omit for the family default."
    },
    "backend": {
      "type": "string",
      "description": "Advanced: force a transcription backend. Omit — the server routes by instrument."
    },
    "strikes": {
      "type": "boolean",
      "description": "With tempo_bpm: also return the harmony_strikes attack grid."
    },
    "register": {
      "type": "string",
      "description": "Optional register hint for the instrument (e.g. \"soprano\", \"tenor\", \"bass\")."
    },
    "audio_key": {
      "type": "string",
      "description": "R2 ingest key of an uploaded audio file (from doseedo_get_upload_url / POST /api/upload/r2/ingest-url; starts with \"ingest/\"). Provide this or audio_url."
    },
    "audio_url": {
      "type": "string",
      "description": "https URL of the audio to transcribe (alternative to audio_key): any public URL, or a doseedo result URL exactly as a job returned it — no key needed."
    },
    "polyphony": {
      "type": "string",
      "description": "Optional: \"solo\" (monophonic line), \"poly\", or \"section\" (a horn section — see roster/chairs)."
    },
    "tempo_bpm": {
      "type": "number",
      "description": "Optional known tempo — enables beat-aligned note times."
    },
    "instrument": {
      "type": "string",
      "description": "Optional instrument hint, e.g. \"piano\", \"sax\", \"vocals\", \"bass\"."
    },
    "beats_per_bar": {
      "type": "number",
      "description": "With tempo_bpm: beats per bar for the beat-domain times (default 4)."
    },
    "idempotency_key": {
      "type": "string",
      "description": "Optional. Any unique string for THIS request (e.g. a UUID). Re-sending the same call with the same key after a timeout returns the same job instead of starting a second one."
    }
  },
  "additionalProperties": false
}
doseedo_transcribe annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
doseedo_analyze_song — Analyze a song
Advertised by the server during the last successful anonymous check.
Open-world

Key detection, BPM/tempo detection, song structure and chords — musical analysis of a recording in one call: key (e.g. "D minor"), bpm + per-beat tempo_map, meter / beats_per_bar, downbeats (seconds), sections (intro/verse/chorus… with times), chords (a progression on the beat grid), dynamics. USE THIS to learn the key and tempo of a reference before writing a part — never guess or ask the user for what the audio already says. Pass audio_key (an upload) or audio_url (a result URL from another job as returned, or any public https URL). Up to 10 minutes of audio is analyzed. Synchronous (~5–20 s): the result is in this response. No separation, so a dense mix's chords are a draft — for a chord chart from stems use doseedo_detect_chords on the separated stems. Cancel: not applicable — the result is returned in the submit response; the job is already finished.

doseedo_analyze_song input schema
{
  "type": "object",
  "properties": {
    "audio_key": {
      "type": "string",
      "description": "R2 ingest key of an uploaded audio file (from doseedo_get_upload_url / POST /api/upload/r2/ingest-url; starts with \"ingest/\"). Provide this or audio_url."
    },
    "audio_url": {
      "type": "string",
      "description": "https URL of the audio (a doseedo result URL as returned, or any public URL). Alternative to audio_key."
    },
    "idempotency_key": {
      "type": "string",
      "description": "Optional. Any unique string for THIS request (e.g. a UUID). Re-sending the same call with the same key after a timeout returns the same job instead of starting a second one."
    }
  },
  "additionalProperties": false
}
doseedo_analyze_song annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
doseedo_check_take — Check a take against its notes
Advertised by the server during the last successful anonymous check.
Open-world

MEASURE a take instead of guessing: give the audio (a render_midi take URL, any recording) and the notes it should play — notes[] + bpm (and tempo_map) or a midi_url — and get back how many written notes were heard at the right pitch and time (recall / precision / f1, octave errors, the first missed notes with their beats), the timing error in ms, loudness (integrated LUFS, peak dBFS, clipped share), any stretch where a note was written but nothing sounds, and a verdict (good / check / bad) with reasons in plain words. doseedo_render_midi results already carry this per take (takes[].check) when the part was given as notes. Method: an independent note transcriber (basic-pitch) — dense chords and very fast runs read less reliably; use it as a screen, and listen to anything marked check. Synchronous (~3–15 s), CPU, no credits. Cancel: not applicable — the result is returned in the submit response; the job is already finished.

doseedo_check_take input schema
{
  "type": "object",
  "properties": {
    "bpm": {
      "type": "number",
      "description": "Tempo of the notes (or tempo_map)."
    },
    "notes": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "pitch",
          "start_beats",
          "duration_beats"
        ],
        "properties": {
          "pitch": {
            "type": "number"
          },
          "velocity": {
            "type": "number"
          },
          "start_beats": {
            "type": "number"
          },
          "duration_beats": {
            "type": "number"
          }
        },
        "additionalProperties": false
      },
      "description": "The notes the take should play, in beats from bar 1 (the rows you sent doseedo_render_midi)."
    },
    "midi_url": {
      "type": "string",
      "description": "Alternative to notes: the part as a Standard MIDI File (≤ 2 MiB)."
    },
    "audio_key": {
      "type": "string",
      "description": "R2 ingest key of an uploaded audio file (from doseedo_get_upload_url / POST /api/upload/r2/ingest-url; starts with \"ingest/\"). Alternative to audio_url."
    },
    "audio_url": {
      "type": "string",
      "description": "The take: a doseedo result URL as returned, or any public https URL."
    },
    "tempo_map": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "beat",
          "bpm"
        ],
        "properties": {
          "bpm": {
            "type": "number"
          },
          "beat": {
            "type": "number"
          }
        },
        "additionalProperties": false
      },
      "description": "Tempo changes [{beat, bpm}]."
    },
    "instrument": {
      "type": "string",
      "description": "The instrument (a drum/percussion name switches to onset-only matching)."
    },
    "time_signature": {
      "type": "string",
      "description": "Meter (default 4/4)."
    },
    "idempotency_key": {
      "type": "string",
      "description": "Optional. Any unique string for THIS request (e.g. a UUID). Re-sending the same call with the same key after a timeout returns the same job instead of starting a second one."
    },
    "onset_tolerance_ms": {
      "type": "number",
      "description": "How far a heard onset may be from the written one and still count (default 50)."
    }
  },
  "additionalProperties": false
}
doseedo_check_take annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
doseedo_analyze_midi — Analyze MIDI harmony
Advertised by the server during the last successful anonymous check.
Open-world

Check the HARMONY of notes before rendering or placing them: factor a MIDI progression into its sonorities (mode "midi", default — chord-detection-free: each segment of simultaneously sounding pitches reduced to an anchor pitch class + register-exact shape, covered with recurring shape cells) or into chord symbols (mode "chord": one estimated chord per ~2 beats + a corpus-grammar cell cover with a schedule). Input: notes[] (the rows you would give doseedo_render_midi / set_midi_notes) with bpm, or midi_url (a .mid ≤2 MiB: a doseedo result link such as a stem's MIDI, or a public URL). Synchronous (~2–10 s), CPU, no credits. Returns sonorities[] or chords[], cover, cells, compression — read chords[].chord / sonorities[].anchor to confirm the key and progression you intended. Cancel: not applicable — the result is returned in the submit response; the job is already finished.

doseedo_analyze_midi input schema
{
  "type": "object",
  "properties": {
    "bpm": {
      "type": "number",
      "description": "Tempo of the notes (needed with notes; default 120)."
    },
    "mode": {
      "enum": [
        "midi",
        "chord"
      ],
      "type": "string",
      "description": "\"midi\" (default): sonority / shape factoring. \"chord\": chord symbols per segment + cell cover."
    },
    "notes": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "pitch",
          "start_beats",
          "duration_beats"
        ],
        "properties": {
          "pitch": {
            "type": "number"
          },
          "velocity": {
            "type": "number"
          },
          "start_beats": {
            "type": "number"
          },
          "duration_beats": {
            "type": "number"
          }
        },
        "additionalProperties": false
      },
      "description": "The notes to analyze, in beats from bar 1 (same rows as doseedo_render_midi). Needs bpm."
    },
    "midi_url": {
      "type": "string",
      "description": "Alternative to notes: https URL of a Standard MIDI File (≤2 MiB)."
    },
    "min_seg_beats": {
      "type": "number",
      "description": "mode midi: shortest sonority segment in beats (default 0.25)."
    },
    "time_signature": {
      "type": "string",
      "description": "Meter of the notes, e.g. \"4/4\" (default)."
    },
    "idempotency_key": {
      "type": "string",
      "description": "Optional. Any unique string for THIS request (e.g. a UUID). Re-sending the same call with the same key after a timeout returns the same job instead of starting a second one."
    },
    "beats_per_segment": {
      "type": "number",
      "description": "mode chord: segment length in beats (0.25–8, default 2)."
    }
  },
  "additionalProperties": false
}
doseedo_analyze_midi annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
doseedo_instruments — Instrument catalog (the sound palette)
Advertised by the server during the last successful anonymous check.
Advertised read-only

The instrument list — the sound palette: every instrument doseedo can PLAY YOUR NOTES on (doseedo_render_midi) — orchestral strings, brass and winds, guitars, basses, keys, drums and percussion, and World instruments (koto, sitar, banjo, pedal steel, tabla, congas…). Each entry gives the EXACT render call (`call`: instrument, plus timbre_preset where one is needed — a koto is the acoustic_guitar model with the koto timbre), its playable range in concert MIDI pitch, whether it plays chords, its presets (pizz, muted, sax registers…) and a demo render. No args → the whole catalog; query:"warm low brass" / family / pitches:[…] (instruments whose range holds them) / chords:true narrow it; id:"cello" → one instrument in full. Read-only, free, no key needed — read it before writing a part.

doseedo_instruments input schema
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "One instrument (id, alias or label, e.g. \"cello\", \"upright bass\", \"koto\") → its full entry: every preset with its own call and range, transposition, aliases, demo, page."
    },
    "query": {
      "type": "string",
      "description": "What the part needs, in words: an instrument (\"trumpet\"), a family (\"woodwinds\"), a technique (\"pizzicato\", \"muted\") or a register (\"low\", \"bright\"). Ranked best-first."
    },
    "chords": {
      "type": "boolean",
      "description": "true = only instruments one player can play chords on."
    },
    "family": {
      "enum": [
        "strings",
        "brass",
        "winds",
        "piano",
        "guitar",
        "bass",
        "drums",
        "world",
        "pitched_percussion",
        "unpitched_percussion",
        "synth"
      ],
      "type": "string",
      "description": "Only this family."
    },
    "pitches": {
      "type": "array",
      "items": {
        "type": "number"
      },
      "description": "MIDI pitches the part uses (concert, 60 = C4) — only instruments whose range holds every one."
    }
  },
  "additionalProperties": false
}
doseedo_instruments annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
doseedo_tempo_map — Tempo map from hit points
Advertised by the server during the last successful anonymous check.
Advertised read-only

Scoring to picture: turn HIT POINTS (a cut, an impact, the title card — as seconds from the cue start or SMPTE timecodes) into a TEMPO MAP that lands each hit exactly on a downbeat (or a beat / an eighth — per hit with hits[].land), staying near your tempo and inside bpm_range. An AD or any cue that must hold ONE tempo: fixed_bpm: true (+ max_deviation_bpm, tolerance_ms) finds the tempo and the bar-1 offset (music_offset_s) landing the most hits, and says which ones it could not. Returns tempo_map [{beat, bpm}] to pass unchanged to doseedo_render_midi / doseedo_render_score / doseedo_session_from_stems, and hits[] with the beat, bar and beat-in-bar each hit lands on — write the accent, stinger or change there. Handles 23.976 / 24 / 25 / 29.97 (";" = drop-frame) / 30 / 50 / 59.94 / 60 fps timecode. Pure math: free, instant, no key needed.

doseedo_tempo_map input schema
{
  "type": "object",
  "required": [
    "hits",
    "bpm"
  ],
  "properties": {
    "bpm": {
      "type": "number",
      "description": "The cue's tempo (20–300) — the map stays as close to it as the hits allow."
    },
    "fps": {
      "type": "number",
      "description": "Frame rate of the timecodes: 23.976, 24, 25, 29.97, 30, 50, 59.94, 60."
    },
    "hits": {
      "type": "array",
      "items": {
        "anyOf": [
          {
            "type": "number"
          },
          {
            "type": "object",
            "properties": {
              "land": {
                "enum": [
                  "downbeat",
                  "beat",
                  "eighth"
                ],
                "type": "string"
              },
              "label": {
                "type": "string"
              },
              "time_s": {
                "type": "number"
              },
              "timecode": {
                "type": "string"
              }
            },
            "additionalProperties": false
          }
        ]
      },
      "description": "The moments the music must land on: [{time_s} (seconds from the cue start) or {timecode: \"01:00:12:08\"} (film timecode, with fps and start_timecode), label?, land?}] — land = THIS hit's landing (\"downbeat\", \"beat\" or \"eighth\"; default: the request's land), e.g. the big cuts on bar lines and a quick accent on an eighth. A bare number = {time_s}."
    },
    "land": {
      "enum": [
        "downbeat",
        "beat",
        "eighth"
      ],
      "type": "string",
      "description": "Land hits on bar lines (default), on any beat, or on any eighth — the default for every hit without its own land."
    },
    "start_s": {
      "type": "number",
      "description": "Alternative: the cue start in seconds of film time (informational when hits are time_s)."
    },
    "bpm_range": {
      "type": "array",
      "items": {
        "type": "number"
      },
      "description": "Allowed tempo range [min, max] (default ±12 % of bpm)."
    },
    "fixed_bpm": {
      "type": "boolean",
      "description": "ONE tempo for the whole cue (an ad, a song-form cue): the solver picks the tempo (within max_deviation_bpm of bpm; default exactly bpm) and where bar 1 sits (music_offset_s, default within the first bar; offset_range_s) so the most hits land within tolerance_ms (default 40). Hits it cannot land come back landed:false with nearest_beat — never an error."
    },
    "tolerance_ms": {
      "type": "number",
      "description": "SOFT hits (0–250 ms): a hit within this of the grid counts as landed, and one the grid can't reach is reported (landed:false), never refused. Every hit then carries error_ms; hit_rate = landed / all."
    },
    "offset_range_s": {
      "type": "array",
      "items": {
        "type": "number"
      },
      "description": "fixed_bpm: where bar 1 may sit on the cue, [min, max] seconds (default [0, one bar])."
    },
    "start_timecode": {
      "type": "string",
      "description": "Film timecode of the cue's first downbeat (bar 1); hit timecodes are measured from it."
    },
    "time_signature": {
      "type": "string",
      "description": "Meter (default 4/4)."
    },
    "max_deviation_bpm": {
      "type": "number",
      "description": "How far the tempo may move from bpm (both modes; in place of bpm_range). A variable map at 124 ±12 % can swing 11+ bpm between hits — 3 keeps it a steady groove."
    }
  },
  "additionalProperties": false
}
doseedo_tempo_map annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
doseedo_get_upload_url — Get an audio upload URL
Advertised by the server during the last successful anonymous check.
Open-world

Get a presigned upload for your own audio file — or a SCORE (.mid / .midi / .musicxml / .xml / .mxl → a score_key for doseedo_render_score: send a big score as a file, not as notes in the call). Returns an upload_url (PUT the raw file bytes there, e.g. `curl -X PUT -H "Content-Type: <content_type>" --data-binary @song.wav "<upload_url>"`) and an audio_key to pass to doseedo_separate_stems / doseedo_transcribe / doseedo_cover_song / doseedo_audio_to_session, or to doseedo_add_audio_to_session. A FILM (mp4, mov, m4v, webm, mkv; ≤ 2 GB) uploads the same way: pass the key as video_key to doseedo_spot_video / doseedo_score_to_picture. Formats: WAV, AIFF, FLAC, MP3, M4A/AAC, Ogg/Opus, WebM — all accepted for processing on every plan. Storing audio IN A SESSION (doseedo_add_audio_to_session) keeps WAV/AIFF/FLAC lossless only on Pro+; below Pro+ lossless is not kept there, so send Opus (audio/ogg). Samples/regions for a session that are on this machine need NO upload: use load_quick_sampler_sample sample_path / attach_audio local_path / set_track_clips audio_ref.local_path and run place_local_files after the download.

doseedo_get_upload_url input schema
{
  "type": "object",
  "required": [
    "filename"
  ],
  "properties": {
    "filename": {
      "type": "string",
      "description": "File name including extension, e.g. \"song.wav\", \"film.mp4\", or \"cue.mid\" / \"cue.musicxml\" for a score."
    },
    "content_type": {
      "type": "string",
      "description": "MIME type; inferred from the extension when omitted (wav, aif/aiff, flac, mp3, m4a/aac, ogg/opus, webm; a film: mp4, mov, m4v, mkv). Required for any other extension."
    }
  },
  "additionalProperties": false
}
doseedo_get_upload_url annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
doseedo_check_task — Check a job
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Check an async doseedo task once. Returns current status, and the result (file URLs / notes) when finished. A big result comes back as a brief (each row cut to its deciding fields): parts: ["<name>" or index, …] returns those rows of the job record IN FULL — a render_score part's every receipt, a mix part's strip, rides (every point) and loudness.

doseedo_check_task input schema
{
  "type": "object",
  "properties": {
    "kind": {
      "enum": [
        "generate_music",
        "generate_sound",
        "cover_stem",
        "fill_gap",
        "render_midi",
        "train_instrument",
        "render_score",
        "mix",
        "session_from_stems",
        "spot_video",
        "score_to_picture",
        "cover_song",
        "separate_stems",
        "split_soundtrack",
        "transcribe",
        "audio_to_session",
        "convert_project",
        "bounce_session",
        "analyze_song",
        "detect_chords",
        "beat_grid",
        "check_take",
        "analyze_midi",
        "generate",
        "sound",
        "coverstem",
        "fillgap",
        "render",
        "train",
        "score",
        "stemsess",
        "spot",
        "picture",
        "cover",
        "separate",
        "soundtrack",
        "session",
        "convert",
        "bounce",
        "analyze",
        "chords",
        "beats",
        "check",
        "harmony"
      ],
      "type": "string",
      "description": "Which job kind created the task — the registry name the submit reply carried (generate_music, generate_sound, cover_stem, fill_gap, render_midi, train_instrument, render_score, mix, session_from_stems, spot_video, score_to_picture, cover_song, separate_stems, split_soundtrack, transcribe, audio_to_session, convert_project, bounce_session, analyze_song, detect_chords, beat_grid, check_take, analyze_midi); the short codes (generate, sound, coverstem, fillgap, render, train, score, stemsess, spot, picture, cover, separate, soundtrack, session, convert, bounce, analyze, chords, beats, check, harmony) are accepted aliases."
    },
    "parts": {
      "type": "array",
      "items": {
        "anyOf": [
          {
            "type": "string"
          },
          {
            "type": "number"
          }
        ]
      },
      "description": "Optional, on a finished job: the parts to return in full, by name (as the result names them) or index — e.g. [\"Oboes\", \"Timpani\"]."
    },
    "job_id": {
      "type": "string",
      "description": "Alternatively the job_id (job_…) from a submit tool or the jobs API — replaces task_id + kind."
    },
    "task_id": {
      "type": "string",
      "description": "The task_id returned by a submit tool."
    }
  },
  "additionalProperties": false
}
doseedo_check_task annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
doseedo_wait_task — Wait for a job
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Wait for an async doseedo job to finish: polls server-side for up to timeout_seconds (5–50; one call can never hold longer than 50 s), then returns the result or the latest status. If it returns still_running:true, call it again — a submit reply's eta_seconds says how long the whole job usually takes (transcribe ~15 s, a render 1–3 min, audio_to_session ~4 min), so a 3-minute job is three or four calls, not one. A finished job answers with its output, artifacts (pre-signed links) and note.

doseedo_wait_task input schema
{
  "type": "object",
  "properties": {
    "kind": {
      "enum": [
        "generate_music",
        "generate_sound",
        "cover_stem",
        "fill_gap",
        "render_midi",
        "train_instrument",
        "render_score",
        "mix",
        "session_from_stems",
        "spot_video",
        "score_to_picture",
        "cover_song",
        "separate_stems",
        "split_soundtrack",
        "transcribe",
        "audio_to_session",
        "convert_project",
        "bounce_session",
        "analyze_song",
        "detect_chords",
        "beat_grid",
        "check_take",
        "analyze_midi",
        "generate",
        "sound",
        "coverstem",
        "fillgap",
        "render",
        "train",
        "score",
        "stemsess",
        "spot",
        "picture",
        "cover",
        "separate",
        "soundtrack",
        "session",
        "convert",
        "bounce",
        "analyze",
        "chords",
        "beats",
        "check",
        "harmony"
      ],
      "type": "string",
      "description": "Which job kind created the task — the registry name the submit reply carried (generate_music, generate_sound, cover_stem, fill_gap, render_midi, train_instrument, render_score, mix, session_from_stems, spot_video, score_to_picture, cover_song, separate_stems, split_soundtrack, transcribe, audio_to_session, convert_project, bounce_session, analyze_song, detect_chords, beat_grid, check_take, analyze_midi); the short codes (generate, sound, coverstem, fillgap, render, train, score, stemsess, spot, picture, cover, separate, soundtrack, session, convert, bounce, analyze, chords, beats, check, harmony) are accepted aliases."
    },
    "job_id": {
      "type": "string",
      "description": "Alternatively the job_id (job_…) from a submit tool or the jobs API — replaces task_id + kind."
    },
    "task_id": {
      "type": "string",
      "description": "The task_id returned by a submit tool."
    },
    "timeout_seconds": {
      "type": "number",
      "description": "Max seconds to wait in this call (5–50, default 45). Start with the submit reply's suggested_next_wait."
    }
  },
  "additionalProperties": false
}
doseedo_wait_task annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
doseedo_list_jobs — List jobs
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

List the jobs this account submitted (generate / render_midi / cover / stems / transcribe / session / convert / bounce / the analyses), newest first — to find an earlier result, check what is still running, or resume after a lost connection. Each row names its artifacts (e.g. ["vocals", "drums", "midi_piano"]); doseedo_check_task {job_id} returns the full result with fresh links. Read-only.

doseedo_list_jobs input schema
{
  "type": "object",
  "properties": {
    "kind": {
      "type": "string",
      "description": "Only this job kind (registry name): generate_music, generate_sound, cover_stem, fill_gap, render_midi, train_instrument, render_score, mix, session_from_stems, spot_video, score_to_picture, cover_song, separate_stems, split_soundtrack, transcribe, audio_to_session, convert_project, bounce_session, analyze_song, detect_chords, beat_grid, check_take, analyze_midi. Short codes (generate, sound, coverstem, fillgap, render, train, score, stemsess, spot, picture, cover, separate, soundtrack, session, convert, bounce, analyze, chords, beats, check, harmony) are accepted aliases."
    },
    "limit": {
      "type": "number",
      "description": "How many (1–100, default 20)."
    },
    "before": {
      "type": "string",
      "description": "Page cursor: the next_cursor of a previous call."
    },
    "status": {
      "enum": [
        "queued",
        "running",
        "succeeded",
        "failed",
        "canceled"
      ],
      "type": "string"
    },
    "session_id": {
      "type": "string",
      "description": "Only jobs whose input names this session — a bounce, stems registered into it, a render of one of its tracks (filtered within the fetched page)."
    }
  },
  "additionalProperties": false
}
doseedo_list_jobs annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
doseedo_cancel_job — Cancel a job
Advertised by the server during the last successful anonymous check.
Advertised destructiveOpen-world

Cancel a queued or running job. What that stops and refunds depends on the kind — generate_music / render_midi / cover_song: a render not yet on the GPU is stopped and its credits refunded; one already rendering finishes (credits spent), the job ends as canceled, output discarded. audio_to_session: cooperative — stops at the next stage boundary and refunds the build's sound-tool unit (a separation already run stays charged). bounce_session: a bounce still queued never renders (budget refunded); one mid-render has its result dropped. separate_stems / transcribe / convert_project: no plane-side stop — the work completes (credits spent), the job ends as canceled and its output is discarded. The synchronous analyses are already finished (409 already_terminal).

doseedo_cancel_job input schema
{
  "type": "object",
  "required": [
    "job_id"
  ],
  "properties": {
    "job_id": {
      "type": "string",
      "description": "The job_id from a submit tool or doseedo_list_jobs."
    }
  },
  "additionalProperties": false
}
doseedo_cancel_job annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": true
}
doseedo_account — Account and credits
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Check the connected doseedo account: plan tier, the monthly credit pool (generation AND DAW conversions draw it), purchased extra-usage credits, and storage. Read-only — consumes nothing. Use it before starting expensive work to confirm there is budget.

doseedo_account input schema
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
doseedo_account annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
doseedo_quote — Price a job
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Estimate what a job will cost in credits BEFORE submitting it, and whether the connected account can afford it right now. Spends nothing. Pass the job (generate_music, generate_sound, cover_stem, fill_gap, render_midi, train_instrument, cover_song, separate_stems, split_soundtrack, transcribe, audio_to_session, convert_project, bounce_session, spot_video, score_to_picture, analyze_song, detect_chords, beat_grid, analyze_midi, render_score, mix, session_from_stems, check_take) and the same args you would give the job tool (duration_seconds, models, instruments; for render_midi: notes + bpm / tempo_map or duration_seconds, voice_split / voices, takes — takes are free, voices cost one batch per ~6; for render_score: parts + bpm / tempo_map — planned exactly as the job plans it (lines, player rows, length), and `refused` says when the job would refuse it). The estimate follows the planes' own price table; the gate is authoritative and a refusal names the exact price.

doseedo_quote input schema
{
  "type": "object",
  "required": [
    "job"
  ],
  "properties": {
    "job": {
      "enum": [
        "generate_music",
        "generate_sound",
        "cover_stem",
        "fill_gap",
        "render_midi",
        "train_instrument",
        "cover_song",
        "separate_stems",
        "split_soundtrack",
        "transcribe",
        "audio_to_session",
        "convert_project",
        "bounce_session",
        "spot_video",
        "score_to_picture",
        "analyze_song",
        "detect_chords",
        "beat_grid",
        "analyze_midi",
        "render_score",
        "mix",
        "session_from_stems",
        "check_take"
      ],
      "type": "string",
      "description": "Which job to price (a doseedo_ tool name or recipe verb is accepted too)."
    },
    "args": {
      "type": "object",
      "description": "The arguments you intend to pass to the job tool; only the price-relevant ones matter.",
      "additionalProperties": true
    }
  },
  "additionalProperties": false
}
doseedo_quote annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
doseedo_recipes — Recipes (named pipelines)
Advertised by the server during the last successful anonymous check.
Advertised read-only

The named pipelines people actually ask for — stems, session (mix → DAW project), cover, transcribe, generate, render-midi (your notes played by an instrument model), custom-instrument (train your own from a recording), timbre-transfer (your notes, a recording's tone), sound-design (SFX / foley / one-shots), analyze-song (key/tempo/sections/chords), convert, bounce, and the compose-part guide (a part in the session's key at its tempo: analyze → you write the MIDI → place or render it) — each with its inputs, the tool calls in order, outputs, a credit/time estimate and how to VERIFY success from the response. Call with no name for the index (pick by use_when), or with a name for the full recipe. Read-only.

doseedo_recipes input schema
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "A recipe name or alias, e.g. \"session\" / \"stems-to-session\". Omit to list all."
    }
  },
  "additionalProperties": false
}
doseedo_recipes annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}

Prompts

Not advertised

No prompt definitions available.

Resources

Not advertised

No resource definitions available.

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
724 ms
95th percentile latency
724 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 724 ms · metadata changed · 24 tool(s)

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http doseedo-film-score-mcp https://api.doseedo.com/mcp/film

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add doseedo-film-score-mcp --url https://api.doseedo.com/mcp/film

Cursor

Merge this server into .cursor/mcp.json in your project, then enable doseedo-film-score-mcp in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "doseedo-film-score-mcp": {
      "url": "https://api.doseedo.com/mcp/film"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "doseedo-film-score-mcp": {
      "type": "streamableHttp",
      "url": "https://api.doseedo.com/mcp/film",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start doseedo-film-score-mcp and inspect its tools.
VS Code configuration
{
  "servers": {
    "doseedo-film-score-mcp": {
      "type": "http",
      "url": "https://api.doseedo.com/mcp/film"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http doseedo-film-score-mcp https://api.doseedo.com/mcp/film

Claude Desktop

Merge this into claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\), then restart Claude Desktop.
Claude Desktop configuration
{
  "mcpServers": {
    "doseedo-film-score-mcp": {
      "url": "https://api.doseedo.com/mcp/film"
    }
  }
}

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "doseedo-film-score-mcp": {
      "serverUrl": "https://api.doseedo.com/mcp/film"
    }
  }
}

Let your agent find its tools

Find servers like Doseedo Film Score MCP from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.

Do you run Doseedo Film Score MCP?

Prove ownership with a DNS record, a well-known file, or your Official MCP Registry entry, and your edits publish immediately. Proofs are re-checked weekly; admins can still hide or remove a listing.

Claim this listing