**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 *

*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.)

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.

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.

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.

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.

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.

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.

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.

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.

**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.

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

}

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

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;

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.