Skip to content

Repository files navigation

audio-bitsqueezer

Convert audio to packed 4-bit C64 digis, a runnable PRG, a streaming EasyFlash cartridge, or an 8-bit 6 kHz MSSIAH WAV.

Supported platforms: Linux and Windows. macOS is currently unsupported and excluded from CI. See platform support.

This is the new default pipeline. --legacy and --legacy-mode select the original converter and player behavior. The original code and player binaries are preserved. See the exact changes and measured results.

New in 0.14.4: Windows VICE detection and reuse

Reuse existing WinGet, custom or portable VICE installations consistently. A successful Windows x64sc -version command with no captured text is checked against its own sibling cartconv.exe instead of being rejected. Setup does not download a second copy to try to repair an already selected installation. Normal version banners, crashes and timeouts remain checked.

WinGet discovery also works with a stale shell PATH. Unless explicitly configured otherwise, cartridge export uses the selected emulator's sibling cartconv.exe. The basic doctor and complete/GUI startup checks report runtime failures and selected paths consistently.

See 0.14.4 update instructions and release notes.

New in 0.14.3: dependency checks and complete setup

GUI startup checks package versions, imports and native reSIDfp operation, plus FFmpeg/ffprobe, VICE, cartconv and bundled player assets. It offers installation in the terminal and shows unavailable features with recovery instructions in the browser. Missing optional preview tools leave ordinary PCM/PRG work usable.

bash install.sh --install-all          # Complete setup; no GUI is opened
bash run.sh gui --auto-install-missing # Check/install/recheck, then open the GUI

On Windows use install.cmd --install-all and run.cmd gui --auto-install-missing. Normal gui asks before installing optional packages or external tools. The explicit flags consent to those installations; your OS may still ask for an administrator password. Python packages stay inside the project .venv. See setup coverage and limitations and 0.14.3 update instructions.

New in 0.14.2: source layout and browser GUI improvements

Drop a source audio file onto the dotted file chooser or 01 - Original panel, or click the boxed plus to browse. GUI exports now default to audio/out/, with a dated subfolder for each render. Custom save locations are retained. Output-folder selection also runs inside the web page, with no Tkinter or desktop dialog dependency. See 0.14.2 release notes.

Python components now live under src/audio_bitsqueezer/, GUI assets under its gui/static/ directory, and dependency lists under requirements/. Standalone CLI commands stay in the root. New source uploads use audio/in/. See the layout and directory fallback rules, update instructions and regression coverage.

New in 0.14.1: Windows setup and dependency recovery

Check/install Python, pip and FFmpeg with setup-windows.cmd. Windows GUI startup now offers the installer when required dependencies are missing, and tool discovery handles stale PATH entries. See 0.14.1 release notes.

Quick install and run

Linux — Ubuntu / Debian

sudo apt update
sudo apt install git python3 python3-venv ffmpeg
git clone https://github.com/FlyingFathead/audio-bitsqueezer.git
cd audio-bitsqueezer
bash install.sh
bash run.sh "input.wav" --format prg --out "audio.prg"

Already have a checkout or release ZIP? Run the last two commands from its directory. Python 3.10+ and FFmpeg 4+, including ffprobe, are required for conversion.

Windows 11 — Command Prompt / PowerShell

From an existing checkout or extracted ZIP:

.\setup-windows.cmd
.\run.cmd gui

install.cmd and run.cmd --setup also start the Windows setup assistant. It checks Python 3.10+, venv, ensurepip, pip, the pinned project packages, and both FFmpeg and ffprobe. Missing Python or FFmpeg can be installed through WinGet after confirmation. Existing working tools are reused; project packages stay inside .venv. No manual activation is needed.

On Windows, run.cmd gui and run.cmd --gui offer to run the setup assistant when required dependencies are missing and the setup scripts are present. The prompt appears in the launch terminal before the browser opens. Successful setup is rechecked before GUI startup continues. Declining or failed setup stops startup.

For manual installation:

winget install --exact --id Python.Python.3.13 --source winget
winget install --exact --id Gyan.FFmpeg --source winget
# Git is needed for cloning/pulling, not for running an extracted ZIP.
winget install --exact --id Git.Git --source winget

Reopen the terminal before using newly installed tools directly, then:

git clone https://github.com/FlyingFathead/audio-bitsqueezer.git
cd audio-bitsqueezer
.\setup-windows.cmd
.\run.cmd "input.wav" --format prg --out "audio.prg"

Gyan's Windows builds include ffmpeg, ffprobe and ffplay; they are linked from the FFmpeg project's download page. The exact package ID and --source winget avoid ambiguous package/source matches. An existing compatible FFmpeg build from another provider also works.

run.cmd calls run.ps1, which refreshes its process PATH while preserving session-only entries, discovers Python, and starts the shared launcher. The wrapper changes execution policy only for its own process. Known WinGet FFmpeg locations and tools/ffmpeg/bin are detected even when the terminal PATH is stale. An explicitly configured tool path still takes precedence and must be corrected if invalid. For an existing installation:

.\setup-windows.cmd -FFmpegPath "C:\Tools\ffmpeg\bin"
.\setup-windows.cmd -Check

See Windows setup and recovery for Python path selection, manual downloads, pip/venv repair, and optional dependencies.

macOS — unsupported

macOS support is suspended. Installation and GUI operation on macOS are not currently validated; use Linux or Windows. Older macOS instructions in release history do not establish current support.

Normal conversion and PRG export need no C64 assembler or VICE installation. The first run creates .venv and installs the pinned Python dependencies automatically; install.sh / install.cmd are explicit setup commands; the Windows installer also handles Python and FFmpeg discovery/install choices. Working environments are reused without rerunning pip. Package versions and Python-version markers are checked, including the audioop-lts compatibility dependency on Python 3.13+.

New in 0.14.0: local web UI

Load a sample, inspect its waveform, adjust the squeeze settings, and compare the original with the converted PCM and optional VICE/reSIDfp audio—all in your browser. The UI runs locally on your computer and uses the same converter as the CLI. The GUI is completely optional; the full workflow is available from the CLI.

# Linux
bash run.sh --gui
# Windows
.\run.cmd --gui
  • See and hear the difference: original, squeezed PCM, and optional VICE feedback each play the complete applicable sample by default. Listening-level matching makes comparison easier without changing the exported audio.
  • Find completed files: a green completion notice shows the full saved-folder path; click it to open the folder on your computer.
  • Choose output names: the cogwheel Settings menu remembers whether to include the input basename (default: audioinputfile_prepared.wav) or use simple names (prepared.wav). Existing files keep their names.
  • Adjust before rendering: choose presets, format, sample rate, SID model, EQ, trimming and normalization. Optional multi-pass searches a bounded set of EQ settings and retains the baseline when it scores best.
  • Choose where results go: the Saving to: row beneath the load controls lets you type a directory or use Change location to browse folders inside the web page. The last used folder is remembered. Every render gets a dated subfolder, shown with Copy path and Open folder beside the output links. The initial destination is audio/out/.
  • Follow the work: progress fills the generating waveform area. PCM becomes playable before VICE finishes, and completed outputs survive a feedback failure or Stop render. Regeneration confirms before replacing the displayed previews and keeps earlier files in their own folders.

The GUI creates gitignored audio/in/ for new sources and audio/out/ for results on first use. Existing sources directly in audio/ remain available. Your audio and conversion stay on your computer. VICE feedback is optional and requires the analysis setup:

bash run.sh --setup-analysis

Both --multi-pass and --multipass also work from the CLI:

bash run.sh audio/sample.wav --format cart --out sample.crt --multi-pass 5 --feedback pcm

See GUI and multi-pass details and what changed in 0.14.0. The Consonant Clarity experiment remains an offline noise-synthesis preview; current C64 exports do not decode that high-band stream.

Choose an output

The examples use Linux shell syntax. On Windows, replace bash run.sh with .\run.cmd.

# Packed samples plus a sample-rate/count/checksum JSON sidecar
bash run.sh "input.wav" --out "audio.raw"

# Runnable C64 program; plays once, then returns to BASIC
bash run.sh "input.wav" --format prg --out "audio.prg" --sid 8580

# Streaming EasyFlash cartridge; requires cartconv only for this format
bash run.sh "input.wav" --format cart --out "audio.crt" --sid 8580

# Loop playback; choose the profile for your physical SID
bash run.sh "input.wav" --format prg --out "loop.prg" --sid 6581 --loop

# Keep a raw file, produce a PRG and write a true unpacked digital preview
bash run.sh "input.wav" --out "audio.raw" --prg "audio.prg" --preview "preview.wav"

# Existing MSSIAH file format, using the improved audio preparation
bash run.sh "input.wav" --mode mssiah --out "mssiah.wav"

# Existing raw output with its .raw.json sidecar
bash run.sh makeprg "audio.raw" "audio.prg"

# A raw file without metadata: specify the rate it was encoded at
bash run.sh makeprg "older.raw" "audio.prg" --rate 4000

# Optional alternative: wrap an existing bitsqueezer PRG in a quick-loading CRT
bash run.sh makecart "audio.prg" "audio.crt"

--format also accepts the alias --output-format. convert is an optional subcommand. --rate defaults to 4000; modern PRGs support 1000–8000 Hz, and streaming carts support 1000–4000 Hz. MSSIAH output is always mono, 8-bit, 6000 Hz. Its default 5.5-second warning remains a warning, not a truncation.

The default preparation downmixes to mono, trims leading/trailing silence with 200 ms of padding, applies optional telephone filtering, resamples with an anti-alias filter, then peak-normalizes to −0.1 dBFS. It uniformly quantizes to four bits without stretching the quantized range. Controls include --no-trim, --no-maximize, --maximize-level -6, --telco and --max SECONDS. --no-stretch-4bit is accepted for command compatibility; modern mode already omits that operation.

Optional emphasis and rumble filtering (v0.13.3)

Both filters are off by default and apply only to modern conversion. They run on the computer before quantization and add no C64 CPU, memory or tool requirements.

# Upper-band emphasis only
bash run.sh "input.mp3" --format cart --out "emphasis.crt" --emphasis 6

# Gentle rumble removal only
bash run.sh "input.mp3" --format cart --out "rumble.crt" --rumble-cut 40

# Combine them, with an unpacked digital preview
bash run.sh "input.mp3" --format cart --out "combined.crt" \
  --emphasis 6 --rumble-cut 40 --post-normalize --preview "combined.wav"
  • --emphasis DB: 0–12 dB upper-band shelf boost, centered at min(1000, rate/4) Hz (1 kHz at the default 4 kHz rate). Zero disables it. This is tonal EQ, with no paired SID de-emphasis or transient detector.
  • --rumble-cut HZ: a two-pole high-pass, 12 dB/octave, with its −3 dB point at the specified frequency. Zero disables it; positive cutoffs must be below Nyquist and no higher than 500 Hz. A 40 Hz setting is gentle; 80 Hz deliberately removes more musical bass. Neither is enabled automatically.

Post-filter peak normalization is already on by default. --post-normalize and --normalize-after are equivalent explicit switches for it; --maximize-level DBFS sets the target and --no-maximize disables it. An explicit normalization switch cannot be combined with --no-maximize. Normalization happens before four-bit quantization, never by stretching the resulting codes.

The order is resampling → optional rumble cut → optional bass shelf → optional emphasis → peak normalization → quantization. The new filters use floating-point samples until the final gain/16-bit conversion, preventing intermediate EQ clipping. With --no-maximize, EQ keeps unity gain unless constant attenuation is needed to fit 16-bit PCM; any such attenuation is reported. This is not dynamic compression. Existing no-filter processing is byte-identical, including normalization behavior.

At 4 kHz, frequencies above 2 kHz cannot be represented correctly; emphasis can only rebalance what remains. Bass and quieter vocal detail share the same 16 amplitude levels. Cutting bass may help the balance, but can thin the music; emphasis may make quantization noise more apparent. The filters do not isolate vocals or compensate every SID revision. Compare at matched listening volume. RAW, PRG and streaming CRT JSON sidecars retain the conversion diagnostics and filter settings. Existing raw exports without analysis still work.

See the EQ and SID notes for measured limitations and the distinction from Cubase64's oscillator-based playback. --legacy / --legacy-mode preserve the original pipeline; the new flags belong to modern mode.

Raw data stays low nibble first, high nibble second. Keep its .raw.json sidecar: it records the exact sample rate and count, source basename, checksum and diagnostics. Padding in an odd final byte is excluded from playback. A packed byte is not an unsigned 8-bit PCM sample; use --preview to hear the unpacked quantization. The preview does not emulate SID analog behavior.

Duration, memory and playback

bash run.sh capacity --rate 4000
bash run.sh capacity --rate 4000 --bytes 65536
bash run.sh --version
bash run.sh --doctor

At 4 kHz, packed audio uses 2000 bytes per second. With the bundled player, a PRG fits about 24.82 seconds. Streaming EasyFlash reserves bank 0 for startup and metadata, leaving 1,032,192 audio bytes: 516.096 seconds (8:36.096) at 4 kHz. These estimates use the encoding rate; timer rounding changes actual duration slightly. The capacity command reads the current player layout, so PRG estimates follow changes to the binary.

Each PRG/cart export reports its actual duration, remaining capacity, load/entry addresses, player/sample sizes, RAM ranges, banks and hardware registers. Its .json report contains the same details. See the memory map.

If a PRG exceeds RAM capacity, export stops before writing the PRG. The error reports the audio duration and PRG limit, recommends streaming --format cart --out OUTPUT.crt, and shows the flash capacity and whether the duration fits. Above 4 kHz, it explains that cartridge output requires resampling the original audio to a supported rate. If the audio also exceeds the flash budget, prepare a shorter excerpt, split the source into separate conversions, or consider a lower supported rate. --max SECONDS adds a warning; it does not trim the audio. Cartridge export additionally needs VICE's cartconv; no C64 assembler is needed.

The player detects PAL/NTSC from the boot flag by default; --system pal or --system ntsc forces the corresponding timer. Choose --sid 6581 or --sid 8580 to match the chip. The 8580 profile uses steady voice bias for volume-register digis; all three SID voices are reserved during playback. The conversion report shows peak/RMS levels, DC offset, full-scale samples and quantization range. There is no universal PCM loudness threshold that opens the SID's GATE bit. Hardware initialization and chip/board behavior matter; see the SID findings.

At rates up to 4 kHz, the default player shows a centered credit screen on black, including VERSION, source basename and duration. The player does not write the border color register. --screen off disables display DMA; --screen auto also blanks the display above 4 kHz to preserve timing headroom. --screen on requires a rate no higher than 4 kHz. Keyboard scanning is paused during playback. --loop repeats until reset; otherwise the PRG restores normal I/O and returns to BASIC. A cartridge returns through a clean reset to BASIC. Hold any key during cartridge startup to bypass playback.

The GitHub line is github:FlyingFathead/audio-bitsqueezer, with one blank column on each side. Source filenames use the remaining space after source file: , up to the full 40-column row. Names that overflow are shortened to a leading portion plus [...] and their extension, for example very-long-source-f[...].wav. Only the basename is shown. An extension too long to fit alongside the marker and one name character is omitted. Regenerate existing PRGs/CRTs to update their embedded screen text.

Screenshot of audio-bitsqueezer v0.13.3

Screenshot of audio-bitsqueezer v0.13.3.

Optional cartridge / emulator tools

cartconv is required only for CRT output. Neither cartconv nor the VICE emulator is required for raw/WAV/PRG output. The emulator is useful for previewing and testing. Setup reports missing optional tools, offers optional VICE installation in an interactive terminal, and still completes basic setup if you decline. Conversion never automatically installs VICE.

OS Optional VICE installation
Ubuntu/Debian sudo apt install vice — enable the appropriate package repository if needed; Debian packages are in contrib
Windows 11 Download/extract VICE, or run .\run.cmd --setup-vice for the checksum-verified portable SDL2VICE 3.10 x64 download

Existing Windows VICE installations on PATH or in WinGet package directories are reused automatically. For a custom selection, point cartconv and optionally vice to its executables in config/bitsqueezer.ini. Distribution packages may need C64 ROM files configured for emulation; cartconv does not need those ROMs. On Ubuntu/Debian, --setup-vice runs the package-manager installation explicitly. None of this is needed for normal PRGs.

--format cart streams samples directly from EasyFlash banks 1–63, using both ROML and ROMH. This provides the larger capacity. makecart existing.prg instead copies the existing PRG to RAM at startup, retaining that PRG's RAM limit. Both create native EasyFlash CRTs with actual EasyAPI and name metadata, validated CHIP packets, reset vectors, cartconv -c, and a complete binary-image comparison. CRT creation does not physically flash a cartridge. Transfer the resulting CRT with EasyProg or a compatible flashing tool. See EasyFlash details.

Configuration and dependencies

Following c64-3d-toolkit, copy the example to a user-owned INI:

cp config/bitsqueezer.ini.example config/bitsqueezer.ini

Windows PowerShell: Copy-Item config\bitsqueezer.ini.example config\bitsqueezer.ini; Command Prompt: copy config\bitsqueezer.ini.example config\bitsqueezer.ini.

[runtime]
platform = auto

[toolchain]
python =
ffmpeg = ffmpeg
ffprobe = ffprobe
vice = x64sc
cartconv = cartconv
tass = 64tass
acme = acme

[windows]
; Examples only: use the paths on your computer.
; python = C:\Tools\Python313\python.exe
; cartconv = C:\Tools\VICE\bin\cartconv.exe

[linux]
; ffmpeg = /usr/bin/ffmpeg

Built-in defaults are overridden by [toolchain], then the current [linux] or [windows] section, then an explicit CLI override where provided. Use platform = auto or select the matching supported host explicitly. The parser retains macos and [macos] compatibility for existing configurations, but macOS is unsupported. Executable values are paths/names, not shell command strings. Spaces and paired surrounding quotes are supported. Prefer absolute paths; relative tool paths are based on the project directory.

Python discovery uses BITSQUEEZER_PYTHON, an existing .venv, configured Python, then installed interpreters. Rename .venv before switching Python versions, moving the project or changing OS. Setup preserves your INI and unrelated files, keeps dependencies inside the project venv, and reports damaged environments instead of deleting them. Audio paths remain relative to the caller's directory. The original legacy PRG builder retains its original working-directory behavior.

Dependency Needed for
Python 3.10+, venv/pip, requirements/base.txt Normal conversion and launcher environment
FFmpeg 4+ and ffprobe Decoding/resampling audio; checked before conversion
Bundled PRG player/layout files PRG export; no external assembler
VICE cartconv CRT export only
VICE x64sc Optional emulator preview/audit
64tass Optional rebuild of our bundled player/boot templates
ACME Optional rebuild of the included third-party EasyAPI driver
requirements/dev.txt Unit/integration tests, including 6502 execution
requirements/audit.txt Optional VICE trace / reSIDfp tone audit

Original pipeline

Run from the project root, as the original PRG builder expects:

bash run.sh --legacy "input.wav" --out "original.raw"
bash run.sh makeprg --legacy "original.raw" "original.prg"
# --legacy-mode is the same switch, on either command.
bash run.sh makeprg --legacy-mode "original.raw" "dual.prg" --dualnibbles

The original converter lives in legacy/converter.py; the original PRG builder is legacy/prg_builder.py. They are unchanged copies, including original option defaults, processing order, quantized stretching, termination, timing and path behavior. Existing asm/, prg/ and bin/player.bin assets remain unchanged. Both conversion and PRG generation must use the legacy flag for the complete original pipeline. New format/display/cart switches belong to modern mode. Original usage notes remain in legacy/README-v0.12.md.

Tests, rebuilds and release

Play the included 400 Hz test tone with matching 8580 profiles:

mkdir -p build
bash run.sh docs/validation/tone-400.wav --format prg --out build/tone-400.prg --rate 4000 --sid 8580 --loop
x64sc -pal -sidengine 1 -sidmodel 1 -autostart build/tone-400.prg

The converter uses --sid 8580; VICE uses numeric -sidmodel 1 for the standard 8580, with -sidengine 1 selecting reSID. Reset or close the emulator to stop looping.

bash install.sh
.venv/bin/python -m pip install -r requirements/dev.txt
.venv/bin/python -m unittest discover -s tests -v

Windows: replace .venv/bin/python with .\.venv\Scripts\python.exe. CI runs the unit/integration suite on Ubuntu and Windows with Python 3.10, 3.12, 3.13 and 3.14. macOS is excluded from the supported matrix. See validation scope and results.

Optional development commands:

# Only needed after editing the player assembly
.venv/bin/python scripts/build_player.py --tass /path/to/64tass

# Only needed for the extended audio/emulator audit
.venv/bin/python -m pip install -r requirements/audit.txt
.venv/bin/python scripts/audit_tones.py --vice /path/to/x64sc

VERSION is the release version source for the launcher, modern tools, C64 screen and generated reports. Original legacy version labels remain historical identifiers. After reviewing an update, use bash scripts/release.sh from the default branch to run checks, commit, create a full source ZIP and checksum, tag, atomically push, and publish the GitHub release. Git and authenticated GitHub CLI are required; see release instructions and CHANGELOG.md.

License and contact

Thanks to Pex "Mahoney" Tufvesson for the inspiration and his longstanding work keeping the C64 scene and music creation alive. His Cubase64 white paper and Musik Run/Stop SID measurements brought valuable directions: interrupt-latency compensation, budgeting for VIC-II bus stealing, oscillator-based sample output, and measured SID volume tables. Thank you for documenting and sharing this work. These are references for further experiments.


Use audio-bitsqueezer freely. If you adapt it in your projects, a nod to FlyingFathead/bitsqueezer is appreciated. Also, if you like this repo, please star it. Third-party EasyAPI retains its own notice in third_party/easyapi.


Not affiliated with MSSIAH or its creators.

Pull requests, bug reports and suggestions are welcome.

Email: flyingfathead@protonmail.com; Twitter/X: @horsperg.

About

Python tool for converting modern audio into 8-bit or 4-bit formats, ideal for Commodore 64 (with or without MSSIAH) and other retro systems.

Topics

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages