Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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.

Notifications You must be signed in to change notification settings

FlyingFathead/audio-bitsqueezer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

33 Commits

Repository files navigation

audio-bitsqueezer

audio-bitsqueezer (or just bitsqueezer) is a Python utility that converts modern audio files (MP3, WAV, FLAC, etc.) into:

  1. Raw 4-bit nibble data — great for old-school tricks on machines like the Commodore 64 (volume-register "digis").
  2. MSSIAH-compatible 8-bit WAV at 6 kHz — the perfect output format if you're using the Commodore 64 MSSIAH cartridge.
  3. A complete .prg — by merging the 4-bit .raw output with a minimal player via maketoprg.py, so you can run it directly on the C64 or an emulator, using its audio output.

How It Works

  • Decode: Uses FFmpeg via pydub to handle nearly any input format (MP3, WAV, AIFF, FLAC, etc.).
  • Downmix: Automatically converts multi-channel audio to mono.
  • Resample: Choose a sample rate (default is 4000 Hz for 4-bit mode; forced 6 kHz in MSSIAH mode).
  • Quantize & Pack (4-bit mode): Maps 16-bit samples to 4 bits (0–15), then packs two samples into each byte for raw nibble data.
  • 8-bit MSSIAH Mode: Forces the audio to 6 kHz, 8-bit mono WAV. You can rename it on disk (e.g., MYFILE .WAV.PRG) and load directly into MSSIAH Wave-Player.
  • Write: Outputs either .raw (for 4-bit nibble data) or .wav (for MSSIAH).

  • (Optional) Merge 4-bit data with a minimal player using maketoprg.py, yielding a self-contained .prg that can be LOADed and RUN on a real Commodore 64 (or emulator).

NOTE: Keep your audio samples short. The MSSIAH Wave-Player routine can typically handle ~5.5 seconds max. Longer than that may not fit in memory.

Requirements

  • Python 3.6+
  • FFmpeg installed and on your PATH.
    • Linux (Debian/Ubuntu): sudo apt-get install ffmpeg
    • macOS: brew install ffmpeg
    • Windows: Download from ffmpeg.org and ensure ffmpeg.exe is on your PATH.
  • pydub library:
    pip install pydub

If you wish to use the maketoprg.pyand run it somewhere, you'll either need i.e. VICE C64 Emulator or an actual Commodore 64.

Installation

git clone https://github.com/FlyingFathead/audio-bitsqueezer.git
cd audio-bitsqueezer
pip install pydub

Make sure FFmpeg is installed and accessible. Otherwise, bitsqueezer won’t function.

Usage

python bitsqueezer.py <infile> [--mode 4bit|mssiah] [--rate SAMPLE_RATE] [--out OUTPUT_FILE]

Modes

  1. 4bit: Creates raw 4-bit nibble data.
  2. mssiah: Creates an 8-bit, 6 kHz WAV suitable for MSSIAH Wave-Player disk import.

Examples

  1. Default 4-bit (no --out, rate = 4000 Hz):

    python bitsqueezer.py my_audio.wav --mode 4bit
    • Outputs my_audio_4bit_4000hz.raw.
  2. Specify a different sample rate (still 4-bit):

    python bitsqueezer.py my_audio.mp3 --mode 4bit --rate 8000
    • Outputs my_audio_4bit_8000hz.raw.
  3. MSSIAH mode (6 kHz, 8-bit WAV):

    python bitsqueezer.py my_audio.flac --mode mssiah
    • Outputs my_audio_mssiah_6khz.wav.
  4. Fully specify output:

    python bitsqueezer.py input.wav --mode mssiah --out final_mssiah.wav
    • Writes an 8-bit, 6 kHz WAV to final_mssiah.wav.

Once you have your file:

  • 4-bit .raw: Ideal for direct volume-register playback on retro hardware.
  • MSSIAH .wav: Rename to the MSSIAH-required .WAV.PRG format, place it on disk (or Savyour/USB), and import it into MSSIAH Wave-Player.

Creating a Self-Running .prg on the C64

After you’ve produced a 4-bit .raw file, you can merge it with one of the minimal C64 "player" binaries included in the asm/ folder. This merging is done via makeprg.py, which:

  1. Reads the existing "player" .prg file (which already has a 2-byte load address and references a label for appended sample data).
  2. Appends your .raw data plus a trailing sentinel 0x00.
  3. Writes out a single .prg that you can load and run on the C64.

Usage example:

# Adjust the `PLAYER_BIN` variable inside makeprg.py to point to
# the minimal player code you want, e.g. `loopplay.prg` or `loopplay_cia1_irq_16k.prg`.
# Then run:
python makeprg.py my_audio_4bit_4000hz.raw final.prg
# "final.prg" is a complete, executable file for the C64 that
# auto-plays your 4-bit sample data.

If you have multiple pre-assembled players (e.g. a 4 kHz vs. 8 kHz vs. 16 kHz version), you can pick which .prg to merge your raw data with by editing the PLAYER_BIN path near the top of makeprg.py.

4-Bit Raw Output Details

  • Samples: Each sample is 4 bits (0–15).
  • Two samples per byte: Low nibble first, then high nibble.
  • No header: Just raw amplitude data.

Default rate is 4000 Hz. If you want a faster or higher-pitched playback, pass a higher --rate (like 8000 or 11025). Bear in mind the C64 routine must match your intended rate.

Testing on a Modern PC

If you attempt to directly play the .raw file (e.g., with aplay or ffplay), it will likely:

  • Sound half as long (since each byte has 2 ×ばつ 4-bit samples).
  • Sound "quiet" or "distorted," because typical PCM players expect 8 bits per sample.

Quick Workaround

Lower the playback rate by half; for example, if bitsqueezer used 4000 Hz:

aplay -f U8 -r 2000 -c 1 my_audio_4bit_4000hz.raw

This forces the data to play at half speed in bytes, matching the correct pitch. Not perfect, but quick.

Proper Approach

To preview more accurately, unpack each 4-bit nibble to a standard 8-bit or 16-bit PCM file:

  1. Read each byte, separate into a low nibble (bits 0–3) and a high nibble (bits 4–7).
  2. Map those nibbles to, e.g., 8-bit range (multiply by 16).
  3. Write them out as two consecutive 8-bit samples.
  4. Play that new file at the original 4 kHz rate.

On a real C64, your playback routine is responsible for reading those nibbles at the correct speed, so no special "unpacking" is needed.

FAQ

Why 4 bits?

The C64’s SID volume register trick can’t easily generate true 8-bit PCM. This yields ~4 bits. bitsqueezer automates that conversion for easy retro-audio playback.

Will It Sound "Good"?

Well... welcome to the crunchy world of 4-bit audio.

MSSIAH Mode

MSSIAH’s Wave-Player expects an 8-bit, 6 kHz WAV. If you’re using MSSIAH, select --mode mssiah. You do not need MSSIAH hardware for normal 4-bit usage though.

License

Use freely. If you adapt it, a nod to FlyingFathead/bitsqueezer is appreciated.

(Not affiliated with MSSIAH or its creators.)

Contributing & Contact

Pull requests, bug reports, suggestions welcome.

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

Changelog

  • v0.12 - telephone filter in bitsqueezer.py w/ --telco switch

  • v0.11 - added single/dual nibble switches to makeprg.py

    You can i.e. force single nibble:

    python makeprg.py sound_4bit.raw --singlenibble

    Or, force dual nibble:

    python makeprg.py sound_4bit.raw --dualnibble
  • v0.1 - initial release


Enjoy the squeeze!

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

Watchers

Forks

Releases

No releases published

Packages

No packages published

AltStyle によって変換されたページ (->オリジナル) /