Csound

WAVESHAPING

Waveshaping can in some ways be thought of as a relation to modulation techniques such as frequency or phase modulation. Waveshaping can achieve quite dramatic sound transformations through the application of a very simple process. In FM (frequency modulation) modulation synthesis occurs between two oscillators, waveshaping is implemented using a single oscillator (usually a simple sine oscillator) and a so-called 'transfer function'. The transfer function transforms and shapes the incoming amplitude values using a simple look-up process: if the incoming value is x, the outgoing value becomes y. This can be written as a table with two columns. Here is a simple example:

  Incoming (x) Value   Outgoing (y) Value
-0.5 or lower
 -1
 between -0.5 and 0.5
 remain unchanged
 0.5 or higher
 1
 

Illustrating this in an x/y coordinate system results in the following graph:


 

Basic Implementation Model

Although Csound contains several opcodes for waveshaping, implementing waveshaping from first principles as Csound code is pretty straightforward. The x-axis is the amplitude of every single sample, which is in the range of -1 to +1.1 This number has to be used as index to a table which stores the transfer function. To create a table like the one above, you can use Csound's sub-routine GEN072 . This statement will create a table of 4096 points in the desired shape:

giTrnsFnc ftgen 0, 0, 4096, -7, -0.5, 1024, -0.5, 2048, 0.5, 1024, 0.5

 

Now two problems must be solved. First, the index of the function table is not -1 to +1. Rather, it is either 0 to 4095 in the raw index mode, or 0 to 1 in the normalized mode. The simplest solution is to use the normalized index and scale the incoming amplitudes, so that an amplitude of -1 becomes an index of 0, and an amplitude of 1 becomes an index of 1:

aIndx = (aAmp + 1) / 2

The other problem stems from the difference in the accuracy of possible values in a sample and in a function table. Every single sample is encoded in a 32-bit floating point number in standard audio applications - or even in a 64-bit float in recent Csound.3 A table with 4096 points results in a 12-bit number, so you will have a serious loss of accuracy (= sound quality) if you use the table values directly.4 Here, the solution is to use an interpolating table reader. The opcode tablei (instead of table) does this job. This opcode then needs an extra point in the table for interpolating, so it is wise to use 4097 as size instead of 4096.5 

This is the code for the simple waveshaping with the transfer function which has been discussed so far:

EXAMPLE 04E01_Simple_waveshaping.csd

<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1

giTrnsFnc ftgen 0, 0, 4097, -7, -0.5, 1024, -0.5, 2048, 0.5, 1024, 0.5
giSine    ftgen 0, 0, 1024, 10, 1

instr 1
aAmp      poscil    1, 400, giSine
aIndx     =         (aAmp + 1) / 2
aWavShp   tablei    aIndx, giTrnsFnc, 1
          outs      aWavShp, aWavShp
endin

</CsInstruments>
<CsScore>
i 1 0 10
</CsScore>
</CsoundSynthesizer>

 

Powershape

The powershape implements a simple case of waveshaping in an easy use package. Powershape simple raises the amplitude of each sample to the power of some value (referred to in the opcode description as 'shape amount'). A special feature of this opcode is that it retains the positive/negative polarity of the original audio signal (normally raising values to the power of something would result in only positive values). Assuming that our amplitude values range between -1 and 1 and that our exponent (shape amount) is greater than 1, amplitude values of -1, 0 or 1 will remain unchanged whereas a value of 0.5 will distorted by being pushed closer to 1 and a value of -0.5 will be pushed closer to -1. As the exponent value is increased this distortion will increase. A simple example is distorting a sine wave with powershape in which the sine wave will increasingly resemble a square wave (but never entirely) as the exponent value (shape amount) is increased. The following example plays a long randomly glissandoing sine tone which is distorted by a varying amount by powershape. It will be heard that the tone morphs between a sine tone and something resembling a square wave.

EXAMPLE 04E02_powershape.csd

<CsoundSynthesizer>
<CsOptions>
-odac -dm0
</CsOptions>
<CsInstruments>
sr     = 44100
ksmps  = 1
0dbfs  = 1
nchnls = 1

 instr  1
koct   rspline    5,8,0.1,1      ; random pitch
aSig   poscil     1,cpsoct(koct) ; create a sine wave
kshape rspline    1,500,0.3,3    ; random shape amount
aSig   powershape aSig,kshape    ; 'powershape' distort the sine wave
       out        aSig
 endin

</CsInstruments>

<CsScore>
i 1 0 120
e
</CsScore>
</CsoundSynthesizer>

Chebychev Polynomials as Transfer Functions

  Coming in a future release of this manual...

 

 



 

 


  1. Use the statement 0dbfs=1 in the orchestra header to ensure this.^
  2. See chapter 03D:FUNCTION TABLES to find more information about creating tables.^
  3. This is the 'd' in some abbreviations like Csound5.17-gnu-win32-d.exe (d = double precision floats).^
  4. Of course you can use an even smaller table if your goal is the degradation of the incoming sound ("distortion"). See chapter 05F for some examples.^
  5. A table size of a power-of-two plus one inserts the "extended guard point" as an extension of the last table value, instead of copying the first index to this location. See http://www.csounds.com/manual/html/f.html for more information.^