additive_synth

Application version 1.0
Trad4 version 3.2
Document version 1.0
Author schevans
Date 26-06-2010

Introduction

This application is a concurrent additive synthesiser. The principal of additive synthesis is that any periodic waveform can be synthesised by adding together sine waves. There's a good introduction to additive synthesis here.

This is a simplified model using no phase shifting and therefore only sinusoidal components. ADSR envelopes are not implemented.

In this beta six presets are available: sine, pulse, sawtooth, square, triangle and Hammond.

This model has an inverse - fourier_transform. Once you've read through this you should take a look at that.

The Model

The model itself is made up of three or more tiers. On the first tier are the harmonics - the pure sine wave feedstock of our synthesiser. Next come one or more tiers of mixers, where the harmonics are added concurrently. On the last tier there is only an amplifier, which serves to normalise the output as appropriate.

The concurrency comes from the fact that each mixer subscribes to a sub-set of harmonics - in all the configurations presented here there are four harmonics per mixer - and the mixers in turn run concurrently. This model uses the Inheritance of Interface (IoI) feature of trad4 where a mixer can subscribe to a set number of harmonics or a set number of mixers, as both types isa sample.

The number of samples per mixer is an optimisation parameter - the fewer samples per mixer you have the more concurrency you get but you also get more tiers. Optimisation of this application is beyond the scope of this document (though highly suitable for analysis as the app involves simple operations on large slabs of RAM which have to get dragged across buses). The concrete diagram below shows 2 samples per mixer for brevity. The application as configured in the distribution uses 4.

A new feature in trad4v3.1.2 is that the abstract and concrete graphs can now be generated for you, the first examples of which are below.

The abstract diagram

abstract

This application uses inheritance of interface (IoI), so it's abstract graph looks a little strange. Each type is considered separately and the tier axis on the left is relative. So that, from right to left, an amplifier of tier m subscribes to a sample of tier n, where m>n. A harmonic can exist on any tier and subscribes to nothing. A mixer subscribes to an array of in this case 4 samples. A sample as our base type does nothing at all.

The inheritance diagram

inheritance

A generated inheritance diagram has been added which should clarify the situation.

Amplifier, harmonic and mixer all inherit their interface (i.e. pub section) from sample. This means, looking at the abstract diagram, an amplifier could subscribe to a harmonic, or a mixer, or indeed another amplifier, as all three present the same pub interface. Likewise a mixer could subscribe to an array of harmonics, mixers or amplifiers for the same reason.

The concrete diagram

concrete

The concrete graph should look more familiar. This shows a graph where NUM_HARMONICS=8 and NUM_HARMONICS_PER_MIXER=2.

From this you can see that in practise, mixers subscribe to harmonics or other mixers. Only one amplifier is used and is only subscribes to a mixer.

The Waveforms

The naming convention for the files below is as follows: name+frequency+num_harmonics. So "square_f1_h1024.wav" is a square wave with frequency 1Hz, synthesised using 1024 harmonics. Likewise "hammond_f261_h16.wav" is the Hammond preset with frequency 262Hz (middle C), synthesised using 16 harmonics. Be aware that only the Hammond wave is at an audible frequency.

The sine waveform

The sine wave is the simplest waveform, comprising simply of a single sine wave of the base frequency. As such, there is no difference between the 16- and 1024-harmonic versions, so only the 16-harmonic is shown here.

sine_f1

sine_f1_h16.wav

This is given by:

    if ( id == 1 )
        level = 1.0;
    else
	level = 0.0;

The pulse waveform

The pulse waveform comprises of an equal amount of each harmonic.

pulse_f1_h16

pulse_f1_h16.wav

This is given by:

    level = 1.0;

The sawtooth waveform

The sawtooth waveform has a geometric drop-off of the amplitude of the component harmonics.

Using 16 harmonics:

sawtooth_f1_h16

sawtooth_f1_h16.wav

Using 64 harmonics:

sawtooth_f1_h64

sawtooth_f1_h64.wav

This is given by:

    level = 1.0 / id;

The square waveform

The square waveform has a geometric drop-off of the amplitude of the odd component harmonics.

Using 16 harmonics:

square_f1_h16

square_f1_h16.wav

Using 1024 harmonics:

square_f1_h1024

square_f1_h1024.wav

This is given by:

    if ( id % 2 != 0 )		// Is odd id
	level = 1.0 / id;
    else			// Is even id
	level = 0.0;

The triangle waveform

The square waveform has an exponential drop-off of the amplitude, where every second active harmonic amplitude is negative.

Using 16 harmonics:

triangle_f1_h16

triangle_f1_h16.wav

Using 1024 harmonics:

triangle_f1_h1024

triangle_f1_h1024.wav

This is given by:

    if ( id % 2 != 0 )		// Is odd id
	level = pow( -1.0, ( id - 1 ) / 2 ) * ( 1.0 / (id*id) );
    else			// Is even id
	level = 0.0;

The Hammond preset

This is a basic model of a Hammond tonewheel organ.

The classic Hammond organ has nine drawbars, each controlling the amplitude of a subset of harmonics. The drawbar amplitudes are labelled from 0 to 8, so it's characteristic to discuss the various arrangements or 'presets' as '800000000' for just a sine wave of the fundamental frequency or '88888888' - all the drawbars fully open.

More detailed information may be found here

As these files are at an audible frequency they are best downloaded and viewed using a sound editor such as Audacity.

hammond_888000000.wav

hammond_888888888.wav

hammond_834211100.wav

hammond_008030200.wav

Usage

Running

To run the application:
1) Download and unpack the distribution
2) cd into trad4_v3_2/additive_synth:

$ cd trad4_v3_2/additive_synth
3) Source additive_synth.conf:
additive_synth$ . ./additive_synth.conf
4) Start additive_synth:
additive_synth$ additive_synth

To increase or decrease the number of threads used (the default is 4), set NUM_THREADS and re-start the application:

$ export NUM_THREADS=64
$ additive_synth

Changing waveforms and frequency

To load a different waveform to be generated or to change the base frequency, use the load_waveform command, with the two arguments being the waveform.csv file and the fundamental frequency of the sample you want generated. E.g (261.626Hz is middle C):

additive_synth$ load_waveform.pl input/sine.csv 261.626

This can be done while additive_synth is running.

NOTE: If you try and model very high frequencies using the 44100Hz sample rate and many harmonics you may run up against the Nyquist frequency cutoff. The Nyquist frequency is the highest frequency a particular sample rate can support. If this happens, you will see a warning that these higher frequencies are being ignored.

Adding new waveforms

To add a new waveform, set up a new input.csv file and place it in additive_synth/input. It should have the format "id,amplitude" e.g. (for sine):

1,1.0
2,0.0
3.0.0
...

See the existing files in this directory for examples.

Reconfiguring

The default configuration of the distribution is with NUM_HARMONICS_PER_MIXER=4 and NUM_HARMONICS=16. If you want to increase the number of harmonics, you do so as follows:

1) Edit src/constants.t4s

First, edit src/constants.t4s as appropriate and save. The two values must be set do that NUM_HARMONICS is equal to NUM_HARMONICS_PER_MIXER raised to an integer power, where that integer power specifies the number of tiers of mixers you'll have, so that:

If NUM_HARMONICS_PER_MIXER=4
pow=2 gives NUM_HARMONICS=16 so that
T3: 1 mixer
T2: 4 mixers
T1: 16 harmonics
pow=3 gives NUM_HARMONICS=64 so that
T4: 1 mixer
T3: 4 mixers
T2: 16 mixers
T1: 64 harmonics

If NUM_HARMONICS_PER_MIXER=8
pow=2 gives NUM_HARMONICS=64 so that
T3: 1 mixer
T2: 8 mixers
T1: 64 harmonics
pow=3 gives NUM_HARMONICS=512 so that
T4: 1 mixer
T3: 8 mixers
T2: 64 mixers
T1: 512 harmonics

There's a performance trade-off with NUM_HARMONICS_PER_MIXER where the fewer mixers you have to sum you'll get more concurrency, but more objects to run. The optimal setting will depend on your hardware and the size of the samples you are using. This discussion is beyond the scope of this document but would make in interesting study.

2) Run the precompiler and compile

Next, run the precompiler and compile as normal. There's some flakiness in the makefiles when adjusting just the constants so you should do a clean build (-a for t4p):

additive_synth$ t4p -a
additive_synth$ make clean
additive_synth$ make

3) Regenerate and reload the database

The generated data is keyed off the constants, so it will need regenerating and and the database will need recreating and reloading. The default location of the generated data is data/worked_example. This is done as follows:

additive_synth/data$ generate_data.pl
additive_synth/data$ recreate_db.sh
additive_synth/data$ reload_db.sh worked_example

4) Run as usual

additive_synth$ additive_synth

Get trad4 at SourceForge.net. Fast, secure and Free Open Source software downloads