From CSWiki
// In the late '60s or early 70's Paia electronics came out with a circuit kit called the
// Stereo Chord EGG (Electronic Gratification Generator). It was a top-octave generator
// along with a resistor network to mix up the I,IV and V chords into the left and right channels.
//
// This little chuck proggy is an attempt to re-create "the wisdom of the ancients" and let you
// hear what this little guy sounded like -- as best as my 35 year old, admittedly alcohol
// addled memories will permit.
class Chord
{
string myName; // a name for us to show the user during debug prints. BTW: what's with <<<>>> ??!?!
//Paia's circuit used a top-octave generator to make a square wave train that they somehow filtered.
// I can't remember how that happened so I used banded wave guides, since it sounded right.
// Paia used three note chords, so that's good enough for us.
BandedWG tonic => gain g;
BandedWG two => g;
BandedWG third => g;
// the banded wave guides will be playing all the time, so the only control is that
// we will be ramping the gain of the mixer from current vol to wanted vol.
float currvol;
float wantedvol;
g => dac;
// set some defaults for the banded wave guide generators. I don't know or care too much
// what these all mean. It is enough that someone out in internet land knows.
// "The internet is the ultimate irreferance": Jim Hinds. and you can quote me on that.
public void setBand ( BandedWG b, float loudness, int note ) {
loudness=> b.gain;
1=> b.preset;
// comp-XXX salesman: "hey Arnie! He doesn't know what std.mtof does!". Spock: "read the source, luke".
std.mtof(21+note) => b.freq;
// we love rand functions to set important parameters so that the wool is firmly over our eyes.
// As the Church of the Subgenius sez: "Praise Bob!"
std.rand2f( 0.1, 0.9 ) => b.bowRate;
std.rand2f( 0.2, 0.35 ) => b.bowPressure;
std.rand2f( 0.6, 0.8 ) => b.startBowing;
}
// adjust the gain up or down as needed without going passed the desired limit
// note that the direction of the adjustment MUST be in agreement with the desired
// direction. This method doesnt know about that. That's one reason it is private.
private void gainTo ( float v, float wanted ) {
currvol + v => float adjust;
if ( wanted > currvol && adjust > wanted ) wanted => adjust;
if ( wanted < currvol && adjust < wanted ) wanted => adjust;
adjust=>currvol;
adjust=>g.gain;
}
// set some reasonable values for this object. Normally we would do this in a constructor. (where
// are the constructors? I must have missed something...
//
public void setChord ( int tonicValue, int twoValue, int thirdValue,string n) {
n => myName;
setBand (tonic, .80, tonicValue);
setBand (two, .85, twoValue);
setBand (third, .9, thirdValue);
0 => wantedvol;
0 => currvol;
0=> g.gain;
}
//ramp the volume on this object incrementally till we hit the wanted volume
public void rampgain ( ) {
float adjust;
while ( 1 ) {
175::ms => now;
if ( currvol > wantedvol ) {
gainTo(-.2, wantedvol);
}
if ( currvol < wantedvol ) {
gainTo(.1, wantedvol);
}
// <<<myName, currvol,wantedvol>>>;
}
}
}
// a function for "syntactic cod-liver-oil" since we can't spork c.rampgain directly
fun void runChord (Chord c) {
c.rampgain();
}
//set up the I, IV and V chords and start them sounding at amplitude zero
Chord I;
I.setChord ( 1,3,5,"I");
spork ~ runChord(I);
Chord IV;
IV.setChord ( 4,6,1,"IV");
spork ~ runChord(IV);
Chord V;
V.setChord ( 5,7,2,"V");
spork ~ runChord(V);
//since machines like to use numbers to access the chords, rather than names like I,IV,V
//we put the chord object (references) into an array
[I,IV,V] @=> Chord @ s[ ] ;
//changeto will bring up the volume on one of the chords and leave it like for a specified duration
fun void changeto (float amplitude,int index,dur t )
{
for ( 0 =>int i; i <3 ; i++ ) {
0 => float v;
if (i == index ) amplitude => v;
v=> s [i].wantedvol;
}
t => now ;
}
//select a chord at random, bring it up to a random level and hold for some reasonable time
while (1) {
changeto (std.rand2f(.5,.9), std.rand2(0,2), std.rand2f(1.5,4)::second );
}