152 lines
6.1 KiB
Plaintext
152 lines
6.1 KiB
Plaintext
Sound basics
|
|
============
|
|
|
|
There is a secondary output port (XOUT) with 8 bits. It is split
|
|
in 4 bits for the LEDs, and 4 bits that go into a 4-bit DAC resistor
|
|
array to form 16 output levels. By default, we have 4 sound channels
|
|
in software that are 6 bits each internally. At the beginning of
|
|
each scan line, one of these software channels gets updated to
|
|
compute a new 6-bits sample. So one software channel update happens
|
|
during every horizontal VGA sync pulse. After every 4 scan lines,
|
|
the top 4 bits of their sum gets output to the sound part of the
|
|
XOUT register.
|
|
|
|
The 4 software channels can independently generate a tone and a
|
|
waveform. Some of the preprogrammed waveforms are triangle, sawtooth,
|
|
pulse and something that looks like noise, but sounds a bit metallic.
|
|
It can all be changed of course, as it is all software-defined.
|
|
|
|
Controls
|
|
--------
|
|
|
|
There are 6 bytes for each sound channel. They're at the top of
|
|
page 1..4.
|
|
|
|
0 255
|
|
+----------------------------------------------------------+
|
|
$0000 | Zero page |
|
|
+------------------------------------+---------+-----------+
|
|
$0100 | Video table | vReset | Channel 1 |
|
|
+------------------------------------+---------+-----------+
|
|
$0200 | | Channel 2 |
|
|
| +-----------+
|
|
$0300 | | Channel 3 |
|
|
| +-----------+
|
|
$0400 | User vCPU code and/or data | Channel 4 |
|
|
| +-----------+
|
|
| |
|
|
|
|
The 6 bytes are:
|
|
|
|
wavA Modulation after table lookup with ADD operation and
|
|
optional clipping (can approximate PWM with this)
|
|
wavX Modulation of index with XOR operation
|
|
keyL:keyH 15-bit frequency (bit7 of keyL should be 0).
|
|
There is a lookup table on page 7 in ROM
|
|
oscL:oscH 15-bit phase, automatically incremented with keyL:keyH
|
|
every 4 scan lines (1 channel per scan line).
|
|
|
|
(You typically don't write into oscL,oscH)
|
|
|
|
The sound synthesises runs during every horizontal pulse of the VGA
|
|
signal. It is implemented twice, once for visible lines and once
|
|
for vertical blank. The sample update runs in the videoC lines, or
|
|
their equivalents in vBlank. A video frame has 521 lines, so there
|
|
is no fixed mapping from video line to channel.
|
|
|
|
In pseudo code:
|
|
----------------------------------------------------------------
|
|
For every channel:
|
|
"osc" += "key" // Advance phase
|
|
byte i = oscH & 0xfc // Keep only the highest 6 bits
|
|
i ^= wavX // Low 2 bits select waveform.
|
|
// High 6 bits give effects.
|
|
i = soundTable[i] + wavA // More sound effect options
|
|
i = i&128 ? 63 : i&63
|
|
sample += i
|
|
|
|
Every 4 scan lines:
|
|
1. Emit sample & 0xf0 // The DAC has 4-bits
|
|
2. Reset sample
|
|
sample = 3 // Why 3? Any value <=3 (=255-4*63) will
|
|
// work, and we have only 1 cycle available
|
|
st $03,[$03] // for this, so operand is address and value
|
|
----------------------------------------------------------------
|
|
|
|
Notes
|
|
-----
|
|
|
|
The ROM has a handy lookup table for the frequencies that is
|
|
calculated for a 6.25 MHz system doing 200 cycles per horizontal
|
|
sync.
|
|
|
|
Note: index 0 = 0 Hz = OFF
|
|
|
|
ROM table at $0900
|
|
----------------------------------------------------------------
|
|
notesTable: 0900 0000 ld $00
|
|
0901 0000 ld $00
|
|
0902 0045 ld $45 ;C-0 (16.4 Hz)
|
|
0903 0000 ld $00
|
|
0904 0049 ld $49 ;C#0 (17.3 Hz)
|
|
0905 0000 ld $00
|
|
[...]
|
|
09be 001a ld $1a ;A#7 (3729.3 Hz)
|
|
09bf 007a ld $7a
|
|
----------------------------------------------------------------
|
|
|
|
The waveforms are stored in RAM page 7. They are initialised once
|
|
at boot time and then never again (so not with soft reset). [Edit:
|
|
from ROM v4 onwards, these are reinitialised with each soft reset!]
|
|
You select one with the low two bits of wavX. They look like below.
|
|
The page is initialised as noise (0), triangle (1), pulse (2) and
|
|
sawtooth (3). You can change them, but then the next application
|
|
will see your changes. You shouldn't change the sawtooth waveform
|
|
(3) because it doubles as a right-shift-2 lookup table for the
|
|
SYS_Unpack_56 function used by Pictures.
|
|
|
|
Image: https://forum.gigatron.io/download/file.php?id=8
|
|
|
|
Noise
|
|
-----
|
|
|
|
Noise is not really noise because it repeats. It sounds a bit
|
|
metallic. You can make it sound like true noise by randomzing the
|
|
top 6 bits of wavX every vertical blank. There are 3 bytes of
|
|
entropy available in the zero page that are updated during vertical
|
|
blank. You might use those. See Apps/Overworld/ for an example.
|
|
|
|
There's also a SYS_ShuffleNoise_v4_46 function in ROM. That one
|
|
is intended for initializing the waveform table. It isn't very
|
|
suitable for changing the noise waveform on the fly.
|
|
|
|
Envelopes
|
|
---------
|
|
|
|
You can make them by changing wavA during vertical blank. See this
|
|
forum topic: https://forum.gigatron.io/viewtopic.php?f=4&t=157
|
|
|
|
Demo code in Contrib/dhkolf/apps/PianoAD.gcl and DrumsAD.gcl
|
|
|
|
MIDI player
|
|
-----------
|
|
|
|
There is midi sequencer in Contrib/at67/midi
|
|
The Tetronis game uses this for its music score.
|
|
The Apps/Overworld/ demo is derived from the same code.
|
|
|
|
channelMask
|
|
-----------
|
|
|
|
ROMv4 has the channelMask bits in the byte at address $21: with
|
|
these you can reduce the number of channels to two or even one.
|
|
|
|
Valid bit combinations are:
|
|
xxxxx011 Default after reset: 4 channels (page 1,2,3,4)
|
|
xxxxx001 2 channels at double update rate (page 1,2)
|
|
xxxxx000 1 channel at quadruple update rate (page 1)
|
|
|
|
The main application for this is to free up the high bytes of page 2,3,4.
|
|
|
|
-- End of document --
|