|
|
|
|
 |
2 Wave shaping things
References : Posted by Frederic Petrot
Notes : Makes nice saturations effects that can be easilly computed using cordic
First using a atan function:
y1 using k=16
max is the max value you can reach (32767 would be a good guess)
Harmonics scale down linealy and not that fast
Second using the hyperbolic tangent function:
y2 using k=2
Harmonics scale down linealy very fast
Code : y1 = (max>>1) * atan(k * x/max)
y2 = max * th(x/max)
14 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Band Limited PWM Generator
Type : PWM generator References : Posted by paul_sernine75 AT hotmail DOT fr
Notes : This is a commented and deobfuscated version of my 1st April fish. It is based on a tutorial code by Thierry Rochebois. I just translated and added comments.
Regards,
Paul Sernine.
Code : // SelfPMpwm.cpp
// Antialised PWM oscillator
// Based on a tutorial code by Thierry Rochebois (98).
// Itself inspired by US patent 4249447 by Norio Tomisawa (81).
// Comments added/translated by P.Sernine (06).
// This program generates a 44100Hz-raw-PCM-mono-wavefile.
// It is based on Tomisawa's self-phase-modulated sinewave generators.
// Rochebois uses a common phase accumulator to feed two half-Tomisawa-
// oscillators. Each half-Tomisawa-oscillator generates a bandlimited
// sawtooth (band limitation depending on the feedback coeff B).
// These half oscillators are phase offseted according to the desired
// pulse width. They are finally combined to obtain the PW signal.
// Note: the anti-"hunting" filter is a critical feature of a good
// implementation of Tomisawa's method.
#include <math.h>
#include <stdio.h>
const float pi=3.14159265359f;
int main()
{
float freq,dphi; //!< frequency (Hz) and phase increment(rad/sample)
float dphif=0; //!< filtered (anti click) phase increment
float phi=-pi; //!< phase
float Y0=0,Y1=0; //!< feedback memories
float PW=pi; //!< pulse width ]0,2pi[
float B=2.3f; //!< feedback coef
FILE *f=fopen("SelfPMpwm.pcm","wb");
// séquence ('a'=mi=E)
// you can edit this if you prefer another melody.
static char seq[]="aiakahiafahadfaiakahiahafahadf"; //!< sequence
int note=sizeof(seq)-2; //!< note number in the sequence
int octave=0; //!< octave number
float env,envf=0; //!< envelopped and filtered envelopped
for(int ns=0;ns<8*(sizeof(seq)-1)*44100/6;ns++)
{
//waveform control --------------------------------------------------
//Frequency
//freq=27.5f*powf(2.0f,8*ns/(8*30*44100.0f/6)); //sweep
freq=27.5f*powf(2.0f,octave+(seq[note]-'a'-5)/12.0f);
//freq*=(1.0f+0.01f*sinf(ns*0.0015f)); //vibrato
dphi=freq*(pi/22050.0f); // phase increment
dphif+=0.1f*(dphi-dphif);
//notes and enveloppe trigger
if((ns%(44100/6))==0)
{
note++;
if(note>=(sizeof(seq)-1))// sequence loop
{
note=0;
octave++;
}
env=1; //env set
//PW=pi*(0.4+0.5f*(rand()%1000)/1000.0f); //random PW
}
env*=0.9998f; // exp enveloppe
envf+=0.1f*(env-envf); // de-clicked enveloppe
B=1.0f; // feedback coefficient
//try this for a nice bass sound:
//B*=envf*envf; // feedback controlled by enveloppe
B*=2.3f*(1-0.0001f*freq); // feedback limitation
if(B<0)
B=0;
//waveform generation -----------------------------------------------
//Common phase
phi+=dphif; // phase increment
if(phi>=pi)
phi-=2*pi; // phase wrapping
// "phase" half Tomisawa generator 0
// B*Y0 -> self phase modulation
float out0=cosf(phi+B*Y0); // half-output 0
Y0=0.5f*(out0+Y0); // anti "hunting" filter
// "phase+PW" half Tomisawa generator 1
// B*Y1 -> self phase modulation
// PW -> phase offset
float out1=cosf(phi+B*Y1+PW); // half-output 1
Y1=0.5f*(out1+Y1); // anti "hunting" filter
// combination, enveloppe and output
short s=short(15000.0f*(out0-out1)*envf);
fwrite(&s,2,1,f); // file output
}
fclose(f);
return 0;
}
7 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Bit quantization/reduction effect
Type : Bit-level noise-generating effect References : Posted by Jon Watte
Notes : This function, run on each sample, will emulate half the effect of running your signal through a Speak-N-Spell or similar low-bit-depth circuitry.
The other half would come from downsampling with no aliasing control, i e replicating every N-th sample N times in the output signal.
Code : short keep_bits_from_16( short input, int keepBits ) {
return (input & (-1 << (16-keepBits)));
}
3 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Class for waveguide/delay effects
Type : IIR filter References : Posted by arguru[AT]smartelectronix.com
Notes : Flexible-time, non-sample quantized delay , can be used for stuff like waveguide synthesis or time-based (chorus/flanger) fx.
MAX_WG_DELAY is a constant determining MAX buffer size (in samples)
Code : class cwaveguide
{
public:
cwaveguide(){clear();}
virtual ~cwaveguide(){};
void clear()
{
counter=0;
for(int s=0;s<MAX_WG_DELAY;s++)
buffer[s]=0;
}
inline float feed(float const in,float const feedback,double const delay)
{
// calculate delay offset
double back=(double)counter-delay;
// clip lookback buffer-bound
if(back<0.0)
back=MAX_WG_DELAY+back;
// compute interpolation left-floor
int const index0=floor_int(back);
// compute interpolation right-floor
int index_1=index0-1;
int index1=index0+1;
int index2=index0+2;
// clip interp. buffer-bound
if(index_1<0)index_1=MAX_WG_DELAY-1;
if(index1>=MAX_WG_DELAY)index1=0;
if(index2>=MAX_WG_DELAY)index2=0;
// get neighbourgh samples
float const y_1= buffer [index_1];
float const y0 = buffer [index0];
float const y1 = buffer [index1];
float const y2 = buffer [index2];
// compute interpolation x
float const x=(float)back-(float)index0;
// calculate
float const c0 = y0;
float const c1 = 0.5f*(y1-y_1);
float const c2 = y_1 - 2.5f*y0 + 2.0f*y1 - 0.5f*y2;
float const c3 = 0.5f*(y2-y_1) + 1.5f*(y0-y1);
float const output=((c3*x+c2)*x+c1)*x+c0;
// add to delay buffer
buffer[counter]=in+output*feedback;
// increment delay counter
counter++;
// clip delay counter
if(counter>=MAX_WG_DELAY)
counter=0;
// return output
return output;
}
float buffer[MAX_WG_DELAY];
int counter;
};
no comments on this item | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Compressor
Type : Hardknee compressor with RMS look-ahead envelope calculation and adjustable attack/decay References : Posted by flashinc[AT]mail[DOT]ru
Notes : RMS is a true way to estimate _musical_ signal energy,
our ears behaves in a same way.
to making all it work,
try this values (as is, routine accepts percents and milliseconds) for first time:
threshold = 50%
slope = 50%
RMS window width = 1 ms
lookahead = 3 ms
attack time = 0.1 ms
release time = 300 ms
This code can be significantly improved in speed by
changing RMS calculation loop to 'running summ'
(keeping the summ in 'window' -
adding next newest sample and subtracting oldest on each step)
Code : void compress
(
float* wav_in, // signal
int n, // N samples
double threshold, // threshold (percents)
double slope, // slope angle (percents)
int sr, // sample rate (smp/sec)
double tla, // lookahead (ms)
double twnd, // window time (ms)
double tatt, // attack time (ms)
double trel // release time (ms)
)
{
typedef float stereodata[2];
stereodata* wav = (stereodata*) wav_in; // our stereo signal
threshold *= 0.01; // threshold to unity (0...1)
slope *= 0.01; // slope to unity
tla *= 1e-3; // lookahead time to seconds
twnd *= 1e-3; // window time to seconds
tatt *= 1e-3; // attack time to seconds
trel *= 1e-3; // release time to seconds
// attack and release "per sample decay"
double att = (tatt == 0.0) ? (0.0) : exp (-1.0 / (sr * tatt));
double rel = (trel == 0.0) ? (0.0) : exp (-1.0 / (sr * trel));
// envelope
double env = 0.0;
// sample offset to lookahead wnd start
int lhsmp = (int) (sr * tla);
// samples count in lookahead window
int nrms = (int) (sr * twnd);
// for each sample...
for (int i = 0; i < n; ++i)
{
// now compute RMS
double summ = 0;
// for each sample in window
for (int j = 0; j < nrms; ++j)
{
int lki = i + j + lhsmp;
double smp;
// if we in bounds of signal?
// if so, convert to mono
if (lki < n)
smp = 0.5 * wav[lki][0] + 0.5 * wav[lki][1];
else
smp = 0.0; // if we out of bounds we just get zero in smp
summ += smp * smp; // square em..
}
double rms = sqrt (summ / nrms); // root-mean-square
// dynamic selection: attack or release?
double theta = rms > env ? att : rel;
// smoothing with capacitor, envelope extraction...
// here be aware of pIV denormal numbers glitch
env = (1.0 - theta) * rms + theta * env;
// the very easy hard knee 1:N compressor
double gain = 1.0;
if (env > threshold)
gain = gain - (env - threshold) * slope;
// result - two hard kneed compressed channels...
float leftchannel = wav[i][0] * gain;
float rightchannel = wav[i][1] * gain;
}
}
6 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Decimator
Type : Bit-reducer and sample&hold unit References : Posted by tobyear[AT]web[DOT]de
Notes : This is a simple bit and sample rate reduction code, maybe some of you can use it. The parameters are bits (1..32) and rate (0..1, 1 is the original samplerate).
Call the function like this:
y=decimate(x);
A VST plugin implementing this algorithm (with full Delphi source code included) can be downloaded from here:
http://tobybear.phreque.com/decimator.zip
Comments/suggestions/improvements are welcome, send them to: tobybear@web.de
Code : // bits: 1..32
// rate: 0..1 (1 is original samplerate)
********** Pascal source **********
var m:longint;
y,cnt,rate:single;
// call this at least once before calling
// decimate() the first time
procedure setparams(bits:integer;shrate:single);
begin
m:=1 shl (bits-1);
cnt:=1;
rate:=shrate;
end;
function decimate(i:single):single;
begin
cnt:=cnt+rate;
if (cnt>1) then
begin
cnt:=cnt-1;
y:=round(i*m)/m;
end;
result:=y;
end;
********** C source **********
int bits=16;
float rate=0.5;
long int m=1<<(bits-1);
float y=0,cnt=0;
float decimate(float i)
{
cnt+=rate;
if (cnt>=1)
{
cnt-=1;
y=(long int)(i*m)/(float)m;
}
return y;
}
9 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Delay time calculation for reverberation
References : Posted by Andy Mucho
Notes : This is from some notes I had scribbled down from a while back on
automatically calculating diffuse delays. Given an intial delay line gain
and time, calculate the times and feedback gain for numlines delay lines..
Code : int numlines = 8;
float t1 = 50.0; // d0 time
float g1 = 0.75; // d0 gain
float rev = -3*t1 / log10 (g1);
for (int n = 0; n < numlines; ++n)
{
float dt = t1 / pow (2, (float (n) / numlines));
float g = pow (10, -((3*dt) / rev));
printf ("d%d t=%.3f g=%.3f\n", n, dt, g);
}
The above with t1=50.0 and g1=0.75 yields:
d0 t=50.000 g=0.750
d1 t=45.850 g=0.768
d2 t=42.045 g=0.785
d3 t=38.555 g=0.801
d4 t=35.355 g=0.816
d5 t=32.421 g=0.830
d6 t=29.730 g=0.843
d7 t=27.263 g=0.855
To go more diffuse, chuck in dual feedback paths with a one cycle delay
effectively creating a phase-shifter in the feedback path, then things get
more exciting.. Though what the optimum phase shifts would be I couldn't
tell you right now..
1 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
DIRAC - Free C/C++ Library for Time and Pitch Manipulation of Audio Based on Time-Frequency Transforms
Type : Time Stretch / Pitch Shift References : Posted by Stephan M. Bernsee
Notes : This is an availability notification for a free object library, no source code.
Code : Past research has shown time domain [pitch] synchronized overlap-add ([P]SOLA) algorithms for independent time and pitch manipulation of audio ("time stretching" and "pitch shifting") to be the method of choice for single-pitched sounds such as voice and musically monophonic instrument recordings due to the prominent periodicity at the fundamental period. On the other hand, frequency domain methods have recently evolved around the concept of the phase vocoder that have proven to be vastly superior for multi-pitched sounds and entire musical pieces.
"Dirac" is a free cross-platform C/C++ object library that exploits the good localization of time-frequency transforms in both domains to build an algorithm for time and pitch manipulation that uses an arbitrary time-frequency tiling depending on the underlying signal. Additionally, the time and frequency localization parameter of the basis can be user-defined, making the algorithm smoothly scalable to provide either the phase coherence properties of a time domain process or the good frequency resolution of the phase vocoder.
The basic "Dirac" library comes as a free download off the DSPdimension web site and is currently available for Microsoft Visual C6+, CodeWarrior 8.x on Windows and MacOS, and for Xcode 2.x on MacOS X. Optional "Studio" and "Pro" versions with increased feature set are available commercially from the author.
More information and download at http://www.dspdimension.com
8 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
dynamic convolution
Type : a naive implementation in C++ References : Posted by Risto Holopainen
Notes : This class illustrates the use of dynamic convolution with a set of IR:s consisting of exponentially damped sinusoids with glissando. There's lots of things to improve for efficiency.
Code : #include <cmath>
class dynaconv
{
public:
// sr=sample rate, cf=resonance frequency,
// dp=frq sweep or nonlinearity amount
dynaconv(const int sr, float cf, float dp);
double operator()(double);
private:
// steps: number of amplitude regions, L: length of impulse response
enum {steps=258, dv=steps-2, L=200};
double x[L];
double h[steps][L];
int S[L];
double conv(double *x, int d);
};
dynaconv::dynaconv(const int sr, float cfr, float dp)
{
for(int i=0; i<L; i++)
x[i] = S[i] = 0;
double sc = 6.0/L;
double frq = twopi*cfr/sr;
// IR's initialised here.
// h[0] holds the IR for samples with lowest amplitude.
for(int k=0; k<steps; k++)
{
double sum = 0;
double theta=0;
double w;
for(int i=0; i<L; i++)
{
// IR of exp. decaying sinusoid with glissando
h[k][i] = sin(theta)*exp(-sc*i);
w = (double)i/L;
theta += frq*(1 + dp*w*(k - 0.4*steps)/steps);
sum += fabs(h[k][i]);
}
double norm = 1.0/sum;
for(int i=0; i<L; i++)
h[k][i] *= norm;
}
}
double dynaconv::operator()(double in)
{
double A = fabs(in);
double a, b, w, y;
int sel = int(dv*A);
for(int j=L-1; j>0; j--)
{
x[j] = x[j-1];
S[j] = S[j-1];
}
x[0] = in;
S[0] = sel;
if(sel == 0)
y = conv(x, 0);
else if(sel > 0)
{
a = conv(x, 0);
b = conv(x, 1);
w = dv*A - sel;
y = w*a + (1-w)*b;
}
return y;
}
double dynaconv::conv(double *x, int d)
{
double y=0;
for(int i=0; i<L; i++)
y += x[i] * h[ S[i]+d ][i];
return y;
}
2 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
fold back distortion
Type : distortion References : Posted by hellfire[AT]upb[DOT]de
Notes : a simple fold-back distortion filter.
if the signal exceeds the given threshold-level, it mirrors at the positive/negative threshold-border as long as the singal lies in the legal range (-threshold..+threshold).
there is no range limit, so inputs doesn't need to be in -1..+1 scale.
threshold should be >0
depending on use (low thresholds) it makes sense to rescale the input to full amplitude
performs approximately the following code
(just without the loop)
while (in>threshold || in<-threshold)
{
// mirror at positive threshold
if (in>threshold) in= threshold - (in-threshold);
// mirror at negative threshold
if (in<-threshold) in= -threshold + (-threshold-in);
}
Code : float foldback(float in, float threshold)
{
if (in>threshold || in<-threshold)
{
in= fabs(fabs(fmod(in - threshold, threshold*4)) - threshold*2) - threshold;
}
return in;
}
1 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Guitar feedback
References : Posted by Sean Costello
Notes : It is fairly simple to simulate guitar feedback with a simple Karplus-Strong algorithm (this was described in a CMJ article in the early 90's):
Code : Run the output of the Karplus-Strong delay lines into a nonlinear shaping function for distortion (i.e. 6 parallel delay lines for 6 strings, going into 1 nonlinear shaping function that simulates an overdriven amplifier, fuzzbox, etc.);
Run part of the output into a delay line, to simulate the distance from the amplifier to the "strings";
The delay line feeds back into the Karplus-Strong delay lines. By controlling the amount of the output fed into the delay line, and the length of the delay line, you can control the intensity and pitch of the feedback note.
13 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Lo-Fi Crusher
Type : Quantizer / Decimator with smooth control References : Posted by David Lowenfels
Notes : Yet another bitcrusher algorithm. But this one has smooth parameter control.
Normfreq goes from 0 to 1.0; (freq/samplerate)
Input is assumed to be between 0 and 1.
Output gain is greater than unity when bits < 1.0;
Code : function output = crusher( input, normfreq, bits );
step = 1/2^(bits);
phasor = 0;
last = 0;
for i = 1:length(input)
phasor = phasor + normfreq;
if (phasor >= 1.0)
phasor = phasor - 1.0;
last = step * floor( input(i)/step + 0.5 ); %quantize
end
output(i) = last; %sample and hold
end
end
3 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Lookahead Limiter
Type : Limiter References : Posted by Christian at savioursofsoul dot de
Notes : I've been thinking about this for a long time and this is the best I came up with so far. I might be all wrong, but according to some simulations this looks quite nice (as I want it to be).
The below algorithm is written in prosa. It's up to you to transfer it into code.
Code : Ingredients:
------------
1 circular buffers (size of the look ahead time)
2 circular buffers (half the size of the look ahead time)
4 parameters ('Lookahead Time [s]', 'Input Gain [dB]', 'Output Gain [dB]' and 'Release Time [s])
a handfull state variables
Recipe:
-------
0. Make sure all buffers are properly initialized and do not contain any dirt (pure zeros are what we need).
For each sample do the following procedure:
1. Store current sample in the lookahead time circular buffer, for later use (and retrieve the value that falls out as the preliminary 'Output')
2. Find maximum within this circular buffer. This can also be implemented efficiently with an hold algorithm.
3. Gain this maximum by the 'Input Gain [dB]' parameter
4. Calculate necessary gain reduction factor (=1, if no gain reduction takes place and <1 for any signal above 0 dBFS)
5. Eventually subtract this value from 1 for a better numerical stability. (MUST BE UNDONE LATER!)
6. Add this gain reduction value to the first of the smaller circular buffers to calculate the short time sum (add this value to a sum and subtract the value that felt out of the circular buffer).
7. normalize the sum by dividing it by the length of the circular buffer (-> / ('Lookahead Time' [samples] / 2))
8. repeat step 6 & 7 with this sum in the second circular buffer. The reason for these steps is to transform dirac impulses to a triangle (dirac -> rect -> triangle)
9. apply the release time (release time -> release slew rate 'factor' -> multiply by that factor) to the 'Maximum Gain Reduction' state variable
10. check whether the currently calculated gain reduction is higher than the 'Maximum Gain Reduction'. If so, replace!
11. eventually remove (1 - x) from step 5 here
12. calculate effective gain reduction by the above value gained by input and output gain.
13. Apply this gain reduction to the preliminary 'Output' from step 1
Repeat the above procedure (step 1-13) for all samples!
2 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Most simple and smooth feedback delay
Type : Feedback delay References : Posted by antiprosynthesis[AT]hotmail[DOT]com
Notes : fDlyTime = delay time parameter (0-1)
i = input index
j = delay index
Code : if( i >= SampleRate )
i = 0;
j = i - (fDlyTime * SampleRate);
if( j < 0 )
j += SampleRate;
Output = DlyBuffer[ i++ ] = Input + (DlyBuffer[ j ] * fFeedback);
4 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Most simple static delay
Type : Static delay References : Posted by antiprosynthesis[AT]hotmail[DOT]com
Notes : This is the most simple static delay (just delays the input sound an amount of samples). Very useful for newbies also probably very easy to change in a feedback delay (for comb filters for example).
Note: fDlyTime is the delay time parameter (0 to 1)
i = input index
j = output index
Code : if( i >= SampleRate )
i = 0;
DlyBuffer[ i ] = Input;
j = i - (fDlyTime * SampleRate);
i++;
if( j < 0 )
j = SampleRate + j;
Output = DlyBuffer[ j ];
9 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Parallel combs delay calculation
References : Posted by Juhana Sadeharju ( kouhia[AT]nic[DOT]funet[DOT]fi )
Notes : This formula can be found from a patent related to parallel combs structure. The formula places the first echoes coming out of parallel combs to uniformly distributed sequence. If T_ ,...,T_n are the delay lines in increasing order, the formula can be derived by setting T_(k-1)/T_k = Constant and T_n/(2*T_1) = Constant, where 2*T_1 is the echo coming just after the echo T_n.
I figured this out myself as it is not told in the patent. The formula is not the best which one can come up. I use a search method to find echo sequences which are uniform enough for long enough time. The formula is uniform for a short time only.
The formula doesn't work good for series allpass and FDN structures, for which a similar formula can be derived with the same idea. The search method works for these structures as well.
no comments on this item | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Polynominal Waveshaper
Type : (discrete harmonics) References : Posted by Christian[AT]savioursofsoul[DOT]de
Notes : The following code will describe how to excite discrete harmonics and only these harmonics. A simple polynominal waveshaper for processing the data is included as well. However the code don't claim to be optimized. Using a horner scheme with precalculated coefficients should be your choice here.
Also remember to oversample the data (optimal in the order of the harmonics) to have them alias free.
Code : We assume the input is a sinewave (works for any input signal, but this makes everything more clear). Then we have x = sin(a)
the first harmonic is plain simple (using trigonometric identities):
cos(2*a)= cos^2(a) - sin^2(a) = 1 - 2 sin^2(a)
using the general trigonometric identities:
sin(x + y) = sin(x)*cos(y) + sin(y)*cos(x)
cos(x + y) = cos(x)*cos(y) - sin(y)*sin(x)
together with some math, you can easily calculate: sin(3x), cos(4x), sin(5x), and so on...
Here's how the resulting waveshaper may look like:
// o = output, i = input
o = fPhase[1]* i * fGains[0]+
fPhase[1]*( 2*i*i - 1 ) * fGains[1]+
fPhase[2]*( 4*i*i*i - 3*i ) * fGains[2]+
fPhase[3]*( 8*i*i*i*i - 8*i*i + 1 ) * fGains[3]-
fPhase[4]*( 16*i*i*i*i*i - 20*i*i*i + 5 * i ) * fGains[4]+
fPhase[5]*( 32*i*i*i*i*i*i - 48*i*i*i*i + 18 * i*i - 1 ) * fGains[5]-
fPhase[6]*( 64*i*i*i*i*i*i*i - 112*i*i*i*i*i + 56 * i*i*i - 7 * i ) * fGains[6]+
fPhase[7]*(128*i*i*i*i*i*i*i*i - 256*i*i*i*i*i*i + 160 * i*i*i*i - 32 * i*i + 1 ) * fGains[7];
fPhase[..] is the sign array and fGains[..] is the gain factor array.
P.S.: I don't want to see a single comment about the fact that the code above is unoptimized. I know that!
8 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Reverberation Algorithms in Matlab
References : Posted by Gautham J. Mysore (gauthamjm [AT] yahoo [DOT] com) Linked file : MATLABReverb.zip
Notes : These M-files implement a few reverberation algorithms (based on Schroeder's and Moorer's algorithms). Each of the M-files include a short description.
There are 5 M-files that implement reverberation. They are:
- schroeder1.m
- schroeder2.m
- schroeder3.m
- moorer.m
- stereoverb.m
The remaining 8 M-files implement filters, delay lines etc. Most of these are used in the above M-files. They can also be used as building blocks for other reverberation algorithms.
2 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Reverberation techniques
References : Posted by Sean Costello
Notes : * Parallel comb filters, followed by series allpass filters. This was the original design by Schroeder, and was extended by Moorer. Has a VERY metallic sound for sharp transients.
* Several allpass filters in serie (also proposed by Schroeder). Also suffers from metallic sound.
* 2nd-order comb and allpass filters (described by Moorer). Not supposed to give much of an advantage over first order sections.
* Nested allpass filters, where an allpass filter will replace the delay line in another allpass filter. Pioneered by Gardner. Haven't heard the results.
* Strange allpass amp delay line based structure in Jon Dattorro article (JAES). Four allpass filters are used as an input to a cool "figure-8" feedback loop, where four allpass reverberators are used in series with
a few delay lines. Outputs derived from various taps in structure. Supposedly based on a Lexicon reverb design. Modulating delay lines are used in some of the allpass structures to "spread out" the eigentones.
* Feedback Delay Networks. Pioneered by Puckette/Stautner, with Jot conducting extensive recent research. Sound VERY good, based on initial experiments. Modulating delay lines and feedback matrixes used to spread out eigentones.
* Waveguide-based reverbs, where the reverb structure is based upon the junction of many waveguides. Julius Smith developed these. Recently, these have been shown to be essentially equivalent to the feedback delay network reverbs. Also sound very nice. Modulating delay lines and scattering values used to spread out eigentones.
* Convolution-based reverbs, where the sound to be reverbed is convolved with the impulse response of a room, or with exponentially-decaying white noise. Supposedly the best sound, but very computationally expensive, and not very flexible.
* FIR-based reverbs. Essentially the same as convolution. Probably not used, but shorter FIR filters are probably used in combination with many of the above techniques, to provide early reflections.
4 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Simple Compressor class (C++)
Type : stereo, feed-forward, peak compressor References : Posted by Citizen Chunk Linked file : http://www.chunkware.com/opensource/SimpleComp.zip
Notes : Everyone seems to want to make their own compressor plugin these days, but very few know where to start. After replying to so many questions on the KVR Dev Forum, I figured I might as well just post some ready-to-use C++ source code.
This is a C++ implementation of a simple, stereo, peak compressor. It uses a feed-forward topology, detecting the sidechain level pre-gain reduction. The sidechain detects the rectified peak level, with stereo linking to preserve imaging. The attack/release uses the EnvelopeDetector class (posted in the Analysis section).
Notes:
- Make sure to call initRuntime() before processing starts (i.e. call it in resume()).
- The process function takes a stereo input.
- VST params must be mapped to a practical range when setting compressor parameters. (i.e. don't try setAttack( 0.f ).)
(see linked files)
11 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Soft saturation
Type : waveshaper References : Posted by Bram de Jong
Notes : This only works for positive values of x. a should be in the range 0..1
Code : x < a:
f(x) = x
x > a:
f(x) = a + (x-a)/(1+((x-a)/(1-a))^2)
x > 1:
f(x) = (a+1)/2
4 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Stereo Enhancer
References : Posted by kurmisk[at]inbox[DOT]lv
Notes :
Stereo Enhanca
Code :
// WideCoeff 0.0 .... 1.5
#define StereoEnhanca(SamplL,SamplR,MonoSign, \
DeltaLeft,WideCoeff ) \
MonoSign = (SamplL + SamplR)/2.0; \
DeltaLeft = SamplL - MonoSign; \
DeltaLeft = DeltaLeft * WideCoeff; \
SamplL=SamplL + DeltaLeft; \
SamplR=SamplR - DeltaLeft;
15 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Stereo Field Rotation Via Transformation Matrix
Type : Stereo Field Rotation References : Posted by Michael Gruhn
Notes : This work is hereby placed in the public domain for all purposes, including use in commercial applications.
'angle' is the angle by which you want to rotate your stereo field.
Code : // Calculate transformation matrix's coefficients
cos_coef = cos(angle);
sin_coef = sin(angle);
// Do this per sample
out_left = in_left * cos_coef - in_right * sin_coef;
out_right = in_left * sin_coef + in_right * cos_coef;
17 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Stereo Width Control (Obtained Via Transfromation Matrix)
Type : Stereo Widener References : Posted by Michael Gruhn
Notes : (I was quite surprised that this wasn't already in the archive, so here it is.)
This work is hereby placed in the public domain for all purposes, including use in commercial applications.
'width' is the stretch factor of the stereo field:
width < 1: decrease in stereo width
width = 1: no change
width > 1: increase in stereo width
width = 0: mono
Code : // calculate scale coefficient
coef_S = width*0.5;
// then do this per sample
m = (in_left + in_right)*0.5;
s = (in_right - in_left )*coef_S;
out_left = m - s;
out_right = m + s;
10 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
transistor differential amplifier simulation
Type : Waveshaper References : Posted by Christian[at]savioursofsoul[dot]de
Notes : Writting an exam about electronic components, i learned several equations about simulating that stuff. One simplified equation was the tanh(x) formula for the differential amplifier. It is not exact, but since the amplifiers are driven with only small amplitudes the behaviour is most often even advanced linear.
The fact, that the amp is differential, means, that the 2n order is eliminated, so the sound is also similar to a tube.
For a very fast use, this code is in pure assembly language (not optimized with SSE-Code yet) and performs in VST-Plugins very fast.
The code was written in delphi and if you want to translate the assembly code, you should know, the the parameters passing is done via registers. So pinp=EAX pout=EDX sf=ECX.
Code : procedure Transistor(pinp,pout : PSingle; sf:Integer; Faktor: Single);
asm
fld Faktor
@Start:
fld [eax].single
fmul st(0),st(1)
fldl2e
fmul
fld st(0)
frndint
fsub st(1),st
fxch st(1)
f2xm1
fld1
fadd
fscale { result := z * 2**i }
fstp st(1)
fld st(0)
fmulp
fld st(0)
fld1
faddp
fld1
fsubp st(2),st(0)
fdivp
fstp [edx].single
add eax,4
add edx,4
loop @Start
fstp st(0)
end;
6 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
UniVox Univibe Emulator
Type : 4 Cascaded all-pass filters and optocoupler approximation References : Posted by ryjobil *@* gmail *dot* com
Notes : This is a class and class member functions for a 'Vibe derived by means of bilinear transform of the all-pass filter stages in a UniVibe. Some unique things happen as this filter is modulated, so this has been somewhat involved computation of filter coefficients, and is based on summation of 1rst-order filter stages as algebraically decoupled during circuit analysis. A second part is an approximated model of the Vactrol used to modulate the filters, including its time response to hopefully recapture the modulation shape. It is likely there is a more efficient way to re-create the LFO shape, and perhaps would be best with a lookup table. Keeping the calculation in the code makes it possible for other people to modify and improve the algorithm.
Notice no wet/dry mix is implemented in this code block's "out" function. Originally this was implemented in the calling routine, but if you use it as a stand-alone function you may want to add summation to the input signal as it is an important part of the "chorus" mode on the Vibe. The code as is represents only the Vibrato (warble) mode.
This is a module found in the Rakarrack guitar effects program. It is GPL, so please give credit due and keep it free. You can find any of the omitted parts to see more precisely how it is implemented with JACK on Linux by looking at the original sources at sourceforge.net/projects/rakarrack.
Code : /*
Copyright (C) 2008-2010 Ryan Billing
Author: Ryan Billing
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (version 2) for more details.
You should have received a copy of the GNU General Public License
(version2) along with this program; if not, write to the Free Software
Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
class Vibe
{
public:
Vibe (float * efxoutl_, float * efxoutr_);
~Vibe ();
//note some of these functions not pasted below to improve clarity
//and to save space
void out (float * smpsl, float * smpsr);
void setvolume(int value);
void setpanning(int value);
void setpreset (int npreset);
void changepar (int npar, int value);
int getpar (int npar);
void cleanup ();
float outvolume;
float *efxoutl;
float *efxoutr;
private:
int Pwidth;
int Pfb;
int Plrcross;
int Pdepth;
int Ppanning;
int Pvolume;
//all the ints above are the parameters to modify with a proper function.
float fwidth;
float fdepth;
float rpanning, lpanning;
float flrcross, fcross;
float fb;
EffectLFO lfo; //EffectLFO is an object that calculates the next sample from the LFO each time it's called
float Ra, Rb, b, dTC, dRCl, dRCr, lampTC, ilampTC, minTC, alphal, alphar, stepl, stepr, oldstepl, oldstepr;
float fbr, fbl;
float dalphal, dalphar;
float lstep,rstep;
float cperiod;
float gl, oldgl;
float gr, oldgr;
class fparams {
public:
float x1;
float y1;
//filter coefficients
float n0;
float n1;
float d0;
float d1;
} vc[8], vcvo[8], ecvc[8], vevo[8], bootstrap[8];
float vibefilter(float data, fparams *ftype, int stage);
void init_vibes();
void modulate(float ldrl, float ldrr);
float bjt_shape(float data);
float R1;
float Rv;
float C2;
float C1[8];
float beta; //transistor forward gain.
float gain, k;
float oldcvolt[8] ;
float en1[8], en0[8], ed1[8], ed0[8];
float cn1[8], cn0[8], cd1[8], cd0[8];
float ecn1[8], ecn0[8], ecd1[8], ecd0[8];
float on1[8], on0[8], od1[8], od0[8];
class FPreset *Fpre;
};
Vibe::Vibe (float * efxoutl_, float * efxoutr_)
{
efxoutl = efxoutl_;
efxoutr = efxoutr_;
//Swing was measured on operating device of: 10K to 250k.
//400K is reported to sound better for the "low end" (high resistance)
//Because of time response, Rb needs to be driven further.
//End resistance will max out to around 10k for most LFO freqs.
//pushing low end a little lower for kicks and giggles
Ra = 500000.0f; //Cds cell dark resistance.
Ra = logf(Ra); //this is done for clarity
Rb = 600.0f; //Cds cell full illumination
b = exp(Ra/logf(Rb)) - CNST_E;
dTC = 0.085f;
dRCl = dTC;
dRCr = dTC; //Right & left channel dynamic time contsants
minTC = logf(0.005f/dTC);
//cSAMPLE_RATE is 1/SAMPLE_RATE
alphal = 1.0f - cSAMPLE_RATE/(dRCl + cSAMPLE_RATE);
alphar = alphal;
dalphal = dalphar = alphal;
lampTC = cSAMPLE_RATE/(0.02 + cSAMPLE_RATE); //guessing 10ms
ilampTC = 1.0f - lampTC;
lstep = 0.0f;
rstep = 0.0f;
Pdepth = 127;
Ppanning = 64;
lpanning = 1.0f;
rpanning = 1.0f;
fdepth = 1.0f;
oldgl = 0.0f;
oldgr = 0.0f;
gl = 0.0f;
gr = 0.0f;
for(int jj = 0; jj<8; jj++) oldcvolt[jj] = 0.0f;
cperiod = 1.0f/fPERIOD;
init_vibes();
cleanup();
}
Vibe::~Vibe ()
{
}
void
Vibe::cleanup ()
{
//Yeah, clean up some stuff
};
void
Vibe::out (float *smpsl, float *smpsr)
{
int i,j;
float lfol, lfor, xl, xr, fxl, fxr;
float vbe,vin;
float cvolt, ocvolt, evolt, input;
float emitterfb = 0.0f;
float outl, outr;
input = cvolt = ocvolt = evolt = 0.0f;
lfo.effectlfoout (&lfol, &lfor);
lfol = fdepth + lfol*fwidth;
lfor = fdepth + lfor*fwidth;
if (lfol > 1.0f)
lfol = 1.0f;
else if (lfol < 0.0f)
lfol = 0.0f;
if (lfor > 1.0f)
lfor = 1.0f;
else if (lfor < 0.0f)
lfor = 0.0f;
lfor = 2.0f - 2.0f/(lfor + 1.0f); //
lfol = 2.0f - 2.0f/(lfol + 1.0f); //emulate lamp turn on/off characteristic by typical curves
for (i = 0; i < PERIOD; i++)
{
//Left Lamp
gl = lfol*lampTC + oldgl*ilampTC;
oldgl = gl;
//Right Lamp
gr = lfor*lampTC + oldgr*ilampTC;
oldgr = gr;
//Left Cds
stepl = gl*alphal + dalphal*oldstepl;
oldstepl = stepl;
dRCl = dTC*expf(stepl*minTC);
alphal = cSAMPLE_RATE/(dRCl + cSAMPLE_RATE);
dalphal = 1.0f - cSAMPLE_RATE/(0.5f*dRCl + cSAMPLE_RATE); //different attack & release character
xl = CNST_E + stepl*b;
fxl = expf(Ra/logf(xl));
//Right Cds
stepr = gr*alphar + dalphar*oldstepr;
oldstepr = stepr;
dRCr = dTC*expf(stepr*minTC);
alphar = cSAMPLE_RATE/(dRCr + cSAMPLE_RATE);
dalphar = 1.0f - cSAMPLE_RATE/(0.5f*dRCr + cSAMPLE_RATE); //different attack & release character
xr = CNST_E + stepr*b;
fxr = expf(Ra/logf(xr));
if(i%16 == 0) modulate(fxl, fxr);
//Left Channel
input = bjt_shape(fbl + smpsl[i]);
emitterfb = 25.0f/fxl;
for(j=0;j<4;j++) //4 stages phasing
{
cvolt = vibefilter(input,ecvc,j) + vibefilter(input + emitterfb*oldcvolt[j],vc,j);
ocvolt = vibefilter(cvolt,vcvo,j);
oldcvolt[j] = ocvolt;
evolt = vibefilter(input, vevo,j);
input = bjt_shape(ocvolt + evolt);
}
fbl = fb*ocvolt;
outl = lpanning*input;
//Right channel
input = bjt_shape(fbr + smpsr[i]);
emitterfb = 25.0f/fxr;
for(j=4;j<8;j++) //4 stages phasing
{
cvolt = vibefilter(input,ecvc,j) + vibefilter(input + emitterfb*oldcvolt[j],vc,j);
ocvolt = vibefilter(cvolt,vcvo,j);
oldcvolt[j] = ocvolt;
evolt = vibefilter(input, vevo,j);
input = bjt_shape(ocvolt + evolt);
}
fbr = fb*ocvolt;
outr = rpanning*input;
efxoutl[i] = outl*fcross + outr*flrcross;
efxoutr[i] = outr*fcross + outl*flrcross;
};
};
float
Vibe::vibefilter(float data, fparams *ftype, int stage)
{
float y0 = 0.0f;
y0 = data*ftype[stage].n0 + ftype[stage].x1*ftype[stage].n1 - ftype[stage].y1*ftype[stage].d1;
ftype[stage].y1 = y0 + DENORMAL_GUARD;
ftype[stage].x1 = data;
return y0;
};
float
Vibe::bjt_shape(float data)
{
float vbe, vout;
float vin = 7.5f*(1.0f + data);
if(vin<0.0f) vin = 0.0f;
if(vin>15.0f) vin = 15.0f;
vbe = 0.8f - 0.8f/(vin + 1.0f); //really rough, simplistic bjt turn-on emulator
vout = vin - vbe;
vout = vout*0.1333333333f -0.90588f; //some magic numbers to return gain to unity & zero the DC
return vout;
}
void
Vibe::init_vibes()
{
k = 2.0f*fSAMPLE_RATE;
float tmpgain = 1.0f;
R1 = 4700.0f;
Rv = 4700.0f;
C2 = 1e-6f;
beta = 150.0f; //transistor forward gain.
gain = -beta/(beta + 1.0f);
//Univibe cap values 0.015uF, 0.22uF, 470pF, and 0.0047uF
C1[0] = 0.015e-6f;
C1[1] = 0.22e-6f;
C1[2] = 470e-12f;
C1[3] = 0.0047e-6f;
C1[4] = 0.015e-6f;
C1[5] = 0.22e-6f;
C1[6] = 470e-12f;
C1[7] = 0.0047e-6f;
for(int i =0; i<8; i++)
{
//Vo/Ve driven from emitter
en1[i] = k*R1*C1[i];
en0[i] = 1.0f;
ed1[i] = k*(R1 + Rv)*C1[i];
ed0[i] = 1.0f + C1[i]/C2;
// Vc~=Ve/(Ic*Re*alpha^2) collector voltage from current input.
//Output here represents voltage at the collector
cn1[i] = k*gain*Rv*C1[i];
cn0[i] = gain*(1.0f + C1[i]/C2);
cd1[i] = k*(R1 + Rv)*C1[i];
cd0[i] = 1.0f + C1[i]/C2;
//Contribution from emitter load through passive filter network
ecn1[i] = k*gain*R1*(R1 + Rv)*C1[i]*C2/(Rv*(C2 + C1[i]));
ecn0[i] = 0.0f;
ecd1[i] = k*(R1 + Rv)*C1[i]*C2/(C2 + C1[i]);
ecd0[i] = 1.0f;
// %Represents Vo/Vc. Output over collector voltage
on1[i] = k*Rv*C2;
on0[i] = 1.0f;
od1[i] = k*Rv*C2;
od0[i] = 1.0f + C2/C1[i];
//%Bilinear xform stuff
tmpgain = 1.0f/(cd1[i] + cd0[i]);
vc[i].n1 = tmpgain*(cn0[i] - cn1[i]);
vc[i].n0 = tmpgain*(cn1[i] + cn0[i]);
vc[i].d1 = tmpgain*(cd0[i] - cd1[i]);
vc[i].d0 = 1.0f;
tmpgain = 1.0f/(ecd1[i] + ecd0[i]);
ecvc[i].n1 = tmpgain*(ecn0[i] - ecn1[i]);
ecvc[i].n0 = tmpgain*(ecn1[i] + ecn0[i]);
ecvc[i].d1 = tmpgain*(ecd0[i] - ecd1[i]);
ecvc[i].d0 = 1.0f;
tmpgain = 1.0f/(od1[i] + od0[i]);
vcvo[i].n1 = tmpgain*(on0[i] - on1[i]);
vcvo[i].n0 = tmpgain*(on1[i] + on0[i]);
vcvo[i].d1 = tmpgain*(od0[i] - od1[i]);
vcvo[i].d0 = 1.0f;
tmpgain = 1.0f/(ed1[i] + ed0[i]);
vevo[i].n1 = tmpgain*(en0[i] - en1[i]);
vevo[i].n0 = tmpgain*(en1[i] + en0[i]);
vevo[i].d1 = tmpgain*(ed0[i] - ed1[i]);
vevo[i].d0 = 1.0f;
// bootstrap[i].n1
// bootstrap[i].n0
// bootstrap[i].d1
}
};
void
Vibe::modulate(float ldrl, float ldrr)
{
float tmpgain;
float R1pRv;
float C2pC1;
Rv = 4700.0f + ldrl;
R1pRv = R1 + Rv;
for(int i =0; i<8; i++)
{
if(i==4) {
Rv = 4700.0f + ldrr;
R1pRv = R1 + Rv;
}
C2pC1 = C2 + C1[i];
//Vo/Ve driven from emitter
ed1[i] = k*(R1pRv)*C1[i];
//ed1[i] = R1pRv*kC1[i];
// Vc~=Ve/(Ic*Re*alpha^2) collector voltage from current input.
//Output here represents voltage at the collector
cn1[i] = k*gain*Rv*C1[i];
//cn1[i] = kgainCl[i]*Rv;
//cd1[i] = (R1pRv)*C1[i];
cd1[i]=ed1[i];
//Contribution from emitter load through passive filter network
ecn1[i] = k*gain*R1*cd1[i]*C2/(Rv*(C2pC1));
//ecn1[i] = iC2pC1[i]*kgainR1C2*cd1[i]/Rv;
ecd1[i] = k*cd1[i]*C2/(C2pC1);
//ecd1[i] = iC2pC1[i]*k*cd1[i]*C2/(C2pC1);
// %Represents Vo/Vc. Output over collector voltage
on1[i] = k*Rv*C2;
od1[i] = on1[i];
//%Bilinear xform stuff
tmpgain = 1.0f/(cd1[i] + cd0[i]);
vc[i].n1 = tmpgain*(cn0[i] - cn1[i]);
vc[i].n0 = tmpgain*(cn1[i] + cn0[i]);
vc[i].d1 = tmpgain*(cd0[i] - cd1[i]);
tmpgain = 1.0f/(ecd1[i] + ecd0[i]);
ecvc[i].n1 = tmpgain*(ecn0[i] - ecn1[i]);
ecvc[i].n0 = tmpgain*(ecn1[i] + ecn0[i]);
ecvc[i].d1 = tmpgain*(ecd0[i] - ecd1[i]);
ecvc[i].d0 = 1.0f;
tmpgain = 1.0f/(od1[i] + od0[i]);
vcvo[i].n1 = tmpgain*(on0[i] - on1[i]);
vcvo[i].n0 = tmpgain*(on1[i] + on0[i]);
vcvo[i].d1 = tmpgain*(od0[i] - od1[i]);
tmpgain = 1.0f/(ed1[i] + ed0[i]);
vevo[i].n1 = tmpgain*(en0[i] - en1[i]);
vevo[i].n0 = tmpgain*(en1[i] + en0[i]);
vevo[i].d1 = tmpgain*(ed0[i] - ed1[i]);
}
};
5 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Variable-hardness clipping function
References : Posted by Laurent de Soras Linked file : laurent.gif
Notes : k >= 1 is the "clipping hardness". 1 gives a smooth clipping, and a high value gives hardclipping.
Don't set k too high, because the formula use the pow() function, which use exp() and would overflow easily. 100 seems to be a reasonable value for "hardclipping"
Code : f (x) = sign (x) * pow (atan (pow (abs (x), k)), (1 / k));
9 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
WaveShaper
Type : waveshaper References : Posted by Bram de Jong
Notes : where x (in [-1..1] will be distorted and a is a distortion parameter that goes from 1 to infinity
The equation is valid for positive and negativ values.
If a is 1, it results in a slight distortion and with bigger a's the signal get's more funky.
A good thing about the shaper is that feeding it with bigger-than-one
values, doesn't create strange fx. The maximum this function will reach is
1.2 for a=1.
Code : f(x,a) = x*(abs(x) + a)/(x^2 + (a-1)*abs(x) + 1)
1 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Waveshaper
Type : waveshaper References : Posted by Jon Watte
Notes : A favourite of mine is using a sin() function instead.
This will have the "unfortunate" side effect of removing
odd harmonics if you take it to the extreme: a triangle
wave gets mapped to a pure sine wave.
This will work with a going from .1 or so to a= 5 and bigger!
The mathematical limits for a = 0 actually turns it into a linear
function at that point, but unfortunately FPUs aren't that good
with calculus :-) Once a goes above 1, you start getting clipping
in addition to the "soft" wave shaping. It starts getting into
more of an effect and less of a mastering tool, though :-)
Seeing as this is just various forms of wave shaping, you
could do it all with a look-up table, too. In my version, that would
get rid of the somewhat-expensive sin() function.
Code : (input: a == "overdrive amount")
z = M_PI * a;
s = 1/sin(z)
b = 1/a
if (x > b)
f(x) = 1
else
f(x) = sin(z*x)*s
3 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Waveshaper
References : Posted by Partice Tarrabia and Bram de Jong
Notes : amount should be in [-1..1[ Plot it and stand back in astonishment! ;)
Code : x = input in [-1..1]
y = output
k = 2*amount/(1-amount);
f(x) = (1+k)*x/(1+k*abs(x))
3 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|
|
 |
Waveshaper (simple description)
Type : Polynomial; Distortion References : Posted by Jon Watte
Notes : > The other question; what's a 'waveshaper' algorithm. Is it simply another
> word for distortion?
A typical "waveshaper" is some function which takes an input sample value
X and transforms it to an output sample X'. A typical implementation would
be a look-up table of some number of points, and some level of interpolation
between those points (say, cubic). When people talk about a wave shaper,
this is most often what they mean. Note that a wave shaper, as opposed to a
filter, does not have any state. The mapping from X -> X' is stateless.
Some wave shapers are implemented as polynomials, or using other math
functions. Hard clipping is a wave shaper implemented using the min() and
max() functions (or the three-argument clamp() function, which is the same
thing). A very mellow and musical-sounding distortion is implemented using
a third-degree polynomial; something like X' = (3/2)X - (1/2)X^3. The nice
thing with polynomial wave shapers is that you know that the maximum they
will expand bandwidth is their order. Thus, you need to oversample 3x to
make sure that a third-degree polynomial is aliasing free. With a lookup
table based wave shaper, you don't know this (unless you treat an N-point
table as an N-point polynomial :-)
Code : float waveshape_distort( float in ) {
return 1.5f * in - 0.5f * in *in * in;
}
3 comment(s) | add a comment | nofrills version |
|
 |
|
|
|
|