Skip to content

Releases: jfalvarez1/chiptune_music_maker

v3.12.0 "Measured"

Choose a tag to compare

@jfalvarez1 jfalvarez1 released this 05 Sep 02:30

⬇️ Download ChiptuneTracker-Windows.zip

Windows, no installer, no dependencies. Unzip and run ChiptuneTracker.exe.


Five things that had been quietly wrong for a long time, found by measuring
rather than by looking. And the interop to go with them.

Fixed

The master limiter was a hard clipper. Its envelope smoothing used
1 - exp(-1/(t·sr)) where the retained fraction needed exp(-1/(t·sr)) —
0.022 instead of 0.978 — so the gain reached its target in a single sample,
every sample. The release control, exposed in every profile, every loudness
preset and the UI, did nothing at any value.

It now has a real envelope and a 1.5 ms lookahead. The test that pins it
measures crest factor: a sine's peak is 1.414 times its RMS, a limiter keeps
that, and a clipper drives it toward 1.

A tanh sat after the limiter on every project, costing 2.2 dB and making
the ceiling fiction — a −0.3 dB setting actually delivered −2.5 dBFS. It also
made both of the suite's clipping tests unable to fail, since |tanh(x)| is
always under 1.

Every MIDI file this program has exported had a nonsensical tempo. The
library's addTempo takes beats per minute; it was being handed
60000000/bpm, which it converted again — so a 132 BPM project wrote 132
microseconds per quarter note and opened elsewhere at about 454,000 BPM.
Nothing caught it because nothing ever read one back. Writing the importer
did, on the first round trip.

Autosave never fired for most edits. Its change detection was a
fingerprint of note counts, so changing a pitch, a velocity, a length or any
mixer setting left it identical. An hour spent voicing a patch and balancing a
mix would autosave exactly never.

The crash handler wrote a log and let go, losing up to ninety seconds of
work while being the one piece of code still able to write the file. It saves
first now, and says in the log whether it managed to.

Also: the WAV writer truncated instead of rounding and had no dither; the
master EQ, compressor and limiter were one mono instance each processed left
then right, halving every dynamics time constant; and the LUFS meter had no
K-weighting despite the name.

Added

MIDI import. One pattern per track, one track per channel. Notes, timing,
length and velocity come across; instruments do not, because a MIDI file has
none — so importing into a project you have already voiced keeps the voicing
and swaps the notes. It reports what it dropped and why.

FLAC export and 24-bit WAV. FLAC matters more here than in most programs:
square waves are about as predictable as audio gets, so the file is often a
third the size of the WAV.

Groove — per-row timing. The swing control is one number applied to every
off-beat and can express 7 5 and nothing else. A groove is the list:
7 5 6 6 swings the first half of the beat and leaves the second alone. A
full cycle always covers exactly as many rows of straight time as it has
entries, whatever the numbers, so a groove can never change the tempo.

Legato and tone portamento. Every note-on used to take a fresh voice and
restart the envelope, so pitch could not change without re-striking. A tied
note keeps the voice that is sounding; with a slide it glides from wherever it
is at that many semitones per second — the tracker's 3xx, and the thing the
old slide could never do, since it targeted the note's own pitch and never
looked at any other note.

PAL — and it is not a scaled NTSC. The noise period tables cross: at
index 2 the PAL period is 14 CPU cycles where NTSC is 16, so PAL noise is
brighter there and darker everywhere above. Region also moves every pitch by
7.65% and the envelope clock from 240 Hz to 200, and neither of those is the
60 Hz people assume.

A chord progression generator that derives chords from the scale. The
progressions that already existed were hardcoded degree lists where every
chord was a fixed minor triad, so "i – VI – III – VII" came out minor on every
degree. In C major the chords on I, ii, V, vi and vii are major, minor, major,
minor and diminished, and no fixed interval produces all five.

Changed

Mastering measures rather than claims. The profiles' LUFS targets were
comments; they are fields now, with a real BS.1770 meter and a 4× oversampled
true-peak meter behind them. The genre table is re-voiced from measured data,
with Darksynth, Drum & Bass, Lo-fi, Pop, Rock and Ambient added.

Chiptune's mastering profile changed direction in three places, from
measurement:

  • its high shelf was +2 dB and is now −0.8, because chip percussion is already
    all edge and lifting the top is the documented way to make it harsh
  • its stereo width was 1.12 and is now exactly 1.00 — measured correlation on
    a centred chip mix is +1.000 at any width setting, because there is no
    side signal to widen
  • its true-peak ceiling is −1.6 dB rather than −1.0, because a square wave
    at 0 dBFS reconstructs at +2.1 dBTP and a noise channel at +5.4

A DC blocker on the master, on by default. A pulse wave's DC offset is
2·duty − 1 by construction, so a 12.5% pulse sits at −0.75. Removing it
raises the usable peak by 4.86 dB.

The four demo songs were never actually mastered — the function that
mastered them set nine fields and neither was width or saturation, both of
which default to off. They now measure between −11.6 and −7.9 LUFS with their
ceilings held.

The user guide gained six sections and three walkthroughs, and its three
lists of sections — which had all drifted, leaving two sections reachable only
by scrolling — are now derived from the document with a test asserting they
agree.

Full notes in CHANGELOG.md.

v3.11.0 "Rack"

Choose a tag to compare

@jfalvarez1 jfalvarez1 released this 04 Sep 03:54

⬇️ Download ChiptuneTracker-Windows.zip

Windows, no installer, no dependencies. Unzip and run ChiptuneTracker.exe.


Four things, and three of them are for chip work.

Note FX — a rack that changes which notes play

The insert rack changes how a note sounds. This one changes what notes there
are. A Chord module turns one written note into three, an Arpeggio
turns those into a running sequence, a Strum stops them all arriving at
once. Also Transpose, Octave, Scale, Range and Velocity — up to eight modules
per channel, in any order, and the order matters: Chord above Arpeggio is an
arpeggiated chord, the other way round is a chord on every step of one.

It is a rack rather than a menu command on purpose. Writing an arpeggio into
the pattern means the notes are simply notes from then on: changing the rate
is undo-and-redo, and there is no way to hear the part without it. As a rack
the pattern keeps what you wrote, and you can reorder, switch off or change it
while it plays.

The panel runs a middle C through the real rack and prints what comes out, so
you can see what a setting does without pressing play. The Chord module's
diatonic shape is scale-aware — the triad on D in C major is D F A, a minor
third, not a fixed table's D F♯ A.

Wave tools, and Shapes

The wavetable editor could draw by hand and initialise four presets.
Everything past that meant redrawing 256 samples with a mouse. Now: scale the
wave horizontally with a choice of interpolation, shift it, smooth it,
reverse it, invert it, and remove its DC offset.

Fit to hardware is the chiptune one, and it is exact rather than
approximate. Reduce the wave to N held steps and round every sample to N
levels, and what comes out is something a real wave channel could store — the
Game Boy's is 32 steps of 4 bits. The steps are held rather than interpolated,
because the stair edges are where the harmonics that make it sound like
hardware come from.

Shapes builds a wave out of parts instead of drawing it: up to six whole
waveforms at chosen harmonics, each with amplitude, phase, pulse width and an
exponent that bends it toward its extremes without the aliasing that clipping
would produce.

A scope on every channel

A level meter says a channel is doing something. It cannot say what — and on
chip work that gap is most of the job. A duty change and a volume change look
identical on a meter; a wave with a DC offset looks perfectly healthy; two
channels beating against each other look like two channels. All of it is
obvious the moment you can see the shape.

Each tile triggers on a rising zero crossing so the picture holds still
instead of crawling, and sizes its window from the pitch actually sounding, so
a bass and a lead both show two cycles rather than one and eight.

There is an X-Y plot too, which puts one signal against another rather
than either against time: two in phase draw a diagonal, two out of phase draw
the other diagonal, and a mix that has quietly collapsed is visible there and
nowhere else in the program.

Open somebody else's Mega Drive patch

Decades of YM2612 voicings exist in the world as .tfi and .vgi files — the
interchange format every Mega Drive tracker reads and writes. The FM editor
opens them, so a bass or a brass stab somebody voiced on real hardware is one
click away instead of forty sliders away.

It is a translation, not emulation, and it says so: four operators, the
algorithm, the levels and the envelopes come across; key scaling, SSG-EG and
the chip LFO have no equivalent here and are dropped. Neither format has a
magic number, so every field is checked against the register it came from, and
a file that fails is refused with a reason — a bad import that loads is one
you spend an hour trying to fix.

Full notes in CHANGELOG.md.

v3.10.0 "Voice"

Choose a tag to compare

@jfalvarez1 jfalvarez1 released this 04 Sep 01:07

⬇️ Download ChiptuneTracker-Windows.zip

Windows, no installer, no dependencies. Unzip and run ChiptuneTracker.exe.


Sing a song into it and get a song out. And a mixer that keeps everything in
time with everything else, which turned out to be a bigger job than it looked.

Voice Mode

Build a song a part at a time by singing it. Beatbox a groove and keep it; hum
a bass line against what you just kept; hum a lead over both. Each take becomes
a real pattern on its own channel with an instrument chosen for the part it is
playing, so what comes out is an ordinary project you can edit like any other.

It works out the tempo you played at, so there is no click to hum along to, and
it puts each part in the register that part belongs in rather than wherever
your voice happened to be.

Drum kits. Voice percussion no longer has to be classified four ways. Pick
a kit — kick and snare only, snare and hat only, 808s, or the full set — and
the classifier chooses among only those. That is both what you meant and
measurably more accurate: the same breathy snares that were 62% right against
four choices are 100% right against two.

Optional tidying, all off by default. Quantise with a strength and a range,
so a part tightens without flattening. Swing measured from what you played
rather than imposed. Snap a hummed line to a key, only where the key is
confident and the note long enough to have meant it.

Everything stays in time

An effect that cannot answer instantly — a convolution reverb, a pitch shifter,
a hosted plugin — makes its channel come out late. Every effect knew its own
latency, and nothing had ever added it up or acted on it. So a pitch shifter on
the lead did not merely process the lead: it pulled the lead 46 ms out of time
with the drums, by an amount nothing in the interface mentioned.

Every path from a channel to the master is now the same length, direct or
through any depth of aux bus. That last part matters more than it sounds: a
send is a copy of a channel processed elsewhere and returned, and a copy that
comes back a few milliseconds late is a comb filter against the original — the
hollow sound people describe when a parallel bus "doesn't work". It lands
exactly on top now.

A project with no latency anywhere pays nothing for this.

The phase vocoder is half as late. Writing the alignment test meant
measuring the delay rather than trusting the number the effect reported, and
the measurement said 2048 samples against a reported 1024. Pitch shift, formant
shift and autotune are now 23 ms rather than 46, and say so truthfully.

Also

  • Four finished, mastered demo songs — one each for FM, wavetable, granular and
    physical modelling.
  • Mastering you can hear: stereo width and saturation alongside the EQ,
    compression and limiting.
  • An application icon, and it now actually reaches the window.
  • No more stray console window (Debug builds, or --console).

Fixed

  • The melody detector, for an actual voice. Notes came out jumpy and some never
    came out at all — stability was decided on the exact MIDI semitone, and any
    vibrato crosses a semitone boundary.
  • Tempo detection read 140 BPM as 70.
  • A saturator set to "clean" quietly boosted by 21%.
  • "Add to Pattern" could sit off-screen in a narrow dock, and the drum kit
    picker could not be reached at all.

Full notes in CHANGELOG.md.

v3.9.0 "Comp"

Choose a tag to compare

@jfalvarez1 jfalvarez1 released this 01 Sep 03:27

Building a keeper out of several takes, finding things by name instead of by memorised key, and the shape of plugin hosting.

Added

Take lanes and comping. Record a part several times, then build the keeper out of the best moments of each. Every pass around the loop becomes its own lane; drag across a lane to choose it for that stretch. Punch in to repair one phrase without risking the rest of the take. What you comp becomes ordinary audio clips on the arrangement, so you can trim, fade and move the result afterwards like anything else.

Command palette. Ctrl+P finds any command by name. Ctrl+/ lists every shortcut, and any of them can be rebound.

Browser. One docked place for the samples in the project, every instrument and engine, the shipped presets, and the audio and project files on disk — with a fuzzy filter, and drag onto a channel or onto the timeline.

Convolution reverb with an impulse response library, and five more reverb algorithms on the existing slot.

Four more equalisers: tilt, ten-band graphic, mid-side on the master bus, and dynamic.

Plugin hosting, as far as it can honestly go. Per-channel racks for VST2, VST3 and CLAP behind one interface, plus a scanner and scan cache. No format loader ships — the VST2 SDK cannot be legally redistributed, the VST3 SDK is a licensing decision, and CLAP still needs its headers vendored and a real plugin to test against. Everything around the loaders ships and is tested, and a project made where plugins do load keeps every plugin, its parameters and its state when opened here.

Fixed

  • 45 Channel Editor effect controls never saved and were silently reverted — they were bound to the live effects chain rather than to the channel's settings.
  • The Save button's tooltip has always said Ctrl+S, and nothing handled it. It does now.
  • A browser pointed at a relative path offered no way out of it.
  • The Take Lanes delete button sat on top of the first beat of every lane.

Changed

Project format v7, for the take lanes and the plugin racks. Both omit-if-default: a project that uses neither writes exactly the bytes it wrote at v6.


4196 headless checks, 62 UI smoke cases, all passing.

v3.8.0 "Check"

Choose a tag to compare

@jfalvarez1 jfalvarez1 released this 31 Aug 22:03

Two things: testing the UI without a window, and telling you when a setting is switched on and doing nothing.

Headless GUI testing

The rendering smoke test answers "does every screen draw". It needs a GPU and a window, takes a minute, and cannot say anything about what was drawn or click on any of it.

This is the other half — an ImGui context with a built font atlas, a fake display and no backend at all, driving the real panel code through real frames as part of the headless suite. Three classes of bug it catches that nothing here did:

  • ID stack imbalance. A PushID without its PopID corrupts every widget ID after it in that window, so unrelated controls silently start sharing state — two sliders that move together, a checkbox that toggles its neighbour. ImGui asserts in debug and misbehaves quietly in release, which is the configuration that ships.
  • Begin/End imbalance, which breaks every panel drawn afterwards rather than only the guilty one.
  • NaN geometry. A divide by zero in layout maths produces vertices a GPU discards silently, so a rendering test still sees a plausible frame while a control has vanished.

It also drives the mouse. A click is three frames — move and press, hold, release — because ImGui reports it on release and only if the press landed on the same widget; doing it in fewer silently does nothing, which is how a naive attempt at this passes while testing nothing. Widget positions come from ImGui via a probe frame rather than being guessed, so the tests do not quietly stop hitting anything when a layout moves.

Coverage: thirteen panels, seven engine editors, five display sizes from 320×240 to 3840×2160, eight genre focuses, ten themes, and clicks and drags.

docs/TEST_PLAN.md had listed a synthetic input harness as "the single biggest remaining gap". It is closed, and the gaps list is rewritten honestly — what remains is the custom-drawn editors (piano roll, arrangement, pad), which hit-test canvas coordinates inside an InvisibleButton rather than using ImGui widgets.

Project Check

Validation asks "could this crash". This asks a different question: is the project quietly not doing what its owner thinks.

Every finding is a setting that is legal, saved, displayed as enabled, and has no effect — or silently cancels something else. A send into a muted bus. An effect switched on with its mix at zero. A modulation route pointing at an engine the channel is not running. A soloed channel three screens away that is why nothing else is audible. None of these is a bug in the program, and all of them are the program failing to say something it already knows.

It never changes anything. A "fix it for me" button that silently unmutes a channel would be worse than the confusion it solves. Every finding says what, why and what to do.

The hard requirement is the negative one: a fresh project, an ordinary working project, every genre starter template and every quick-start kit must all produce nothing at all. A panel that always has something in it is one everybody learns to ignore, at which point it is worse than not having it. All of them are clean, with tests holding that.

Changed

  • The genre focus now covers the instrument engines. Chiptune is shown wavetable and FM — the Game Boy's wave channel and the Mega Drive's YM2612 — and not the multisample sampler, which is the one thing that era could not do. Hip hop is the reverse. Same rule as the rest of the focus system: a filter on attention, never on capability.
  • The Channel Editor is reorganised. Its Oscillator section had grown to 850 lines holding every engine plus the modulation matrix, with the noise controls stranded 800 lines below the other basic settings. Separate headers now.

Fixed

  • Project Check's suggested-fix line was clipped rather than wrapped. TextColored does not wrap, and the fix is the half of a finding you actually need.

3685 headless checks across 82 groups, 57 UI rendering cases.

v3.7.0 "Instruments"

Choose a tag to compare

@jfalvarez1 jfalvarez1 released this 31 Aug 22:03

Five configurable instrument engines, a modulation matrix, audio on the timeline, and song structure.

Everything here defaults to off or absent, so an existing project sounds exactly as it did.

Engines

  • Wavetable synthesis. The editor had existed for a long time and drove nothing — the "Custom" oscillator ran generateTriangle(), so you could draw a waveform, watch the preview redraw, save it, reopen it, and hear a triangle. There is a real engine now, band-limited with mipmapped tables so a drawn square played three octaves up is dull rather than full of alias tones descending as the note ascends.
  • Six-operator FM. A full routing matrix rather than a numbered list of algorithms, feedback, and a separate envelope per operator — which is where FM's expressiveness actually lives. Modulation is applied to phase, not frequency; feedback averages the last two samples, not the last one. Both are the standard mistakes and both are tested.
  • Multisample sampler. Key zones, velocity layers and round-robin, so repeated hits do not comb-filter against each other.
  • Granular. Grain size, density, spray, pitch scatter, reverse chance and window shape. Freeze the read position for a drone that holds one moment without moving its pitch.
  • Modelled drums. The editable version of the 21 fixed drum voices, built the way the analogue circuits were: the kick's pitch sweep, the snare's shell-versus-rattle balance, the 808 hat's six inharmonic squares.

And

  • Modulation matrix — nine sources to nine destinations, per-voice LFOs and a second envelope so held notes do not wobble in lockstep, plus per-channel polyphony limits and pitch-bend range.
  • Audio clips on the timeline, running through a channel's volume, pan, effects and sends like anything else.
  • Tempo and time-signature changes, markers and regions.
  • Voice to notes — sing or beatbox and get notes, live or from a take.
  • Pitch shift, formant shift and autotune — a phase vocoder — plus an offline time stretch wired to clips as "Fit to Clip".

Fixed

Seven bugs, all older than the work that found them:

  • Sample playback was a semitone and a half sharp. The read step never accounted for the 48 kHz pool against the 44.1 kHz engine, so every sample instrument was out of tune with the synths.
  • The per-channel filter cutoff and resonance did nothing. process() reads cached coefficients and only setCutoff() recomputed them, so the filter ran at its 1000 Hz default wherever the slider was.
  • The filter went unstable above about 7 kHz once that was fixed, producing NaN — silence for the rest of the session. It had never surfaced because the first bug pinned the cutoff.
  • MIDI export wrote the wrong time signature, x/2 rather than x/4, so any DAW importing one saw bars of twice the intended length.
  • The loop ruler was unreachable after the channel count went to 32 — it was positioned below the last lane, roughly 960px down.
  • The Channel Editor's type dropdown selected the wrong instrument. Six names indexed straight into an enum whose sixth entry is Supersaw, not Custom.
  • Five engines were invisible in the Sound Palette, which drew types 0 to 64 and stopped.

3604 headless checks, 55 UI rendering cases.

v3.6.0 "Welcome"

Choose a tag to compare

@jfalvarez1 jfalvarez1 released this 30 Aug 20:33

The two ways projects die, and what this release does about them.

The research pass was unambiguous: projects die at the blank page nobody can start, or as an eight-bar loop nobody can turn into a song. This release is aimed at both — and every piece of it is optional, dismissible, and leaves the manual way of working exactly as it was.

What are you making?

A first run now opens with that question — Chiptune, Synthwave, Hip Hop, Reggaeton, EDM, Rock, Lofi — with "Other, show me everything" as a real answer beside the rest, not an escape hatch. Asked once and remembered.

The choice sets a genre focus: the palette sections, generators and panels for that kind of music are put in front of you, and the rest is set aside. Nothing is removed — the View menu still lists every panel, and both the palette and the Tools panel carry a "show everything" switch that says exactly how much is being held back ("9 hidden by Chiptune focus"). Changeable at any time.

Four bars that already play

Tick "Start me off with four bars I can change" and the project arrives with drums, a bass following a progression, chords and a lead — in the genre's own key and tempo. It is not meant to sound finished; it is meant to be something to change. Also under File > New From Template, and undoable like any other edit.

The drums are one bar placed four times rather than a four-bar pattern, because pattern reuse is the habit that turns bars into songs, and showing it beats explaining it.

Quick Start kits

The patterns each style is built on — the dembow, boom bap, four on the floor, the octave bass, minor pop chords, a triad arp — as buttons at the top of the Tools panel, filtered by your focus. Each writes ordinary notes into the selected pattern: one undo step, editable, layerable, and never displacing what is already there. A faster pencil, not a different instrument — drawing everything yourself remains exactly as available as it was.

A next-step hint

One line in the menu bar reads the project and names one concrete thing to do next: an empty project is pointed at a template, drums with no bass at the bass, and — the important one — a complete loop in a single pattern is pushed toward making a variation rather than adding another layer, because the layer is the trap. One click dismisses it, the View menu restores it, and the dismissal persists across sessions.

Two bugs the tests caught before you saw them

The hint's bass/melody boundary was C3, so a template in A minor with its bass on A3 was told to add a bassline it already had. And the first dembow written for the kits had six snare hits including a straight backbeat on beat two — precisely what a dembow is not; the 3-3-2 assertion refused it and the canonical four-hit pattern ships instead.

Download

ChiptuneTracker-3.6.0-win64.zip — no installer and no runtime needed. The CRT is linked statically, and the packaging script verifies the executable launches from a clean directory with an empty PATH before publishing.

2090 headless checks and 39 UI smoke cases, all passing.

v3.5.0 "Reachable"

Choose a tag to compare

@jfalvarez1 jfalvarez1 released this 30 Aug 09:56

Eight things this program already had, that no user could reach.

A research pass across FL Studio, Furnace, Renoise, OpenMPT, FamiTracker, LSDj and Bosca Ceoil turned up plenty of features worth building. It also turned up something more useful: an audit of this codebase found eight features that were already written, stored and serialised, and that nothing could get to. Those were cheaper to fix than any new feature, and each one undermined a claim the README already makes.

Fixed

The loop range was unreachable, and the engine ignored it anyway. loopStart and loopEnd have been in the playback state since the beginning, but setLoop() was called exactly once — at startup, with (false, 0, 16) — and the playback loop never read loopEnd at all; it wrapped at the end of the content instead. Drag the ruler under the arrangement tracks to set one now, with the looping region shaded across the tracks.

Four note effects were stored but never played. Note delay, note cut, retrigger and echo were declared on every note and written to the .ctp file, with zero references in the synth or the sequencer. They are the classic tracker commands, and they are how chiptune gets flams and stutters without spending another channel.

Pitch sweep had no control — fully implemented in the audio path since the NES sweep unit went in, and reachable from nowhere.

Edit > Undo was literally an empty if-statement, and the real undo covered notes in a single pattern. Editing a macro, a channel effect or the arrangement was not undoable at all. A snapshot is the whole project now, taken through the same serializer the save format uses — so a field that survives save and load survives undo automatically.

"Snap to Scale" set a flag that nothing read. The scale tables and the snapping function existed, but sat several thousand lines below the piano roll, out of reach of the code that places notes.

Grid snap was hardcoded to a 1/16 note in fourteen separate places, so shuffle and 6/8 were unwritable.

Added

  • Transpose — it did not exist in any form. Shift+Up/Down by a semitone, Ctrl+Shift by an octave, plus toolbar buttons. Notes that would leave the playable range are skipped rather than clamped, so a chord cannot silently collapse onto one pitch.
  • Note probability — a per-note chance, rolled fresh on every pass through the loop. A sixteen-step loop repeats a great deal; this is the cheapest thing that stops it sounding like one.
  • Selectable grid snap from a bar down to a 1/32, including 1/4, 1/8 and 1/16 triplets. [ and ] step through the divisions, Alt bypasses the grid for a single gesture.
  • A To Scale button that pulls a selection into key.

Testing

1883 headless checks and 30 UI smoke cases, all passing.

The audio tests assert the feature is audible rather than that the code ran: note delay silences the start of a note, note cut kills its tail, echo keeps sounding past the note's end, retrigger adds dips to the RMS envelope, a zero-probability note renders silence, and a loop range limits how far the playhead ever travels.

One of them caught a real bug during development. The NaN guard on probability was written as !(x < 1.0f), which this project's fast floating-point setting lets the compiler fold into x >= 1.0f — false for NaN, which would have silenced the note instead of playing it. It is a bit-level check now.

Download

ChiptuneTracker-3.5.0-win64.zip — no installer and no runtime needed. The CRT is linked statically, and the packaging script verifies the packaged executable launches from a clean directory with an empty PATH before publishing it.


A note on the research. One of the four agents fabricated Reddit citations and vote counts when it could not reach the site, then retracted them; a second later retrieved much of the same material properly and graded every claim by how it was obtained. The roadmap marks each item verified or unverified accordingly. Nothing in this release rests on the retracted material — all of it came from reading our own source.

v3.4.1

Choose a tag to compare

@jfalvarez1 jfalvarez1 released this 30 Aug 08:41

Fixes the layout you hit after upgrading.

Upgrading from a pre-docking build left every panel scattered. An imgui.ini written before docking existed holds a position for every window and no DockId assignments, so ImGui restores them as floating windows at their old coordinates while the dock space sits empty behind them. The app only applied its default layout when no ini existed, so having any ini at all — including one predating the feature — was enough to skip it. Nothing corrected it, and nothing hinted that Ctrl+0 would.

It now inspects the dock tree a couple of frames in and rebuilds if nothing is docked. Reading the tree rather than sniffing the file also covers an ini truncated mid-write or hand-edited into uselessness.

Verified against the actual broken file this was found with, now a test fixture (22 windows, zero DockId entries), plus a new --keep-ini flag so the saved-layout path can be tested at all.

Two real-time defects found alongside

  • The audio callback allocated on every call — two std::vectors per block, roughly 86 heap allocations a second on the real-time thread. malloc can block on a lock, which is exactly what that thread must not do, and the README's "zero allocations in the audio thread" was not true. Allocated once at startup now.
  • The sequencer pointer was a data race — a plain pointer written by the main thread and read by the audio thread. Now atomic, and shutdown stops the device before retiring it rather than after.

Recorded honestly

One run of the 27-case smoke suite produced a single access violation. It has not reproduced in ~60 later runs and I could not find a cause. It may be one of the two races above, but there is no evidence for that and I am not claiming it.

v3.4.0 "Legible"

Choose a tag to compare

@jfalvarez1 jfalvarez1 released this 30 Aug 08:19

Download ChiptuneTracker-3.4.0-win64.zip below, unzip, run ChiptuneTracker.exe. Nothing to install.


Chasing one theme found a problem in nine

"The Game Boy theme is hard on the eyes" turned out not to be about green. Measuring text against every surface it is actually drawn on, rather than only against the window background:

Theme Worst label contrast On
Game Boy 1.22:1 ButtonActive
Matrix 2.02:1 ButtonActive
Synthwave 2.19:1 ButtonActive
Minimal 2.20:1 ButtonHovered
Retro Terminal 2.24:1 HeaderActive
Vaporwave 2.32:1 ButtonActive
Cyberpunk 2.39:1 ButtonActive
Stock 2.96:1 ButtonHovered
Daylight 3.12:1 ButtonActive

Nine of ten below 4.5:1, seven below 3:1. The cause is structural and identical everywhere: ImGui draws every label in a single ImGuiCol_Text, so a surface cannot choose a label colour that suits it. Every theme brightens its button fill on hover and again on press — which, under light text, means the label gets harder to read the more you interact with it. Pressing a Game Boy button made its own label almost disappear.

It hid for two releases because the audit compared Text against WindowBg only, where every theme passes comfortably at 6:1 to 17:1.

Fixing sixty colours by hand would get it wrong again on the eleventh theme, so the derive pass does it: every surface a label lands on moves away from the text — darker under light text, lighter under dark — until it clears 4.5:1. Only value moves, so each theme keeps its hue.

Game Boy, on the DMG-01's actual colour

The palette everyone copies — 0f380f / 306230 / 8bac0f / 9bbc0f — is community convention, not measurement. The DMG screen is a reflective STN panel with four transmission levels behind a green polariser; there is no RGB in the hardware to sample, so those values were chosen by someone rather than measured.

Palettes sampled from photographs of a running unit come out markedly more olive and much less saturated, around 1b2a09 / 0e450b / 496b22 / 9a9e3f. Convenient: the accurate direction and the comfortable direction turned out to be the same one.

The four sampled shades are used literally in the piano roll, where there is no text; the chrome uses the same hue family with the value spacing a label needs.

This is the DMG-01 specifically. The Pocket and Light moved to FSTN and read as neutral grey; the Color, Advance, SP and Micro share none of it.

Channel identity colours also ignored the theme — which is how a four-shade olive Game Boy ended up with orange notes and a red channel label. Both now blend toward the theme instead of overriding it.

Testing

Theme legibility is a test now rather than a hope. ChiptuneTests links ImGui and calls ApplyTheme for real, then checks ten themes against seventeen surfaces — 170 contrast assertions — plus that Header and Button stay distinguishable after the correction moves both, that interactive alpha is not traded away for contrast, and that no colour slot is left unset.

It also applies all ten themes in sequence and re-applies one, asserting the result is byte-identical: a theme that inherits a value from whichever theme preceded it looks different depending on the route taken to it.

tools/audit-themes.py reports contrast twice — as authored, and as it ships after correction. A large gap means the palette leans on the correction rather than being designed for it, which is true of nine themes today.

1727 checks, up from 1446.