From CSWiki
// 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