Sound Controller
15.1 Overview
The Zeal Video Board includes a powerful sound subsystem with five voices:
- 4 standard voices that generate basic waveforms: square, triangle, sawtooth, and noise.
- 1 sample-based voice capable of playing arbitrary wave data from a 256-byte FIFO buffer.
The 4 standard voices share a single 16-bit Linear Feedback Shift Register (LFSR) for noise generation. The sample-based voice is independent but mixed together with the other voices before output.
Mixing is done by averaging the four standard voices and then adding the current sample from the sample table voice. This approach keeps the audio output balanced but requires care when playing both types of voices simultaneously to avoid saturation or clipping.
Like other controllers, the DMA Controller can be mapped into the device bank, starting at address 0xA0 on the I/O port. Its device index is 3.
15.2 Voice Control and Synchronization
- The hold register allows muting voices by freezing their output at the current sample value, effectively silencing them without stopping waveform generation.
- This hold feature enables atomic start/stop of multiple voices simultaneously to avoid timing glitches or artifacts. The register
MASTER_HOLD can be set to choose the voices to hold and unhold.
- The sound select register determines which voices are affected by writes to frequency and waveform configuration registers, allowing flexible per-voice setup. The voice to select/enable can be choosen in the
MASTER_ENABLE register.
For example, if voices 0 and 1 must be set to frequency 0x1000, the following pseudo code can be used:
// Enable both voices 0 and 1 (1 bit per voice)
MASTER_ENABLE = (1 << 1) | (1 << 0);
// All the selected voices will listen to the the following registers changes.
FREQUENCY_LOW = 0x00;
FREQUENCY_HIGH = 0x10;
// Set voice 0 to SQUARE wave, 50% duty cycle
MASTER_ENABLE = (1 << 0);
WAVEFORM = 0x80;
// Set voice 1 to SAWTOOTH wave
MASTER_ENABLE = (1 << 1);
WAVEFORM = 0x02;
Each of the 4 standard voices can produce one of the following waveforms, selectable per voice:
| Index |
Waveform |
| 0 |
Square wave (with configurable duty cycle) |
| 1 |
Triangle wave |
| 2 |
Sawtooth wave |
| 3 |
White noise (using the shared LFSR) |
Setting the waveform of one or multiple voices can be done by setting their WAVEFORM. The voices to configure must first be selected in the MASTER_ENABLE register.
The duty cycle of the square wave is controlled by a 3-bit field, allowing for these settings:
| Bits |
Duty Cycle (%) |
| 000 |
0 |
| 001 |
12.5 |
| 010 |
25 |
| 011 |
37.5 |
| 100 |
50 |
| 101 |
62.5 |
| 110 |
75 |
| 111 |
87.5 |
Setting the duty cycle, only applicable when setting a voice to square wave, can be done by setting the Duty cycle fields in the WAVEFORM register.
⚠️ Changes to frequency, waveform, or duty cycle are latched and only take effect at the end of the current waveform period, preventing clicks or pops.
15.4 Frequency and Pitch
The frequency of each voice is set by a 16-bit register. The resulting frequency in Hz is calculated as:
$\text{Frequency} = \frac{\text{sample_rate} \times \text{freq_reg}}{2^{16}}$
Where the fixed sample rate is 44,091Hz for the standard voices.
The hardware does not limit frequency values, so very high or low values may produce inaudible sounds.
The frequency for the enabled voice(s) can be written by setting the FREQUENCY_LOW and FREQUENCY_HIGH for the LSB and MSB respectively.
15.5 Volume and Mixing
- Each voice has a disable bit and a 2-bit linear volume control allowing five levels: 0%, 25%, 50%, 75%, and 100%. Enabled voice(s) volume can be set via the
VOLUME register.
- The master volume controls output volume per channel (left and right) separately, also with 2-bit linear scaling. The left/right channels volumes can be set via the register
MASTER_VOLUME
- Voices can be assigned individually to the left and/or right audio channels, including the sample table voice, to do so, the registers MASTER_LEFT_CHANNEL and MASTER_RIGHT_CHANNEL are provided.
- When mixing, the outputs of the four standard voices are averaged before adding the sample table voice's current sample.
This scheme keeps the output balanced and avoids simple sum clipping, but be aware that playing the sample voice alongside all four standard voices can cause saturation since its sample is added after the averaging.
15.6 Sample Table Voice
The fifth voice uses a 256-byte FIFO buffer to play arbitrary digital samples.
It can be selected by setting SAMPLE_TAB bit in MASTER_ENABLE register. This special does not use the same WAVEFORM, FREQUENCY and VOLUME registers as the other voices. The registers offsets are repurposed as the ST_FIFO, ST_SRATE_DIV and ST_STATUS registers.
- The following sample formats are supported, which can be set in register
ST_SRATE_DIV:
- 8-bit unsigned
- 16-bit signed or unsigned (little-endian)
- The Sample playback rate is programmable via a divider, set in
ST_SRATE_DIV, and is interpreted as:
$\text{Sample Rate} = \frac{44091}{\text{divider} + 1}$
- The FIFO has full and empty status flags:
- Playback halts if the FIFO is empty.
- Writing stops when FIFO is full.
The current FIFO status is readable from the ST_STATUS register.
This voice allows playback of complex sounds or sampled audio alongside the standard synthesized voices.
15.7 Programming the Sound Controller
The SDK provides a straightforward API to control voices and samples:
- Initialize and reset the sound subsystem.
- Configure voices' frequency, waveform, volume, and channel assignment.
- Manage the sample table voice: configure sample format, sample rate divider, and write sample data to the FIFO.
- Use the hold register to mute or start voices atomically.
For detailed examples and complete API usage, check out the SDK sound example.
15.8 Registers Summary
| Name |
Description |
Base-relative address |
Access |
| FREQUENCY_LOW |
Frequency divider LSB for all the currently selected voices |
0x00 |
WO |
| FREQUENCY_HIGH |
Frequency divider MSB for all the currently selected voices |
0x01 |
WO |
| WAVEFORM |
Sound voice waveform for all the currently selected voices |
0x02 |
WO |
| VOLUME |
Volume for all the currently selected voices |
0x03 |
WO |
| ST_FIFO |
Write a byte to the FIFO (Sample Table ONLY) |
0x00 |
WO |
| ST_SRATE_DIV |
Sample rate divider (Sample Table ONLY) |
0x01 |
R/W |
| ST_STATUS |
Status of the Sample Table voice. (Sample Table ONLY) |
0x02 |
varies |
| MASTER_LEFT_CHANNEL |
Bitmap to assign sound voices to output on the left sound channel |
0x0B |
WO |
| MASTER_RIGHT_CHANNEL |
Bitmap to assign sound voices to output on the right sound channel |
0x0C |
WO |
| MASTER_HOLD |
Bitmap to hold the sound voices, preventing them from generating the next sample |
0x0D |
WO |
| MASTER_VOLUME |
Master volume control for the left and right channels |
0x0E |
WO |
| MASTER_ENABLE |
Master enable control to select the voices that will listen on register range 0x00-0x08 |
0x0F |
WO |
15.9 Registers
FREQUENCY_LOW (0x00)
Frequency divider LSB for all the currently selected voices
Reset
FREQ_LSB
Frequency divider LSB (WO)
FREQUENCY_HIGH (0x01)
Frequency divider MSB for all the currently selected voices
Reset
FREQ_MSB
Frequency divider MSB (WO)
VOLUME (0x03)
Volume for all the currently selected voices
Reset
DISABLE
Disable sound output, equivalent to a 0% volume. (WO)
Steps
Sound steps. Refer to the thable above to see all the options. (WO)
ST_FIFO (0x00)
Write a byte to the FIFO (Sample Table ONLY)
Reset
DATA
Write a byte to the Sample Table voice's FIFO. (WO)
ST_SRATE_DIV (0x01)
Sample rate divider (Sample Table ONLY)
Reset
DIV
Sample rate divider. The resulting sample rate is 44,091/(DIV+1). (R/W)
ST_STATUS (0x02)
Status of the Sample Table voice. (Sample Table ONLY)
Reset
EMPTY
Set if the FIFO is empty. (RO)
FULL
Set if the FIFO is full. (RO)
SIGNED
Set if the samples are signed (in 16-bit ONLY, ignored in 8-bit mode). (R/W)
LOOP
When set, the sample playback loops continuously using the existing FIFO contents. (R/W)
MODE_8BIT
Set if the samples are in unsigned 8-bit format, unset if the samples are 16-bit (signed or unsigned). (R/W)
MASTER_LEFT_CHANNEL (0x0B)
Bitmap to assign sound voices to output on the left sound channel
Reset
SAMPLE_TAB
Output the sample table voice when set. (WO)
VOICE_3
Output voice 3 when set. (WO)
VOICE_2
Output voice 2 when set. (WO)
VOICE_1
Output voice 1 when set. (WO)
VOICE_0
Output voice 0 when set. (WO)
MASTER_RIGHT_CHANNEL (0x0C)
Bitmap to assign sound voices to output on the right sound channel
Reset
SAMPLE_TAB
Output the sample table voice when set. (WO)
VOICE_3
Output voice 3 when set. (WO)
VOICE_2
Output voice 2 when set. (WO)
VOICE_1
Output voice 1 when set. (WO)
VOICE_0
Output voice 0 when set. (WO)
MASTER_HOLD (0x0D)
Bitmap to hold the sound voices, preventing them from generating the next sample
Reset
SAMPLE_TAB
Hold the sample table voice when set. (WO)
VOICE_3
Hold voice 3 when set. (WO)
VOICE_2
Hold voice 2 when set. (WO)
VOICE_1
Hold voice 1 when set. (WO)
VOICE_0
Hold voice 0 when set. (WO)
MASTER_VOLUME (0x0E)
Master volume control for the left and right channels
Reset
DISABLE_RIGHT
Disable sound output on the right channel, equivalent to a 0% volume. (WO)
DISABLE_LEFT
Disable sound output on the left channel, equivalent to a 0% volume. (WO)
STEPS_RIGHT
Sound steps for the right channel. Refer to the table above to see all the options. (WO)
STEPS_LEFT
Sound steps for the left channel. Refer to the thable above to see all the options. (WO)
MASTER_ENABLE (0x0F)
Master enable control to select the voices that will listen on register range 0x00-0x08
Reset
SAMPLE_TAB
Select the sample table voice when set. (WO)
VOICE_3
Select voice 3 when set. (WO)
VOICE_2
Select voice 2 when set. (WO)
VOICE_1
Select voice 1 when set. (WO)
VOICE_0
Select voice 0 when set. (WO)