Wavetable Synthesis

A project log for Novasaur CP/M TTL Retrocomputer

Retrocomputer built from TTL logic running CP/M with no CPU or ALU

Alastair HewittAlastair Hewitt 05/20/2020 at 00:100 Comments

Development moved back to the hardware abstraction layer to finalize the timing and resource requirements for the audio and serial features. Added to this was hardware interrupt handling for the virtual CPU. Most of the interrupt handling of the 8085 should be achievable, but more on this in another log.

The coding for the virtual sound chip is now complete and the design is based around a wavetable synthesizer. This is a somewhat basic version, but it was easy to implement given the design of the ALU and spare operation freed up by eliminating SUB (subtract).

The new operation (WAV) consists of two cycles: The first cycle takes an index from 0-255 and looks up one of 16 waveforms. This waveform is encoded as 8-bit signed integers to return the amplitude of the wave at the index. The second cycle attenuates the wave amplitude by one of 16 possible values. These range from -12 to -42dB in 2dB steps. This takes the 8-bit waveform and reduces it to a maximum of 6-bits (42dB dynamic range). A DC offset is added so the final result varies from 0 to 63. Up to 4 of these waves can be mixed using the ADD operation on the audio register.

Another function is used with the synthesizer to load a 16-bit value for each note indexed by their MIDI value. This value is added to a 16 register in the zero-page at the 9593Hz process speed. The highest reproducible note is D8, two semitones above the top of the piano scale at 4.7kHz. Anything above this frequency will result in alias distortion. This not only includes the fundamental frequency of notes above this range, but also many of the harmonics of notes much lower in the range.

The wavetable serves two purposes: The table contains the classic square, sawtooth, triangle, and some noise waveforms. There is also room in the wavetable to include morphed versions between these classic waves that can be swept from one to the other. This can be done via an envelope, low-frequency oscillator, or both (matrix modulation). The second purpose of the wavetable relates to the issue of aliasing. The table can be swept towards a pure sine-wave by progressively dropping the higher harmonics. Higher frequency notes would only migrate to this band-limited area to prevent inharmonic aliasing.

Sawtooth wave with a 42dB dynamic range up to the 15th harmonic

The gain-control part of the synthesizer is used to adjust the amplitude of the wave. This would typically follow an ADSR envelope to give a natural musical quality to the notes played. The same envelope can be applied to the wavetable wave selection to dynamically change the harmonic content of the wave. This produces a similar effect to a voltage-controlled filter in an analog synthesizer.

The envelope and LFO values change quite slowly and these could be controlled by the virtual CPU. Predefined envelops could be automatically applied by simply indexing a table on every frame with minimal cost. The wavetable index needs to be calculated on every virtual process cycle though and this has a more significant cost. Because of this the number of voices can be selected from 0-3 by selecting one of 4 audio modes:

000100%audio off
11 + noise287%
22 + noise380%
33 + noise473%entire line

The table shows the audio modes, the number of voices, and the number of virtual machine cycles (VMCs) needed to implement those voices. The CPU column shows the impact of supporting these voices in terms of CPU performance. The additional noise channel is an unvoiced audio source used with a noise waveform. This can be modulated using the envelope to generate percussive sounds and sound effects.