EIN KLEINER FILTER COMPILER

Introduction
Basic Syntax
Random number generators
Variable Delay Lines
Running a script and Plotting Sounds
Amplitude plot
Fft plot
Sonogram
Output Files
Other C files
Errors
Other
Installation Notes
Example ein scripts
Credits

Introduction

version 3.0

ein is a simple interface that makes it easy to implement digital filters process sounds and tinker with signal processing algorithms. . The results are converted to sound, and analyzed using the discrete Fourier transform. The interface was developed by Paul Lansky, using the filter compiler designed by Ken Steiglitz (see his ``Ein Kleiner Filter Compiler,'' Tech. Report CS-TR-279-90, Dept. of Computer Science, Princeton Univ., Aug. 1990). The syntax of ein scripts is basically a description of digital filter flow diagrams. It is essentially a scratchpad for dsp and is not meant to be a production tool.

This version is a port of the NeXT version (see EIN: A Digital Signal Processing Scratchpad, Paul Lansky, Kenneth Steiglitz, Computer Music Journal, Vol. 19/3, pp.18-25) and is essentially the same except that it is prettier and much faster.

Caveat

Ein is neither useful or reliable for reading or writing soundfiles greater than 20 seconds or so. It is meant to be a scratchfile for working with digital signal processing algorithms, not a production or editing tool. (I will shortly be posting an Ein-Cmix Wrapper, which will produce a Cmix instrument from Ein code.)

BASIC SYNTAX

The user describes a filter in the text window, or an existing ein script can be opened from the file menu. There are 8 text windows which can be used. The code you write in the window is in C for everything except the statements defining delay lines (tap statements). The code is essentially a function which will be called once per sample. There are several predefined variables:

extern float y,left,right,sr,two_PI;
extern int t,nsamps;

t represents the current sample number. The default sampling rate is normally 22050 and the number of channels, 1. Both can be reset by radio buttons on the lower panel.

y represents the current signal value, type float. If an input soundfile is being used the intial value of y is the current sample from the input file.

left and right are the values sent to the left and right channels if a stereo output (set by radio button) is selected. If the output is mono y is sent to the only output channel. If the input file and output files are stereo, left and right are initially the values of the left and right input channels, and are output as the left and right output channels, after modification. If the output file is mono, only the left channel of the input file is read and is initially set to y.

nsamps represents the number of sample "frames" to be computed (a sample "frame" consists of one sample for each output channel--so this number is independent of mono or stereo output selection).

S1, S2, S3, etc. represent values in delay lines as defined by tap statements. (Caution: Any variable beginning with ``S'' will be treated as such a signal, and using such symbols for other things, like SR for sampling rate, will bomb. Ultimately this hack will be fixed by using a non-C symbol.)

sr represents the sampling rate. This is set by a radio button to either 22050 or 44100, or is set by the sampling rate of an input file.

The file <math.h> is automatically included with each ein compilation so that its definitions are globally available as well (such as M_PI, etc.).

two_PI is predefined just as a convenience.

The syntax of a tap statement is

tap tapnum taplen

where tapnum is the reference number of the delay line, and taplen is the length of the delay line in samples.

As an example, ``tap 1 200'' does three things:

1) it establishes a 200 sample delay line called S1;

2) it stores the current value of y in that delay line; and

3) it increments the pointer in the delay line, mod 200.

Any reference to S1 retrieves the value in the delay line at the current pointer location. Therefore when the tap statement precedes a reference to it, it is a feedforward delay line; and when it follows, it is a feedback line.

The following is a simple digital filter (actually, a ``comb'' filter) with loop delay 200:

y = y + .99*S1;
tap 1 200

The following is a simple moving-average digital filter

tap 1 1
y = .5*y + .5*S1;

Note that semicolons need not follow the tap statements. All other lines are treated like C code, and except for the translation of S1, S2, etc., are transparent to ein.

The input signal to the filter can be from an input file, or user-defined. The following defines a unit input pulse for the comb filter:

if (t == 0) y=1; else y=0;
y=y + .99*S1;
tap 1 200

In order to use an external soundfile as an input signal it is only necessary to open the file with the "Open Input soundfile" menu item in the File menu or the name of the soundfile can be entered in the bottom textfield in the window, to the right of the "play input" button, followed by a carriage return. Input skip and end can be specified in the forms below the button. In this case it is not necessary to specify any value for y in the text window at all. The compiler will automatically take the initial values of y from the specified input file. Thus,

y = y + .99*S1;
tap 1 200

will filter the selected input file through the comb filter.

There is a built-in function get_frame(float, float *) which will fetch a frame of samples (all channels for a given sample number) from arbitrary locations in the input file. Thus

float in[2];
get_frame((float)nsamps-t/1.111, in);
y = in[0]+in[1];

will sum the channels of a stereo input file and play the file in reverse, slowing it down by a ratio of 1.1111. get_frame() interpolates linearly between samples, so the resulting signal will have artifacts. It does not use any sort of sampling rate conversion.

Random number generators

There are two random number generators. rrand(), generates integer random numbers in the range -32768 to 32768, and frand() generates floating point random numbers in the range 0 to 1. srrand(int) will seed both random number generators.

Variable Delay Lines

In the compiled filter itself, the length of a given delay line Sn is stored as an integer variable Ln. Therefore, if you want to change the length of a delayline dynamically it is only necessary to alter the value Ln. It is necessary, however to allocate enough storage for the tap. Ln should not exceed the size of tap Sn. (And you are at the mercy of a filter with a variable length.)

Running a script and Plotting Sounds

After you have written a script, you click on the compile button. At this point wheels will grind for a moment or two, and then you should hear a sound. Following this an amplitude, fft and sonogram will be drawn. Try the following:

type

y = rrand();
y = y + .99 * S1;
tap 1 100

Then hit the compile button and see what happens.

Amplitude Plot

This is a min/max plot of the amplitude envelope of the sound. Initially the whole sound will be drawn. The zoom in button will zoom into the current selection you make with the mouse (left button drag). Zoom out will zoom back to the entire soundfile. As you drag the mouse notice that the FFT will change. At a certain point (depending on the size of the file) zoom will go to the sample level.

FFT plot

Initially this is an fft of the signal at time 0. Subsequently it changes to an fft of the signal at the cursor position in the amplitude plot. If you drag a selection using the middle mouse button in will zoom in. A grid and various different fft sizes can be toggled from the menu. If you drag the left mousebutton, the diff window will tell you the frequency difference between the two points, which is one way to approximate the frequency of a harmonic series.

Sonogram

The Sonogram plots time/frequency/amplitude, with the third dimension being represented by darkness. You can set the frequency scale of the plot in the lower and upper frequency windows. The redraw button will redraw it, plotting the selection in the amplitude window, and with the specified lower and upper limits. If you drag the middle mouse button from one frequency to another, these frequencies will be set in the lower and upper frequency windows for you. Pressing the right mouse button anywhere in the window will reset the defaults.

The sample incr window controls the number of samples advanced for each fft. The size of the fft is set from the fft menu in the fft window

On the main window there is a plot input/plot output radio button. This will force the plotting of either input or output if either is not the case at the moment. A compile will force a plot of the output.

The fft window has several menus which will affect the sonogram. You can reset the fft size, toggle to linear or decibel plots, draw grids, invert the colors, and scale the sonogram locally or globally. If it is scaled locally, this means that each fft will be plotted according to its peak amplitude. If it is scaled globally this means that the entire spectrogram will be scaled to its maximum amplitude. The former option gives much better resolution, but the latter will reflect overall differences in spectral amplitudes.

The input and output plots can be redrawn simply by clicking the plot input or plot output buttons.

Output Files

Normally ein will select a unique name for the soundfiles it creates and write to the /tmp directory. If you type any other name in the output file text field, it will write to that file instead.

Other C files

If you want to link your code with any other .c, .o or library files, enter them in this textfield. Watch the console log or the terminal window you launched ein from (recommended practice) for errors.

Errors

Ein does is compilations invisibly, which is a bother if you have complicated syntax. Furthermore the reported errors will sometimes not be obvious since the precompiler has its own tongue. If you run into mysterious errors the most convenient thing to do is open the Console window or the terminal window you launched ein from. You can also check the generated c files in /tmp. They will be called something like yournamenexty.c.

Other

If you have any extensive initializations to do, such as creating coefficients it is recommended that you do this on the first sample, e.g.

if (t==0) {

do some initializations

}

Installation Notes

Read the installation README in the distribution. The Resource file, Ein is required.

Example ein scripts

You can copy, paste and try any of the following ein scripts.

1) simple comb filter

y=rrand(); /* generate random signal*/
y = y + .99 * S1; /* comb */
tap 1 200 /* 200 sample delay line*/

----------------------------------------------------

2) Plucked string filter

if(t < 100) y=rrand(); else y=0; /*generate random numbers for first 100 samples */
y = y + .99 * S1; /* comb */
tap 2 1 /*1 sample feed forward delay */
y = .5*y + .5 * S2; /* lowpass */
tap 1 100 /* feed back delay line */

---------------------------------------------------

3) Comb with internal allpass filter

#define D -.5
#define LEN 60
if( t < LEN) y = rrand(); else y=0;
y = y + .99 * S1; /*comb */
tap 2 1
y = S2 + D * (y - S3); /*allpass */
tap 3 1
tap 1 100

----------------------------------------------------

4) simple FM instrument

#define Phase two_PI*t/sr
float modulator,envelope,line;
line = (float)t/nsamps; /*line from 0 to 1 over dur*/
envelope = exp(-3. * (float)t/sr); /*exponential decay */
modulator=20*line*sin(100.*Phase); /*modulator*/
y= envelope * sin(modulator + 200. * Phase); /*carrier */

----------------------------------------------------

5) simple two pole resonator

float cf,bw,a2,a1;
if(!t) { /*initialize filter*/
cf=1000; /* set center freq */
bw=10; /*set bandwidth */
a2=exp(-2.* M_PI * bw/sr); /* set coefficients */
a1= 4. * a2/(a2+1) * cos(2 * M_PI * cf/sr);
}
y=rrand(); /*white noise*/
y=y+a1*S1-a2*S2; /*filter*/
tap 1 1
tap 2 2

---------------------------------------------------

6) signal moving in stereo field

float cf,bw,a0,a1,a2,a3,pos,boost;
if(!t) { /*setup two pole filter on white noise*/
cf=200; /*with one zero, as per Julius smith recommendation. */
bw=4;
a0=exp(-M_PI * bw/sr);
a1=1.-a0;
a2= 2 * a0 * cos(2 * M_PI * cf/sr);
a3 = -a0*a0;
}
y=rrand();
tap 1 2
y=a1 * (y-a0*S1) + a2*S2 + a3*S3;
tap 2 1
tap 3 2

/*set radio button (below) to stereo*/

pos = (float)t/nsamps;/*current position */
boost = 1./sqrt(pos*pos + (1.-pos)*(1.-pos)); /*compensation*/
left = y * pos * boost; /*left speaker */
right = y * (1.-pos) * boost; /*right speaker*/

------------------------------------------------------

7) Comb filter with changing length

y=rrand(); /*generate white noise*/
y=y+.99*S1; /*comb filter*/
L1 = 90. +((float)t/sr) * (-20);/*change length of delay */
tap 1 2000 /*initialize very long delay*/

---------------------------------------------------------

8) Doppler shift

/* make sure to toggle stereo button */

#define c 769.3 /* speed of sound in mph */
#define Dfeet 20.0 /* distance from railroad tracks in feet */
#define s 50.0 /* speed of train in mph*/
float D, D2, duration, length, mid, d2, theta, x;
float p, boost;
float cf, bw, a2, a1;
if (!t)
{ D = Dfeet/5280.0; /* distance from railroad tracks in miles */
D2 = D*D;
duration = ((float)nsamps/sr)/3600.0; /* in hours */
length = s*duration;
mid = length/2.0;}
x = ((float)t/sr)*s/3600.0; /* current distance along track */
d2 = D2 + (x - mid)*(x - mid); /* distance-squared to train */
if (x < mid) theta = atan(D/(mid-x));
else if (mid==x) theta = M_PI/2;
else theta = atan(D/(mid-x))+M_PI; /* atan between -pi/2, pi/2 */
cf =500*(c/(c-s*cos(theta))); /* reson with doppler shift */
bw = 4;
a2 = exp(-2.*M_PI*bw/sr);
a1 = 4.*(a2/(a2+1))*cos(2*M_PI*cf/sr);
y = rrand();
y = y + a1*S1 - a2*S2;
tap 1 1
tap 2 2
left = y * cos(theta/2) /d2;
right = y * sin(theta/2 )/d2;

Credits

The interface was designed by Paul Lansky, Music Department Princeton University

paul@silvertone.princeton.edu

The ein compiler was written by Kenneth Steiglitz, Department of Computer Science, Princeton University

ken@cs.princeton.edu

thanks to Bertolt Sobolik for help with openGL and the soundview.