Forked from anonymous/assignment_3_Multipart_32_Step_Sequencer3.ck
Created
November 18, 2013 01:55
-
-
Save zeffii/7521180 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 3 - Multipart 32 Step Sequencer" >>>; | |
/* | |
Assignment 3: Make Techno | |
Please save this file to the directory that contains the folder called audio, | |
and play it from that location. If you can't get it to work, please ask for help | |
in the forums. | |
*/ | |
// think of this as the master fader | |
Gain master => dac; | |
0.7 => master.gain; | |
// initialize an oscillator/voice | |
// this whitenoise will appear on the kickdrum during the first 32 kicks | |
Noise noise_hat => Pan2 noise_location => master; | |
0.0 => noise_hat.gain; | |
// subbass | |
SinOsc sin_bass => master; | |
0.3 => sin_bass.gain; | |
42 => sin_bass.freq; | |
// these arrays will eventually hold sample data for several different sounds | |
SndBuf samples[4]; | |
SndBuf2 pads[2]; // these are stereo | |
SndBuf clicks[5]; | |
// me.dir() returns a string representation of the path in which the current | |
// chuck file is located. Adding me.dir() to the substring "/audio"/ is | |
// is called 'concatenation' | |
me.dir() + "/audio/" => string path; | |
// the string array of sample names to load. | |
[ "kick_01.wav", | |
"snare_03.wav", | |
"snare_01.wav", | |
"hihat_03.wav" | |
] @=> string sample_names[]; | |
// the string array of sample names to load. | |
[ "click_01.wav", | |
"click_02.wav", | |
"click_03.wav", | |
"click_04.wav", | |
"click_05.wav" | |
] @=> string click_names[]; | |
// i'll use this as a pad. | |
path + "stereo_fx_03.wav" => string pad_name; | |
// stores how many waveforms im going to store the samples array | |
sample_names.cap() => int num_waveforms; | |
// this single loop will do a few things, namely: | |
// - .read each sample into samples[] | |
// - set their .pos to the end of the sample | |
// - chuck all samples[] to master | |
for (0 =>int i; i<num_waveforms; i++){ | |
// concatenate the audio directory path name with the sample name, and | |
// .read from it. | |
path + sample_names[i] => samples[i].read; | |
// use the sample length (ie: .samples()) to set the playhead to the end | |
// this avoids triggering every sound in an audible way on the first beat | |
samples[i].samples() => samples[i].pos; | |
samples[i] => master; | |
} | |
// this loads the pad sample twice, so i can easily play two versions of it | |
// side by side. Unlike the previous loop, these samples will first be chucked | |
// to a Pan2 device. So let's make a Pan2 array first | |
Pan2 pad_pans[2]; | |
for(0 => int i; i< pads.cap(); i++){ | |
pad_name => pads[i].read; | |
pads[i].samples() => pads[i].pos; | |
pads[i] => pad_pans[i] => dac; | |
} | |
// this loads the percussion (second layer samples) - it's identical | |
// in many ways to the for loop that fills the samples[] array, forgive me | |
// if i don't comment it heavily | |
click_names.cap() => int num_clickwaveforms; | |
Pan2 click_pans[num_clickwaveforms]; | |
// fill clicks with .samples(), set playhead (pos), add pan then send to dac | |
for (0 =>int i; i<num_clickwaveforms; i++){ | |
path + click_names[i] => clicks[i].read; | |
clicks[i].samples() => clicks[i].pos; | |
clicks[i] => click_pans[i] => dac; | |
} | |
// setup the multipart step sequencer, each 1 means trigger. | |
[ | |
[1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0, 1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0], // kick | |
[0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0, 0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1], // snare | |
[0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0, 0,0,1,0,0,1,1,0,0,0,1,0,0,0,1,0], // hat1 | |
[1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0, 1,0,0,0,1,0,0,0,1,0,0,1,1,0,0,0] // hat2 | |
] @=> int multi_parts[][]; | |
multi_parts.cap() => int parts; | |
multi_parts[0].cap() => int steps; | |
<<< "parts:", parts, " ", "steps: ", steps >>>; | |
// all dorian notes | |
// these notes will be played side by side for 8 kicks each. | |
//[c4, g4], [d4, g4], [d4, a4], [e4, a4] | |
[[60, 67], [62, 67], [62, 69], [64, 69] | |
] @=> int dorians[][]; | |
[ | |
[0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0], | |
[1,1,0,1, 1,1,0,1, 1,1,0,0, 1,1,0,0, 1,1,0,1, 1,1,0,0, 1,1,0,1, 1,1,0,1] | |
] @=> int percs[][]; | |
// I don't like straight techno, shuffle is used to get a swing effect, | |
// this simulates the swing parameter of drum machines | |
int shuffle; | |
float tick_duration; | |
121 => float long_tick; | |
long_tick * .74 => float short_tick; | |
// let's start with some faux vinyl release. | |
3 => int into_sample; | |
samples[into_sample].samples()-13000 => samples[into_sample].pos; | |
-.4 => samples[into_sample].rate; // reverse | |
2.15::second => now; | |
0.0 => sin_bass.gain; | |
1.0 => float bend; // used for the ride [hihat] pitchbend | |
0 => int chords; // placeholder counter for which two notes to play together. | |
0 => int beat; | |
0 => int tick; | |
// this will play the above 32 steps, 8 times. | |
while(beat<8){ | |
// initialize the two pad notes | |
if (beat % 1 == 0){ | |
<<< "print that thing" >>>; | |
0.9 => pad_pans[0].pan; | |
-0.9 => pad_pans[1].pan; | |
0 => pads[0].pos; | |
10 => pads[1].pos; // slight offset | |
0.6 => pads[0].gain; | |
0.6 => pads[1].gain; | |
// this is where functions will be very welcome in the following weeks | |
// yes, at the moment this is a lot of code to do trivial stuff, but you | |
// must understand how this works before knowing why functions are so | |
// amazing. | |
Std.mtof(dorians[chords][0]) / 440.0 => float note_rate1; | |
Std.mtof(dorians[chords][1]) / 440.0 => float note_rate2; | |
1.4 => float transpose; | |
note_rate1 * transpose => pads[0].rate; | |
note_rate2 * transpose => pads[1].rate; | |
// increment chord counter, if we are below 3 | |
if (chords <3) 1+=> chords; | |
// this must mean we are at 3 and must reset for the next iteration, | |
// this avoids triggering a note combo that doesn't exist | |
else 0 => chords; | |
} | |
for(0 => int i; i<steps; i++){ | |
// every second step (i) in steps will set shuffle to 0, | |
// staring from step 0, this will output 0101010101010101.... | |
i % 2 => shuffle; | |
for(0 => int j; j<parts; j++){ | |
multi_parts[j][i] => int trigger; | |
if (trigger==1){ | |
// cool! trigger is 1, set position to 0 (or some offset) | |
0 => samples[j].pos; | |
// kick | |
if (j==0){ | |
// this doesn't start the sample from 0, but uses an offset | |
// to drop some portion of the attack | |
2446 => samples[j].pos; | |
1.74 => samples[j].rate; | |
2.2 => samples[j].gain; | |
} | |
// snare | |
if (j==1){ | |
Math.random2f(.32,.3) => samples[j].gain; | |
150 => samples[j].pos; | |
.78 => samples[j].rate; | |
} | |
// 3rd sample is hihat (index 2) | |
if (j==2){ | |
Math.random2f(.1,.2)+0.2 => samples[j].gain; | |
4.3 => samples[j].rate; | |
1200 => samples[j].pos; | |
if (beat >3) { | |
0.0 => samples[j].gain; | |
} | |
} | |
// 4th sample is also a long hihat (index 3) | |
if (j==3){ | |
(Math.random2f(2.2, 2.4) - 3.3) => samples[j].gain; | |
Math.random2f(3.22, 3.24) - 1.1 => samples[j].rate; | |
13900 => samples[j].pos; | |
if (beat >2) { | |
// transition, pitch up each time | |
2.13 * bend => samples[j].rate; | |
1.02 *=> bend; | |
12900 => samples[j].pos; | |
} | |
if (beat >3) { | |
2.3 => samples[j].rate; | |
2800 => samples[j].pos; | |
0.3 => samples[j].gain; | |
} | |
else{ | |
// piggyback this offbeat trigger to make noise OSC audible | |
noise_hat.gain(0.011); | |
} | |
} | |
} | |
} | |
// percussion layer 2 | |
// similar to the previous for loop, so i'll ease of on the comments | |
for(0 => int j; j<2; j++){ | |
// don't do anything inside this block, unless | |
// it is from the 4th beat onward | |
if (beat < 4) break; | |
percs[j][i] => int trigger; | |
if (trigger==1){ | |
if (j==0){ | |
0.2 => clicks[2].gain; | |
360 => clicks[2].pos; | |
.7 => clicks[2].rate; | |
} | |
if (j==1){ | |
// pick random sample from 0,1,3,4 | |
[0,1,3,4] @=> int choices[]; | |
Math.random2(0, choices.cap()) => int choice; | |
0.4 => clicks[choice].gain; | |
Math.random2f(-.3, .3) => click_pans[choice].pan; | |
Math.random2(60, 190) => clicks[choice].pos; | |
Math.random2f(.2, .7) => clicks[choice].rate; | |
} | |
} | |
} | |
if (shuffle==0) long_tick => tick_duration; | |
else short_tick => tick_duration; | |
tick_duration::ms => now; | |
tick++; | |
noise_hat.gain(0.0); // this is the same as writing 0.0 => .. | |
} | |
beat++; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment