Synth Tutorial
von HAL
Grundlegende Wellenformen:

Additative Synthese
Der Modulator moduliert die Tonhöhe des Oszillators (Vibrato):
Modulator -> Oscillator -> Verstärker -> Output
Subtraktive Synthese
Der (volle) Klang des Oszillators wird durch einen Filter geleitet
(um die Klangfülle zu reduzieren):
Oszillator -> Filter -> Verstärker -> Output
ADSR Hüllkurve

1.) Die Lautstärke wächst von 0% auf 100% in x Sekunden
("attack time").
2.) Die Lautstärke fällt von 100% auf xx% in y Sekunden
(x = "sustain level", y = "decay time").
3.) Die Laustärke verbleibt bei auf dem Sustain-Level,
bis ein "key off" erfolgt
4.) Die Lautstärke fällt auf 0% in x Sekunden
("release time").
Filter
Lowpass:
- löscht alle Frequenzen überhalb der "CutOff-Frequenz"
Highpass:
- löscht alle Frequenzen unterhalb der "CutOff-Frequenz"
BandPass:
- lässt nur Frequenzen eines bestimmten Frequenzbereichs durch
Notch:
- löscht eine bestimmte Frequenz
Einige Filter besitzen noch einen Parameter namens "Resonanz", der angibt wie stark die Frequenzen um die "CutOff-Frequenz" herum verstärkt werden.
Hier die wichtigsten Funktionen zum Berechnen der Koeffizienten für IIR-Filter:
// omega & bandwidth = 0 - pi (pi ~ output freq / 2)
//
#define LN2O2 0.34657359027997265470861606072909
/*
usage:
y[n] = a0 * x[n] + a1 * x[n-1] + a2 * x[n-2] +
b1 * y[n-1] + b2 * y[n-2]
*/
typedef struct {
double a0, a1, a2,
b1, b2;
} biquad_coeff_t;
// lowpass
void
biquad_lp(biquad_coeff_t *biquad, float32 omega, float32 bandwidth)
{
float64 sn, cs, al, a0, a1, a2, b0, b1, b2, ooa0;
sn = sin(omega);
cs = cos(omega);
al = sn * sinh(LN2O2 * bandwidth * omega / sn);
b0 = (1 - cs) * 0.5;
b1 = 1 - cs;
b2 = (1 - cs) * 0.5;
a0 = 1 + al;
a1 = -2 * cs;
a2 = 1 - al;
ooa0 = 1.0f / a0;
biquad->a0 = b0 * ooa0;
biquad->a1 = b1 * ooa0;
biquad->a2 = b2 * ooa0;
biquad->b1 = -a1 * ooa0;
biquad->b2 = -a2 * ooa0;
}
// bandpass
void
biquad_bp(biquad_coeff_t *biquad, float32 omega, float32 bandwidth)
{
float64 sn, cs, al, a0, a1, a2, b0, b1, b2, ooa0;
sn = sin(omega);
cs = cos(omega);
al = sn * sinh(LN2O2 * bandwidth * omega / sn);
b0 = al;
b1 = 0;
b2 = -al;
a0 = 1 + al;
a1 = -2 * cs;
a2 = 1 - al;
ooa0 = 1.0f / a0;
biquad->a0 = b0 * ooa0;
biquad->a1 = b1 * ooa0;
biquad->a2 = b2 * ooa0;
biquad->b1 = -a1 * ooa0;
biquad->b2 = -a2 * ooa0;
}
// highpass
void
biquad_hp(biquad_coeff_t *biquad, float32 omega, float32 bandwidth)
{
float64 sn, cs, al, a0, a1, a2, b0, b1, b2, ooa0;
sn = sin(omega);
cs = cos(omega);
al = sn * sinh(LN2O2 * bandwidth * omega / sn);
b0 = (1 + cs) * 0.5;
b1 = -(1 + cs);
b2 = (1 + cs) * 0.5;
a0 = 1 + al;
a1 = -2 * cs;
a2 = 1 - al;
ooa0 = 1.0f / a0;
biquad->a0 = b0 * ooa0;
biquad->a1 = b1 * ooa0;
biquad->a2 = b2 * ooa0;
biquad->b1 = -a1 * ooa0;
biquad->b2 = -a2 * ooa0;
}
// notch
void
biquad_no(biquad_coeff_t *biquad, float32 omega, float32 bandwidth)
{
float64 sn, cs, al, a0, a1, a2, b0, b1, b2, ooa0;
sn = sin(omega);
cs = cos(omega);
al = sn * sinh(LN2O2 * bandwidth * omega / sn);
b0 = 1;
b1 = -2 * cs;
b2 = 1;
a0 = 1 + al;
a1 = b1;
a2 = 1 - al;
ooa0 = 1.0f / a0;
biquad->a0 = b0 * ooa0;
biquad->a1 = b1 * ooa0;
biquad->a2 = b2 * ooa0;
biquad->b1 = -a1 * ooa0;
biquad->b2 = -a2 * ooa0;
}
// peaking equalizer
void
biquad_peq(biquad_coeff_t *biquad, float32 omega, float32 bandwidth, float32 dbgain)
{
float64 sn, cs, al, a, a0, a1, a2, b0, b1, b2, ooa, ooa0;
sn = sin(omega);
cs = cos(omega);
a = pow(10, dbgain * 0.025);
ooa = 1.0 / a;
al = sn * sinh(LN2O2 * bandwidth * omega / sn);
b0 = 1 + al * a;
b1 = -2 * cs;
b2 = 1 - al * a;
a0 = 1 + al * ooa;
a1 = b1;
a2 = 1 - al * ooa;
ooa0 = 1.0f / a0;
biquad->a0 = b0 * ooa0;
biquad->a1 = b1 * ooa0;
biquad->a2 = b2 * ooa0;
biquad->b1 = -a1 * ooa0;
biquad->b2 = -a2 * ooa0;
}
// low shelf
void
biquad_ls(biquad_coeff_t *biquad, float32 omega, float32 dbgain, float32 slope)
{
float64 sn, cs, be, a, a0, a1, a2, b0, b1, b2, ooa0, ap1, am1;
sn = sin(omega);
cs = cos(omega);
a = pow(10, dbgain * 0.025);
ap1 = a + 1;
am1 = a - 1;
be = sqrt((a * a + 1) / slope - am1 * am1);
b0 = a * (ap1 - am1 * cs + be * sn);
b1 = 2* a * (am1 - ap1 * cs);
b2 = a * (ap1 - am1 * cs - be * sn);
a0 = ap1 + am1 * cs + be * sn;
a1 = -2 * (am1 + ap1 * cs);
a2 = ap1 + am1 * cs - be * sn;
ooa0 = 1.0f / a0;
biquad->a0 = b0 * ooa0;
biquad->a1 = b1 * ooa0;
biquad->a2 = b2 * ooa0;
biquad->b1 = -a1 * ooa0;
biquad->b2 = -a2 * ooa0;
}
// high shelf
void
biquad_hs(biquad_coeff_t *biquad, float32 omega, float32 dbgain, float32 slope)
{
float64 sn, cs, be, a, a0, a1, a2, b0, b1, b2, ooa0, ap1, am1;
sn = sin(omega);
cs = cos(omega);
a = pow(10, dbgain * 0.025);
ap1 = a + 1;
am1 = a - 1;
be = sqrt((a * a + 1) / slope - am1 * am1);
b0 = a * (ap1 + am1 * cs + be * sn);
b1 = -2 * a * (am1 + ap1 * cs);
b2 = a * (ap1 + am1 * cs - be * sn);
a0 = ap1 - am1 * cs + be * sn;
a1 = 2 * (am1 - ap1 * cs);
a2 = ap1 - am1 * cs - be * sn;
ooa0 = 1.0f / a0;
biquad->a0 = b0 * ooa0;
biquad->a1 = b1 * ooa0;
biquad->a2 = b2 * ooa0;
biquad->b1 = -a1 * ooa0;
biquad->b2 = -a2 * ooa0;
}
Beispielaufbau eines Oszillatorblocks
Folgendes benutze ich zur Zeit (mehr oder weniger):
LFO-->ENV---\
\
LFO-->OSC-->BIQUAD--*-->DISTORT---\
|
LFO-->ENV---\ |
\ |
LFO-->OSC-->BIQUAD--*-->DISTORT---+
|
LFO-->ENV---\ |
\ |
LFO-->OSC-->BIQUAD--*-->DISTORT---+
|
LFO-->ENV---\ |
\ |
LFO-->OSC-->BIQUAD--*-->DISTORT---+->CHORUS/ECHO/REVERB
LFO = low frequency oscillator
ENV = adsr envelope
DISTORT = a distortion function
Was jetzt?
1.] Beweg' deinen Hintern zu www.harmonic-central.com
und folge allen weiteren Links.
2.] Gehe zu www.maz-sound.com
und downloade so viele Synthesizer wie möglich.
3.] Schau dir die mitgelieferten Sourcen an. (synth-demo.zip).
4.] ...
EXPERIMENTIERE!!!
EXPERIMENTIERE!!!
EXPERIMENTIERE!!!
Okay, that's all.......
Bei Fragen, Mail an bschoedlbauer@gmx.net.
(Hal)