-
-
Save zeffii/7634042 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<<< "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