Son of ethereal.ck

From CSWiki
Revision as of 12:16, 27 September 2007 by Art (talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
// Son_of_Ethereal
// -- originally written by Graham Percival, gperciva@uvic.ca, 
// Oct 2005
// Placed in the public domain.
// 20070401 tps -- ok; try is changing the oscillators at random. 
// Do it by making an array of pointers to oscillators  
// 20070427 tps -- fixing up deprecated references (i.e.,
// std becomes Std, sinosc => SinOsc, etc. Rationalizing formatting
// the way I likes it, etc.
// 20070503 tps -- rather than just use integer multiples of the fundamental
// for the harmonics, let's use some simple intervals... change harms
// to be a float array to start with.
// (Notes from original author at end)


// ----------------------------------------------------------------------------
// Global stuff...
//	PARAMETERS
1 => int DEBUG;		   // 1 = debug print on, 0 = off
15 => int MAXOSC;          // max of 15 oscillators at any one time
20 => int MAXHARM;         // max of 20 pitches at any one time
110.0 => float fundfreq;   // start with fundamental frequency of 110 Hz
0.2 => float NEWFREQPROB;  // probability of changing the fundamental 
//                         // frequency.  1-NEWFREQPROB = chance of 
//                         // adding a new harmonic.
1000 => int MINEVENTTIME;  // minimum time between events.	(in ms)
3000 => int MAXEVENTTIME;  // max time... (also in ms)
SinOsc oscs[MAXOSC];       // oscs don't have to be sinosc;
//TriOsc oscs[MAXOSC];     // try uncommenting one of
//SqrOsc oscs[MAXOSC];     // these lines.

// create relatively consonant intervals (2:1, 3:2, 5:3, 7:5)
// over two octaves...
[2., 3./2., 5./3., 7./5., 4., 3., 10./3., 14./5. ] @=> float intervals[];
intervals.cap() - 1 => int maxintervals;

// auto init (don't touch these things)
1.0/MAXOSC => float MAXGAIN;
float harms[MAXOSC];	// binary "is this in use?" array
Pan2 pans[MAXOSC];
Envelope envs[MAXOSC];
0 => int fundfreqblock;

// init the oscs
for (0 => int i; i < oscs.cap() ; i++ )
{
	oscs[i] => envs[i] => pans[i] => dac;
	0 => harms[i];
	0 => pans[i].pan;
}

// functions: 
// newfundfreq()  change gradually to a new fundamental frequency
// addharm()      add a new harmonic
// walkharm()     add a random pan
// --------------------------------------------------------------------
fun void newfundfreq ()
{
	// don't get a new fundamental frequency if we're already
	// changing one.
	if (fundfreqblock==1) {
		return;
	}
	else {
		1=> fundfreqblock;
	}

	// pick new fundamental fundfrequency
	float newfundfreq;
	fundfreq * Std.rand2f(0.8,1.25) => newfundfreq;
	if (newfundfreq < 110) 110 => newfundfreq;
	if (newfundfreq > 330) 330 => newfundfreq; 
	// limit how far it
	//                                               
	// can change
	if (DEBUG) <<< "start fundfreq moving to", newfundfreq >>>;
	// gliss to new fundfreq over 5 seconds...divide into 50 steps
	Std.fabs( (fundfreq-newfundfreq)/50) => float stepsize;
	for (0 => int i; i < 50; i++)
	{
		// yes, this can result in the fundreq alternating around the result 
		// -- i.e. if funfreq is close to newfundfreq, it might reach (and
		// surpass) the goal, and then move in the opposite direction.  This 
		// is not a bug!  :)
		//   (ok, it _was_ unintentional, but I kind-of like
		//    this behavior, so I didn't fix the bug)
		if (fundfreq < newfundfreq) stepsize +=> fundfreq;
		if (fundfreq > newfundfreq) stepsize -=> fundfreq;
		100::ms => now;
	}

	if (DEBUG) <<< "fundfreq now", fundfreq >>>;
	0 => fundfreqblock;
}

// --------------------------------------------------------------------
fun void addharm ()
{
	int i;
	// Find first unused osc -- reuse, recycle, and... err... something 
	// else!
	// If all oscs are used, don't add a harmonic.  Number of unused
	// osc is stored in i.
	for( 0 => i; i < oscs.cap(); i++ )
		if (harms[i] == 0) break;
	if (i==MAXOSC) return;
	// pick harmonic to play...
	//	Std.rand2(2, MAXHARM) => harms[i];
	// new way to try to do this....
	intervals[Std.rand2(0, maxintervals)] => harms[i];
	fundfreq * harms[i] => oscs[i].freq;	
	// ...and a random pan.
	Std.randf() => float t;
	Math.sin(t) => pans[i].pan;
	// target volume and speed at which we fade in
	Std.rand2f(MAXGAIN/4, MAXGAIN) => envs[i].target;
	// WTF is .time measured in?!?!
	//	 .time is measured in 100::ms for some ungodly reason.
	//	 This really needs to be documented!!!
	Std.rand2f(10, 20) => envs[i].time;
	envs[i].time() * 100::ms => now;
	if (DEBUG) <<< "into harms[", i, "] = ", harms[i] >>>;
	Std.rand2(500,10000) => int length;
	// isn't this a cool language?  It's got a "maybe" statement! QED.
	// (tps}
	Sheesh...whatever. Isn't it basically the same as 
	// Std.rand2(0, 1)?
	// anyway...half the time choose a new pan for harmonic
	if ( maybe ) walkharm(i, t, length/10);

	length::ms=>now;
	// fadeout; don't do it as fast as fadein
	0 => envs[i].target;
	Std.rand2f(15, 30) => envs[i].time; // in seconds...
	envs[i].time() * 100::ms => now;
	if (DEBUG) <<< "from harms[", i, "] = ", harms[i] >>>;
	// release osc  (lets us know that we're not using it)
	0 => harms[i];
	}

// --------------------------------------------------------------------
fun void walkharm( int i, float t, int length )
{
	// panning harmonic; get speed of movement
	// Std.rand2(10, 30) / 1000.0 => float stepsize;
	// hm, why this rather than Std.rand2f (0.010, 0.030)? quicker?
	// let's try it:
	Std.rand2f (.010, .030) => float stepsize;
	for( 0 => int j; j < length; j++ )
	{
		Math.sin(t) => pans[i].pan;
		stepsize +=> t;
		10::ms => now;
	}

}

// --------------------------------------------------------------------
// main loop; random stuff happens
//
while( true ) {
	Std.rand2f(0, 1) => float doact; // now where's your precious
	//                                     // maybe??!
	// do nothing if it's exactly NEWFREQPROB!	This is not a bug! :)
	// (so you say)
	if (doact < NEWFREQPROB ) spork ~ newfundfreq();
	if (doact > NEWFREQPROB ) spork ~ addharm();
	Std.rand2(MINEVENTTIME, MAXEVENTTIME)::ms => now;
}

// Notes from original author:
// Generates new age music.  Make $$$ fast by holding down the 
// shift and pressing the "4" key!  [ha ha]
// Also by generating harmonically-related pitches.
// (no, seriously; that was the class assignment.  "Make
// a program that generates harmonically-related pitches.  You
// can use it to produce reams of crappy new age music and
// sell it") Well, in my opinion the professor sounds like
// a real supercilious snob but what do I know? Maybe I'm reading
// too much into his comment