Skip to content

Auris Studio v0.4.0

Choose a tag to compare

@github-actions github-actions released this 31 Aug 09:16
· 491 commits to main since this release

Downloads

File What it holds
…-macos-universal.tar.gz Auris Studio.app and the auris command line tool — Apple Silicon and Intel in one binary
…-windows-x86_64.zip auris-studio.exe and auris.exe
…-linux-x86_64.tar.gz the auris command line tool only

Every archive carries MuseScore General, a General MIDI SoundFont under the MIT
licence, in a SoundFonts directory — which is most of the download size and all of the
instruments beyond the four built in. On macOS it lives inside the bundle, so dragging
Auris Studio.app to /Applications takes it along.

None of it is code-signed. macOS will refuse the first launch and say the developer
cannot be verified: open it from the right-click menu once, or run
xattr -dr com.apple.quarantine "Auris Studio.app". Windows SmartScreen wants
More info → Run anyway.

Auris Studio is at 0.x, where nothing is stable: the project format, the configuration
files and every public API may change in any release, with no migration.


The singer gets a real voice

  • Singing-voice synthesis works end to end. A singer track can now be pointed at a trained
    auris-singer voice model — one self-contained
    .onnx file, chosen the way a SoundFont is and left where it lies — and asked to sing:
    notes and lyrics become frames, the frames become a waveform, and the waveform lands in
    Audio/ as the track's take, an ordinary audio source that plays, exports and reopens
    with everything else. Track → Choose Voice… and Track → Sing in the window (the render runs
    off the main thread behind the export overlay, with the same stop button), and
    auris sing <project> [--track <name>] [--voice <model.onnx>] [--seed <n>] at the command
    line.
  • A take is kept, never silently rewritten. Every random choice is pinned by a seed the
    document stores, so the same document, seed and voice render the same audio on any machine.
    Editing notes after a render leaves the take playing — a voice someone chose does not fall
    back to the formant preview over one edited word — and the track header says
    behind the notes until Sing is pressed again. Clicked notes still audition through the
    preview instrument while a take plays.
  • The take keeps up with the score by itself. Once a track has a voice, editing the notes
    is the ask: shortly after an edit settles, the window re-renders the take in the
    background — no overlay, no button, just the header badge reading … ♪ voice while the
    CPU is spent, which is the standing rule that a cost switched on without a click must say
    so on screen. A burst of edits coalesces into one render; an edit landing mid-render
    cancels the stale work between chunks; a manual Sing or an export takes the stage and the
    background render steps off it. Refusals worth acting on — an unsaved project, a voice
    that stopped loading — are said once in the status bar rather than once per frame, and an
    empty or voiceless track is simply left alone.
  • The model doors sing too. Three additions to the toolbox, identical over MCP and in the
    agent panel: add_track accepts kind singer, write_lyrics lays a phrase across a clip's
    notes one syllable each (notes reads the words back beside the pitches), and sing
    renders the track through its voice model into the project's take — so a model can write a
    song and make it sing without a window open. Twenty-nine tools in all.
  • A dragged note previews in the real voice. Grabbing or dragging a note on a voiced
    singer track no longer strikes the formant instrument: the model sings that one note — its
    own syllable, half a second at the grabbed pitch — in the background, and the engine plays
    the render at once, transport rolling or not. Renders are cached by voice, seed, pitch and
    syllable, so a drag is instant everywhere it has already been; short sequences sit well
    inside what a desktop CPU renders faster than real time. Tracks with no voice keep the
    formant preview, and chords stay on the instrument path. Under the hood the engine grew
    one-shot playback: a pre-rendered buffer crosses the command channel whole, is mixed in by
    the callback without ever being freed there, and travels back up the retired-data channel
    when replaced — the SetGraph discipline in miniature.
  • The piano roll draws the sung pitch curve. Editing a singer clip overlays the contour
    the voice will actually sing — the note's pitch plus its bend curve, in fractional
    semitones, with consonants riding their vowel and rests leaving a gap in the line — over
    the notes, the way vocal editors draw it. It is computed from the same frames the model is
    fed, cached against the document revision like the take badge, so what is drawn and what
    is sung cannot drift apart.
  • And the phoneme segmentation beside it. The same frames say where each phoneme's time
    actually falls — the sixty milliseconds a consonant takes at a note's edge, the vowel
    stretched over the rest — and the roll now draws it: a faint divider inside the note at
    each cut, the symbol above the note at the position its frames begin. The old untimed
    phoneme list above a note yields to this wherever frames exist; zoomed far out the symbols
    step aside and the dividers stay.
  • The cuts are draggable. Take hold of a divider and the phoneme to its left is pinned
    to that many seconds — stored on the note beside its phonemes (Note::phoneme_seconds),
    so the adjustment travels, saves and re-renders with the word. The timing rule lays the
    unpinned phonemes out around the pins, squeezing proportionally where pins outgrow the
    note; retyping a lyric clears its pins, the note's menu offers Reset Phoneme Timing while
    any stand, and one drag is one undo step.
  • A note can wear pitch ornaments: scoop, fall, vibrato. しゃくり rises into the note
    from below, a fall drops away at its end, a vibrato sways around it once settled — each a
    handful of numbers stored on the note (Note::scoop / fall / vibrato), shaped by one
    public function (auris_vocal::ornament_offset) that the frames, the painter and the
    editor's grab test all read, so drawn, heard and grabbed can never disagree. The note's
    menu toggles each ornament on with defaults taken from singer measurements, and every
    ornament then carries a handle on the drawn pitch curve: the scoop's and fall's at the
    corner of the gesture (drag for span and depth at once), the vibrato's at the crest of its
    first sway (onset and depth). Scoop and fall cap at half the note so they cannot collide;
    ornaments are pitch, not phonemes, so they survive the lyric being retyped; one drag is
    one undo step, and the take re-renders itself afterwards like after any other edit.
  • Under the hood: the new auris-singer crate runs the model on the CPU via onnxruntime,
    cutting the timeline at silences into chunks of at most twenty seconds — the model's
    attention grows with the square of the frame count, and a whole song in one inference has
    taken a machine down — and deciding each frame's voicing from the phoneme class
    (auris_vocal::is_voiceless), never from f0 > 0, which would hum through every /k/.

The conversation moves into the window

  • The Agent panel — View → Agent, docked on the right beside the inspector the way an
    editor's chat sidebar sits. Ask for a piece, watch each tool call land in the transcript,
    read the answer; provider, model, URL and API-key variable are set in the panel and saved
    to the shared settings, which auris-agent on the command line now reads as its flag
    defaults. The window spawns auris-agent --json beside its own binary and speaks JSON
    lines over its pipes, so the frontend boundary holds: the window never learns what an LLM
    client is.
  • The two ends of one file, settled: the window saves before every message, so the model
    always reads the document as it stands; when a tool call writes the project back, the agent
    reports it (changed events, from the new auris_toolbox::WRITES_PROJECTS list) and the
    window reloads — automatically while it holds nothing unsaved, by an offered button when it
    does. The whole policy is a plain function with tests; the window only obeys it.
  • auris-agent --json: newline-delimited JSON for a host program — {"say": …} in;
    ready, call, result, changed, answer, error out. A provider failure is an
    error event rather than an exit, because the host's window is still open.
  • From the first live sitting: provider and model are both picked, not typed — the panel
    runs the new auris-agent models (Ollama asked in its own words, context windows included;
    OpenAI-compatible via /models) and fills a dropdown, refetching when the provider or URL
    changes. A context gauge in picocode's image sits over the input — ↑ prompt ↓ written,
    a bar filling the chosen model's window, yellow from 60% and red from 85% — fed by token
    counts that now ride every answer event. Tool rows open on a click to the whole answer
    the model saw, which makes the transcript the loop's log. And a send with no model
    configured now says so in the transcript instead of silently doing nothing, which is what
    the first Enter ever pressed in the panel ran into. From the sittings after it: picking a
    model applies it on the spot
    — the choice writes through to the settings as the menu
    closes, and Enter in the chat treats a completed settings form as applied, so the Apply
    button now only serves the typed fields.
  • The model can edit the arrangement in place. Six tools at both model doors, so one more
    part is an edit rather than a recomposition: add_track (a new track in an existing
    project, voiced by a built-in id or any General MIDI sound by name or number — the shipped
    font is adopted into the project in the same step), add_part (a generated part written
    onto a track from the harmony already under the song, recipe kept so another_take and
    write_again apply), set_instrument, rename_track, remove_track, and
    list_instruments for the vocabulary. Twenty-three tools in all. Behind them,
    Session::set_track_general_midi is new: font adoption and preset choice as one undo step.
  • The window notices another writer. Edit an open project from outside — the MCP door
    driven by Claude Code, a sync service, anything with the file — and the window follows: it
    reloads silently while it holds nothing unsaved, and puts a Reload button in the status bar
    when it does, saying what stands to be lost either way. While that choice stands, autosave
    holds its fire — writing over another writer's version is a decision, and ⌘S is where
    decisions are made. Watching is a half-second stat of one file; the whole policy is two
    tested functions (should_autosave grew an overwritten clause, and
    external_change_action is the window's side).
  • The model can place notes, and derive a band from them. Four more tools at both doors,
    for the melody-first way around: add_clip opens an empty clip, edit_notes places and
    removes notes by name and bar in one call ("F#4", bar 2, beat 3.5), notes reads a clip
    back numbered in time order — the numbers are the removal address — and accompany reads a
    melody clip and writes the key, the chords and a backing band under it without touching a
    note of the tune, Session::accompany finally reachable from outside the window.
    Twenty-seven tools in all.
  • The model can be handed audio. auris-agent --attach mix.wav "how is this?" sends the
    file base64-encoded as an OpenAI input_audio content part beside the words, and the JSON
    wire takes {"say": …, "audio": ["mix.wav"]} per message. OpenAI-compatible servers only —
    Ollama's API has no audio field, and the agent says so up front rather than after the
    request is built. wav, mp3, flac, ogg, aac, aiff and m4a are typed by extension.
  • A review of the two model doors and the panel, and the mends it asked for: a silent reload
    no longer leaves a rename sheet open over a document that is gone (the sheet comes down
    with the document that raised it); an audio attachment over 25 MB is refused by its size
    on disk before a byte is read; the models listing gives up after twenty seconds instead
    of parking a thread behind a server that never answers, and a second press of refresh no
    longer stacks another question behind the first; a non-UTF-8 line on the --json wire is
    an error event, as promised, rather than the end of the process; edit_notes refuses a
    velocity outside 0-1 the way its siblings refuse their ranges — and its description now
    names the real default, 0.75, with a test pinning the prose to the constant; absurd
    octaves and bar counts are refused rather than overflowed.

The model gets its hands on the mixer

  • Five tools at both model doors, asked for by the first model to drive the improve loop —
    it had spent an afternoon moving tenths of an LU by reseeding parts, against problems that
    were mixer-level all along. mixer reads the whole board: every fader, pan, send and
    effect parameter with its key, value and range. set_level, set_send and
    set_effect move one each — the master limiter's input_db is now a dial a model can
    back off when analyze says the climax is pinned. section_gain holds a track's (or
    the master's) gain at a level across one named section: dynamics without rewriting a note.
  • Behind that last one is a new session command, Session::hold_automation — hold a
    parameter at a value across a stretch, short ramps at the edges, the surrounding lane (or
    the fader's own position) preserved, holds on different sections composing, one undo step.
    Every frontend gets it; the desktop's automation lanes draw what it writes.

The doors meet a model where it stands

  • Every tool that opens a project now resolves its path the way compose saves: absolute
    (relative asks are read against the host's working directory), and reaching one folder down
    when Name.auris really went to Name/Name.auris — the nesting is one-to-one, so it is
    walked rather than taught as an error. compose and render answer with absolute paths,
    because that line is what a model copies its next call from. Found live: a gemma-class
    8B model handed back exactly the relative shorthand it had asked compose for, stalled on
    file-not-found before the change, and completed the same loop after it.

A fourth frontend, where Auris dials the model

  • auris-agent is the mirror of auris-mcp: instead of waiting for a model's harness to
    connect, Auris connects to the model — a local Ollama server
    (--model qwen3:8b), or anything speaking the OpenAI chat-completions dialect behind
    --provider openai and --url (OpenAI, LM Studio, vLLM, OpenRouter) — hands it the same
    twelve tools, and runs the loop itself via rig. With a
    prompt it asks once and prints the answer on stdout, narrating each tool call on stderr so
    a pipe keeps the answer and a person sees the work; without one it holds a conversation,
    carrying the transcript forward — the improve loop with a person in it. An API key is only
    ever named by environment variable, never typed into a command line.
  • The tools themselves moved into auris-toolbox, a presentation crate for the reader
    that is a model, the way auris-i18n is for the reader that is a person: one module per
    tool — name, description, argument schema, work — shared by both doors, so compose at the
    MCP door and compose at the agent door are the same text, the same schema and the same
    code by construction. (The MCP macro reads descriptions only from literals, so that door
    carries a copy — held word-for-word equal to the toolbox text by a test.) The agent's whole
    loop is itself under test against a scripted OpenAI-compatible server: the fake model calls
    a tool, the real toolbox answers, and the answer rides back over the wire.

A third frontend, for readers that act on the answer

  • auris-mcp puts the same headless session behind the
    Model Context Protocol, over stdio, so a language model's
    harness can drive Auris as tools — claude mcp add auris -- ./target/release/auris-mcp is
    the whole installation. Seven tools cover the loop of writing a song and hearing it:
    spec_reference teaches the .asong format by example, check_spec validates a draft and
    answers with every default filled in, compose writes and saves the piece, render makes
    WAV files (the mix, or stems), describe reads a project back, and list_presets /
    list_progressions are the quotable vocabulary. Errors are tool answers rather than
    failures — a rejected spec names its lines and fields, which is exactly the loop a model
    iterates in.
  • The loop that improves a piece: analyze renders a project and listens in the model's
    place — loudness and peaks for the mix, per named section (the dynamic arc as numbers), and
    on request per track alone, soloed through its buses. Behind it is a new session command,
    Session::analyze, so every frontend can ask the same question. Against the answer a model
    edits the spec and composes again, or aims another_take (same ask, next seed) or
    write_again (same seed, follows the harmony as it stands now) at one clip, addressed
    by the numbering describe now prints beside every clip — along with each clip's recipe,
    seed, and whether a hand has edited it. Every take names its seed, and another_take takes
    a named seed back — the first model to drive the loop lost a take it liked behind the
    advancing counter, and a rewrite that measured worse should never be a one-way door.
    teach_progression keeps a chord progression by name on this machine;
    forget_progression takes it back out.

Arrangement: the joins learn a second word

  • A new part role, the riser: General MIDI's reverse cymbal (program 119), written by the
    same writer that places the crash and answering the same question one section early — it
    swells through the last second before every arrival the crash then opens, the sample's own
    peak landing five milliseconds ahead of the downbeat (measured on the shipped font; the lead
    converts per section tempo, so the swell is the same second of cymbal at any speed). Declare
    it with role = "riser"; the pop-band, city-pop and synthwave presets now do.
  • A piece can end by leaving the room: ending = "fade" rides the master fader from unity
    to silence across the final eight bars (or the whole final section, where it is shorter), and
    is the composer's first piece of written automation — the ride arrives as an ordinary
    lane on the master gain, in the same view as one drawn by hand, so it can be reshaped or
    deleted like anything a person wrote. A fading piece takes no landing bar; the fade is the
    deliberate refusal of one.

Performance: the score does not change, the playing does

  • Any note clip can now carry a performance: humanize, swing and gate dials in the
    inspector that change what is heard — playback and MIDI export both — while the piano roll
    keeps showing the notes exactly as written. Setting a dial back to its resting position
    removes it entirely; Keep the Performance writes what is heard into the notes, the same
    trade as freezing a generated clip's recipe. The format version moves to 18 for the new
    field.
  • The humanize wander is drawn from a seed stored in the file, so a project sounds the same on
    every open — and it is drawn afresh for every pass of a loop, so a repeated bar is loose
    differently each time around instead of rehearsing the same accidents. Its feel is
    calibrated to the composer's own: the same milliseconds of wander, the same velocity spread,
    at every tempo.
  • A transpose transform exists alongside the three dials (session API only for now), and the
    stack is ordered — the panel keeps swing in front of humanize, so the swing still finds its
    offbeats before the wander moves them off the grid.
  • The composer performs through the same stack now. A generated clip's text lands on the
    grid — only the groove's swing is still written — and its feel arrives as transforms on the
    clip: the wander per pitched part, plus a new deterministic lean (the hat a touch early,
    the snare laying back, exactly as the composer always played them). One Humanize dial edits
    it, in the Performance section; the recipe's own humanize dial is gone, and regenerating or
    re-rolling a clip touches the text alone — the feel you set stays set, though another take
    re-seeds the wander so one number still names both. The kit keeps time as before and, being
    wander-exempt, no longer varies its stroke with the dial. The format version moves to 19 for
    the new transform kind.
  • A generated clip knows when it has been edited by hand. Every write stamps a digest of
    the notes into the recipe, and a clip whose notes have drifted from it shows a standing note
    in the inspector — "edited by hand; writing it again replaces the edits" — for as long as
    they differ. Undoing the edit clears it, a resize or a split does not raise it, and nothing
    is blocked: regenerate still does exactly what it says. Files from before this field simply
    never warn.
  • A saved file now records which build saved it. Opening a project another build saved puts
    a note on the status line (and on the command line's stderr): the piece sounds exactly as
    saved, but regenerating any clip writes in the current composer's style — so a take worth
    keeping wants freezing first. Informational only; nothing is blocked.

Singer tracks: notes that carry words

  • A new track kind for a singing-voice synthesiser. Its clips are ordinary note clips — the
    piano roll, the lanes and every clip gesture apply unchanged, and a melody moves freely
    between an instrument track and a singer track — but each note can carry a lyric and the
    IPA phonemes it is sung as. The format version moves to 17 for the new track kind.
  • Double-click a note to type its word; Return commits and walks to the next note, so a
    verse is typed straight through. Write Lyrics… lays a phrase across the selection one mora
    to a note; Edit Phonemes… corrects a reading by hand and leaves the word as spelt.
  • Kana lyrics need nothing installed. Kanji is read through a Japanese dictionary — a
    prebuilt naist-jdic folder from the jpreprocess releases, named in Settings → General
    and loaded where it lies, like a SoundFont. Only new kanji ever asks for it, with an error
    naming the setting.
  • File → Export Singer Frames… writes what a voice model consumes: phoneme id, pitch in
    Hz and energy per 10 ms frame (the hop is stored on the track), as JSON. Pitch is the note
    plus its bend curve; energy is the velocity under an envelope, scaled by controller 11.
  • Until a model is wired in, the track previews through Vocal, a new built-in instrument —
    three formants over a saw, one open vowel — that answers the modulation wheel with vibrato
    and the expression pedal with level, the same controller the frames read as energy.

Four more colour schemes

  • One Dark, One Light, GitHub Dark and GitHub Light join the four the application shipped
    with, so a project can be worked on in the palette the rest of the desk is already in. Each is
    the editor theme's own numbers — its background's hue and lightness, and the blue it marks a
    selection with — and the other thirty colours are derived from those, as every scheme's are.
  • They are held to the same rules as the rest: the surfaces stack the same way round, body text
    clears 7:1 on the hardest of them, and a group's mark can be seen at every hue. Two of the four
    had to give a little to pass — One Dark's window is a shade deeper than the editor's, or a
    failure in the status bar would not have been readable, and GitHub Light's is a hair under white
    so the timeline still sits below the window it is cut into.

The whole band can hear itself

  • Monitoring is a switch per track, like the arm, and up to eight play at once — each through
    its own strip and its own armed input channels. A band therefore hears itself the way it will be
    recorded: every player through their own fader, their own effects and their own microphone.
  • The ninth is refused rather than silently dropped. Every path back into the mix is a ring that
    has to exist before the input device starts, because the input callback may not make one.

A meter for every armed track

  • Each armed track's header shows what is arriving on its own input channels, as a thin bar
    beside the one that shows what the track puts out. Four microphones on a four-input interface
    are four readings; the transport bar's meter stays what it was, the whole device in one number.
  • It appears only while the track is armed and something has the device open, and it latches a
    clip like every other meter — cleared with the rest by clicking the master block.

Two takes can be joined without a hole in the join

  • Crossfades. Drag one clip over another on the same track and let go: the join is shaped as
    part of the same undo step as the move, the earlier fading out across the overlap while the
    later fades in across the same stretch. How long the join is is how far you dragged — nothing
    moves to make room. A fade you drew is never written over, and Crossfade on either clip's
    menu makes the join on demand where one is.
  • Fade-In Shape and Fade-Out Shape on the same menu choose the curve by hand, for a join
    made by dragging a fade rather than by asking for one.
  • A clip's fades now carry a shape, one for each edge. An edge fade stays a straight line;
    a crossfade uses the equal-power curve, so the pair holds its level through the middle of the
    join instead of dipping about three decibels there. The arrangement draws the curve it plays.
  • The project format is version 16. An older build ignores the shapes, so every crossfade in
    the piece would play as two straight ramps with a hole in the middle — and the next save would
    write the shapes away.

The mix, taken apart

  • Stem export: File → Export Stems…, or auris render --stems <folder>, writes one WAV per
    track into a folder, named after the track. Each one is that track as it sounds soloed — the
    buses it is routed through included, so a part sent to a reverb arrives with its reverb.
  • Buses get no stem of their own and muted tracks are skipped, so the set of files is the mix
    taken apart rather than the mix plus a handful of silent ones. Two tracks of one name still make
    two files.
  • Every stem carries the master chain, because a stem is what the mix sounds like with one part in
    it. That means they sum back to the mix exactly where the chain is linear, and not where it is a
    limiter — one to bypass before exporting stems that have to add up.
  • One graph, played once per track: a hosted plugin is instantiated once for the whole export
    rather than once per stem.

Four bars before you have to play anything

  • A count-in, one to four bars, chosen by right-clicking the metronome button. Press Record
    from a standstill and the click counts those bars before the song moves: the playhead waits
    where the take will begin, and the transport bar counts the beats down where it usually shows
    the take's clock.
  • Bars are counted in the meter you are in and at the tempo where the take begins — fourteen beats
    for two bars of 7/8, four for two bars of 6/8 — and the click sounds for the count whether or
    not the click itself is switched on.
  • It works at bar one, which is where it is wanted most: the count is held in front of the
    playhead rather than played through a stretch of timeline that would have to exist.
  • Recording begins with the count rather than after it, so nothing of the first beat is lost to a
    device opening. The count is trimmed off the clip and kept in the file.
  • Pressing Record over a song that is already playing starts recording at once. The bars are going
    past already.

A band can go down at once

  • Every armed track records, each from its own input channel — one file and one clip per
    track, from one press of Record. Arming a second track adds a take rather than moving the first
    one, which is what the arm button did before.
  • A track takes the whole device where that is a pair or less and a single channel otherwise — the
    lowest one nobody else is reading — so four armed tracks on a four-input interface land on
    inputs 1 to 4 without anything being chosen, and one track through a stereo interface still
    records both sides. Record Input on a track's menu offers every channel on its own and every
    pair together, and picking one arms the track as well.
  • A channel the interface does not have records silence rather than its neighbour: an arm outlives
    the box it was made for, and a take holding the wrong microphone would pass for a good one.
  • Monitoring follows the channels the track it plays is armed to read, so listening to a track
    armed to input 5 plays input 5. Still one track at a time — there is one ring and it carries one
    stereo pair.
  • A take on several tracks is one undo step, and the report says which of them came back with
    nothing.

An effect can listen to another track

  • A compressor can be keyed from the kick drum. An effect slot names a track to listen to, and
    the effect hears that instead of the signal passing through it — the built-in compressor, and
    any hosted CLAP plugin with a sidechain input, which has been handed a silent port since the day
    ports were handed over at all. The Sidechain row appears on the slots whose plugin has
    somewhere to put a key and nowhere else.
  • What the effect hears is what the source puts into the mix: its chain, its fader, its pan and its
    mute. Pulling the kick down ducks less; muting it stops the duck.
  • A key is an edge in the routing like an output or a send, so the track that makes it is mixed
    first, and one that would leave a strip waiting for itself is refused — left off the list rather
    than offered and then rejected. Deleting a track clears the keys read from it, and a file
    carrying an impossible one is repaired on open.
  • Nothing pays for it unless it is used: a buffer per track something actually listens to, and only
    where the effect in the slot has a reading for one. A project with no key in it copies nothing.
  • Format version 15.

The shipped font's pianos play

  • rustysynth is forked into vendor/rustysynth, and reads the modulator lists the published
    crate discards.
    A modulator is how a SoundFont says that a controller reaches a parameter, and
    the ordinary way to make a sampled sound respond to how hard it is played is to set a filter low
    in the generators and open it with one driven by velocity. MuseScore General's acoustic pianos do
    exactly that, so with the modulators thrown away the piano played through a filter nothing ever
    opened — twenty decibels under every other program in the font, and falling by twenty more
    between MIDI velocity 74 and 76, where a layer boundary swapped one static filter setting for
    another that the discarded modulators were meant to override. Playing harder made it quieter.
  • One note at middle C now runs -21.1, -20.2, -18.4, -17.6, -14.3, -13.4, -12.1 and -11.3 dBFS
    across velocities 70 to 115, monotonic and level with the rest of the font. The jazz trio preset,
    whose piano and lead were both 25 dB under their own bass, composes to -14.1 LUFS instead of
    -16.9 with no fader running out of travel; it was -27.0 before any of this.
  • Of the 128 melodic programs, 101 are unchanged to the sample, the three acoustic pianos come up
    19.9, 19.5 and 11.7 dB, and the other 24 move by less than 3 dB.
  • Deliberately partial, and the fork's README says why: only the two filter destinations are read
    through modulators, only controllers that hold still for the length of a note are modelled, and
    the specification's own default modulator list is still not implemented. What a font says about
    loudness with a modulator is left alone, because the velocity-to-attenuation curve the sampler
    already compensates for would otherwise be counted twice.

A composed piece arrives mixed

  • Composing ends by listening to what it wrote. Every track is rendered on its own, measured as
    programme loudness to ITU-R BS.1770, and its fader moved until the part sits where a part of that
    kind belongs; then the whole piece is lifted onto −14 LUFS. A fader position is not a level — what
    a track is worth depends on the instrument that answered, and the same number on the same fader
    is a lead at −18.6 LUFS on the built-in synth and −25.8 through the shipped font. The eight
    presets used to span ten decibels, from −17.0 to −27.0 LUFS; they now sit between −14.0 and
    −17.9.
  • Compose → Balance the Mix does it to the open project, for a piece written before this
    existed or one whose instruments have changed since. Only a track that knows what it is gets
    moved — the composer writes down what each part is aiming at, and a hand-made track has no such
    number — so running it over a mix of your own normalises the loudness and leaves your balance
    alone. Running it twice does nothing the second time.
  • It costs a render per part and two of the whole piece, which is about two and a half seconds for
    an eight-part song, and the window does not answer while it runs.
  • Two limits it reports rather than hides: a fader stops at +12 dB, so a part on an instrument
    quieter than that can reach ends up short and the status line says by how much; and the master
    fader sits after the master's effects, so the limiter's ceiling now guarantees what leaves the
    chain rather than what leaves the mixer.
  • auris_dsp::loudness is the meter behind all of it — the K-weighting, 400 ms blocks and the two
    gates, with the filter re-derived per sample rate rather than tabulated at 48 kHz. Checked
    against libebur128 on five composed renders, agreeing within a tenth of a decibel.

The composer plays steadier

  • A composed part no longer varies how hard it strikes by half its own level. Every difference
    in strength the composer writes — the metric hierarchy, a ghost note, the lean across a phrase,
    the rise through a fill — is now played at a quarter of what it says, so a part's strokes sit
    within about a tenth of the level it is played at instead of a fifth to a half of it. The kit is
    where it was most audible: a rock hat ran from 0.20 to 1.00, which on an instrument whose every
    stroke is the same sound reads as a drummer who cannot hit the thing evenly. The proportions are
    scaled and not replaced, so a ghost is still the quietest thing in the bar and a downbeat still
    the loudest.
  • The timing wander is 6 ms at the top of the humanize dial rather than 15. Fifteen was chosen
    against the default setting alone, and the presets ask for more than the default: measured across
    the eight of them it moved a jazz piano by up to 27 ms and an ambient bell by 25, which is not a
    player relaxing. Two parts written on one tick still do not land on one tick, and nobody is
    waiting for anybody.
  • A section's intensity is untouched and keeps the whole of its travel: how hard a passage is
    played is a different question from how much one stroke of it varies, and it was the second one
    that sounded like bad playing. Both dials still reach 0, which is still a sequencer.

The palette answers to English too

  • A command is found by its English name whatever language the interface is in. A window drawn
    in Japanese shows 保存 and now also matches save, which is what the documentation, the
    keystroke chart and every other audio program call it — the language on screen is a display
    choice, and it should not decide which alphabet somebody has to type in. Both spellings are
    matched and neither is preferred: the better of the two scores counts, so a row cannot out-rank
    another merely by being matched twice. The key search in the settings window reads the same way,
    since the two lists are supposed to mean the same thing.
  • The language rows are matched from both sides, since they are the one place where what is drawn
    does not follow the setting: language now finds 言語 · 日本語 from an English window, as it
    already found Language · English from a Japanese one.