Skip to content

Instantly share code, notes, and snippets.

@zeffii
Forked from anonymous/week4_flight_of_the_moth.ck
Created November 24, 2013 23:39
Show Gist options
  • Save zeffii/7634042 to your computer and use it in GitHub Desktop.
Save zeffii/7634042 to your computer and use it in GitHub Desktop.
<<< "Assignment_4_Flight of the Moth" >>>;
// coded an ADSR alternative, no effect generators harmed
fun void instructions(){
"\n[x] 30 second composition\n" +
"[x] use functions ( at least 3 ) (i won't count this one)\n" +
"[x] use only midi note: 51, 53, 55, 56, 58, 60, 61, 63 (Eb Mixolydian scale)\n" +
"[x] You can use octaves above/below (either via frequency or midi note)\n" +
"[x] You must use the sound files from audio.zip\n" +
"[x] Use of Oscillator\n" +
"[x] Use of SndBuf\n" +
"[x] Use of if/else statements\n" +
"[x] Use of for loop or while\n" +
"[x] Use of variables\n" +
"[x] Use of comments\n" +
"[x] Std.mtof()\n" +
"[x] Random Number\n" +
"[x] Use of Arrays\n" +
"[x] Use of Panning\n" +
"[x] Use of right timing (.6::second quarter notes)\n\n"
=> string instructions;
<<< instructions >>>;
}
instructions();
fun void load_sample(string wavname, SndBuf temp_buff){
// turn incoming wavname into path/wavename.wav
me.dir() + "/audio/" + wavname + ".wav" => temp_buff.read;
// set to end of sample.
temp_buff.samples() => temp_buff.pos;
0.8 => temp_buff.gain;
}
fun float get_fair_gain(int polyphony, int partials){
// gets a reasonable initial volume for all oscillators,
// the number of partial gains should equal the number of partials.
polyphony * partials => int total_oscillators;
return (1.0 / total_oscillators);
}
0.8 => float global_synth_gain;
1.0 => float envelope_speed;
8.6 => float global_detune; // factor to adjust 'detune' of partials
// scale
[51, 53, 55, 56, 58, 60, 61, 63] @=> int mixoLydian[];
// polyphony (5)
[0,1,3,4,7] @=> int notes[];
// each note is made from:
// -- 1 prime oscillator (First Partial)
// -- n detuned oscillators (n Partials)
// for the spread code to work there must be an uneven amount of oscillators
// per voice.
[ 0.0,
0.22 * global_detune * -1, // down
0.24 * global_detune // up
] @=> float detunes[];
notes.cap() => int polyphony; // keep at 5 for now.
detunes.cap() => int partials; // oscillators per voice
get_fair_gain(polyphony, partials) => float fair_gain;
// I don't want partials to be as loud as the prime oscillator
// so second partial is 0.8 of fair_gain, third partial is 0.6 of fair_gain.
[ fair_gain,
fair_gain*0.8,
fair_gain*0.6
] @=> float partial_gains[];
// set up pans.. lots of them
Pan2 partial_pans[polyphony * partials];
SawOsc swOSC_bank[polyphony][partials];
// this loop
// -- connects each oscillator to a pan unit
// -- connects the pan unit to the dac
// -- modifies the phase of each oscillator (not sure if allowed?)
// ------ reason to use .phase is otherwise the peaks and valleys of these
// ------ oscillator experience interference.
// ------ comment out line [**] to hear the difference
fun void init_sound_chain(){
0 => int voice_counter;
for(0 => int p; p<notes.cap(); p++){
for(0 => int d; d<detunes.cap(); d++){
swOSC_bank[p][d] => partial_pans[voice_counter] => dac;
(pi / voice_counter) => swOSC_bank[p][d].phase; // [**]
voice_counter++;
}
}
}
fun void fill_oscillators(int notes[], int trans[], float detune){
// because this function is called each time the notes are changed
// it's a good opportunity to make the individual oscillators spread out
// from left to right.
0.7 => float spread;
Std.ftoi(Math.sgn(Math.random2f(-1, 1))) => int scalar; // -1 or 1
[spread, -spread] @=> float pan_position[];
0 => int voice_counter;
0 => int octave_shift;
for(0 => int p; p<notes.cap(); p++){
mixoLydian[notes[p]] => int midi_note;
trans[p] * 12 => octave_shift;
// each key, has a 'root' and several detuned oscillator
// all of which i will call 'partials' (though I think technically
// only the detuned oscillators should be called partials)
Std.mtof(midi_note + octave_shift) => float voice_freq;
// for osc in partials modify the osc freq using the members of the
// detunes array. First partial (0, or prime oscillator) is not detuned,
// subsequent partials are, depending on the value of detunes[d]
for(0 => int d; d<detunes.cap(); d++){
float detune_amount;
// sometimes if/else statement is a little longwinded.
if (d>=1){
1.0 => detune_amount;
}
else{
detune => detune_amount;
}
// i could have written this [&] instead of the if/else
// it's called a " Conditional (Ternary) Operator (?:) "
// and has the form: test ? expression1 : expression2
// (d>=1) ? 1.0 : detune => detune_amount; // [&]
voice_freq + (detunes[d] * detune)=> swOSC_bank[p][d].freq;
// this happens to place the subsequent panning positions
// in such a way that the prime oscillator (the un-detuned
// oscillator of each note) is always opposite the next prime
// oscillator. This works because there are uneven number of
// partials per note.
voice_counter % 2 => int pan_index;
pan_position[pan_index] * scalar => partial_pans[voice_counter].pan;
voice_counter++;
}
}
}
fun void setGains(float amplify){
for(0 => int p; p<notes.cap(); p++){
for(0 => int d; d<detunes.cap(); d++){
// statements can wrap to the next line, if they aren't
// broken by a semi colon. ;
partial_gains[d] * amplify * global_synth_gain =>
swOSC_bank[p][d].gain;
}
}
}
init_sound_chain();
setGains(0.0);
fun int adjust_speed(int in_speed){
// used for scale the current attack, peak and decay values while
// retaining that they are also returned as ints
return Std.ftoi(in_speed*envelope_speed);
}
// a lazy mans ADSR, with the only features I'm interested in.
// --generally it's not good practice to have more than 3 arguments per function
// --so, these are not the droids you're looking for.
fun void chordAPD(int notes[], int transposes[], int apd[], int max_time, float detune){
// whatever the apd, max_time will dominate
// and end duration of the stab
adjust_speed(apd[0]) => int a; // attack duration
adjust_speed(apd[1]) => int p; // peak duration
adjust_speed(apd[2]) => int d; // decay duration
fill_oscillators(notes, transposes, detune);
// decide on a reasonable control rate, 2 ms seems ok
// - too large and you notice jumps (stairs)
// - too fine and it consumes CPU with little benefit
2 => int min_time;
min_time::ms => dur control_rate;
max_time::ms => dur sound_length;
// input sanity checking and correcting.
if (a < min_time) { min_time => a; }
if (a > max_time) { max_time => a; }
// Time info
now => time start_time;
a::ms => dur attack_length;
p::ms => dur peak_length;
d::ms => dur decay_length;
attack_length + start_time => time attack_period;
sound_length + start_time => time total_period;
// linear amplification, for each iteration of the attack loop
1.0 / (a/min_time) => float gain_up_iterate;
0.0 => float amplify;
// -- Attack
setGains(0.0); // this might be overkill
0 => int iteration;
while(attack_period > now){
(iteration*gain_up_iterate) => amplify;
// this amplifies each partial, from 0 to almost 1 for the duration of
// the attack, broken out into a function because the same code is used again below
setGains(amplify);
control_rate => now;
iteration++;
if ( now > total_period) return;
}
// -- Peak (hold)
now => time peak;
peak_length + peak => time peak_period;
while(peak_period >= now){
control_rate => now;
if ( now > total_period) return;
}
// -- Decay
now => time decay_start;
decay_start + decay_length => time decay_period;
// linear de-amp
1.0 / (d/min_time) => float gain_down_iterate;
0 => iteration;
while(decay_period > now){
1.0 - (iteration*gain_down_iterate) => amplify;
setGains(amplify);
control_rate => now;
if ( now > total_period) return;
iteration++;
}
// this consumes the rest of max_time until _now_ exceeds it
// this indicates the end of the desired note.
while(total_period > now){
control_rate => now;
}
}
// if a cheese had a morning breakfast show.
if(1){
chordAPD([0,1,3,4,7], [0,0,0,0,0], [24,260,560], 600, 1.1);
chordAPD([0,1,3,4,7], [1,1,0,0,-1], [24,260,560], 600, 1.04);
chordAPD([2,3,4,5,7], [1,0,0,-1,0], [24,260,560], 600, 1.02);
chordAPD([0,1,3,4,7], [0,0,0,1,1], [624,260,1360], 2100, 1.03);
0::ms => now;
chordAPD([0,2,4,5,7], [-1,-1,1,0,0], [24,260,560], 600, 1.01);
chordAPD([1,3,4,5,7], [-1,1,1,-1,0], [24,260,560], 600, 1.06);
chordAPD([0,1,2,3,4], [1,1,1,-1,0], [24,260,560], 600, 1.0023);
chordAPD([1,3,4,6,7], [1,1,-1,-1,0], [624,260,1206], 2100, 1.0002);
0::ms => now;
chordAPD([0,3,4,5,7], [-1,1,1,0,0], [24,260,560], 600, 1.05);
chordAPD([1,3,2,5,7], [-1,1,1,0,0], [24,260,520], 600, 1.02);
chordAPD([5,0,4,6,1], [1,1,0,0,1], [24,260,420], 600, 0.8);
chordAPD([5,0,4,2,1], [1,1,0,0,1], [24,320,329], 600, 0.2);
100::ms => now;
chordAPD([1,3,4,5,7], [1,-1,1,-1,-1], [1200,218,1100], 600*4, 0.9);
chordAPD([1,3,5,6,7], [1,1,1,-1,-1], [1223,152,1043], 600*4, 1.0293);
chordAPD([1,4,3,5,6], [2,0,1,1,-1], [1323,152,1343], 600*4, 1.033);
chordAPD([1,4,3,5,6], [2,1,2,1,-1], [1323,152,1043], 600*4, 1.043);
}
// The sample !
SndBuf cymbal => dac;
load_sample("hihat_04", cymbal);
cymbal.samples() - 12400 => cymbal.pos;
-0.168 => cymbal.rate;
0.3 => cymbal.gain;
chordAPD([1,2,3,5,7], [1,1,-2,2,-1], [1123,1152,6125], 600*14, .523);
0.24::second => now; //scored silence.
/*EOF*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment