44 views

Uploaded by RCJacH

Differentiated Polynomial Wave Tables

- Generacion de Trayectorias
- CHAP1c
- SYSC 4405 - Quiz3 Solution
- pp
- 2.3.2.3
- Revision Ktfocuss Ver
- Fourier
- VSN Data Sheet
- A Guided Tour of the Fast Fourier Transform
- 2-%20Interpolation.pdf
- DSP-Lec-3
- Interpolation
- 2018 Aug 30 - Manual ME436(4) (5)
- 0 Essentials Vectors
- Analyze the Frequency and Strength of Sound in Android
- Adash-A4400-VA4-Pro-data-sheet.pdf
- Analysis 6
- Massive Manual English Copy
- supercollider
- 1.1.3-sound.pdf

You are on page 1of 31

Rochebois

Distortions with Pre Integrated

Wave Tables

rev 0.4 14 september 2016

by T.Rochebois*

rev0.2: (3 sept 2016) added example code : Anti Aliased 3 Operator Parabola Phase Modulation

Synth

rev0.3: (12 sept 2016) added example code: Walsh synth.

Rev0.4:(14 sept 2016) added example code:Tubey distortion.

1 Notation

v() function

v(1.3) function evaluation

v[] table

v[7] access to a table (integers only)

Most short code examples are sort of jsfx (plugin language embedded in the Reaper DAW).

* I'm DR from the Universit Paris Sud Orsay (French Thesis about Multiple Wavetable Synthesis). I worked with the

Canam Computers company in the late 90s on many audio DSP algorithms including the Ongaku pitch tracker, the

EdgE anti aliasing method (very similar to "bleps") and variants of DPW. Contact:

Smashed.Transistors@gmail.com

1

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

Contents

1 Notation.............................................................................................................................................1

2 A simple Wavetable Oscillator...........................................................................................................3

2.1 Wave tables, How to read them.................................................................................................3

2.2 Digital Oscillators, Phases and Frequencies..............................................................................4

2.3 Digital Oscillators, Accessing the wavetable.............................................................................4

2.4 Digital Hell: Wavetable Aliasing...............................................................................................5

3 The popular cure: MIP MAPs............................................................................................................6

4 The old trick: Pre integration.............................................................................................................6

4.1 Memories...................................................................................................................................6

4.2 Back to the problem...................................................................................................................7

4.3 Oversampling.............................................................................................................................7

4.4 Area............................................................................................................................................7

4.5 Integral.......................................................................................................................................8

4.6 Pre integration............................................................................................................................8

4.7 A perfect world ?........................................................................................................................8

5 Cousins..............................................................................................................................................9

5.1 Differentiated Parabolic Waveforms..........................................................................................9

5.2 Differentiated Polynomial Waveforms......................................................................................9

6 Back to Pre integrated wavetables...................................................................................................10

6.1 Higher order pre integrated wavetables...................................................................................10

6.2 Anti Aliased Distortion............................................................................................................10

6.3 Anti Aliased Phase Modulation................................................................................................11

Appendix A Commented JSFX code examples..................................................................................12

Appendix A.1 An Anti Aliased Walsh Function synth...................................................................12

Appendix A.2 An Anti Aliased 3 Operator Parabola Phase Modulation synth..............................24

2

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

2.1 Wave tables, How to read them

A wave table consists in a table that contains a set of samples that can be looped. The samples

represent a cycle of the signal. "Samples" means that values "v" of the analog signal have been

picked at regular time intervals.

A wavetable is very handy, it allows to access any value of the wave at any moment. For example,

let's say that we want to know what's the value of the wavetable at position 7, we can tell it directly

by accessing the table. v[7]

To get an idea of what happens between two samples we must interpolate. The straightforward

interpolation method is linear interpolation, we just connect the dots.

We guess that the value is somewhere between the values v[7] and v[8]. Linear interpolation

consists in mixing those two values:

with dv[7] = v[8] - v[7], being the slope between sample 7 and sample 8.

3

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

To do so, the most convenient way, in the digital domain, consists in using a phase accumulator

"p" that will remember where we are in the cycle.

p will be incremented at every tick of the main sampling rate (you know, 44.1kHz or 48KHz aka

srate in jsfx).

The rate at which it will be incremented "dp" controls the frequency of the oscillator.

p += dp;

Oups, i forgot that an oscillator has to cycle, here "p" goes away. We can fix a limit to "p" and tell

him to go back.

Let's say we want it to stay in the [0 16[ range, we can code:

p += dp;

p >= 16 ? p -= 16;

p >= 16 ? p -= 16; // phase cycling

p0 = floor(p); // integer part of the phase

a = p - floor(p); // float part of the phase (for linear interpolation)

v[p0] + a * dv[p0];

4

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

Everything sounds good so far, but what happens if we read our wavetable faster ?

We miss some details and get some others from the original waveform, depending on the position of

the phase accumulator for each iteration.

And even worse, we don't miss or get the same details at each cycle of the oscillator. The oscillator's

output is no more cyclic, there is some aperiodic dirt in the high end of the spectrum. Some say it is

metallic and cold, digital cold.

5

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

Aliasing occurs when you read a wavetable with lots of details too fast.

The MIPMAP idea is simple: remove the details when you want to read fast !

It's hard to remove details on the fly, so, the solution consists in preparing a set of wavetables (the

MIPMAP). Each wavetable of the MIPMAP set contains just the right amount of details for a

certain range of frequencies (i.e. MIDI notes).

The main drawback is that it uses lots of memory, but today, memory is cheap. So...

This is the method i use for the PWT_synth. It is a very efficient method and the most popular

antialiasing method for wavetables so far.

4.1 Memories

Pre integration is a trick I used in the 90s. The computers I used for my thesis work were not very

powerful and had much less memory than an average laptop of 2016. As my work implied multiple

wavetables, I had to find a cure to my wavetable aliasing problem. Pre integration was the method I

opted for.

So let's go back to how we read wavetables.

Ideally, we should read what is in a certain region of the wavetable, the "what happened since last

time region".

4.3 Oversampling

Sure, but how can we do that ? How can we know what was in the "what happened since last time

region".

Well, we can cheat and take more samples - that's called oversampling - but it will cost some CPU

6

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

4.4 Area

How can we get the mean value of the "what happened since last time region" instead of a ponctual

value taken in this region ?

Has far as i learnt in high school, the mean value is the area under the curve devided by the width of

the region.

mean value = A/dp

A is the area.

dp is the phase increment (proportional to the frequency).

4.5 Integral

An area under a function can be exactly calculated by substracting its integrand at the limit points.

A = Iv(p) - Iv(previous p)

i.e.

A = Iv(p) - Iv(p - dp)

( Iv(p) - Iv(p - dp) ) / dp

So, we need this nice and handy Iv() function. Sorry, we have to do some maths.

The v() function - the linearly interpolated samples - is made of line segments.

7

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

p0 being the integer part of p and a being the float part.

Iv(p) = Iv[p0] + a * v[p0] + 1/2 a^2 dv[p0]

The Iv() function is made of second order polynomial segments.

These polynomial segments are defined by the values contained in tables v[], dv[] and the new table

Iv[].

The new table Iv[] simply contains offset values that ensures continuity between segments. This

looks like a detail but it is mandatory: it ensures that the polynomial segments still connects the

dots.

Pre integration allows to calculate the mean value of our wavetable on any region by substracting to

values and dividing the result by the width of the region.

It is far superior to the trivial linear interpolation scheme.

Sure, it gives the mean value of the "what happened since last time region", but that mean value is

far from a perfect anti aliasing filter.

A mean value provides what's called a "box filter". The box filter frequency response is a sinc

function (DSP Related:Running Sum Lowpass Filter). Let's say that it cannot rivalise with Fourier

band limiting used in MIPMAPs.

That's why Pre integration should not be seen as a perfect antialiasing scheme. To be efficient it can

be used with other methods such as oversampling. In this case, it can be as effective as MIPMAPing

and more importantly it can be extended beyond wavetables.

5 Cousins

Pre integration of wavetables have some cousins: the DPWs, Differenciated Parabolic Waveforms.

A DPW oscillator does not use wave tables. It generates a parabolic waveform based on its phase.

This parabola is differentiated to generate a somewhat band limited sawtooth. Other waveforms,

such as square and rectangle are generated by combining sawteeth.

8

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

In the late 90s a group of my students worked at a final year project. This final year project

consisted in coding a simple band limited oscillator without wavetables. The generator should

exclusively be based on math functions.

I suggested the idea of Parabolic Waveforms and that they may experiment with higher order

Polynomials and higher order differentiators.

The results were quite good: low aliasing and low CPU. The "box filter" implied by the first order

differentiator was replaced by something like "the box filter of a box filter of a box filter"... which

starts to look like a gaussian filter.

The main drawback of the method is that it needs high accuracy. Floats were enough for a third

order integration/differentiation scheme. Higher order schemes needed doubles.

See, for example, this old thread on the KVR DSP forum:

http://www.kvraudio.com/forum/viewtopic.php?p=1710116#1710116

Later i used this method in a VSTi synth you may find somewhere on the net "The Vagabond King".

The Vagabond King VSTi

The case of wavetables is more sensitive to accuracy than the generation of sawteeth by the DPW

method. Instead of a polynomial, you have to deal with a set of polynomial segments and ensure

that calc errors are kept low.

Nevertheless, today, performing maths with 64bit doubles is not a big deal. So I can use a second or

third order integration/differentiation scheme on wavetables without problems.

I use this method in the TiaR_Ze_Cheesy_Synth.jsfx, it is a jsfx plugin for Reaper (available from

the Reapack/ReaTeam repository).

The main advantage VS mipmaping is that the wavetable pre integration process is much simpler

than MIPMAPing.

9

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

Pre integrated tables can be used beyond wavetables. In areas not available to MIPMAPing.

A distortion is a non linear function of the input signal.

The non linearity produce lots of overharmonics (niiice) and lots of aliasing (arrrg).

Many years ago i tried to use dynamic MIPMAPing i.e. switching or crossfading between

MIPMAP levels depending on the slew rate of the input... The crossfading induced aliasing by

itself.

So let's see how we can use pre integrated tables to get an anti aliased distortion.

It's much like a wavetable, but instead of accessing the table with a phase we will access it with the

input signal. We have to go from a serial access to a random access.

I tried it, it works. But as the input signal is much much less predictable than a phase, calc accuracy

is quite delicate. Anyway, I achieved nice results using a second order scheme even with

complicated distortion. (for example: Pre integrated Dissymetric smooth "tube" saturation).

Second Order pre integration + oversampling by two may be an effective and elegant solution to the

problem of aliasing in non linearities.

Distortion is a special case of phase modulation. It is the case were the frequency of the oscillator is

zero.

So, based on my experiments with anti aliasing distortion, i went a step further.

Generaly speaking, phase modulation (or frequency modulation) implies sine oscillators. Phase

modulation of complex wavetable oscillators often generate too many harmonics and induce strong

aliasing. A second orderpre integration allows to implement such a modulation. See Anti aliased 3

operator PM synth. for an operational implementation (detailed information in the appendix).

10

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

These are fully operational JSFX plugins you can test and use under Reaper.

This JSFX synth shows how a third order differentiation scheme can be used to anti alias a wave

table oscillator.

Ze Cheesy Harmonic Synth is a small JSFX synth reminiscent of early digital additive synthesizers.

(It is available for Reaper from ReaPack). In this "Harmonic" synth, instead of adding sine waves,

the basic waveforms are Walsh sequences: sort of square waves. This JSFX synth is anti aliased

thanks to the third order integration differentiation scheme i extended to wave tables.

Here is the complete listing:

/**

* JSFX Name:ZeCheesyHarmo_01

* About:

* An additive synth based on Walsh functions

* Author: T.Rochebois

* Licence: LGPL

* REAPER: 5.0

* Version: 0.3

*/

slider1:1<-1,1,0.001>Seq. 1

slider2:0<-1,1,0.001>Seq. 2

slider3:0<-1,1,0.001>Seq. 3

slider4:0<-1,1,0.001>Seq. 4

slider5:0<-1,1,0.001>Seq. 5

slider6:0<-1,1,0.001>Seq. 6

slider7:0<-1,1,0.001>Seq. 7

slider8:0<-1,1,0.001>Seq. 8

slider9:0<-1,1,0.001>Seq. 9

slider10:0<-1,1,0.001>Seq. 10

slider11:0<-1,1,0.001>Seq. 11

slider12:0<-1,1,0.001>Seq. 12

slider13:0<-1,1,0.001>Seq. 13

slider14:0<-1,1,0.001>Seq. 14

slider15:0<-1,1,0.001>Seq. 15

slider16:0<-1,1,0.001>Seq. 16

slider20:0<-24,24,0.0001>Detune (semitones)

slider21:5.5<1,20>Vibrato rate

slider22:0.4<0,1,0.0001>Vibrato depth

slider23:1<0,1,0.0001>Glide rate

slider24:0<0,1>Tremolo depth

slider31:-3<-3,1,0.0001>Attack

slider32:0<-3,1,0.0001>Decay

slider33:1<0,1,0.0001>Sustain

slider34:-2<-3,1,0.0001>Release

11

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

slider41:0.5<0,1,0.0001>Pan

slider42:0<-24,12>Gain (dB)

// ___________________________________________________________________

@init

//_____________________________________________________________________

function ADSR_setP10(A D S R) (

// decay coefs ref time(s)

this.A = 1 - exp(log(0.33) / ((10^A) * (srate/KRATE)));

this.A = min(this.A, 1);

this.D = 1 - exp(log(0.10) / ((10^D) * (srate/KRATE)));

this.D = min(this.D, 1);

this.S = S;

this.R = 1 - exp(log(0.10) / ((10^R) * (srate/KRATE)));

this.R = min(this.R, 1);

);

//_____________________________________________________________________

// Control rate processing

function ADSR_kProc(gate trig)

local() (

gate === 0 ? this.AttackSeg = 0;

trig ? this.AttackSeg = 1;

this.ASR < gate ?

this.ASR += this.A * (gate - this.ASR)

: this.ASR += this.R * (gate + 0.00000001 - this.ASR);

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

this.AttackSeg === 1 ? (

this.ADSR += this.A * (1.5 - this.ADSR);

this.ADSR >= 1 ?

( this.ADSR = 1.0;

this.AttackSeg = 0;

);

);

this.AttackSeg === 0 ? (

gate != 0 ? (

this.ADSR += this.D * (this.S - this.ADSR);

) : (

this.ADSR += this.R * (0.00000001 - this.ADSR);

);

);

this.ASR * this.ADSR;

);

// ____________________________________________________________________

function WSH_init()

instance(sal) local(seq x x0 x1 x2 x3 x4)(

sal = ad; ad += 32*16;

x = 0; loop(32,

x0 = (x& 1) ?1:-1;

x1 = ((x& 2)>>1)?1:-1;

x2 = ((x& 4)>>2)?1:-1;

x3 = ((x& 8)>>3)?1:-1;

x4 = ((x&16)>>4)?1:-1;

sal[x + 32 * 0] = x4;

sal[x + 32 * 1] = x3 ;

sal[x + 32 * 2] = x2*x3*x4;

sal[x + 32 * 3] = x2 ;

sal[x + 32 * 4] = x1*x2* x4;

sal[x + 32 * 5] = x1*x2*x3 ;

sal[x + 32 * 6] = x1 *x3*x4;

sal[x + 32 * 7] = x1 ;

12

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

sal[x + 32 * 9] = x0*x1 *x3 ;

sal[x + 32 * 10] = x0*x1*x2*x3*x4;

sal[x + 32 * 11] = x0*x1*x2 ;

sal[x + 32 * 12] = x0 *x2 *x4;

sal[x + 32 * 13] = x0 *x2*x3 ;

sal[x + 32 * 14] = x0 *x3*x4;

sal[x + 32 * 15] = x0 ;

x += 1;

);

);

function PIWT_init(nMax) instance(n x y z w dp _dp _dp3) (

this.nMax = nMax;

n = nMax;

x = ad; ad += n;

y = ad; ad += n;

z = ad; ad += n;

w = ad; ad += n;

dp = n*440/srate;

_dp = 1 / dp;

_dp3 = _dp * _dp * _dp;

);

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

function PIWT_dcRemove(t n) local(i m)(

m = 0; i = 0; loop(n, m += t[i]; i += 1; );

m /= n; i = 0; loop(n, t[i] -= m; i += 1; );

);

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

function PIWT_norm(t n) local(i m)(

m = 0; i = 0; loop(n, m = max(t[i], abs(m)); i += 1; );

m = 1.1/(m+0.1); i = 0; loop(n, t[i] *= m; i += 1; );

);

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

function PIWT_integ()

instance(x n y z w z y x)

local(i)(

PIWT_dcRemove(x, n);

i = 0; loop(n-1, y[i+1] = y[i] + x[i]; i += 1; );

PIWT_dcRemove(y, n);

i = 0; loop(n-1, z[i+1] = z[i] + y[i] + (1/2) * x[i]; i += 1; );

PIWT_dcRemove(z, n);

i = 0; loop(n,

y[i] *= 1/2;

x[i] *= 1/6;

i < n-1 ? w[i+1] = w[i] + z[i] + y[i] + x[i];

i += 1;

);

PIWT_dcRemove(w, n);

);

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

// Call before aProc if there is a discontinuity of dp _dp

function PIWT_disc()

instance(n dp _dp _dp3 p out w z y x w0 w1 w2) local(p0 a)(

p -= 2*dp; p += n * (p < 0); p -= n * (p >= n); p0 = p|0; a = p - p0;

w0 = w[p0] + a * (z[p0] + a * (y[p0] + a * x[p0]));

w1 = w[p0] + a * (z[p0] + a * (y[p0] + a * x[p0]));

13

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

w2 = w[p0] + a * (z[p0] + a * (y[p0] + a * x[p0]));

);

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

function PIWT_aProc()

instance(n dp _dp _dp3 p out w z y x w0 w1 w2 w3) local(p0 a)(

p += dp; p -= n*(p>=n); p0 = p|0; a = p-p0;

w3 = w[p0] + a * (z[p0] + a * (y[p0] + a * x[p0]));

out = (w3 - w0 + 3 * (w1 - w2)) * _dp3;

w0 = w1; w1 = w2; w2 = w3;

out;

);

// ___________________________________________________________________

// Init the wave table

_srate = 1 / srate;

KRATE = 8; _KRATE = 1/KRATE;

piwt.PIWT_init(32);

wsh.WSH_init();

kbNotes = ad; ad += 128;

bendFactor = bendFactorF = 1;

// ___________________________________________________________________

@slider

gain = 2^((1/6)*slider42);

gLeft = gain * cos(0.5*$pi*slider41);

gRight = gain * sin(0.5*$pi*slider41);

dpLfo = 2*$pi*slider21*32*_srate;

vibMin = slider22*0.05;

vibMax = 0.025 + 2 * vibMin;

adsr.ADSR_setP10(slider31, slider32, slider33, slider34);

glideCoef = 32 * _srate * (0.1 + 100 * slider23 * slider23);

x = 0; loop(32,

piwt.x[x] = slider1 * wsh.sal[x + 32 * 0];

seq = 1;loop(15,

piwt.x[x] += slider(seq + 1) * wsh.sal[x + 32 * seq];

seq += 1;

);

x += 1;

);

PIWT_dcRemove(piwt.x,32);

PIWT_norm(piwt.x,32);

piwt.PIWT_integ();

piwt.PIWT_disc();

// ___________________________________________________________________

@block

while (midirecv(offset, msg1, msg23)) (

msg2 = msg23 & 0x7F; msg3 = msg23 >> 8; status = msg1 & $xF0;

status == $x80 ? ( status = $x90; msg3 = 0; ); // note off

status == $x90 ? ( // note on

msg3 == 0 ? (

kbNotes[msg2] = 0;

gate = 0; i=0; loop(128, kbNotes[i] !== 0 ? (gate = kBNotes[i]; note=i;);

i+=1;);

gate ? dpc = (piwt.n*440*_srate) * 2 ^ ((note + slider20 - 69) * (1/12));

) : (

gate == 0 ? trig = 1;

kbNotes[msg2] = gate = sqrt(msg3 * (1/127));

note = msg2;

dpc = (piwt.n*440*_srate) * 2 ^ ((note + slider20 - 69) * (1/12));

trig ? (dpg = dpc;);

14

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

);

)

: status == $xB0 ? (

msg2 == 1 ? modWhl = msg3 * (1/127);

msg2 == 123 ? (trig = gate = 0;i=0; loop(128, kBNotes[i] = 0; note=i;);

i+=1;);

)

: status == $xD0 ? (aftertouch = msg2 * (1/127);)

: status === 14 * 16 ? (

bend = (msg3 << 7 | msg2) - 8192;

bend <= 0 ? bend *= 1 / 8192 : bend *= 1 / 8191;

bendFactor = 2^bend;

);

co_vib = vibMin + modWhl * (vibMax - vibMin);

co_vibA = max(co_vib, vibMin + aftertouch * (vibMax - vibMin));

);

// ___________________________________________________________________

@sample

k2 <= 0 ? (

k2 = KRATE;

dEnv = (adsr.ADSR_kProc(gate, trig) - env)*_KRATE;

trig = 0;

);

k2 -= 1;

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

k <= 0 ? (

k = 32;

bendFactorF += 0.05 * (bendFactor - bendFactorF);

slider23 >=1 ? dpg = bendFactorF * dpc

: dpg += glideCoef * (bendFactorF * dpc - dpg);

pLfo += dpLfo; pLfo -= 2*$pi*(pLfo>0);

lfo = sin(pLfo);

dTrem = (1/32)*( 1 + slider24 * (0.5*lfo+0.5 - 1) - trem);

co_vibAf += 0.05 * (co_vibA - co_vibAf);

piwt.dp = dpg * (1 + co_vibAf * lfo);

piwt._dp = 1 / piwt.dp;

piwt._dp3 = piwt._dp * piwt._dp * piwt._dp;

piwt.PIWT_disc();

);

k -= 1;

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

env += dEnv;

trem += dTrem;

out = min(10,max(-10,trem*env*piwt.PIWT_aProc()));

spl0 += gLeft * out;

spl1 += gRight * out;

// ___________________________________________________________________

@gfx 0 64

function col(r g b a) (gfx_r = r; gfx_g = g; gfx_b = b; gfx_a = a;);

col(0.0,0,0.5,1);

gfx_rect(0,0,32*12+16, 64);

col(1.0,1.0,1.0,1);

15

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

gi = 0; loop(32,

gx = gi*12+8;

gi>0?

gfx_line(gx, 32-144*piwt.x[gi-1], gx, 32-144*piwt.x[gi])

:

gfx_line(gx, 32, gx, 32-144*piwt.x[gi]);

gfx_line(gx, 32-144*piwt.x[gi], gx+12, 32-144*piwt.x[gi]);

gi+=1;

);

gx = gi*12+8;

gfx_line(gx, 32-144*piwt.x[31], gx, 32);

slider1:1<-1,1,0.001>Seq. 1

slider2:0<-1,1,0.001>Seq. 2

slider3:0<-1,1,0.001>Seq. 3

slider4:0<-1,1,0.001>Seq. 4

slider5:0<-1,1,0.001>Seq. 5

slider6:0<-1,1,0.001>Seq. 6

slider7:0<-1,1,0.001>Seq. 7

slider8:0<-1,1,0.001>Seq. 8

slider9:0<-1,1,0.001>Seq. 9

slider10:0<-1,1,0.001>Seq. 10

slider11:0<-1,1,0.001>Seq. 11

slider12:0<-1,1,0.001>Seq. 12

slider13:0<-1,1,0.001>Seq. 13

slider14:0<-1,1,0.001>Seq. 14

slider15:0<-1,1,0.001>Seq. 15

slider16:0<-1,1,0.001>Seq. 16

[...]

slider41:0.5<0,1,0.0001>Pan

slider42:0<-24,12>Gain (dB)

This is the header section which defines the sliders: Digital "Harmonics" amplitudes, Detune and

Vibrato, Enveloppe and Pan controls are defined here.

// ___________________________________________________________________

@init

//_____________________________________________________________________

[...]

The @init section contains function declarations and init code such as the ADSR enveloppe code.

We won't focus on that, we'd rather look for what's specific to this synth.

16

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

// ____________________________________________________________________

function WSH_init()

instance(sal) local(seq x x0 x1 x2 x3 x4)(

sal = ad; ad += 32*16;

x = 0; loop(32,

x0 = (x& 1) ?1:-1;

x1 = ((x& 2)>>1)?1:-1;

x2 = ((x& 4)>>2)?1:-1;

x3 = ((x& 8)>>3)?1:-1;

x4 = ((x&16)>>4)?1:-1;

sal[x + 32 * 0] = x4;

sal[x + 32 * 1] = x3 ;

sal[x + 32 * 2] = x2*x3*x4;

sal[x + 32 * 3] = x2 ;

sal[x + 32 * 4] = x1*x2* x4;

sal[x + 32 * 5] = x1*x2*x3 ;

sal[x + 32 * 6] = x1 *x3*x4;

sal[x + 32 * 7] = x1 ;

sal[x + 32 * 8] = x0*x1 *x4;

sal[x + 32 * 9] = x0*x1 *x3 ;

sal[x + 32 * 10] = x0*x1*x2*x3*x4;

sal[x + 32 * 11] = x0*x1*x2 ;

sal[x + 32 * 12] = x0 *x2 *x4;

sal[x + 32 * 13] = x0 *x2*x3 ;

sal[x + 32 * 14] = x0 *x3*x4;

sal[x + 32 * 15] = x0 ;

x += 1;

);

);

That initialises the 16 walsh function tables. These tables will be added together in the Pre

Integrated Wave Table everytime a slider is moved.

function PIWT_init(nMax) instance(n x y z w dp _dp _dp3) (

this.nMax = nMax;

n = nMax;

x = ad; ad += n;

y = ad; ad += n;

z = ad; ad += n;

w = ad; ad += n;

dp = n*440/srate;

_dp = 1 / dp;

_dp3 = _dp * _dp * _dp;

);

x, y, z and w are the tables that will contain the pre integrated values. ("ad" is an address counter

that keeps track of the memory usage in the plugin).

17

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

function PIWT_dcRemove(t n) local(i m)(

m = 0; i = 0; loop(n, m += t[i]; i += 1; );

m /= n; i = 0; loop(n, t[i] -= m; i += 1; );

);

This is a utility function that removes the mean value (DC) of a table. It is an important feature

when you integrate periodic tables more than once: it guaranties that the integrals will be periodic

too.

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

function PIWT_norm(t n) local(i m)(

m = 0; i = 0; loop(n, m = max(t[i], abs(m)); i += 1; );

m = 1.1/(m+0.1); i = 0; loop(n, t[i] *= m; i += 1; );

);

This is another utility function that roughly normalize the content of a table.

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

function PIWT_integ()

instance(x n y z w z y x)

local(i)(

PIWT_dcRemove(x, n);

i = 0; loop(n-1, y[i+1] = y[i] + x[i]; i += 1; );

PIWT_dcRemove(y, n);

i = 0; loop(n-1, z[i+1] = z[i] + y[i] + (1/2) * x[i]; i += 1; );

PIWT_dcRemove(z, n);

i = 0; loop(n,

y[i] *= 1/2;

x[i] *= 1/6;

i < n-1 ? w[i+1] = w[i] + z[i] + y[i] + x[i];

i += 1;

);

PIWT_dcRemove(w, n);

);

The input table is x. It consists of 32 steps, a mixture of Walsh functions, no linear interpolation.

First of all, we remove its mean value by calling the utility function PIWT_dcRemove(x, n);

i = 0; loop(n-1, y[i+1] = y[i] + x[i]; i += 1; );

18

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

Its value in segment number p0 is:

I 1 ( p)=I 1 (a) p = y [ p0 ]+a x [ p 0 ]

0

"p0" being the integer part of p and "a" being its float part.

i = 0; loop(n-1, z[i+1] = z[i] + y[i] + (1/2) * x[i]; i += 1; );

Why does the x[i] have a (1/2) coefficient ?

Why do we accumulate z[i] to z[i+1]

In the segment number p0, we have

1 2

I 2 ( p)=I 2 (a) p =z [ p 0 ]+a y [ p 0 ]+ a x [ p0 ]

0

2

its derivate is

dI 2 ( p)

=I 1 ( p)= y [ p0 ]+a x [ p0 ]

dp

It does not depend on z[p0]. This relationship between I2 and I1 will still be true even if we did not

accumulate the previous values of z.

Accumulating z values ensures that the polynomial segments "connect the dots".

At the boundaries, we have

I 2 ( p0 + )I 2 ( p 0)

i.e.

1 1

z [ p0 ]+ y [ p 0]+ 2 x [ p0 ]z [ p01]+(1) y [ p 01]+ (1)2 x [ p01]

2 2

The limit of this equation gives us our integration code.

The last step calculates w, the third order integral. It is mostly similar to the previous step, so i won't

go into it.

19

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

Nota: I scale x and y with 1/2 and 1/6 factors. It is an optimisation that will be used in

PIWT_aProc()

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

function PIWT_aProc()

instance(n dp _dp _dp3 p out w z y x w0 w1 w2 w3) local(p0 a)(

p += dp; p -= n*(p>=n); p0 = p|0; a = p-p0;

w3 = w[p0] + a * (z[p0] + a * (y[p0] + a * x[p0]));

out = (w3 - w0 + 3 * (w1 - w2)) * _dp3;

w0 = w1; w1 = w2; w2 = w3;

out;

);

instance(n dp _dp _dp3 p out w z y x w0 w1 w2 w3)

are the state variables of the oscillator

n is the table size

dp is the phase increment (frequency)

_dp is the reciprocal of the phase increment

_dp3 is the cube of the reciprocal of the phase increment. It is used

to normalize the differentiation

p is the phase

x y z w are the polynomial tables

w0 w1 w2 w3 are delayed values of the polynomial

p += dp; p -= n*(p>=n);

This is the phase increment and modulo (we stay in [0, n[ )

p0 = p|0; a = p-p0;

Splits p into its integer and float parts

Calculates the third order integral of the point

Note:

it should be

w[p0] + a * z[p0] + a*a * (1/2)*y[p0] + a*a*a * (1/6)*x[p0];

but I already scaled y by (1/2) and x by (1/6).

Another optimisation is that I put the polynomial in its Horner form.

So that

w3 = w[p0] + a * (z[p0] + a * (y[p0] + a * x[p0]));

20

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

out = (w3 - w0 + 3 * (w1 - w2)) * _dp3;

The output is the third order differentiation normalized by the cube of the phase increment.

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

// Call before aProc if there is a discontinuity of dp _dp

function PIWT_disc()

instance(n dp _dp _dp3 p out w z y x w0 w1 w2) local(p0 a)(

p -= 2*dp; p += n * (p < 0); p -= n * (p >= n); p0 = p|0; a = p - p0;

w0 = w[p0] + a * (z[p0] + a * (y[p0] + a * x[p0]));

w1 = w[p0] + a * (z[p0] + a * (y[p0] + a * x[p0]));

w2 = w[p0] + a * (z[p0] + a * (y[p0] + a * x[p0]));

);

called every time there is a discontinuity of dp to prevent glitches.

[...]

// ___________________________________________________________________

@slider

[...]

x = 0; loop(32,

piwt.x[x] = slider1 * wsh.sal[x + 32 * 0];

seq = 1;loop(15,

piwt.x[x] += slider(seq + 1) * wsh.sal[x + 32 * seq];

seq += 1;

);

x += 1;

);

PIWT_dcRemove(piwt.x,32);

PIWT_norm(piwt.x,32);

piwt.PIWT_integ();

piwt.PIWT_disc();

The @slider section takes in charge the update of the Pre Integrated

Wave Table.

x = 0; loop(32,

piwt.x[x] = slider1 * wsh.sal[x + 32 * 0];

seq = 1;loop(15,

piwt.x[x] += slider(seq + 1) * wsh.sal[x + 32 * seq];

seq += 1;

);

21

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

x += 1;

);

PIWT_dcRemove(piwt.x,32);

PIWT_norm(piwt.x,32);

piwt.PIWT_integ();

piwt.PIWT_disc();

// ___________________________________________________________________

@block

while (midirecv(offset, msg1, msg23)) (

[...]

);

// ___________________________________________________________________

@sample

k2 <= 0 ? (

k2 = KRATE;

dEnv = (adsr.ADSR_kProc(gate, trig) - env)*_KRATE;

trig = 0;

);

k2 -= 1;

This is a "control rate" sub-section. This code is run every KRATE sample. This kind of

optimisation saves lots of CPU without compromising sound quality if you choose KRATE

correctly.

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

k <= 0 ? (

k = 32;

bendFactorF += 0.05 * (bendFactor - bendFactorF);

slider23 >=1 ? dpg = bendFactorF * dpc

: dpg += glideCoef * (bendFactorF * dpc - dpg);

pLfo += dpLfo; pLfo -= 2*$pi*(pLfo>0);

lfo = sin(pLfo);

dTrem = (1/32)*( 1 + slider24 * (0.5*lfo+0.5 - 1) - trem);

co_vibAf += 0.05 * (co_vibA - co_vibAf);

piwt.dp = dpg * (1 + co_vibAf * lfo);

piwt._dp = 1 / piwt.dp;

piwt._dp3 = piwt._dp * piwt._dp * piwt._dp;

22

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

piwt.PIWT_disc();

);

k -= 1;

This other sub-section is run every 32 samples, it contains updates of the various MIDI controls that

affect the pitch of the sound. Here piwt.dp, piwt._dp and piwt._dp3 are updated and

piwt.PIWT_disc() is called to avoid glitches.

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

env += dEnv;

trem += dTrem;

out = min(10,max(-10,trem*env*piwt.PIWT_aProc()));

spl0 += gLeft * out;

spl1 += gRight * out;

This code is run for every sample. The enveloppe and tremolo are linearly interpolated.

env += dEnv;

trem += dTrem;

out = min(10,max(-10,trem*env*piwt.PIWT_aProc()));

Modulation synth

Here is a mono synth that consist in a chain of three operators. Each operator generates a cyclic

parabola signal that modulates the phase of the operator below it.

The anti aliasing consist of a second order integration/diff extendent to phase modulation.

// Author: T.Rochebois 08/2016

slider3:sl_R2=1.0001<0,3,0.000001>Ratio 2

slider4:sl_I2=1.5<0,3,0.0001>I2

slider6:sl_R1=0.9997<0,3,0.000001>Ratio 1

slider7:sl_I1=0.5<0,3,0.0001>I1

slider9:sl_R0=1<0,3,0.000001>Ratio 0

slider11:sl_A=-3<-3,0,0.001>Attack

slider12:sl_R=0<-2,1,0.001>Release

// ___________________________________________________________________

23

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

@init

function I0(x)( x+=16;x-=x|0; (6*x-6)*x+1 );

function I1(x)( x+=16;x-=x|0; ((2*x-3)*x+1)*x );

function I2(x)( x+=16;x-=x|0; ((0.5*x-1)*x+0.5)*x*x );

function AA2_para(dp m)

instance(p x0 x1 y0 y1 I2_0 I2_1 I1_0 I1_1 out)(

p += dp; p >= 1 ? (p -= 1; x0 -= 1; y0 -= 1; );

x1 = x0; I2_1 = I2_0; x0 = p + m;

I2_0 = I2(x0);

y1 = y0; I1_1 = I1_0; y0 = 0.5 * (x0 + x1);

I1_0 = x0 == x1 ? I1(y0) : (I2_0 - I2_1)/(x0 - x1);

out = y0 == y1 ? I0(y0) : (I1_0 - I1_1)/(y0 - y1);

);

// ___________________________________________________________________

@block

while (midirecv(offset, msg1, msg23)) (

msg2 = msg23 & 0x7F; msg3 = msg23 >> 8; status = msg1 & $xF0;

status == $x80 ? ( status = $x90; msg3 = 0; ); // note off

status == $x90 ? ( // note on

msg3 == 0 ? (msg2 == note ? gate = 0;)

: ( gate = sqrt(msg3 * (1/127)); note = msg2;

dp = (440/srate)*2^((note-69)*(1/12));

);

);

midisend(offset, msg1, msg23);

);

dp0 = sl_R0 * dp; dp1 = sl_R1 * dp; dp2 = sl_R2 * dp;

A = 1/(srate*10^sl_A);

R = 1/(srate*10^sl_R);

// ___________________________________________________________________

@sample

env += env>gate ? R*(gate-env) : A*(gate-env);

y2 = osc2.AA2_para(dp2,0);

y1 = osc1.AA2_para(dp1, env*sl_I2 * y2);

y0 = osc0.AA2_para(dp0, (0.5+0.5*env)*sl_I1 * y1);

spl0 = spl1 = min(3,max(-3,env*y0));

That's clearly why I like JSFX: you can test your ideas with very few code lines.

The first section describes the sliders that will control the synth.

The second section is the @init section, it contains stuff the plugin will do at init. It's also the place

where you can declare your functions.

The @block section is usually the place where you process MIDI information.

The @sample section is called for every sample. The stereo output is spl0 and spl1.

In this example, the specific code is in the functions and in the @sample section (colored red). I will

comment those.

This is our parabola, integrated 0 times (hence the I0).

24

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

The coefficients of (6*x-6)*x+1 have been calculated so that the mean value of I0(x) over [0,1[ is

equal to zero, this is an important feature for integrating it has a periodic function.

This is our parabola, integrated once (hence the I1).

Same thing as before, the x parameter is retrained to the [0,1[ interval.

The derivative of ((2*x-3)*x+1)*x is (6*x-6)*x+1 . The integration constant have been chosen

so that the mean value of I1(x) over [0,1[ is equal to zero so that it can be integrated one more time

without a DC offset.

This is our parabola, integrated twice (hence the I2).

The second derivative of ((0.5*x-1)*x+0.5)*x*x is (6*x-6)*x+1.

This time, we do not intend to integrate it once more, we do not need its DC offset (aka mean value)

to be zero. So i go for the simplest polynomial.

The function

function AA2_para(dp m)

instance(p x0 x1 y0 y1 I2_0 I2_1 I1_0 I1_1 out)(

p += dp; p >= 1 ? (p -= 1; x0 -= 1; y0 -= 1; );

x1 = x0; I2_1 = I2_0; x0 = p + m;

I2_0 = I2(x0);

y1 = y0; I1_1 = I1_0; y0 = 0.5 * (x0 + x1);

I1_0 = x0 == x1 ? I1(y0) : (I2_0 - I2_1)/(x0 - x1);

out = y0 == y1 ? I0(y0) : (I1_0 - I1_1)/(y0 - y1);

);

This function uses the "namespace" facility of jsfx (which is sort of object oriented feature). It is

called in the @sample section for each oscillator osc0, osc1, osc2:

y2 = osc2.AA2_para(dp2,0);

y1 = osc1.AA2_para(dp1, env*sl_I2 * y2);

y0 = osc0.AA2_para(dp0, (0.5+0.5*env)*sl_I1 * y1);

dp: the phase increment

m: the phase modulation signal

The @sample code is straightforward and it will be easy for you to edit it and add/change

oscillators.

25

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

So...

function AA2_para(dp m)

instance(p x0 x1 y0 y1 I2_0 I2_1 I1_0 I1_1 out)(

p += dp; p >= 1 ? (p -= 1; x0 -= 1; y0 -= 1; );

x1 = x0; I2_1 = I2_0; x0 = p + m;

I2_0 = I2(x0);

y1 = y0; I1_1 = I1_0; y0 = 0.5 * (x0 + x1);

I1_0 = x0 == x1 ? I1(y0) : (I2_0 - I2_1)/(x0 - x1);

out = y0 == y1 ? I0(y0) : (I1_0 - I1_1)/(y0 - y1);

);

Lists all the internal state variables of an oscillator.

p is the phase accumulator, it will be incremented by dp. Its range is [0, 1[

x0 is p+m, the total input phase (i.e. including modulation).

x1 is x delayed by one sample

I2_0 is the second order integral of our parabola taken at x0

I2_1 is the second order integral of our parabola taken at x1

y0 is the mean of x0 and x1 (the value in the middle of x0 and x1)

y1 is y0 delayed by one sample

I1_0 is the first order integral of our parabola taken at y0

I1_1 is the first order integral of our parabola taken at y1

out is the output aka our parabola (aka the zero order integral of our parabola).

Note: in a jsfx function, the last statement is the returned value (here it is "out").

p += dp;

Increments the phase

p >= 1 ? (p -= 1; x0 -= 1; y0 -= 1; );

Note: there is no "if" in jsfx, the "?" operator is used instead.

This line means "if p is greater or equal to 1, decrease p, x0 and y0 by one".

This line guaranties that p won't go beyond one.

I also decrease x0 and y0 by the same amount because i need them to be consistent with p (note:

always be careful on that point if you design your own oscillators).

x1 = x0;

x1 is the previous value of x0

I2_1 = I2_0;

I2_1 is the previous value of the second order integral of our parabola.

x0 = p + m;

The new value for x0 is the phase + the modulation input

I2_0 = I2(x0);

26

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

The new value for I2_0 is the second order integral of our parabola at x0.

y1 = y0; I1_1 = I1_0; y0 = 0.5 * (x0 + x1);

Same thing as before with the first order integral of our parabola.

Is a little tricky.

Note: in jsfx == means "almost equal".

Most of the time, x0 != x1 and we have:

I1_0 = (I2_0 - I2_1)/(x0 - x1);

We take as first order integral at point y0 the mean of the integral between x0 and x1 by

differentiationg the second order integral.

The problem is when x0 is near x1, dividing by zero is a bad option, that's why we have a plan B:

directly calculate I1(y0).

Note that the functions I0 I1 and I2 are consistent (no DC offset, no scale factors, etc). This is

important in order to avoid glitches.

Is mostly the same as the previous line, if y0 is close to y1, we go to plan B and evaluate the

parabola directly. Otherwise, we go for plan A and we take its mean value in the interval y0 y1.

27

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

Tubey Sat is an antialiased Saturation JSFX plugin :

http://forum.cockos.com/showthread.php?t=180951

The Saturation function is a smooth dissymetric function defined in a table. The function is defined

by linear segments. The algorithm is much similar to the one used int Ze Cheezy Harmonic Synth

(even if its wavetable is defined by steps).

scheme.

As usual, the @init section contains function definitions and initialisations.

init2_3(x) and dec2_96_59(x0 x1) are the interpolator and decimator functions. I won't talk about

these, they are classic FIR design.

function PIT_init(n)(

this.n = n; this.n16 = n * 16;

this.dv = ad; ad += n; this.v = ad; ad += n;

this.Iv = ad; ad += n; this.IIv = ad; ad += n;

);

v will contain the values

dv will contain the deltas (for linear interpolation)

Iv will contain the first order integral coefficients

IIv will contain the second order integral coefficients.

// _____________________________________________________________________

// (performs the pre integrations)

function PIT_update()

instance(n dv v Iv IIv) local(p dcOffset)(

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

// Remove DC offset from the input table

dcOffset = v[0]; p = 1; loop(n - 1, dcOffset += v[p]; p += 1; );

dcOffset /= n; p = 0; loop(n, v[p] -= dcOffset; p += 1; );

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

// calc the slope between two values (for linear interpolation)

p = 0; loop(n, dv[p] = v[(p+1) % n] - v[p]; p += 1; );

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

// First polynomial integration Iv

dcOffset = Iv[0] = 0; 0; p = 0;

loop(n - 1,

Iv[p+1] = Iv[p] + v[p] + 0.5 * dv[p];

dcOffset += Iv[p+1]; p+=1; );

dcOffset /= n; p = 0; loop(n, Iv[p] -= dcOffset; p += 1;);

28

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

// Second polynomial integration IIv

dcOffset = IIv[0] = 0;

p = 0; loop(n - 1,

IIv[p+1] = IIv[p] + Iv[p] + 0.5 * v[p]+(1/6)*dv[p];

dcOffset += IIv[p+1]; p+=1; );

dcOffset /= n;p = 0;loop(n, IIv[p] -= dcOffset;p+=1;);

);

First, it removes its mean value to avoid bias during integration (dcOffset).

Then it calculates its delta dv[p] (for linear interpolation).

Then the first and second order integration are performed taking account of v and dv.

Note that they are not integration-by-accumulation, they are polynomial segment integration (hence

the presence of v and dv in the integration code for IIv.

function PIT_I0(p)

instance(n n16 dv v) local(p0 a)(

a = p - (p0 = p|0); // "a" is the float part, "p0" is the integer part

p0 %= n; // modulo

dv[p0] * a + v[p0];

);

returns the I0 i.e. the raw function (with linear interpolation)

function PIT_I1(p)

instance(n n16 dv v Iv) local(p0 a)(

a = p - (p0 = p|0);

p0 %= n;

( dv[p0] * 0.5*a + v[p0]) * a + Iv[p0];

);

returns the first integration value I1(p)

function PIT_I2(p)

instance(n n16 dv v Iv IIv) local(p0 a)(

a = p - (p0 = p|0);

p0 %= n;

(( dv[p0] * (1/3)*a + v[p0]) * 0.5*a + Iv[p0]) * a + IIv[p0];

);

returns the second integration value I2(p)

PIT_I1 and PIT_I0 are "plan B" functions that allows to deal with delicate situations... it is much

similar to what I have done in the PM parabola synth.

29

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

function PIO_aProc2(m)

instance(p n x1 x2 I2_1 I2_2 y0 y1 I1_0 I1_1 out)(

x1 = x2; I2_1 = I2_2; x2 = m; I2_2 = this.PIT_I2(x2);

y0 = y1; I1_0 = I1_1; y1 = 0.5 * (x2 + x1);

I1_1 = abs(x2 - x1)<0.0001 ? this.PIT_I1(y1) : (I2_2 - I2_1) / (x2 -

x1);

out = abs(y1 - y0)<0.01 ? this.PIT_I0(0.5*(y0+y1)) : (I1_1 - I1_0) / (y1 -

y0);

);

The main difference with the parabola PM synth is that the I0, I1 and I2 functions use tables instead

of an integrated parabolic function.

function PIO_update2()

instance(p x1 x2 I2_1 I2_2 y0 y1 I1_0 I1_1 out)(

I2_2 = this.PIT_I2(x2);

I1_1 = x1 == x2 ? this.PIT_I1(y1) : (I2_2 - this.PIT_I2(x1)) / (x2 - x1);

);

// _____________________________________________________________________

pit.PIT_init(256);

i=0;loop(pit.n,

x = i < pit.n / 2 ? i : i - pit.n;

x *= 0.2;

x > 0 ? pit.v[i] = x / ((1+abs(x)^2.5)^(1/2.5))

: pit.v[i] = x / ((1+abs(x)^4.0)^(1/4.0));

i+=1;

);

This is the initialisaton code. It initializes the table with the dissymetric and smooth saturation.

pit.PIT_update();

pio.PIO_link(pit);

pio.PIO_update2();

@sample

gDriveSmooth += 0.01 * (gDrive - gDriveSmooth);

x = gDriveSmooth * spl0;

x = max(-125, min(125,x));

interp.int2_3(x + pit.n16-0.03717);

spl0 = dec2_96_59(

pio.PIO_aProc2(interp.y0),

pio.PIO_aProc2(interp.y1)

);

30

AA of Oscillators and Distortions with Pre Integrated WaveTables T.Rochebois

Notes:

+ pit.n16 avoids negative indexing of the table

-0.03717 offsets the input so that a zero input implies a zero output

Both interpolator outputs interp.y0 and interp.y1 are sent to the core processing

function pio.PIO_aProc2.

The results are decimated with dec2_96_59.

The integration/differentiation scheme does not suppress aliasing but it

drastically accentuate its decay rate.

- it chops off the remaining aliasing

- it fixes the low pass filtering side effect of the box filtering.

31

- Generacion de TrayectoriasUploaded byjhonotero_19
- CHAP1cUploaded byYanna Fabricante
- SYSC 4405 - Quiz3 SolutionUploaded byAleksandra Diotte
- ppUploaded byahmad albab
- 2.3.2.3Uploaded byFirda Firda Ooaalaa
- Revision Ktfocuss VerUploaded byJohn Kent
- FourierUploaded byResmi Johnson
- VSN Data SheetUploaded byMauricio Oropeza Cabrera
- A Guided Tour of the Fast Fourier TransformUploaded byRodolfoyGaby Piña Mendoza
- 2-%20Interpolation.pdfUploaded byArushi Rawat
- DSP-Lec-3Uploaded byمشاهير celebrities
- InterpolationUploaded byWael Larbi
- 2018 Aug 30 - Manual ME436(4) (5)Uploaded byCaleb Kreeger
- 0 Essentials VectorsUploaded bygranjero_mx
- Analyze the Frequency and Strength of Sound in AndroidUploaded byvmacari
- Adash-A4400-VA4-Pro-data-sheet.pdfUploaded byidham khalik
- Analysis 6Uploaded byGantulga Nergui
- Massive Manual English CopyUploaded byTace McNamara
- supercolliderUploaded byCarlos de Anda
- 1.1.3-sound.pdfUploaded byMehfooz Ur Rehman
- English 96Uploaded byNicolás Alvarez
- Aeromagnetic Survey DesignUploaded byAnggit Pramudita Wicaksono
- Introduction to Communication System-lecture4Uploaded byJumanne Ally
- Wo-Hydrophone Heading and Range Sensor Applied to Formation-FlyingUploaded byRuchit Pathak
- Sound Quality Vs Data RateUploaded byVijay Ds
- Roland JP-8080Uploaded byMAS
- GRC tutorialUploaded byEbenezer Samson
- 217_2005WT1Uploaded byCas
- Practice Questions 2011Uploaded bySwati Verma
- new product survey responses - form responses 1Uploaded byapi-272770403

- CottleSC3Uploaded bysnailFury
- Resonance of hang drumUploaded byMartin Červenka
- AdrenaLinn ManualUploaded byFotografia Coimbra
- PX-5S FAQUploaded byValerie Ilari Martini
- Pretty Lights - A Color Map of the Sun - Digital BookletUploaded bychauhanrishabh
- Emulator X3 Ref Manual-EnUploaded byradicalproject
- An Introduction to Machine DesignUploaded byFauiqe Yousaf
- Perceiving Categorical Emotion in Sound-.pdfUploaded byXimena Lainfiesta
- MAPEH (3)Uploaded byLouise Axalan
- Circle VST pluginUploaded bySzalkai Zsolt
- REAKTOR Factory Library Manual EnglishUploaded byJosé Barrera Martínez
- Ableton Live 10 Manual - EnglishUploaded byJohn Wesley Barker
- Keyboard Magazine Nick Rhodes Cover Story September 2015 1440449972116Uploaded byluckyswiss7776848
- Midi and the AVRUploaded byJustin Tomlin
- Concert Strings 2 Manual TVEC 4Uploaded bygarthog
- MusicTech - September 2016Uploaded byNik Pep
- TX16Wx User Manual v0.9Uploaded byWinston Conway
- Toolkit ManualUploaded byAura Auro
- Keyboard.march.2014.Uploaded byTuna Sen
- Session Strings ProUploaded byPablo Villegas
- ER1_OMUploaded byChristopher Lewis
- Em 052010Uploaded bykapr1c0rn
- Noise.io Manual 03Uploaded bytammoz
- Johnny Greenwood Guitar SetupUploaded byignaciobivona
- gratingmod3.1Uploaded byDele
- IJETR031414Uploaded byerpublication
- P 140 TutorialUploaded byBruno Tadej
- Lass 2.0.1 Arc ManualUploaded byRufus Loacker
- PCM90 Presets Rev1Uploaded byNacho Varela
- Murail Tristan the Revolution of Complex SoundsUploaded byOmar Hernandez Lazo