Skip to content

Releases: monzta1/ToneCommand

ToneCommand 1.2.2

Choose a tag to compare

@monzta1 monzta1 released this 06 Sep 22:17

The one-click updater is now fail-safe.

A bad release can no longer break the app. Before ToneCommand restarts into an update, it proves the new code actually loads (imports and builds the app) in a throwaway check. If the pull, the reinstall, or that check fails, the checkout is reverted to the version that was already running and the failure is reported plainly. The working server is never stopped for code that has not been shown to start, and if a restart somehow does not come back, the page gives a clear recovery step instead of a blank error.

Everything from 1.2.1 (one-click hands-off updates) is included. Update from the banner or Settings with one click.

ToneCommand 1.2.1

Choose a tag to compare

@monzta1 monzta1 released this 06 Sep 21:53

One-click, fully hands-off updates.

Click UPDATE in the banner or in Settings and ToneCommand pulls, reinstalls, and restarts itself, then the page reloads onto the new version. No terminal, no manual restart. The restart is graceful and releases the FM9's MIDI port first, so it never leaves a poisoned port, and the update button always shows once a newer release exists (disabled with a reason when it cannot auto-update, so it never silently vanishes).

Everything from 1.2.0 (the updater, the three-OS install docs, the delay-dry-note rulebook fix) is included.

Upgrading from 1.2.0: just click UPDATE. From anything older, git pull then .venv/bin/pip install -e . then restart, once.

ToneCommand 1.2.0

Choose a tag to compare

@monzta1 monzta1 released this 06 Sep 21:18

The app now knows its own version, tells you when a new one ships, and can update itself in one click.

What's new in 1.2.0

  • Version awareness + one-click updates. Settings shows the running version and a CHECK FOR UPDATES button. On launch ToneCommand asks GitHub for the latest release (cached, silent when offline) and, when you're behind, shows a banner with the new version, a release-notes link, and the exact upgrade commands for your OS. On a clean checkout on the main branch it also offers a one-click UPDATE button that runs git pull and the reinstall for you.
    • It never pulls or restarts on its own, is blocked during gig lock, and refuses a checkout with local changes or on another branch, so a live FM9 session is never yanked out from under you.
  • Install docs make macOS, Windows and Linux unmissable. The README Install section now shows all three OSes with visible commands instead of hiding Windows behind a link, and SETUP.md gains a proper Linux section (ALSA/rtmidi build deps, USB-MIDI note, ffmpeg).
  • A rulebook guardrail so a delay never hides the note again. From a live SLO-100 build where the delay ran so wet the dry note was inaudible: the planner now knows the dry must always be heard, a pedal on a wet mix must never reach a dry-killing value, effects bypass Thru not Mute, and it must never claim a structure it does not actually build.

Upgrading from 1.1.0: git pull, then .venv/bin/pip install -e . (Windows: .venv\Scripts\pip install -e .), then restart. After this one manual update, future updates are one click from inside the app.

Full notes in CHANGELOG.md.

ToneCommand 1.1.0

Choose a tag to compare

@monzta1 monzta1 released this 06 Sep 12:43

A full visual redesign, and the Review stage is now editable.

🎸 Watch it build: TONECOMMAND builds a Soldano SLO-100 inspired preset on a real FM9 — seven scenes from crunch and rhythm through high-gain to a singing SLO-style lead, planned and voiced by ToneCommand, confirmed by the player, then played.

What's new in 1.1.0

  • Inline value editing in Review. Every numeric parameter with a published range is now an editable field in the AFTER column. Type a new value, it lights amber the moment it differs from the plan, and on commit it clamps to the block's real min and max. The number you set is exactly what the confirmed send transmits, through the same read-back-verified path. The plan is a starting point you dial in, not take-it-or-leave-it.
  • A rack/HUD redesign of the whole interface. The five-stage flow (Request, Plan, Review, Confirm, Send) is now a row of angled hexagon step-tabs, each with an icon, lit on the active stage and flipping to a green check once done. Machined panels, a brushed faceplate and footer, a labelled command shelf, and the signal chain shown by default, centred and enlarged to use the full width.
  • Prev/next preset arrows and a wider preset field, so a full name reads and you can step presets without opening the list.
  • Holds up on a short desktop: the signal chain yields its space so a populated Review list is never squeezed to nothing.

Same five-stage flow, scene rack, review table and confirm gate as 1.0.0, reskinned. No functionality was removed, and the full test suite (1159 tests) stayed green. Old soul. New machine. Humans in command.

Full notes in CHANGELOG.md.

ToneCommand 1.0.0

Choose a tag to compare

@monzta1 monzta1 released this 05 Sep 23:26

OLD SOUL. NEW MACHINE. HUMANS IN COMMAND.

Talk to your FM9. It builds the tone. Type a sentence, get a gig-ready,
multi-scene preset on your Fractal FM9 in minutes: the signal chain wired, amps
voiced per scene, cabs chosen, scenes named on the footswitches. You read every
parameter change it proposes, you confirm, and only then does it land on the
hardware over USB MIDI, verified by reading the unit back. A language model
proposes; it never sends. Nothing reaches your amp until you say go.

Proof, not screenshots: all eight scenes of a drop C metalcore preset, each built by typing a sentence, played through on a real FM9 → https://youtu.be/7f840w5mQis

What's in 1.0.0

  • Build a tone from a video you don't have time to watch. Paste a YouTube
    rig rundown, a forum thread, or a chat. On a captioned video it reads the whole
    transcript and keeps only the gear and settings in about twenty seconds; the
    intro, the sponsor read and every tangent are thrown out, so a two-hour
    walkthrough becomes a compact tone spec without watching it. No captions? It
    transcribes locally on your machine.
  • One-tap +Create lays a fresh rig on a free slot, or clears the loaded
    buffer on a full unit, so there is always a blank canvas to build on (#44).
  • Both expression pedals, bound by name. "Put global volume on Pedal 1 and
    wah on Pedal 2" (#11).
  • Pick a cabinet by name in a build: "put a 4x12 V30 on the rhythm" (#45).
  • Compose a preset from parts of the tones you already own: "clone BT Marco
    Sfogli, take the gain stage from it and the delay from the Andy Timmons tone."
  • A prebuilt starter template laid in one pass, and batched verification,
    so the slow, fragile part of a build is gone (#47).
  • A bounded repair pass (#39) and a health scan after every build (#51)
    that catches a dead or duplicated scene the moment it lands.
  • A tone rulebook, dialed in by ear on real hardware: cleans ship loud,
    leads out-saturate the rhythm, no scene ships silent.

Read-back verification on every write, a blast-radius preview before anything is
sent, gig lock, and bring-your-own-AI (ChatGPT, Gemini, Grok, DeepSeek, Kimi, a
local model, or the Claude CLI).

Full notes in CHANGELOG.md.

0.9.0: install tones from Gift of Tone, one request box, builds that finish

Choose a tag to compare

@monzta1 monzta1 released this 04 Sep 02:42

The release that turns ToneCommand into something a player can just talk to.

Install tones by asking (#42)

  • Say "get me the Periphery tones from Gift of Tone" and it downloads the pack, pulls out the FM9 presets, skips the Axe-Fx/FM3 versions, lets you pick slots, and writes them to the unit. First one loads automatically.
  • Works offline against a local tone folder too: "find the luke tones and load them" combs the folder you set, matches nicknames, and installs, no unzipping. Filler words, plurals and typos no longer break the search.
  • Purchased .fasBundle packs install with their cabs, filed to the slots the artist intended. Loose .syx files install through the same review-and-send flow, whitelisted and verified by read-back.
  • A pick-what-to-install plan when a pack has more than one preset: everything checked by default, editable slots, a live summary of exactly what WRITE will do.

One place to ask, and it just builds

  • The desktop control surface: a single request box for every kind of request. Describe a sound, ask for a whole rig, or paste a link, and it routes internally. No modes to choose.
  • Empty slots build their own starting chain when a tone needs one, then voice it, inside the reviewed send.
  • One planner window for every request, so a big multi-scene build never times out for being large.
  • Plain language throughout: waiting, plans, and failures speak like a player, with the protocol and model details kept in diagnostics.

Multiple AI planners, set up in a click

  • ChatGPT, Gemini, Grok, DeepSeek and Kimi are one-click services; each keeps its own key and model. The signed-in Claude CLI is found on its own. The picker asks who plans rather than handing you a form.

Fixes worth calling out

  • From-empty builds no longer abort halfway: a block that had landed but read back with a transient id (a gate reads 18 for a beat before 146) no longer false-fails the whole build. Verified on hardware.
  • Long preset names at the FM9's length limit no longer fail a build.
  • Changing presets on the front panel no longer makes the rig cycle through every scene.

Nothing reaches the FM9 until you send, every flash write is whitelisted and read back, and nothing writes blind.

Nightly 2026-09-03: the desktop control surface

Choose a tag to compare

@monzta1 monzta1 released this 03 Sep 02:35

Nightly build of the redesigned desktop control surface. Not a stable
release: this is the new five-stage console fresh off a full day of live
hardening, published for early eyes and brave rigs. v0.8.0 remains the
last stable release.

What is in it

  • The five-stage command console. REQUEST, PLAN, REVIEW, CONFIRM,
    SEND replace the tabbed page. Scenes and the live signal path stay on
    screen through every stage; the final SEND TO FM9 exists only inside an
    armed, eight-second confirm gate that Esc or any target change disarms.
  • Review as a change table with filters and blast radius, Confirm as
    a facts matrix (target, scope, destination, recovery), Send as an
    observable transmission ending at EARS: PENDING.
  • Manual control as an Inspector opened from the signal chain itself.
  • WHO PLANS YOUR TONES. AI setup is named cards with plain costs and
    states (READY, NEEDS A KEY with a link, NEEDS SETUP); keys and models
    are stored per service; ChatGPT, Gemini, Grok, DeepSeek, Kimi, local
    models and OpenRouter are one click each; the Local model card finds
    LM Studio or Ollama on its own.
  • Verified end to end on real FM9 hardware: writes sent through the armed
    gate, read back verified, and undone from the shelf.

Honest state

1033 tests pass and the workflow has survived one long evening of real
use, which is exactly one evening. Edit-buffer safety, store whitelists
and read-back verification are unchanged underneath. Expect rough edges
in layout and copy; file what you find.

0.8.0: one wait experience everywhere, and STOP means stop

Choose a tag to compare

@monzta1 monzta1 released this 02 Sep 05:44

Watch 0.8.0 do its thing: one sentence, an empty FM9 slot, six minutes, a complete 8-scene Judas Priest preset. Unedited: https://www.youtube.com/watch?v=jCNAZyyt5o8

One wait experience, everywhere. The COMMAND box learned to count seconds,
listen for a heartbeat and offer a way out months before the rest of the app
did; every other path that could run for more than a few seconds still hid
behind a frozen line of text. This release moves all of them onto the same
machinery, and makes STOP mean stop.

Added

  • The claude CLI backend streams. Plans stream their action count and
    chat streams its words through --output-format stream-json, so the
    "31 changes written" counter finally fires on the zero-configuration
    default install instead of only behind an OpenAI-compatible router. The
    wire format was verified against the real CLI, not assumed.
  • STOP stops the backend. Abandoning a plan, chat, build or source read
    kills the planner subprocess on the server instead of leaving it burning
    for minutes while holding the settings lock. A request that does have to
    wait behind an earlier one now says QUEUED instead of pretending to work.
  • BUILD FROM A SOURCE streams. Reading narrates its stages off the wire
    (fetching, downloading audio, the first-run whisper model, transcribing,
    extracting) with an elapsed count and STOP; building shows the same
    changes-written counter as the COMMAND box. Both used to be a static line
    over minutes of work, the exact failure the plan stream was built to end.
  • FIX IT goes through the streaming planner, with the banner, the count
    and STOP, instead of a bare ASKING... over a multi-minute call.
  • The preset scan counts. Reading all 512 slot names is about fifteen
    seconds of MIDI; the popover, RESCAN and NAMES now count n/512 through it
    instead of showing "nothing matches" over an empty list, which read as
    "your unit is empty".
  • The health scan names the scene it is standing on, so the noises the
    rig makes during a scan are narrated rather than left unexplained, and the
    button counts SCENE n/8.
  • GIG MODE is on the page. A header pill shows the performance lockout
    and toggles it; it used to exist only as an environment variable and an
    API, so the page looked normal while every control failed one refusal at a
    time, and the refusal then told a guitarist to POST JSON.
  • The blast-radius sweep says so: mapping which scenes share channels steps
    the rig through all eight scenes audibly, and the log now names that read
    when it happens.
  • The LINK pill tracks the cable, both ways, in about a second. The
    server keeps one CoreMIDI client with a real notify callback on a runloop
    thread, which is the thing that actually keeps a macOS process's view of
    the MIDI bus alive (two earlier fixes for this are recorded, disproven,
    in KNOWN_QUIRKS), and GET /api/link/stream pushes presence changes to
    the page the moment they happen. Verified across six live plug/unplug
    cycles on real hardware.

Fixed

  • A build aimed at an empty slot lays its own foundations. A 135-action
    plan used to halt at "ADD amp" on a freshly erased slot and tell the
    player to go and press BUILD A STARTING CHAIN themselves: the tool knew
    the problem, knew the remedy, owned the code for it, and handed the work
    back. A transmit containing add_block now builds the starting chain
    itself when the loaded slot is empty (announced on the plan card before
    confirmation, reported as its own row in the results), and an add for a
    block already present counts as satisfied instead of halting everything
    after it.
  • Erasing is one confirmation now; the typing test is retired. The
    typed-name echo refused legitimate attempts over invisible double
    spaces, then over a machine-built title, then got fed the slot number,
    all in one evening. What survives is the part that protects: ERASE only
    arms once the slot's name has been read and shown, the single dialog
    names the slot and exactly what it holds, and the page sends the name it
    displayed for the server to match against flash, so a slot that changed
    since it was shown still refuses. API callers keep the name contract,
    spacing and case forgiven, and a typed slot number gets a reply naming
    the actual ask instead of a bare refusal.
  • Every slot label now leads with the number on your unit. The header
    pill said 159 while every label below said "158 (FM9-Edit 159)", so the
    app looked like it disagreed with itself about which preset was loaded.
    Labels are "159 (wire 158)" everywhere now: the front-panel number
    first, the MIDI wire number named for what it is in the bracket. One
    function owns the format, so nothing can drift back. The SAVE dropdown
    also fills its names right after startup instead of saying "name not
    read" until NAMES was pressed.
  • The SAVE panel contradicted its own dropdown after an unsaved build.
    The "Loaded:" line printed the edit buffer's name as though it were the
    slot's, directly above a dropdown showing what flash actually holds, and
    the two read as mismatching data. They are two true facts about two
    different things; when they differ the panel now names both and says
    which is which: what the slot holds, what your unsaved edits are called,
    and that SAVE writes the second over the first.
  • A refused action was missing from the live transmit count. The
    validation branch skipped the progress callback, so the SENDING counter
    stuck at n-1 of N while the final banner said otherwise. Found by running
    the stream against the real server, not by reading the code, which had
    looked fine. Refusals now count as failed steps.
  • ERASE refused legitimate attempts and looked broken. The typed-name
    confirmation demanded an exact match against names that carry internal
    double spaces the eye cannot see and machine-built titles nobody retypes
    from memory; two real attempts in a row were refused, and the refusal
    landed only in the LOG panel, far from the button. Spacing runs and case
    are forgiven now (the name itself is not), refusals are logged
    server-side, and both the refusal and the success are announced in the
    strip. (The typing itself was retired later the same evening; see the
    one-confirmation entry above.)
  • A failed action was pointed at, not named. "1 did not apply, marked
    above" left the player hunting through a hundred folded cards, twice in
    one evening. The outcome banner now names each failure with its reason,
    the card list opens itself and scrolls to the first failed card, and the
    server logs every refused action so a failure can be diagnosed without
    asking the player to read their browser back.
  • SHOW LOG sometimes needed several presses. The working strip rebuilt
    its entire HTML every second, so the button being pressed was destroyed
    between mousedown and mouseup and the click fell into the gap. The strip
    is built once now and only its text updates.
  • The strip vanished at the finish, so completion looked like nothing.
    It now holds the verdict for a few seconds, in the one place that cannot
    be scrolled away from: DONE in green with the count (and the stored slot
    when one was written), partial sends in amber, failures in red, and
    BUILD COMPLETE when a plan lands. A click dismisses it early.
  • A finished build looked like nothing happening. The completion note
    landed in the chat transcript while the page scrolled you to the plan
    panel, so the one line saying "the build worked, TRANSMIT is next" sat
    exactly where you no longer were. The plan panel now opens with its own
    verdict strip: how many changes, that nothing has been sent, that
    TRANSMIT is the next step, and the truth about UNDO for store plans.
  • The preset dropdown kept the overwritten preset's name after a stored
    plan.
    The slot-name cache was only ever invalidated by RESCAN, NAMES,
    rename and erase; a store inside a transmitted plan never told it. The
    store result already carries the slot's new name, so the cache entry is
    now corrected in place (no rescan) and the page re-pulls its lists after
    any transmit that stored. The SAVE button's full rescan-after-save is
    gone for the same reason: it spent seconds of MIDI to learn a name the
    store result had already delivered.
  • The transmit banner lied after a stored plan. "Your presets are
    untouched; UNDO covers what landed" was shown after a plan whose store had
    just overwritten a flash slot, false on both counts and caught live on the
    first real build through the new pipeline. The outcome copy now checks
    whether a store landed and names the overwritten slot when one did.
  • Gig mode could audibly walk all eight scenes mid-song. The shared-
    channel sweep ran automatically when the preset changed and was not gig
    gated, so a front-panel preset change during a set had the tool stepping
    scenes while someone played through it. It now refuses during gig mode and
    answers from cache only.
  • HOLD A and HOLD B stayed dead for the rest of the session after any undo
    or recall: the recall disabled all five buttons and the refresh only ever
    re-enabled three.
  • UNDO and RECALL say UNDOING.../RECALLING... while a forty-value restore
    runs; the preset pill shows a busy state while the unit switches.

Recipe sharing stops being code and starts being a thing that exists. The
worker and schema had been written and tested since 0.4.x and never deployed,
so the tone database existed as source and as nothing else: every recipe
anybody wrote went into the local outbox and stayed there.

Added

  • The sharing service is live, on Cloudflare Workers and D1. It holds an
    inbox and a counter and never content: recipes live in this repository's
    recipes/ folder. That split decides the failure mode. If the worker is
    down, browsing and using recipes still work, only submission and ranking
    pause, and the client hol...
Read more

0.7.0

Choose a tag to compare

@monzta1 monzta1 released this 01 Sep 02:00

Recipe sharing stops being code and starts being a thing that exists. The
worker and schema had been written and tested since 0.4.x and never deployed,
so the tone database existed as source and as nothing else: every recipe
anybody wrote went into the local outbox and stayed there.

Added

  • The sharing service is live, on Cloudflare Workers and D1. It holds an
    inbox and a counter and never content: recipes live in this repository's
    recipes/ folder. That split decides the failure mode. If the worker is
    down, browsing and using recipes still work, only submission and ranking
    pause, and the client holds both until it returns. Nothing anybody writes
    depends on it being up.
  • AUTO_PUBLISH, off by default. On, a submission is committed straight
    into recipes/ and is live immediately, with no human step. Every
    submission is recorded in D1 either way, so it is one flag to undo with
    nothing lost. Be clear-eyed about what it means: /submit is
    unauthenticated, so with it on, anyone who can reach the worker can write a
    file into a public repository. That is a deliberate choice while there are
    no users rather than an oversight.
  • A note when a recipe was made on different firmware. Recipes name models
    rather than numbering them, so a step resolves through the loading rig's own
    roster and a model that does not exist there is refused rather than becoming
    its neighbour. That covers the structural risk and not the audible one:
    Fractal revises voicings between releases, and tested_firmware was being
    recorded and shown to nobody. The plan now says so before you transmit.
  • service/wrangler.toml, which did not exist, so there was nothing to deploy
    with.

Fixed

  • The app could not find the service the docs told you to configure.
    share.endpoint() read only os.environ, while the planner and the store
    whitelist both fall back to .env. So the documented setup left sharing
    silently dark: no endpoint, recipes queueing forever, nothing anywhere
    saying why. A configuration that fails closed AND says nothing is the worst
    of both.
  • A submitted recipe could carry anything. The validator checked the
    envelope and never looked inside a step, so steps: [1,2,3] passed, and
    publishing writes the whole body, so any invented top-level key was
    preserved verbatim into the repository. On an unauthenticated endpoint with
    auto-publish on, that is "anyone may write arbitrary JSON into a public
    repo". Now: known keys only, every step an object whose kind is a real
    action, no invented keys inside steps, text bounded. store is refused
    outright, being the one action that writes to flash.
  • Publishing refuses to overwrite. Not moderation, integrity: without it
    anyone could post a recipe named after a curated tone and silently replace
    it.
  • The suite no longer reads the developer's real .env when testing sharing.
    The .env fallback leaked within a minute of being added, into a test
    asserting sharing was local only.

0.6.1

Choose a tag to compare

@monzta1 monzta1 released this 31 Aug 11:07

Fixed

  • The simulator let a discrete write of zero land on an enum. Hardware
    never does: sub 09 carrying a zero value IS the zeroed GET, and it reads
    rather than writes for EVERY parameter, not only the continuous ones. The
    proof was already in device.set_param_ordinal, which sends ordinal 0 as a
    CONTINUOUS 0.0 precisely because the discrete path cannot carry it.
  • 0.6.0 shipped the narrower guard, taken from #37 while merging #35 on the
    reasoning that it protected a legitimate discrete write of ordinal 0. There
    is no such write. Narrowing by parameter kind invented a distinction the
    device does not make, and left the simulator MORE permissive than the
    hardware, which is the wrong direction for a test double: code doing a
    discrete zero would have passed here and silently done nothing on the rig.
    Reported by @bschmalz81401 on #37.
  • test_zeroed_get_is_noop now stands the settle window down. It had been
    passing for a reason unrelated to what it claimed: the window served the
    read from the pre-write snapshot while the live buffer had already been
    zeroed, so the damage was hidden on any machine fast enough. It failed only
    on CI. With no window there is nowhere for a bad write to hide, and a
    companion test now covers the enum case the old guard got wrong.