Part 11: Sound Tricks - Noise, Phasing, SID Sound, Earth Shaker, Duty Modulation
Digital/PCM sound is a powerful and flexible tool, but unfortunately it tends to consume a lot of RAM. So in this part of the tutorial, let's go back to the good old Pulse Interleaving technique, and talk about various tricks that can be used to spice up its sound.
Noise
Historically speaking, 1-bit routines have always been lacking in terms of percussive effects. However, converting a tone generator into a simple noise generator is surprisingly simple, and costs a mere 8 cycles on Z80-based systems. Consider the usual way we generate square wave tones:
add hl,de ;add base frequency divider (DE) to channel accumulator (HL)
ld a,h ;grab hi-byte of channel accumulator
cp DUTY ;compare against duty threshold value
sbc a,a ;set A to 0 or 0xFF depending on result
out (#fe),a ;output A to beeper port
Now, in order to generate noise instead of tones, one would obviously need to randomize the value held by the channel accumulator. But pseudo-random number generators are slow, so how do we do it? The answer is to simply reduce the quality of the PRNG as much as possible. Believe it or not, adding a single
after the add hl,de operation will provide enough entropy to create a convincing illusion of white noise. But it will do so only if it is fed a suitable
frequency divider as a seed. I usually use a fixed value of 0x2174 in my routines. Other values are possible of course, and may give slightly different results, though most values will just generate nasty glitch sounds instead of noise.
There's a nice side effect that you get for free - you can create the illusion of controlling the volume of the noise by changing the duty threshold. Changing the pitch of the noise however is much more difficult, and requires the use of additional counters. I'm still looking for an efficient way of doing it, if you have any ideas please let me know.
Last note, you can of course also rotate the hi-byte of frequency divider instead of the accu. The result of that however is almost guaranteed to be a glitch sound rather than noise.
Phasing
The Phasing technique was developed by Shiru, and is used to generate the signature sound of his Phaser1-3 engines. It comes at a rather heavy cost in terms of cycle count and register usage, but it's power and flexibility undoubtedly outweigh those drawbacks.
For regular tone generation, we use a single oscillator to generate the square wave (represented by the add hl,de operation). The main idea of Phasing, on the other hand, is to use two oscillators, and mix their outputs into a single signal. The mixing can be done via a binary XOR of the two oscillator outputs (the method used in Phaser1), or via a binary OR or AND (added in Phaser2/3).
add hl,de ;OSC 1: add base freq divider (DE) to channel accu (HL) as usual
ld a,h ;grab hi-byte of channel accumulator
cp DUTY1 ;compare against duty threshold value
sbc a,a ;set A to 0 or 0xFF depending on result
ld b,a ;preserve result in B
exx ;shadow register set, yay
add hl,de ;OSC 2: exactly the same operation as above
ld a,h ;grab hi-byte of channel accumulator
cp DUTY2 ;compare against duty threshold value
sbc a,a ;set A to 0 or 0xFF depending on result
exx ;back to primary register set
xor b ;combine output of OSC 1 and 2. (xor|or|and)
out (#fe),a ;output A to beeper port
As you can see, this method offers a wide range of parameters that affect timbre. The most important one, from which the technique derives its name, is the phase offset between the two oscillators. To make use of this feature, simply initialize the OSC1 accu to another value than the initial value of the OSC2 accu, eg. initialize HL to 0 and HL' to a non-zero value. Especially in conjunction with a slight offset between the OSC1 and OSC2 base dividers, some surprisingly complex timbres can be produced.
Side note: By using a binary OR to mix the signal and keeping the duty thresholds down to a reasonable level, the two oscillators can be used as independant tone generators. This method is used to mix channels in Squeeker and derived engines.
SID Sound
This effect, which derives its name from the key sound that can be heard in many of the early SID tunes, is formed by a simple duty cycle sweep. The velocity of the sweep is in sync with the frequency of the tone generator. Basically, every time the channel accumulator overflows, the duty threshold is increased or decreased. As with noise, this is trivial to pull off and costs only a few cycles. Using the standard tone generation procedure, we can implement it as follows
add hl,de ;add base frequency divider (DE) to channel accumulator (HL)
sbc a,a ;set A to 0 or 0xFF depending on result
add a,c ;add duty threshold (C)
ld c,a ;update duty threshold value (C = C - 1 if add hl,de carried)
cp h ;compare duty threshold value against hi-byte of channel accu
sbc a,a ;set A to 0 or 0xFF depending on result
out (#fe),a ;output A
As you can see, this operation costs a mere 4 cycles compared to the standard procedure without duty cycle sweep.
Earth Shaker
This effect is named after the game Earth Shaker, which used a rather unusual sound routine with two semi-independant tone channels, written by Michael Batty. As an actual method of generating multi-channel sound, it is of limited practicality, but it can be applied as an effect to regular Pulse Interleaving at a minimal cost. The core concept here is to continually modulate the duty threshold within the sound loop. Depending on the ratio of the duty cycle change vs the oscillator speed, the result can be a nice chord, phatness, or - in most cases - gruesome disharmony that will strike fear in the hearts of even the most accustomed 1-bit connaisseurs. A simple implementation, as used in HoustonTracker 2 for example, looks like this:
add hl,de ;add base frequency divider (DE) to channel accumulator (HL)
ld a,c ;load duty threshold (C)
add a,DUTY_MOD ;add duty threshold modifier
ld c,a ;store new duty threshold
cp h ;compare duty threshold value against hi-byte of channel accu
sbc a,a ;set A to 0 or 0xFF depending on result
out (#fe),a ;output A to beeper port
Duty Modulation
The aforementioned SID sound and Earth Shaker effects are actually basic implementations of a family of effects that may best be described as "Duty Modulation". As a first step into the world of Duty Modulation, let's take the Earth Shaker effect and modify it to change the duty threshold in sync with the main oscillator.
add hl,de ;add base frequency divider (DE) to channel accumulator (HL)
sbc a,a ;set A to 0 or 0xFF depending on result
and DUTY_MOD ;set A to 0 or DUTY_MOD
xor c ;XOR with current duty threshold (C)
ld c,a ;store new duty threshold
cp h ;compare duty threshold value against hi-byte of channel accu
sbc a,a ;set A to 0 or 0xFF depending on result
out (#fe),a ;output A to beeper port
By syncing the modulation in this way, the nasty glitches of the Earth Shaker effect can be avoided entirely (but also, no chords will be produced). Instead, we can now control harmonic components that share an octave relation with the base note. In other words, we can amplify over- and undertones at will, as long as they are a multiple of 12 half-tones away from the main note.
Things can be pushed even further by decoupling the sync and using a second oscillator to time the duty threshold updates.
exx
add hl,de ;independant oscillator for timed duty threshold updates
exx
sbc a,a ;set A to 0 or 0xFF depending on result
and DUTY_MOD ;set A to 0 or DUTY_MOD
xor c ;XOR with current duty threshold (C)
ld c,a ;store new duty threshold
add hl,de ;add base frequency divider (DE) to channel accumulator (HL)
cp h ;compare duty threshold value against hi-byte of channel accu
sbc a,a ;set A to 0 or 0xFF depending on result
out (#fe),a ;output A to beeper port
This way, we can create the octave effects from the previous example (by setting the "duty" oscillator to the same value as the main tone oscillator), as well as Earth Shaker style chords, while also gaining better control over the latter. Additionally, some interesting slow-running timbre changes can be achieved by setting the duty oscillator to a frequency near (but not equal to) the main oscillator.
The usefulness of this approach might seem a bit questionable considering the hefty cost in CPU cycles and register usage. However, the required code is almost the same as the one used for the Phasing technique, so with a tiny amount of self-modifying code, it can be implemented in a Phaser style engine at virtually no extra cost.
There's also an added bonus when combining this technique with the noise generator explained above. By setting the duty threshold to the same value as the duty modifier, the duty oscillator can be used as a tone generator, meaning you can actually mix noise and tone on the same channel!
That's all for this time. If you know of any other cool tricks please post them here!