Skip to content

Releases: TerraByte-Dev/Keys

v0.8.4 — Keys only answers its own page

Choose a tag to compare

@TerraByte-Dev TerraByte-Dev released this 16 Oct 23:32

Keys only answers its own page

Keys listens on 127.0.0.1, but that does not make it private: any web page you visit can
make your browser call it. Before this release a hostile page could read the live note feed
and press Panic or start an update; DNS rebinding could reach the whole API.

Now every request must carry a loopback Host, and any Origin the browser sends must match
it — on the websocket too. Anything else gets a 403. There is no per-note cost.

Names from the network are checked

Preset ids, saved-loop names and soundfont names become file names. A ..\ in any of them
could reach outside its folder. They are now validated (find_asset, preset save/delete,
loop load).

Score import checks the declared size before reading the body and converts off the event
loop, so a large file can no longer stall the UI feed.

Adds tools/security_check.py (43 assertions). Not fixed here: a crafted .mid can still
stall the importer, and /api/data reports the data folder path.

SHA-256 of the zip: 67d8ae87b91bdb6efbf1fab79105ccf754456e68c2115dec516399910c2c1b1e

v0.8.3 — One shelf at a time, and a download you cannot order twice

Choose a tag to compare

@TerraByte-Dev TerraByte-Dev released this 30 Aug 05:16

You could order the same download twice

After a download finished, Check for updates came back to life next to Restart and
install
— and pressing it painted over the ready-to-install panel and offered to fetch the
same 55 MB again. Taking that offer threw away the copy already on disk and re-downloaded
the whole thing, as many times as you liked.

Fixed on both sides. The backend now refuses a download while one is already staged, and the
button follows the state instead of being switched back on unconditionally.

The staged panel also gained a Discard download button. Keys has always been able to
throw away a staged update — it is 88 MB sitting beside the application, and declining it
should be possible — but nothing on screen ever offered, so the only ways out were installing
it or restarting.

Instruments and Profiles are a toggle now

The Instruments panel showed both shelves stacked, which made it two scrollers tall and
neither of them a comfortable height. They are also two different questions: what sound do I
want
means browsing 287 of them, load the rig I built means picking one of a handful.

They are now a toggle, with Instruments showing by default. The panel is a third shorter,
which also makes it a much better neighbour on Play's first row.

v0.8.2 — Play stops wasting half its first row

Choose a tag to compare

@TerraByte-Dev TerraByte-Dev released this 30 Aug 04:58

A layout fix for the Play tab.

Instruments is the tallest panel in the app — a 287-instrument scroller with the profile
shelf above it — and Effects was full width, so it could not fit beside it. The result was
that half of the first row sat empty under the one panel you look at first, and Scale
highlighter was stranded alone at the bottom with the same dead space beside it.

Effects is now half width and sits next to Instruments, and Scale highlighter fills the gap
underneath it. The grid is masonry, so everything below repacks: Play is 27% shorter, and
no panel is left alone on a row.

Nothing moved between tabs and nothing changed behaviour — if you have dragged your Play
panels into an arrangement you like, your saved layout still wins.

v0.8.1 — The dials were a landmine and the counters never moved

Choose a tag to compare

@TerraByte-Dev TerraByte-Dev released this 30 Aug 04:34

A hotfix for three defects in 0.8.0 — two of them in the parts of that release that
existed specifically to stop things failing silently. If you are on 0.8.0, update.

If MIDI stopped working after updating to 0.8.0

That was this. midi_port used to store a port index; 0.8.0 started reading it as a
name and did not migrate old values. If you had ever picked a MIDI port by hand, your
saved 1 pinned Keys to a device literally named "1", which matched nothing — so nothing
opened, and the panel read "could not open any MIDI input" with every port listed and
silent. A saved 0 survived by pure luck, which meant this hit exactly the people who had
gone looking for a working port.

A legacy value is now recognised, discarded, and written back so the migration happens once.

And a pin naming a device that is not plugged in no longer goes quiet: Keys falls back to
listening to every input and tells you which device it could not find. Silence is never the
right answer to "the keyboard you chose is not here."

Stuck on 0.8.0 right now?

You do not need this update to get out, and this is confirmed working in the field:

Settings → MIDI input → "Listen to everything"

That clears the bad setting on the build you already have, and it sticks. Then update.

The dials had a landmine on them

The invisible range input inside each knob lost a CSS cascade, so it ended up pinned across
the top of the dial at full row width. A click there set the value from the horizontal
position and saved it. Every 0–1 dial — zone volume, pan, the reverb and chorus sends, the
loop layer controls — could silently rewrite the setting it exists to set, the first time
you reached for it.

The port counters never moved

0.8.0 added a per-port message counter to Settings → MIDI input, with the panel telling
you "the port that moves is your keyboard." The panel was built once when the tab opened and
never repainted, so the counters were frozen at zero. Anyone who updated because their
keyboard was silent would have watched every port read silent forever and concluded Keys
was not receiving anything — the exact opposite of the truth.

They update live now, and they are keyed by device name rather than port number, so a count
follows your keyboard across a replug instead of being stranded on whatever took its slot.

Also fixed

  • Pinning a port another application is holding no longer closes the input that was
    working. Keys keeps listening and reports what it could not open.
  • After a failed audio-device open, MIDI events can no longer reach a synth that was torn
    down.
  • A preset's saved effects are filtered on load as well as on save, so a preset file cannot
    reach past the two effect units into the rest of your settings.
  • Loading a profile now refreshes the settings snapshot, so saving a profile straight
    afterwards no longer stores the reverb from before the load.
  • The tour, the --dev status line, and the README no longer point at panels that 0.8.0
    moved or removed.

v0.8.0 — Two silences, and a place for everything

Choose a tag to compare

@TerraByte-Dev TerraByte-Dev released this 30 Aug 03:39

⚠️ This release has a MIDI bug. Install v0.8.1.

If MIDI stopped working after you installed 0.8.0, that is this bug and it is not your setup.

This release changed how the chosen MIDI port is stored — from a port number to a port
name — and did not convert the old value. If you had ever picked a port by hand, the saved
number was read as a device name, matched nothing, and Keys opened no input at all:
could not open any MIDI input, with every port listed and silent. A saved 0 survived by
accident, so this hit exactly the people who had already gone looking for a working port.

You can fix it right now, without updating — confirmed working in the field:

Settings → MIDI input → "Listen to everything"

That clears the bad setting on the build you already have, and it sticks. Then update to
v0.8.1, which fixes the cause and two worse defects found while chasing it.


Two things in this release were not features. They were failures with no signal attached
to them — the app looked fine, reported fine, and made no sound. Both are fixed, and both
now say what is wrong.

Your keyboard works, whichever one it is

Keys opened exactly one MIDI input: the first one, or the one you last picked. A single-port
piano never made that wrong, so it stood for months. A controller that exposes two inputs
— an Alesis V49 shows up as both V49 and MIDIIN2 (V49) — had a one-in-two chance of having
its keys on the port nobody opened. When it lost, the app listed both ports, reported no
error, lit no keys and made no sound.

Keys now listens to every MIDI input at once. There is nothing to pick and nothing to get
wrong. A device that mirrors its keys onto both ports still sounds each note once, and a port
another application is holding no longer takes the rest down with it.

Settings → MIDI input shows a message counter per port. Press a key: the port whose
counter moves is your keyboard. Pinning one input still works and is now remembered by name,
because Windows hands out port numbers in plug order — a saved number quietly came to mean a
different device the moment anything else was plugged in.

The audio device that could never open

Any output endpoint whose name is longer than 31 characters could not be opened, ever. The
name was being truncated on its way to the driver, the open failed, and every layer above
reported success — started: true, a confident 3.00 ms, and silence. Five of the seven
endpoints on the development machine were over the limit; Bluetooth headphones essentially
always are.

Now: the full name reaches the driver, a refused device falls back to the system default and
says so, and the Audio output panel shows -- instead of inventing a latency for a stream
that was never opened.

Where things live

The tabs were reorganised around when you reach for something rather than how it is built.

  • Play — Instruments, Effects, the metronome, the Loop station, Scale highlighter.
  • Practice — Now, Timing, Exercises, Songs.
  • Tools — Touch response, Pedal, Chords & scales, Backing tracks.
  • Layers — splits and layers.

The metronome moved to Play because you look at it while you use it. Touch response and Pedal
left Play for Tools because you set them up once and stop thinking about them. Songs moved to
Practice, because Play is the tab you are in when you want a tone, not when you want
something to play along to.

Profiles replace the Presets panel and live inside Instruments. A profile is a sound, its
zones, and the two effect units together — saved, renamed and loaded from one place. The 62
presets that ship with Keys still cannot touch a reverb you tuned; only a profile you saved
yourself carries the units, and only that restores them.

Sheet music is a mode of the roll, not a panel of its own. Every song row has a Roll
button and a Sheet button: two doors into the same full-screen player, same clock, and you
can switch between them once you are in there.

Mix amounts — zone volume, pan, the reverb and chorus sends — are dials now. Anything measured
in its own units (BPM, Hz, milliseconds, a split point) is still a slider, because a dial would
be lying about the scale.

Removed

  • The Sound panel in Play — it was a second copy of the volume already in the top bar.
  • Events in Settings — queue depth belongs in a debugger, not in a piano.
  • Pick up where you left off.
  • The standalone Sheet music panel, replaced by the roll's sheet mode. Playing a score back
    through the synth went with it; the roll makes no sound of its own, on purpose, which is why
    a play-along still works with the audio engine down.

v0.7.3 — A key you press must never be ignored in silence

Choose a tag to compare

@TerraByte-Dev TerraByte-Dev released this 15 Aug 10:48

A key you press must never be ignored in silence

Declare a keyboard whose range isn't the one the size chip assumed, and Keys looked broken.

Every key still sounded — the engine routes on the raw incoming note and never consulted your
declared range — but the drawn keyboard has no element for a key outside it. So nothing lit up, the
roll had no column, and from the other side of the screen that reads as "my keyboard stopped
working"
.

Now a note outside the declared range says so, once, and says what to do:

C1 is outside the 61 keys you told Keys you have, so it sounds but does not light up.
Sound → Your keyboard → "Press my keys instead" sets it from your actual keyboard.

"61 keys" is not one range. Plenty of controllers sit somewhere other than C2–C7, and many have
their own octave buttons that move where they start — so the size chips are a shortcut, not a fact
about your hardware. Press my keys instead reads what your keyboard actually sends, and it's the
only answer that can't be wrong.

The notice stays quiet while Detect is armed, because there, pressing a key outside the current
range is exactly what you were just asked to do.

If your keys have stopped lighting up right now

You don't need this update to fix it. In Keys: Sound → Your keyboard → Press my keys instead,
then press your lowest and highest key. Or pick 88 keys to put it back how it was. Your playing
was never lost — it was sounding the whole time.


947 assertions across the thirteen checks that need nothing plugged in.

SHA-256 bb23d94b89e971d73148ce784083f679a2c625b5b0755b6f817343873e4bc58a

v0.7.2 — A dot on the gear, and no surprises

Choose a tag to compare

@TerraByte-Dev TerraByte-Dev released this 12 Aug 20:41

A dot on the gear, and no surprises

Keys does not update itself. Downloading is a button, installing is a second button, and the
swap happens when you close the app because you asked it to. That was already true — what was
missing was any way to find out there was something to press.

Now Keys asks GitHub for the public release list once, when it opens. If there's something
newer, the gear grows a small amber dot — and the same dot appears on the About & updates row,
so the badge tells you where rather than only that. Nothing downloads and nothing installs
because of it.

You can switch it off. About & updates → Look for updates when Keys opens. Turn it off and
Keys touches the network only when you press Check for updates.

This changed a promise the app used to make — that it never contacted the network at startup — and
the promise moved rather than the badge quietly breaking it. You can't show a dot for news you
refused to hear. Every place that stated the old promise now states the new one. The request still
sends nothing about you: it's a GET for a public list, once.

The dot is small and doesn't animate. This is a piano; a pulsing notification in the corner while
you're playing is the app asking for attention it hasn't earned. It waits, and it's still there
tomorrow.


947 assertions across the thirteen checks that need nothing plugged in, plus engine_check,
looper_check and pipeline_check on the audio device.

Install: Check for updates in Settings → About, or extract the zip over your Keys folder.
Your practice history, settings and recordings live in %LOCALAPPDATA%\Keys and are not touched.

SHA-256 f9e60e59819c964284ab174de17659d94df663bf15b1741c0823e0330c56372c

v0.7.1 — PANIC means everything

Choose a tag to compare

@TerraByte-Dev TerraByte-Dev released this 12 Aug 19:27

PANIC means everything

PANIC sent all-notes-off and nothing else — which left the metronome clicking, the loop
going round and a score still playing. Those are the three things most likely to be the noise
you wanted stopped. "All notes off" is a MIDI operation; "I don't know what's making that sound,
make it stop" is a different request, and it's the one the button is for.

Press it now and it stops:

  • the metronome
  • the loop station
  • a score that is playing
  • a backing-track video
  • every sounding note

…and hands back a tempo an exercise had borrowed, so your next click isn't mysteriously at the
exercise's bpm.

It stops things and clears nothing. Your loop keeps its layers, your tempo stays put, a take
that was recording is kept, and everything starts again from where you left it. It's an emergency
brake, not a reset.

It also tells you what it silenced — "Stopped metronome, loop, 3 notes" — or "Nothing was
playing"
, which is its own useful answer when you can't tell where a sound is coming from and
needed to rule Keys out.

Esc still fires it from anywhere, including while you're typing in a box.


947 assertions across the thirteen checks that need nothing plugged in, plus engine_check,
looper_check and pipeline_check green on the audio device — pipeline_check section 11 now
pins the whole behaviour, including that nothing gets destroyed.

Install: Check for updates in Settings → About, or extract the zip over your existing
Keys folder. Your practice history, settings and recordings live in %LOCALAPPDATA%\Keys and
are not touched.

SHA-256 dd910b2725855ed84b20c1d7780d7285b495e2b00e0f61af846d5e62b921145d

v0.7.0 — Not everyone owns 88 keys

Choose a tag to compare

@TerraByte-Dev TerraByte-Dev released this 12 Aug 07:43

Not everyone owns 88 keys

Keys was built on a Yamaha P-71B and assumed one everywhere. On a 61-key controller that
assumption was not cosmetic: it drew twenty-seven keys you do not have, set scale exercises
down in them, sight-read into them, and marked none of it unreachable.

Tell it what you own. The second card of the tutorial now asks, and
Sound → Your keyboard is where you change it — 25 to 88, a custom range, or press your
lowest and highest key and let it work them out. Everything follows: what gets drawn, how far
a scale runs, where sight reading puts its notes, the pedal range, and the play-along.

Octaves

  • OCT −/+ above the keys (Z and X) shifts the whole instrument. Safe to press
    mid-chord — a key still down is released at the pitch it was struck at.
  • Per-hand octaves in a split, and one per zone in the Layers editor. Set both hands to the
    same octave and you have the plain split back.

Play-alongs fit themselves

A piece written for more keys than you own now moves into reach by whole octaves, keeping every
interval as written, and says so on the bar — with a manual ± override, because the automatic
choice is a guess. Measured across 89 real scores on a 61-key board: 49 already fitted, 24 had a
couple of stray notes and were left alone, and 16 did not fit — all 16 improved, none were made
worse.
Where notes still cannot be reached, the count is on screen instead of the bass quietly
disappearing.

Fixes

Three of these predate this release:

  • A scale that did not fit hung the exercise forever — the grader blocks on a note your
    keyboard cannot produce. It now refuses with a readable message.
  • Sight reading raised IndexError on a bass-range keyboard, and a second, drifted copy of the
    same helper silently generated notes above the top of the instrument.
  • The sustain pedal could orphan a voice across an octave change, leaving a note ringing
    that no note-off could reach.
  • Declaring a smaller keyboard destroyed settings it had no business touching — a saved pedal
    zone died to a nudge of the Decay slider. Both the pedal zone and the sight-reading window now
    keep what you asked for and clamp only where they are used.

Nothing recorded changes meaning, so there is no migration. Narrowing your keyboard is never
destructive: the stats still read all of MIDI, and Reset settings keeps how many keys you own,
because that is a fact about your hardware rather than a preference.


946 assertions across the thirteen checks that need nothing plugged in, plus engine_check,
looper_check and pipeline_check green on the audio device.

Install: extract the zip over your existing Keys folder, or press Check for updates in
Settings → About. Your practice history, settings and recordings live in %LOCALAPPDATA%\Keys and
are not touched. The build is unsigned, so a fresh browser download still shows
"Windows protected your PC".

SHA-256 1b3f637de000a70250bf0cfd4527dbf621e06a70d3329a793a8e66f8d5bcd989

v0.6.0 — Many files at once, a section on repeat, and knobs instead of opinions

Choose a tag to compare

@TerraByte-Dev TerraByte-Dev released this 07 Aug 09:41

Many files at once, a section on repeat, and knobs instead of opinions

Import a folder's worth of MIDI in one go

The picker takes as many files as you can select, and so does dropping them on the roll.
One file still lands straight on the roll and starts — importing a song is asking to
play it. Forty files import forty and play none, because forty songs opening in sequence
is nobody's idea of an import.

Anything that fails — a bad export, an unreadable .mid — is collected into one line
naming the files, instead of forty error toasts.

Put four bars on repeat

Set A starts the section at the top of the bar you're in, Set B ends it at the end
of the bar you're in, and it loops until you press Clear loop. Because both ends land
on bar lines, "bars 9–12" means bars 9 to 12, and setting both inside one bar loops that
one bar rather than nothing.

Wait mode still waits inside a loop, and a note you're still holding won't open the
section's first chord on the next lap — you have to actually play it again.

Restart is now called Restart. It was the |‹ button, it always worked, and nobody
could tell what it was.

An effects panel, next to the instrument

Reverb and chorus moved out of the rig tab into Play → Effects, beside the instrument
list, with a send for how much of what you're playing goes into each unit. Two
unsynced copies of one slider was the bug.

There is no tone or brightness knob, and that is deliberate rather than an omission:
FluidSynth has exactly two effect units, and the MIDI controllers that would drive a
filter render byte-identical output at 0, 64 and 127. A brightness knob here would be
a placebo, so it isn't offered.

The soft piano and the concert hall are gone

Soft Piano, Midnight Piano, Cathedral Keys, Concert Grand, Close Piano, Soft
Rhodes
and the two Soft Grand presets have been removed, along with the whisper
velocity curve and the second SoundFont. A velocity ceiling is not a soft piano, and a
preset that carried its own reverb quietly rewrote a room you had tuned. 62 presets, one
SoundFont
, and the knobs above to shape any of them.

Keys can install its own updates

Settings → About now downloads a new release and installs it: Check, Download,
Restart and install. Three presses, and nothing on a timer — Keys still only talks to
GitHub when you press the button, never on launch and never in the background.

Your practice history, settings, recordings and anything you added live in
%LOCALAPPDATA%\Keys and are not touched by an update.

This is the first release you can install from inside the app — 0.5.1 and earlier have
to be replaced by hand, one more time.

Also fixed

  • Pressing Play at the end of a score played silence instead of playing it again.

Install

Download Keys-0.6.0-win-x64.zip and extract it into a folder of its own —
Keys.exe and _internal sit at the root of the archive, so "Extract Here" empties them
into whatever folder you're standing in. Then run Keys.exe.

Windows SmartScreen will warn on first run; the build is not code-signed.
More info → Run anyway.

Verify the download if you like:

sha256  a2320fde43fb9cf3761a02cb21731d138ab9106597bfbc1c2d8f525779536136