Render MIDI on AI instruments (MIDI to audio)
doseedo_render_midiMIDI to audio, a virtual instrument: plays a part you wrote. 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. Input ONE of: notes[] ({pitch, start_beats, duration_beats, velocity} — beats from bar 1), 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): the drum model (default) PLAYS a kit — the sound of acoustic drums; it can drop a hit (takes[].fit.missing). drum_engine:"oneshot" = a programmed sampler kit for drum_genre (default for "electronic"/"808"), every hit as written — drum machines only; real drums (a kit, boom bap) stay on the model (drop a hit: take again). "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; a drum kit too: a ride / snare recording), 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.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| bpm | No | 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. | |
| name | No | Optional: a name for this job's files, e.g. "rainy-loop-3". Every file it returns downloads as <name>_<part>.<ext> (rainy-loop-3_Celesta.wav, rainy-loop-3_rough-mix.wav; one take: rainy-loop-3.wav), so the user can find them. Give each job its own name (a second take: "rainy-loop-3b"). Default: doseedo-<last 6 characters of the job id>. | |
| seed | No | Random seed (−1 or 0–4294967295). Reuse a result's seed to re-render close to the same take. | |
| tune | No | 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 | No | 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 | No | 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. | |
| lyrics | No | A SUNG part (instrument "voice" — a solo singer — or "choir"): the words, one syllable per note in time order ("Hold the light a lit-tle lon-ger": hyphens split a word where you want, "_" holds a syllable over the next note; one word such as "Ah" is sung on every note). ≤ 2000 characters. Without it a sung part sings "Ooh". Any other instrument: an error. | |
| prompt | No | Optional caption override (default: the instrument id), ≤ 16000 characters. Style words help a little; the notes are the control. | |
| refine | No | 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 | No | 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 | No | 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 | No | 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 | No | 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 | No | With session_id: the t_… id of the MIDI/instrument track to render. | |
| tempo_map | No | 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 | No | The drum style. A drum machine (instrument "electronic" or "808"): the genre whose one-shot kit plays the part — "house", "techno", "trap", "drill", "dnb", "amapiano", "synthwave"… or free words. A real-drums style ("boom bap", "lo-fi", "breakbeat", "funk", "rock", "jazz"…) plays on the drum model's drum_kit instead, never a one-shot kit. Default: the instrument. | |
| instrument | Yes | 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 | No | Alternative to notes: with track_id, render THAT session track's notes (read from doseedo_get_session's summary). | |
| synth_role | No | 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 | No | Who plays a drum part: "model" = the drum model (the default for every kit but electronic / 808): a drummer playing the kit in a room — cymbals ring, hits vary, ghost notes speak; the sound for acoustic drums. It can drop or smear a hit: read takes[].fit and take it again. "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, but it sounds programmed (one sample per drum, the same on every hit) — for drum-machine genres only. An acoustic kit (drum_kit: rock, punk, jazz, soul, a band seen playing) stays on the model, never oneshot — and so does a real-drums drum_genre on whatever kit, which plays on drum_kit (funk, boom bap, lo-fi, breakbeat: a sampled break is a drummer; its dust comes from the mix, doseedo_mix style "1990s-hiphop" / "lofi-hiphop"; the submit reply says so, prepared.drums). A take that drops a hit is taken again (takes: 3, keep the best). 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 | No | instrument "synth": the genre the patch is made for — "house", "techno", "trap", "synthwave", "lo-fi", "dnb"… or free words. | |
| synth_words | No | instrument "synth": extra sound words for the patch ("reese", "warm analog", "acid"), ≤ 80 characters (a longer one is cut at a word boundary, with a warning). | |
| voice_split | No | true = the notes hold chords: split them into the voices one player of this instrument can hold and render every voice (result.voices). A SUNG chord (choir, voice) is sung one stem per voice, each in its register, one take each. Default false (a chord in a mono part is an error the model will fudge). | |
| synth_engine | No | 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 | No | 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 | No | 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 | No | 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 | No | Meter of the part, e.g. "4/4", "3/4", "6/8" (default 4/4). Written into the MIDI and the caption. | |
| idempotency_key | No | 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 | No | 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 | No | 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 | No | With revise_take_url: end of the region to redo (exclusive), beats from bar 1. | |
| timbre_audio_url | No | 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 | No | With revise_take_url: start of the region to redo, beats from bar 1. |