Skip to content

Releases: jweigend/JamPilot

JamPilot 1.2.0 — Windows gets a download

Choose a tag to compare

@jweigend jweigend released this 29 Aug 02:23

The theme of this release: Windows gets a download. No new analysis, no
change to what you see on stage — this is about the last platform that could
only be installed from source, and about one thing that looked wrong there.

The 1.1.1 downloads were broken — these replace them

Every 1.1.1 binary — Linux, macOS and the first Windows upload — crashed on
jampilot run with a FileNotFoundError: the bundle was missing
data/timeline_poc.html. The spec listed the data files by name, the file was
added to the code, and nobody extended the list. Two things changed so this
cannot happen again: the spec now bundles everything in data/ instead of
a hand-maintained list, and jampilot selftest — which runs in CI on every
build — imports the web module, so a missing data file stops the build instead
of the user.

A standalone build for Windows

run.cmd --bundle now produces dist\JamPilot\ and a release ZIP next to it,
the same way ./run.sh --bundle has always produced a binary on Linux and
macOS. It skips when nothing changed, --force builds anyway, --check builds
twice and proves the result is reproducible, --venv builds from a fresh
environment out of the lock file. packaging/venv.ps1 carries the environment
logic that run.ps1 had inline, so there is one copy of it and not two.

It is a folder in a ZIP, not a single file — the one place where Windows
gets a different answer than the other platforms, and the reason the build was
held back until now. A single unsigned executable of this size that unpacks
itself into %TEMP% on every launch is a packer, technically, and heuristics
treat it as one. The folder does not trip that, and it starts in ~0.45 s
instead of ~2.5 s. It does not remove the SmartScreen warning: unsigned is
unsigned until someone buys a certificate. What it removes is the part that
also upsets virus scanners.

Linux / macOS Windows
Shape one file a folder in a ZIP
Size ~183 MB 150 MB zipped, 363 MB unpacked
Start ~2.5 s ~0.45 s
Reproducible yes (SHA-256 of the file) yes (all ~1700 files compared)

The ZIP carries a JamPilot.cmddouble-click that, not the .exe. A
console program takes its window down the instant it exits, so a JamPilot that
stops with a message ("no second output endpoint") would show that message for
a few milliseconds and then look like a program that did nothing. The .cmd
keeps the window open when the exit code is not zero, and only then. A
README.txt next to it covers the two dialogs Windows shows on a first start,
including the firewall one that silently breaks the QR code if it is dismissed.

Windows is now in .github/workflows/build.yml and builds on tag push together
with Linux and macOS.

Reproducibility holds on Windows too — that was an open question, not an
assumption: the PE format carries a timestamp in its header, and whether
PyInstaller normalises it was unknown. Measured over two full builds: identical
across all ~1700 files. What is compared there is the contents of the folder,
file by file; a ZIP stores modification times and therefore cannot be
bit-stable, which is a property of the format rather than a hole in the claim.

An icon

JamPilot has a symbol now — a pick with a note and a spectrum — and it shows up
everywhere the program does: in the control window and its taskbar entry, as
the favicon of the web display (and as the home-screen icon when you add the
page on your phone), on the Linux launcher that jampilot install writes, and
embedded in the Windows executable and the macOS app bundle. The Linux launcher
used to borrow a generic audio icon from the theme; it now installs its own
into ~/.local/share/icons and removes it again with --remove.

The big chord was too heavy on Windows

#current asked for font-weight: 750. Only fonts with a continuous weight
axis can give you that — SF Pro Display on macOS can, Segoe UI on Windows
cannot. CSS then searches upwards, finds no 800, and lands on Segoe UI
Black
: at 52 vh the chord came out blocky and far too fat. It was the only
element on the page asking for more than 700, which is why it was the only one
that looked wrong. Now 700, a real face everywhere, and Windows 11's
Segoe UI Variable Display is preferred where it exists.

The version number had three homes, and one of them was wrong

jampilot/__init__.py and pyproject.toml both said 1.1.1 and agreed — but
the third copy did not: packaging/jampilot.spec wrote a hardcoded 0.1.0
into the macOS bundle's Info.plist, and had done since 0.1.0 was current. Two
releases of JamPilot.app reported a version two minors behind the program
inside them, and nothing could have caught it, because nothing compared them.

The number now lives in __init__.py alone. pyproject.toml reads it from
there (tool.setuptools.dynamic), the spec parses it out for the Info.plist,
and build.ps1 uses it for the name of the ZIP. A value that is written once
cannot drift.

Binaries: Linux x86_64, macOS Apple Silicon and Windows x86_64 below.
Intel Mac: ./run.sh from a fresh clone is the whole setup.

JamPilot 1.1.1 — a timeline that holds still under fire

Choose a tag to compare

@jweigend jweigend released this 28 Aug 17:55

A timeline that holds still under fire.

1.1.0 made committed chords immutable. 1.1.1 makes sure they also keep their
distance — and it was validated in the rehearsal room the same day: the
display stays stable through fast changes. Every number below is measured;
the scripts are in the repo (tests/reference/messung_event_abstand.py).

Events keep a minimum spacing

The publish-once channel could commit chords a few hundredths of a second
apart — five chips in one second, cutting each other off into unreadable
fragments. The model never produces segments shorter than 0.25 s; the
density arose afterwards in the live path (boundary refinement with a
0.05 s floor, and the late-boundary correction landing 0.02 s behind an
event that had just been committed).

The EventLedger now guarantees a minimum gap of 0.25 s: within one
hop the later entry wins (the model's more recent judgement), against an
already published event the newcomer moves up to the minimum gap instead
of colliding — and an entry that merely repeats its predecessor (same
chord, same bass) is no event.

Track (reference set, --delay 5) Events closer than 0.25 s Root accuracy vs. ground truth
Let It Be 44 of 184 → 0 of 165 84.2 % → 83.4 %
Something 44 of 151 → 0 of 139 74.8 % → 75.0 %

The big chord no longer turns grey on every change

The change animation faded the chord name in from 40 % opacity — on black
that read as a grey chord for almost half a second, right when you want to
read it. It also fired whenever anything in the header changed (scale
degree arriving, spelling flipping, bass moving up). Now: a size-only
nudge, and only on an actual change of the chord.

Feature extraction 3× cheaper, bit-identical

librosa rebuilt the CQT filterbank on every call — ~50 ms of fixed cost
per hop. It is now built once per process: feature extraction per hop
70 → 21 ms (48 kHz, 10 s window), output bit-identical, covered by
tests. An incremental CQT (a further 21 → 12 ms) was measured end-to-end
and deliberately left out: identical features, but its hop-stable frame
grid yields ~7 % more committed events — the wandering grid of the full
recomputation acts as a dither the debounce relies on.

Also

  • README brought in line with the product: rehearsal rooms are a core use
    case (everyone hears JamPilot's output, the delay is invisible), fretboard
    and keyboard diagrams exist, the key pin and the inverted scale-degree
    view are documented, all screenshots are current.

Binaries: Linux x86_64, macOS Apple Silicon and Windows x86_64 below.
Intel Mac: ./run.sh from a fresh clone is the whole setup.

Warning

The binaries of this release were broken and have been removed. Download
1.2.0 instead.

JamPilot 1.1.0 — a timeline you can trust

Choose a tag to compare

@jweigend jweigend released this 27 Aug 22:51

A timeline you can trust.

JamPilot 1.1.0 rebuilds the heart of the product — the chord timeline — around
one promise: what you see will not change. Every improvement below is
measured; methodology and raw numbers are in the repo
(docs/exploration/zeitleiste-redesign.md, tests/reference/README.md,
tests/realaudio/REPORT_key_labels.md).

Publish-once timeline

The display no longer shows a continuously revised hypothesis. The server
commits every chord exactly once at a commit boundary ahead of the audible
position — a committed chord can never move, get renamed, or vanish.
Structurally, not by damping.

  • Shown events later contradicted by the analysis (live, full song):
    19 % → 5 % — and none of it reaches the screen anymore
  • Chips deleted/recreated on screen: zero (event identity)
  • Contract violations across all live runs: 0
  • Measured cost of freezing: ~1 point root accuracy (83.3 % vs. 84.5 %) —
    a price we pay knowingly, for a display that holds still
  • Fast chord changes now occlude cleanly instead of overlapping; the lane
    animates on the compositor and absorbs network jitter as an imperceptible
    tempo change — no more visible jumps

Better chord recognition

Retrained BTC weights (fine-tuned on offset-corrected Isophonics ground
truth), on the 5-track annotated reference set:

  • Labels exactly right: 69.8 % → 73.1 %
  • Root right: 75.6 % → 79.1 %

No off-the-shelf teacher beat this finetune.

A bass that never flickers

The measured slash bass follows a monotone rule: committed only after ~1 s
of stable measurement, allowed to appear once later — never retracted, never
swapped. The flicker's root cause was the moving segment end, not the
measurement: bass is now pooled over a fixed 2 s window that matches the
full-segment verdict exactly (1 % false slashes against the Isophonics bass
annotations). Live: 24 retracted basses per song → 0 visible.

Key detection that gets minor right

The tonic keeps coming from the chroma (measured best); the major/minor
decision
is now voted by the recognised chords. Third-less voicings no
longer flip a minor song to major:

  • Correct major/minor: 66.9 % → 98.4 % (10-track set)
  • Two songs that were wrong for their entire duration are now correct
    (G minor shown as G major: 0 % → 98 %; A dorian shown as A major:
    0 % → 100 %) — and no major song flipped
  • Plus: two-scale key estimator (calm, but modulation-capable) and a
    key pin in the settings for when you simply know the key

Nashville scale degrees (new)

Degrees above the timeline and the big chord — plain, inverted (degree large,
name small), or off. A progression looks the same in every key.

The delay is now a real control

--delay defaults to 5 s and splits half and half into visible lookahead
and model settle time: 5 s → 2 s lane / 2 s settle (the measured sweet spot),
6 s → 2.5 s / 2.5 s. More buffer means seeing further ahead and
better-settled chords.

Under the hood

  • /timeline-poc: internal debug view — committed events, the still-revisable
    hypothesis as ghost chips, live counters for discarded revisions and
    contract violations
  • Measurement scripts checked in (tests/reference/messung_*.py)
  • 468 tests (0.1.0: 419), reproducible builds

Warning

The binaries of this release were broken and have been removed. Download
1.2.0 instead.

JamPilot 0.1.0 — first public release

Choose a tag to compare

@jweigend jweigend released this 13 Aug 19:44

The first public release of JamPilot.

Play any song on your computer — Spotify, YouTube, an MP3 — and JamPilot shows
you the chords before you hear them: it delays your system audio by a few
seconds, analyses the fresh signal in that window, and puts the coming chords
on a timeline that every phone and tablet in the room can join with one QR
scan.

60-second demo

What's in 0.1.0

  • Live chord detection from system audio — a learned recogniser (BTC
    transformer, ported to pure NumPy), 14 chord qualities × 12 roots, boundaries
    pulled onto the audible attack
  • The lead: the delayed playback is the feature — you see every chord
    seconds before it sounds, with a countdown
  • Band sync via QR code: one computer listens, every device on the Wi-Fi is
    a display — no app, no account
  • Instrument views, per device: plain chords, measured bass (C/E) with a
    four-string neck diagram, guitar fretboard shapes, and a keyboard view with
    look-ahead voicings that keep your hand in place
  • Key detection and spelling (♯/♭ chosen to match the key, or forced)
  • Automatic audio routing on Linux (PipeWire/PulseAudio) and Windows
    (driver-free in the common case) — transactional, restored on exit, with
    jampilot cleanup for hard kills; macOS runs via BlackHole
  • One-script setup: ./run.sh / run.cmd builds and repairs the
    environment itself; standalone binary via --bundle
  • 419 tests, reproducible builds

Platforms

Linux is fully tested (the reference platform); macOS and Windows run but have
not had long musical sessions yet — details in the
README.

Get it

git clone https://github.com/jweigend/JamPilot.git && cd JamPilot
./run.sh        # Windows: run.cmd

Feedback wanted

This is an early release, and honest feedback from people who actually rehearse
is worth more than anything else right now: what would make this useful in
your practice room? →
Discussions

More on how it works: HOW-IT-WORKS.md ·
UNDER-THE-HOOD.md

Downloads

Standalone binaries, no Python needed (~200 MB — why):

File Platform SHA-256
jampilot-0.1.0-linux-x86_64.tar.gz Linux x86_64 (glibc ≥ Ubuntu 22.04) 2da038ff…ad9ee2c0
jampilot-0.1.0-macos-arm64.tar.gz macOS Apple Silicon (binary + JamPilot.app) dca4261b…6077a0f4

Linux: needs libportaudio2 and PipeWire/PulseAudio. macOS: needs
BlackHole 2ch; the binaries are
unsigned, so clear the quarantine flag once:
xattr -dr com.apple.quarantine jampilot JamPilot.app.
There is no Intel-macOS or Windows binary: Intel Macs and Windows install
from source (./run.sh / run.cmd is the whole setup).