Difference between revisions of "Son of ethereal.ck"
From CSWiki
Line 1: | Line 1: | ||
− | < | + | <pre> |
− | |||
− | |||
− | |||
− | |||
− | |||
// Son_of_Ethereal | // Son_of_Ethereal | ||
− | |||
// -- originally written by Graham Percival, gperciva@uvic.ca, | // -- originally written by Graham Percival, gperciva@uvic.ca, | ||
− | |||
// Oct 2005 | // Oct 2005 | ||
− | |||
// Placed in the public domain. | // Placed in the public domain. | ||
− | |||
// 20070401 tps -- ok; try is changing the oscillators at random. | // 20070401 tps -- ok; try is changing the oscillators at random. | ||
− | |||
// Do it by making an array of pointers to oscillators | // Do it by making an array of pointers to oscillators | ||
− | |||
− | |||
− | |||
// 20070427 tps -- fixing up deprecated references (i.e., | // 20070427 tps -- fixing up deprecated references (i.e., | ||
− | |||
// std becomes Std, sinosc => SinOsc, etc. Rationalizing formatting | // std becomes Std, sinosc => SinOsc, etc. Rationalizing formatting | ||
− | |||
// the way I likes it, etc. | // the way I likes it, etc. | ||
− | |||
// 20070503 tps -- rather than just use integer multiples of the fundamental | // 20070503 tps -- rather than just use integer multiples of the fundamental | ||
− | |||
// for the harmonics, let's use some simple intervals... change harms | // for the harmonics, let's use some simple intervals... change harms | ||
− | |||
// to be a float array to start with. | // to be a float array to start with. | ||
+ | // (Notes from original author at end) | ||
− | |||
// ---------------------------------------------------------------------------- | // ---------------------------------------------------------------------------- | ||
− | |||
// Global stuff... | // Global stuff... | ||
− | |||
// PARAMETERS | // PARAMETERS | ||
− | |||
1 => int DEBUG; // 1 = debug print on, 0 = off | 1 => int DEBUG; // 1 = debug print on, 0 = off | ||
− | |||
15 => int MAXOSC; // max of 15 oscillators at any one time | 15 => int MAXOSC; // max of 15 oscillators at any one time | ||
− | |||
20 => int MAXHARM; // max of 20 pitches 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 | 110.0 => float fundfreq; // start with fundamental frequency of 110 Hz | ||
− | |||
0.2 => float NEWFREQPROB; // probability of changing the fundamental | 0.2 => float NEWFREQPROB; // probability of changing the fundamental | ||
− | |||
// // frequency. 1-NEWFREQPROB = chance of | // // frequency. 1-NEWFREQPROB = chance of | ||
− | |||
// // adding a new harmonic. | // // adding a new harmonic. | ||
− | |||
1000 => int MINEVENTTIME; // minimum time between events. (in ms) | 1000 => int MINEVENTTIME; // minimum time between events. (in ms) | ||
− | |||
3000 => int MAXEVENTTIME; // max time... (also in ms) | 3000 => int MAXEVENTTIME; // max time... (also in ms) | ||
− | |||
SinOsc oscs[MAXOSC]; // oscs don't have to be sinosc; | SinOsc oscs[MAXOSC]; // oscs don't have to be sinosc; | ||
− | |||
//TriOsc oscs[MAXOSC]; // try uncommenting one of | //TriOsc oscs[MAXOSC]; // try uncommenting one of | ||
− | |||
//SqrOsc oscs[MAXOSC]; // these lines. | //SqrOsc oscs[MAXOSC]; // these lines. | ||
// create relatively consonant intervals (2:1, 3:2, 5:3, 7:5) | // create relatively consonant intervals (2:1, 3:2, 5:3, 7:5) | ||
− | |||
// over two octaves... | // over two octaves... | ||
− | |||
[2., 3./2., 5./3., 7./5., 4., 3., 10./3., 14./5. ] @=> float intervals[]; | [2., 3./2., 5./3., 7./5., 4., 3., 10./3., 14./5. ] @=> float intervals[]; | ||
− | |||
intervals.cap() - 1 => int maxintervals; | intervals.cap() - 1 => int maxintervals; | ||
// auto init (don't touch these things) | // auto init (don't touch these things) | ||
− | |||
1.0/MAXOSC => float MAXGAIN; | 1.0/MAXOSC => float MAXGAIN; | ||
− | |||
float harms[MAXOSC]; // binary "is this in use?" array | float harms[MAXOSC]; // binary "is this in use?" array | ||
− | |||
Pan2 pans[MAXOSC]; | Pan2 pans[MAXOSC]; | ||
− | |||
Envelope envs[MAXOSC]; | Envelope envs[MAXOSC]; | ||
− | |||
0 => int fundfreqblock; | 0 => int fundfreqblock; | ||
// init the oscs | // init the oscs | ||
− | |||
for (0 => int i; i < oscs.cap() ; i++ ) | for (0 => int i; i < oscs.cap() ; i++ ) | ||
− | |||
{ | { | ||
− | |||
oscs[i] => envs[i] => pans[i] => dac; | oscs[i] => envs[i] => pans[i] => dac; | ||
− | |||
0 => harms[i]; | 0 => harms[i]; | ||
− | |||
0 => pans[i].pan; | 0 => pans[i].pan; | ||
− | |||
} | } | ||
// functions: | // functions: | ||
− | |||
// newfundfreq() change gradually to a new fundamental frequency | // newfundfreq() change gradually to a new fundamental frequency | ||
− | |||
// addharm() add a new harmonic | // addharm() add a new harmonic | ||
− | |||
// walkharm() add a random pan | // walkharm() add a random pan | ||
− | |||
// -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
− | |||
fun void newfundfreq () | fun void newfundfreq () | ||
− | |||
{ | { | ||
− | + | // don't get a new fundamental frequency if we're already | |
− | // don't get a new fundamental frequency if we're already | + | // changing one. |
− | |||
− | // changing one. | ||
− | |||
if (fundfreqblock==1) { | if (fundfreqblock==1) { | ||
+ | return; | ||
+ | } | ||
+ | else { | ||
+ | 1=> fundfreqblock; | ||
+ | } | ||
− | + | // pick new fundamental fundfrequency | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | // pick new fundamental fundfrequency | ||
− | |||
float newfundfreq; | float newfundfreq; | ||
− | |||
fundfreq * Std.rand2f(0.8,1.25) => newfundfreq; | fundfreq * Std.rand2f(0.8,1.25) => newfundfreq; | ||
− | |||
if (newfundfreq < 110) 110 => newfundfreq; | if (newfundfreq < 110) 110 => newfundfreq; | ||
− | + | if (newfundfreq > 330) 330 => newfundfreq; | |
− | if (newfundfreq > 330) 330 => newfundfreq; // limit how far it | + | // limit how far it |
− | + | // | |
− | // // can change | + | // can change |
− | |||
if (DEBUG) <<< "start fundfreq moving to", newfundfreq >>>; | if (DEBUG) <<< "start fundfreq moving to", newfundfreq >>>; | ||
− | + | // gliss to new fundfreq over 5 seconds...divide into 50 steps | |
− | // gliss to new fundfreq over 5 seconds...divide into 50 steps | ||
− | |||
Std.fabs( (fundfreq-newfundfreq)/50) => float stepsize; | Std.fabs( (fundfreq-newfundfreq)/50) => float stepsize; | ||
− | |||
for (0 => int i; i < 50; i++) | for (0 => int i; i < 50; i++) | ||
− | |||
{ | { | ||
− | + | // yes, this can result in the fundreq alternating around the result | |
− | // 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 | |
− | // -- i.e. if funfreq is close to newfundfreq, it might reach (and | + | // is not a bug! :) |
− | + | // (ok, it _was_ unintentional, but I kind-of like | |
− | // surpass) the goal, and then move in the opposite direction. This | + | // this behavior, so I didn't fix the bug) |
− | |||
− | // 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; | ||
− | |||
if (fundfreq > newfundfreq) stepsize -=> fundfreq; | if (fundfreq > newfundfreq) stepsize -=> fundfreq; | ||
− | |||
100::ms => now; | 100::ms => now; | ||
− | |||
} | } | ||
if (DEBUG) <<< "fundfreq now", fundfreq >>>; | if (DEBUG) <<< "fundfreq now", fundfreq >>>; | ||
− | |||
0 => fundfreqblock; | 0 => fundfreqblock; | ||
− | |||
} | } | ||
// -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
− | |||
fun void addharm () | fun void addharm () | ||
− | |||
{ | { | ||
− | |||
int i; | int i; | ||
− | + | // Find first unused osc -- reuse, recycle, and... err... something | |
− | // Find first unused osc -- reuse, recycle, and... err... something | + | // else! |
− | + | // If all oscs are used, don't add a harmonic. Number of unused | |
− | // else! | + | // osc is stored in i. |
− | |||
− | // 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++ ) | for( 0 => i; i < oscs.cap(); i++ ) | ||
− | |||
if (harms[i] == 0) break; | if (harms[i] == 0) break; | ||
− | |||
if (i==MAXOSC) return; | if (i==MAXOSC) return; | ||
− | + | // pick harmonic to play... | |
− | // pick harmonic to play... | + | // Std.rand2(2, MAXHARM) => harms[i]; |
− | + | // new way to try to do this.... | |
− | // Std.rand2(2, MAXHARM) => harms[i]; | + | intervals[Std.rand2(0, maxintervals)] => harms[i]; |
− | + | fundfreq * harms[i] => oscs[i].freq; | |
− | // new way to try to do this.... | + | // ...and a random pan. |
− | |||
− | |||
− | |||
− | |||
− | |||
− | // ...and a random pan. | ||
− | |||
Std.randf() => float t; | Std.randf() => float t; | ||
− | |||
Math.sin(t) => pans[i].pan; | Math.sin(t) => pans[i].pan; | ||
− | + | // target volume and speed at which we fade in | |
− | // target volume and speed at which we fade in | ||
− | |||
Std.rand2f(MAXGAIN/4, MAXGAIN) => envs[i].target; | Std.rand2f(MAXGAIN/4, MAXGAIN) => envs[i].target; | ||
− | + | // WTF is .time measured in?!?! | |
− | // WTF is .time measured in?!?! | + | // .time is measured in 100::ms for some ungodly reason. |
− | + | // This really needs to be documented!!! | |
− | // .time is measured in 100::ms for some ungodly reason. | ||
− | |||
− | // This really needs to be documented!!! | ||
− | |||
Std.rand2f(10, 20) => envs[i].time; | Std.rand2f(10, 20) => envs[i].time; | ||
− | |||
envs[i].time() * 100::ms => now; | envs[i].time() * 100::ms => now; | ||
− | |||
if (DEBUG) <<< "into harms[", i, "] = ", harms[i] >>>; | if (DEBUG) <<< "into harms[", i, "] = ", harms[i] >>>; | ||
− | |||
Std.rand2(500,10000) => int length; | Std.rand2(500,10000) => int length; | ||
− | + | // isn't this a cool language? It's got a "maybe" statement! QED. | |
− | // isn't this a cool language? It's got a "maybe" statement! QED. | + | // (tps} |
− | + | Sheesh...whatever. Isn't it basically the same as | |
− | // (tps} Sheesh...whatever. Isn't it basically the same as | + | // Std.rand2(0, 1)? |
− | + | // anyway...half the time choose a new pan for harmonic | |
− | // Std.rand2(0, 1)? | ||
− | |||
− | // anyway...half the time choose a new pan for harmonic | ||
− | |||
if ( maybe ) walkharm(i, t, length/10); | if ( maybe ) walkharm(i, t, length/10); | ||
− | |||
− | |||
length::ms=>now; | length::ms=>now; | ||
− | + | // fadeout; don't do it as fast as fadein | |
− | // fadeout; don't do it as fast as fadein | ||
− | |||
0 => envs[i].target; | 0 => envs[i].target; | ||
− | |||
Std.rand2f(15, 30) => envs[i].time; // in seconds... | Std.rand2f(15, 30) => envs[i].time; // in seconds... | ||
− | |||
envs[i].time() * 100::ms => now; | envs[i].time() * 100::ms => now; | ||
− | |||
if (DEBUG) <<< "from harms[", i, "] = ", harms[i] >>>; | if (DEBUG) <<< "from harms[", i, "] = ", harms[i] >>>; | ||
− | + | // release osc (lets us know that we're not using it) | |
− | // release osc (lets us know that we're not using it) | ||
− | |||
0 => harms[i]; | 0 => harms[i]; | ||
− | + | } | |
− | } | ||
// -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
− | |||
fun void walkharm( int i, float t, int length ) | fun void walkharm( int i, float t, int length ) | ||
− | |||
{ | { | ||
− | + | // panning harmonic; get speed of movement | |
− | // panning harmonic; get speed of movement | ||
− | |||
// Std.rand2(10, 30) / 1000.0 => float stepsize; | // Std.rand2(10, 30) / 1000.0 => float stepsize; | ||
− | + | // hm, why this rather than Std.rand2f (0.010, 0.030)? quicker? | |
− | // hm, why this rather than Std.rand2f (0.010, 0.030)? quicker? | + | // let's try it: |
− | + | Std.rand2f (.010, .030) => float stepsize; | |
− | // let's try it: | + | for( 0 => int j; j < length; j++ ) |
− | |||
− | Std.rand2f (.010, .030) => float stepsize; | ||
− | |||
− | for( 0 => int j; j < length; j++ ) | ||
− | |||
{ | { | ||
− | |||
Math.sin(t) => pans[i].pan; | Math.sin(t) => pans[i].pan; | ||
− | |||
stepsize +=> t; | stepsize +=> t; | ||
− | |||
10::ms => now; | 10::ms => now; | ||
− | |||
} | } | ||
Line 290: | Line 158: | ||
// -------------------------------------------------------------------- | // -------------------------------------------------------------------- | ||
− | |||
// main loop; random stuff happens | // main loop; random stuff happens | ||
− | |||
// | // | ||
− | |||
while( true ) { | while( true ) { | ||
− | |||
Std.rand2f(0, 1) => float doact; // now where's your precious | Std.rand2f(0, 1) => float doact; // now where's your precious | ||
− | + | // // maybe??! | |
− | // // maybe??! | + | // do nothing if it's exactly NEWFREQPROB! This is not a bug! :) |
− | + | // (so you say) | |
− | // do nothing if it's exactly NEWFREQPROB! This is not a bug! :) | ||
− | |||
− | // (so you say) | ||
− | |||
if (doact < NEWFREQPROB ) spork ~ newfundfreq(); | if (doact < NEWFREQPROB ) spork ~ newfundfreq(); | ||
− | |||
if (doact > NEWFREQPROB ) spork ~ addharm(); | if (doact > NEWFREQPROB ) spork ~ addharm(); | ||
− | |||
Std.rand2(MINEVENTTIME, MAXEVENTTIME)::ms => now; | Std.rand2(MINEVENTTIME, MAXEVENTTIME)::ms => now; | ||
− | |||
} | } | ||
// Notes from original author: | // Notes from original author: | ||
− | |||
// Generates new age music. Make $$$ fast by holding down the | // Generates new age music. Make $$$ fast by holding down the | ||
− | |||
// shift and pressing the "4" key! [ha ha] | // shift and pressing the "4" key! [ha ha] | ||
− | |||
// Also by generating harmonically-related pitches. | // Also by generating harmonically-related pitches. | ||
− | |||
// (no, seriously; that was the class assignment. "Make | // (no, seriously; that was the class assignment. "Make | ||
− | |||
// a program that generates harmonically-related pitches. You | // a program that generates harmonically-related pitches. You | ||
− | |||
// can use it to produce reams of crappy new age music and | // can use it to produce reams of crappy new age music and | ||
− | |||
// sell it") Well, in my opinion the professor sounds like | // sell it") Well, in my opinion the professor sounds like | ||
− | |||
// a real supercilious snob but what do I know? Maybe I'm reading | // a real supercilious snob but what do I know? Maybe I'm reading | ||
− | |||
// too much into his comment | // too much into his comment | ||
− | + | </pre> | |
− | |||
− | </ |
Latest revision as of 13:16, 27 September 2007
// 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