Retrochallenge 2017/10 #4

Another small update of the Retrochallenge 2017/10 Speech256 project. The source part is finished! It can produce both the pulse wave and noise:

source_pulsewave

source_noise

Here’s the audio for the brave:

Noise generator

In case you were wondering, the linear feedback shift register used for the noise generator is a single Verilog line:

lfsr = {lfsr[15:0], lfsr[16] ^ lfsr[2]}

The updated Verilog source code is up on the Github page!

Next…

Up next will be integrating the filter engine and the source to complete the source-filter model of the synthesizer.

Advertisements

Retrochallenge 2017/10 #3

During the past few days I’ve managed to squeeze some time out of my schedule to make progress on the Verilog code for the Speech256 synthesizer.

Basically, I think I have the 12-pole filter working. This is the most complicated part of the project, with three shift registers holding the filter coefficients and the filter states:

filter_engine_18_10_2017

The coefficient shift register is used as a roundabout, which avoids the use of an actual addressable structure, like a RAM. It also simplifies loading a new set of coefficients into the filter engine, as these can simply be served in a serial fashion, reducing the interface complexity to the Speech256 controller.

The multiplier shown is implemented using a shift-and-add technique, which has a reduced speed but a lower resource requirement compared to a fully parallel implementation.

The 12-pole filter control logic (not shown) was a bit tricky due to the serial nature of many of the components, see the meaningless screenshot here:

gtkwave_filter_18_10_2017

The most recent code is up on my Speech256 Github page..

Retrochallenge 2017/10 #2

Here is the first real RC2017 update in the form of a video on the inner workings of the SP0256-AL2 speech synthesis chip.

It took a lot longer to produce than I originally thought, partly because I had a severe cold and sounded like a wet newspaper, and partly because open-source video recording and editing is still a P.I.T.A, apparently.

Noise shaper system equations

Here are a few quick notes on digital noise shapers, primarily for my own reference.

Introduction

Here we have a traditional noise shaper setup, consisting of an Nth-order loop filter and a quantizer:

SD_loop

When a linear model is assumed, the noise transfer function (NTF) H_{ntf} can be expresses as a function of the loop filter:

H_{ntf}(z) = \frac{1}{1+H_{loop}(z)}.

The loop filter also changes the frequency response of the input signal:

H_{sig}(z) = \frac{H_{loop}(z)}{1+H_{loop}(z)}.

Assuming the loop gain is very high in the lower part of the spectrum, where the desired signal is present, this frequency response can be approximated as:

H_{sig}(z) = \frac{H_{loop}(z)}{1+H_{loop}(z)} \approx \frac{H_{loop}(z)}{H_{loop}(z)} = 1.

In effect, given enough in-band loop gain, the frequency domain distortion is negligible.

Causal loop filters

Given a desired noise transfer function H_{ntf}, the loop filter can be derived as follows:

H_{loop}(z) = \frac{1}{H_{ntf}(z)} - 1

Now, splitting the NTF into its poles A(z) and zeroes B(z), the loop filter can be expressed as:

H_{loop}(z) = \frac{A_{ntf}(z)}{B_{ntf}(z)} - 1

and therefore:

H_{loop}(z) = \frac{A_{ntf}(z) - B_{ntf}(z)}{B_{ntf}(z)}.

For the loop to be causal, the loop filter must have at least one pure delay. Given that A(z) = 1 - a_1 \cdot z^{-1} - a_2 \cdot z^{-2} - \ldots - a_n \cdot z^{-n}, if we force the first coefficient of B(z), i.e. b_0, to be equal to 1, the first coefficient of the resulting loop filter numerator will be zero and the loop filter is guaranteed to have at least one delay.