Skip to main content

wav2tapsp

CI

Converts WAV files (recordings of Commodore 64 cassette tapes) into C64 .tap files an emulator can read.

Hardly unique, but also somewhat easier to experiment with than having to hack ancient C code...

Install

$ pip install .

or, once released, pip install wav2tapsp.

Command line

$ wav2tapsp recording.wav        # writes recording.tap
$ wav2tapsp --cpufreq 1022727 recording.wav   # NTSC

(python -m wav2tapsp recording.wav works too.)

Library

import scipy.io.wavfile
import wav2tapsp

sr, samples = scipy.io.wavfile.read('recording.wav')
tap_bytes = wav2tapsp.wav_to_tap(samples, sr)          # bytes of a .tap file
pulses = wav2tapsp.wav_to_pulses(samples, sr)          # raw TAP pulse lengths

It works by finding rising zero crossings in the audio and emitting the time between them as TAP pulse lengths (CPU cycles / 8).

C64 tape formats

The package can synthesise and decode real C64 tape formats end to end (used by the tests, and handy for generating test tapes):

  • Standard ROM / Kernal loader — three pulse lengths, two pulses per bit, odd parity, per-block checksum, two block copies. c64tape.py.

  • Fast / turbo loaders — two pulse lengths, one pulse per bit (a short pulse for 0, a long pulse for 1). formats.py models the pulse-timing scheme of several real loaders using their documented pulse widths:

    Loader short / long pulse (TAP, cycles) reference
    Firebird T1 $44 / $7E (544 / 1008) FinalTAP ft[]
    Novaload $24 / $56 (288 / 688) FinalTAP docs
    Freeload $24 / $42 (288 / 528) FinalTAP docs
    Turbo Tape 250 $1A / $28 (208 / 320) FinalTAP docs / c64-wiki
    Turrican $1B / $27 (216 / 312) FinalTAP ft[]

References: the TAP format (pulse byte = CPU cycles / 8), PAL/NTSC CPU clocks (985248 / 1022727 Hz), the ROM loader timing ($30/$42/$56), and the FinalTAP loader database (per-loader pulse widths, the de-facto reference).

Testing

Tests run in CI across Python 3.10–3.13. To run them yourself:

$ pip install -e .[test]
$ pytest -v

The headline test is a full, emulator-free round trip that proves wav2tapsp preserves tape timing well enough to recover the original program:

PRG  ->  TAP  ->  WAV  --(wav2tapsp)-->  TAP  ->  PRG

A real C64 BASIC program is generated, encoded as a .tap, rendered to a .wav, run back through wav2tapsp, and decoded; the recovered PRG must be byte-identical to the original and still a structurally valid, runnable BASIC program. The same round trip runs for every fast-loader format above.

Minimum sample rate and resolution, from first principles

wav2tapsp recovers a pulse by counting samples between rising zero crossings: recovered_cycles = Δsamples × cpufreq / SR. Two bounds follow (derived in analysis.py, checked per format in the tests):

  • Sample rate. A pulse is one wave cycle of frequency cpufreq / pulse, so representing the shortest pulse needs SR ≥ 2·cpufreq/short (Nyquist). To keep a pulse on the correct side of the short/long threshold despite ±2 samples of crossing jitter needs SR > 4·cpufreq/gap, where gap is the cycle distance between the two pulse widths. The reliable minimum is 2 × max(...). Because gap dominates, the requirement scales like 1/gap — faster loaders need proportionally higher sample rates (PAL, safety ×2):

    Loader gap (cycles) reliable min sample rate
    Firebird T1 464 ~17.0 kHz
    Novaload 400 ~19.7 kHz
    Freeload 240 ~32.8 kHz
    C64 ROM 144 ~54.7 kHz
    Turbo Tape 250 112 ~70.4 kHz
    Turrican 96 ~82.1 kHz

    The tests confirm each loader fails below Nyquist and decodes at its reliable rate, and that the measured minimum is bracketed between the two.

  • Resolution (bit depth). Zero-crossing detection depends only on the sign of the signal, so one bit (the sign) is enough for a clean recording; amplitude resolution barely matters. The tests verify the round trip survives 1-bit quantisation of a sine wave for every loader. Sample rate, not bit depth, is the real constraint.

Layout

Metadata

Release files for wav2tapsp 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wav2tapsp 0.2.0
File Size Uploaded
wav2tapsp-0.2.0.tar.gz 22.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for wav2tapsp 0.2.0
File Interpreter ABI Platform
wav2tapsp-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 40.7 kB

Release files / wav2tapsp-0.2.0.tar.gz

Download URL wav2tapsp-0.2.0.tar.gz
Size 22.1 kB
Tags Source
SHA-256 checksum
How to use checksums
39d727305e38ba0dae384dcb8124cf9b5a84c2ae0f3635738dc0aaa2aecbd681
BLAKE2b-256 checksum
How to use checksums
f150033fdfe021c2dd58029fa68c8d018ca5d20f5d346c7024a963c9231308e0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 13, 2026.

Transparency log

Release files / wav2tapsp-0.2.0-py3-none-any.whl

Download URL wav2tapsp-0.2.0-py3-none-any.whl
Size 18.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d2068ed847b1f48f478258ee0fb6be560ccf55a6fe974292fe021d05e2bdc2b9
BLAKE2b-256 checksum
How to use checksums
42da6b1db347725da3c131ba44e115881280866274fdf679ef887270601f98d9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 13, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page