When I started this project I had absolutely no idea about how to implement the Spectrum beeper. It did indeed turn out to be quite a challenge, partly because I didn't know anything about coding audio at all and partly because of the way the Spectrum beeper works.
How does the beeper work? In Sinclair BASIC, you can activate the beeper with the Beep command. For example "Beep 2,8" will produce a 2 second tone in G#. Sound is generated by a small speaker (beeper) which is controlled by on/off signals produced by the Spectrum. These on/off signals produce a square wave (in theory at least) with a tone corresponding to the frequency of the on/off signals - a 1-bit sound generator. There is a special routine in ROM for controlling the beeper, and it is precisely timed, based on the Z80 clock frequency. Of course, it is possible to control the beeper directly via machine code, which many games do, often producing multi channel sounds. It should also be noted that the ROM routine halts the computer during audio playback to ensure correct timing. In games this is impractical, so programmers had to create sound routines that could produce consistent sounds even during gameplay. How to emulate the beeper? As mentioned above timing is essential. The problem is that an emulator would be challenged to time every Z80 instruction exactly - at least as far as I know. The SoftSpectrum Z80 emulation timing is based on the CPU interrupt period, which is 1/50.88 second. The emulator processes the same number of instructions as a real Spectrum during an interrupt period. In effect the emulator processes a batch of Z80 instructions once every 50th second, but at much higher speed than a real Z80 CPU, so the instructions are processed in bursts. During the rest of the time it does other things like update the screen, handle keyboard input etc. Plugging the emulator directly to a 1-bit audio generator would of course produce very strange sounds due to the emulator processing speed, so something more needs to be done. The solution I found was to send the signals from the Z80 meant for the beeper to a buffered array, where the length of each pulse corresponds to a number of bytes with a value for on or off in the array. The array is processed by an audio generator (NAudio) to create a sound. While this should work in theory I put down many hours trying to get a decent sound from the emulator. No matter what I did I couldn't get rid of intermittent "stutters" and other artifacts. Finally I stumbled upon a discussion somewhere on the web about the lack of precision in the .NET timer class. Apparently it is well known that it lacks the precision or priority for this kind of application, causing irregular interrupt periods, which led to gaps in the audio feed. Luckily I found the Multimedia Timer class at Codeproject.com, which saved my day! Actually I still have to de-tune the beeper somewhat to avoid stutters, but this should not be noticeable. Update 29 December 2016: The de-tuning mentioned above is no longer needed, after I have improved the beeper functions.
0 Comments
|
Archives
November 2020
Categories
All
|