logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·30.10.26
← Back to news

Digital Audio on the ZX Spectrum's 1-Bit Beeper

Original article
bumbershootsoft.wordpress.com|37 points|14 comments|by ibobev|Sep 27, 2026

Achieving Digital Audio via the ZX Spectrum's 1-Bit Beeper

This article serves as the conclusion to a series of software experiments by Michael Martin (Bumbershoot Software), focusing on pushing the limits of the ZX Spectrum's audio capabilities.

The goal was simple yet ambitious: force the ubiquitous, primitive 1-bit beeper found on all Spectrum models to produce high-fidelity digital audio. The end result is a stark contrast to previous PCM playback methods—the difference is, quite literally, night and day.

🛠️ Verification and Compatibility

To ensure the code wasn't just a fluke of emulation, hardware verification was conducted. Thanks to the community, the demo was tested on:

  • Original 48K Spectrum (Verified by "gmc" via Mastodon)
  • Omni 128 (A modern system rebuild, verified by Tom Harte)

Furthermore, the implementation is highly compatible across various emulators.

EmulatorStatus
Fuse✓\checkmark✓ (Used for demo capture)
EightyOne✓\checkmark✓
Zesarux✓\checkmark✓
Spectaculator✓\checkmark✓
Clock Signal✓\checkmark✓

⚠️ The Trade-offs

While the technique works, it isn't without its drawbacks:

  1. Volume: Without an external amplifier, the audio is very quiet.
  2. Memory: The RAM requirements for samples of this quality are immense—essentially consuming all of it.

💻 The Engineering Struggle

Getting this to work required two complete rewrites. The timing constraints were incredibly rigid, forcing me to utilize techniques I hadn't previously employed on this architecture.

While I typically prefer C when compiler support is robust (similar to the DOS era), this project demanded Assembly Language. The reasons were twofold:

  • Cycle-Exact Timing: The audio effects required instruction sequences timed to the clock cycle.
  • Resource Constraints: The "fierce register pressure" and the need for direct chip access made higher-level languages impractical.

I initially thought this was the hardest version of the problem; in reality, there are more sophisticated ways to improve the technique, though they weren't necessary for these specific tests. For those interested in similar challenges, I recommend looking into Dmitry Milk's sound engines or Michael J. Mahon's RTSynth for the Apple IIe. Despite different CPUs, their approaches are more similar to each other than to mine.


📚 Theoretical Foundation & Prior Art

My interest in 1-bit sampling began with 1990s DOS gaming. I recall:

  • Eric the Unready: Used a system called RealSound to play effects through the PC speaker.
  • Star Control II: The Ur-Quan Masters: Ported Amiga-style tracker music to the PC speaker.

The PC vs. The Spectrum

Programming the PC speaker is significantly easier. While both use I/O ports to toggle the speaker, the PC possesses a highly programmable hardware timer. This timer acts as a Digital-to-Analog Converter (DAC) by allowing the programmer to set microsecond-precise pulse widths without the CPU having to "babysit" the speaker in a tight loop.

The Physics of Sound

The core principle relies on the physical properties of the hardware:

  1. Capacitance: Circuits have inherent capacitance.
  2. Mass: Speaker cones have physical mass.

Because of these, a signal doesn't jump from 000 to 111 instantaneously. By interrupting the signal mid-transition, we prevent the cone from reaching its full excursion, creating a more nuanced, varying wave.


⚙️ The Implementation Logic

Since the Spectrum lacks a dedicated timer for sample rates or pulse widths, everything must be handled in software. The logic follows a specific loop:

The key is that Delay 1 + Delay 2 must always equal a constant value to ensure the sample rate remains stable regardless of the pulse width.

📉 Misguided Attempt 1: The Math

My first attempt relied on "napkin math" based on my old PC system, which used pulse widths from 111 to 127μs127\mu s127μs.

For an 8kHz8\text{kHz}8kHz sample rate, the window for each pulse is: Sample Period=18000Hz=125μs\text{Sample Period} = \frac{1}{8000\text{Hz}} = 125\mu sSample Period=8000Hz1​=125μs

I noticed a red flag here: my test program used a 16kHz16\text{kHz}16kHz rate, meaning I would overrun the timing if the sample used the full range. Regardless, I proceeded with the calculations for the Spectrum's 3.5MHz3.5\text{MHz}3.5MHz CPU:

125μs×3.5MHz=437.5 cycles125\mu s \times 3.5\text{MHz} = 437.5 \text{ cycles}125μs×3.5MHz=437.5 cycles

Thus, I had roughly 438 cycles to execute the entire loop per sample.

Visual References

Figure 1: Sawtooth digital output

Figure 2: Voice clip sample


Images provided by Bumbershoot Software.