Last active
December 28, 2015 00:49
-
-
Save zeffii/7416350 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
// Machine.add("utilities.ck"); | |
me.dir() + "/audio/" => string path; | |
// utility functions | |
fun int random_from(int arr[]){ | |
return arr[Math.random2(0, arr.cap()-1)]; | |
} | |
fun int[] randomArray_from(int arr[], int length){ | |
// return new array of capacity length chosing elements from arr | |
int generated_arr[0]; | |
for(0 => int i; i<length; i++){ | |
generated_arr << random_from(arr); // append element | |
} | |
return generated_arr; | |
} | |
fun int[] range(int start, int end){ | |
// includes start and end | |
int arr[0]; | |
for(start => int i; i<=end; i++) arr << i; | |
return arr; | |
} | |
fun string repr(int arr[]){ | |
/* | |
this returns a string representation of an int array, | |
useful for debug printing. | |
*/ | |
"[" + Std.itoa(arr[0]) => string s; | |
for (1 => int i; i < arr.cap(); i++){ | |
"," + Std.itoa(arr[i]) +=> s; | |
} | |
return s + "]"; | |
} | |
fun int inArray(int token, int arr[]){ | |
/* | |
tests membership of an array | |
*/ | |
for(0 => int i; i<arr.cap(); i++){ | |
if (arr[i]==token) return 1; | |
} | |
return 0; | |
} | |
fun int[][] stepify(string sequence_data[]){ | |
/* | |
this function converts a string array to an int array, | |
it accepts fuzzy formatting: | |
"1..1..1..1.." becomes [1,0,0,1,0,0,1,0,0,1,0,0] | |
"1..1. .1..1.." also becomes [1,0,0,1,0,0,1,0,0,1,0,0] | |
*/ | |
sequence_data.cap() => int parts; | |
32 => int steps; // hard code for now | |
<<< parts, steps >>>; | |
int data_store[parts][steps]; | |
for(0 => int i; i<sequence_data.cap(); i++){ | |
triggers_to_int_array(sequence_data[i]) @=> data_store[i]; | |
} | |
return data_store; | |
} | |
fun string[] split(string arr, string token){ | |
// you trim arr before feeding it to this function | |
string return_array[0]; | |
string collect; | |
string ch; | |
for(0 => int i; i<arr.length(); i++){ | |
arr.substring(i, 1) => ch; | |
if (ch == token){ | |
return_array << collect.substring(0); | |
"" => collect; | |
} | |
else { ch +=> collect; } | |
} | |
return return_array << collect; | |
} | |
fun void connect( | |
Pan2 pans[], string names[], | |
SndBuf buffers[], Gain master){ | |
for (0 =>int i; i<names.cap(); i++){ | |
path + names[i] => buffers[i].read; | |
buffers[i].samples() => buffers[i].pos; | |
buffers[i] => pans[i] => master; | |
} | |
} | |
fun void buff_control(string ctrl_msg, SndBuf sb[], int idx){ | |
split(ctrl_msg, " ") @=> string sysexp[]; | |
Std.atof(sysexp[0]) => float fg => sb[idx].gain; | |
Std.atoi(sysexp[1]) => int ip => sb[idx].pos; | |
Std.atof(sysexp[2]) => float fr => sb[idx].rate; | |
// <<< fg, ip, fr >>>; | |
} | |
fun void trigger_parts(int step, int triggers[][], string sysex[], SndBuf buffers[]){ | |
for(0 => int j; j<triggers.cap(); j++){ | |
triggers[j][step] => int trigger; | |
if (trigger){ | |
for(0 => int n; n<triggers.cap(); n++){ | |
if (j==n) buff_control(sysex[n], buffers, n); | |
} | |
} | |
} | |
} | |
fun int[] triggers_to_int_array(string seq_string){ | |
int part_data[0]; | |
int trigger; | |
//<<< seq_string >>>; | |
for(0 => int j; j < seq_string.length(); j++){ | |
seq_string.substring(j,1) => string token; // crazy | |
if (token == " ") continue; | |
if (token == ".") 0 => trigger; | |
else if (token == "1") 1 => trigger; | |
part_data << trigger; | |
} | |
//<<< repr_int_array(part_data)>>>; | |
return part_data; | |
} | |
fun string int_pattern_to_string(int trig_pat[]){ | |
/* slight overkill to convert back.. */ | |
string trig_repr; | |
for(0 => int i; i<trig_pat.cap(); i++){ | |
((trig_pat[i] == 1) ? "1" : ".") +=> trig_repr; | |
} | |
return trig_repr; | |
} | |
Gain bd_master => dac; | |
Gain perc_master => dac; | |
Gain hat_master => dac; | |
// BD | |
[ "1... 1... 1... 1...", | |
"1... 1... 1... 1...", | |
"1... 1... 1... 1...", | |
"1... 1... 1... 1..." | |
] @=> string bd_patterns[]; | |
[ "kick_01.wav", | |
"kick_02.wav", | |
"kick_03.wav", | |
"kick_04.wav" | |
] @=> string bd_names[]; | |
[ "0.11 2360 0.58", | |
"0.8 2883 0.88", | |
"0.13 560 0.02", | |
"0.01 137 0.77" | |
] @=> string bd_sysex[]; | |
bd_names.cap() => int num_wavs; | |
Pan2 bd_pans[num_wavs]; | |
SndBuf bd_buffers[num_wavs]; | |
connect(bd_pans, bd_names, bd_buffers, bd_master); | |
stepify(bd_patterns) @=> int bd_triggers[][]; | |
// PERCUSSION | |
[ "...1 ..1. .1.. 1.1.", | |
"1.1. ..1. 1.1. .1.1", | |
".1.. 1..1 .... 1...", | |
"..1. .1.. ...1 ...1" | |
] @=> string perc_patterns[]; | |
[ "click_01.wav", | |
"click_02.wav", | |
"click_02.wav", | |
"click_03.wav" | |
] @=> string perc_names[]; | |
// gain, pos, rate | |
[ "0.11 87 3.57", | |
"0.31 467 2.47", | |
"0.41 57 1.27", | |
"0.1 17 2.77" | |
] @=> string perc_sysex[]; | |
perc_names.cap() => int num_pwavs; | |
Pan2 perc_pans[num_pwavs]; | |
SndBuf perc_buffers[num_pwavs]; | |
connect(perc_pans, perc_names, perc_buffers, perc_master); | |
stepify(perc_patterns) @=> int perc_triggers[][]; | |
// HATS | |
[ ".11. ..1. ..1. ..1.", | |
"1... 1... 1... 1...", | |
".... .1.. ..1. ...." | |
] @=> string hat_patterns[]; | |
[ "hihat_01.wav", | |
"hihat_02.wav", | |
"hihat_03.wav" | |
] @=> string hat_names[]; | |
// gain, pos, rate | |
[ "0.41 1757 1.37", | |
"0.11 67 1.47", | |
"0.21 8657 1.5" | |
] @=> string hat_sysex[]; | |
hat_names.cap() => int num_hwavs; | |
Pan2 hat_pans[num_hwavs]; | |
SndBuf hat_buffers[num_hwavs]; | |
connect(hat_pans, hat_names, hat_buffers, hat_master); | |
stepify(hat_patterns) @=> int hat_triggers[][]; | |
// ENSEMBLE | |
[ bd_triggers, | |
perc_triggers, | |
hat_triggers | |
] @=> int sound_parts[][][]; | |
// shuffle setup | |
int shuffle; | |
float tick_duration; | |
1.0 => float global_rate; // multiplier for anything that affects time | |
124 * global_rate => float long_tick; | |
long_tick * .74 => float short_tick; | |
0 => int beat; | |
0 => int tick; | |
fun string repeatn(string chr, int n){ | |
string signature; | |
for(0 => int i; i<n; i++) chr +=> signature; | |
return signature; | |
} | |
fun void send_ensemble_to_processing(int port, int sound_parts[][][]){ | |
sound_parts.cap() => int num_parts; | |
repeatn("s", num_parts) => string signature; | |
OscSend xmit; | |
"localhost" => string hostname; | |
xmit.setHost( hostname, port ); | |
xmit.startMsg( "/processing/trigger/patches", signature ); | |
// a message is kicked as soon as it is complete | |
for(0 => int i; i<num_parts; i++){ | |
// join sequences as strings, separated by space | |
string pattern_string; | |
for(0 => int j; j<sound_parts[i].cap(); j++){ | |
" " + int_pattern_to_string(sound_parts[i][j]) +=> pattern_string; | |
} | |
pattern_string => xmit.addString; | |
} | |
0.1::second => now; // carefull, smaller? | |
} | |
// OSC config | |
6449 => int port; | |
// OSC transmitter | |
send_ensemble_to_processing(port, sound_parts); | |
// OSC receiver | |
OscRecv recv; | |
port => recv.port; | |
recv.listen(); | |
recv.event( "/seq_triggers, iis" ) @=> OscEvent oe; | |
// SEQUENCER | |
12 => int total_beats; | |
sound_parts[0][0].cap() => int steps; | |
while(beat < total_beats){ | |
<<< "beat:", beat >>>; | |
for(0 => int i; i<steps; i++){ | |
// wait, briefly, for event to arrive | |
// oe => now; | |
// grab the next message from the queue. | |
while ( oe.nextMsg() != 0 ){ | |
oe.getInt() => int part; | |
oe.getInt() => int sound_index; | |
oe.getString() => string trigger_pattern; | |
triggers_to_int_array(trigger_pattern) @=> int incoming_triggers[]; | |
sound_parts[part][sound_index] @=> int triggers[]; | |
// only modify content if it is different. | |
if (!(incoming_triggers == triggers)){ | |
incoming_triggers @=> sound_parts[part][sound_index]; | |
} | |
} | |
i % 2 => shuffle; | |
trigger_parts(i, sound_parts[0], bd_sysex, bd_buffers); | |
trigger_parts(i, sound_parts[1], perc_sysex, perc_buffers); | |
trigger_parts(i, sound_parts[2], hat_sysex, hat_buffers); | |
// TIME | |
if (shuffle==0) long_tick => tick_duration; | |
else short_tick => tick_duration; | |
tick_duration::ms => now; | |
tick++; | |
} | |
beat++; | |
} |
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
import oscP5.*; | |
import netP5.*; | |
OscP5 oscP5; | |
/* a NetAddress contains the ip address and port number of a remote location in the network. */ | |
NetAddress myBroadcastLocation; | |
int num_parts = 3; | |
int track_separation = 30; | |
int part_separation = 30; | |
int trigger_x_distance = 28; | |
int diameter = 12; | |
int matrix_x_offset = 40; | |
int marker_jumps = 200; | |
int marker = 0; | |
String[] pattern_strings = new String[num_parts]; | |
// maybe fill with a default pattern, for starters | |
void setup() { | |
size(600,500); | |
frameRate(15); | |
smooth(); | |
fill_init_pattern(); | |
oscP5 = new OscP5(this, 6449); | |
/* the address of the osc broadcast server */ | |
myBroadcastLocation = new NetAddress("127.0.0.1",32000); | |
ellipseMode(RADIUS); | |
} | |
void fill_init_pattern(){ | |
for (int i = 0; i<num_parts; i++){ | |
pattern_strings[i] = "1.1.1.1. 1.1.1.1. 1.1.1.1."; | |
} | |
} | |
void draw() { | |
background(190); | |
float visual_width = 0.0; | |
float visual_height = 0.0; | |
// for every trigger part | |
int current_track = 0; | |
int part_height = 0; | |
for (int i = 0; i<num_parts; i++){ | |
String[] separate_tracks = pattern_strings[i].split(" "); | |
int num_triggers = separate_tracks[0].length(); | |
int num_tracks = separate_tracks.length; | |
if (i==0){ visual_width = (num_triggers-1)*trigger_x_distance; } | |
strokeWeight(0.2); | |
for(int j = 0; j<num_tracks; j++){ | |
current_track += 1; | |
int[] triggers = new int[num_triggers]; | |
for(int p = 0; p<num_triggers; p++){ | |
char pad_state = separate_tracks[j].charAt(p); | |
triggers[p] = (pad_state =='1')? 1 : 0; | |
} | |
part_height = num_tracks * track_separation; | |
int y_pos = (part_separation*i) + (current_track*track_separation); | |
for(int tr=0; tr<triggers.length; tr++){ | |
int x_pos = (trigger_x_distance * tr) + matrix_x_offset; | |
int value = (triggers[tr]==1) ? 40: 220; | |
fill(value); | |
stroke(70); | |
ellipse(x_pos, y_pos, diameter, diameter); | |
} | |
} | |
visual_height += part_height; | |
} | |
visual_height += ((num_parts) * part_separation); | |
// draw time indicator line | |
int y1=30; int y2=int(visual_height); | |
int start_x, end_x; | |
stroke(240,20,20,40); | |
strokeWeight(diameter*0.2); | |
start_x = matrix_x_offset; | |
end_x = int(start_x + visual_width); | |
int total_width = end_x - start_x; | |
float jump_x = total_width / marker_jumps; | |
int marker_x = start_x + int(marker * jump_x); | |
line(marker_x, y1, marker_x, y2); | |
marker = (marker >= marker_jumps) ? 0 : marker+10; | |
} | |
/* incoming osc message are forwarded to the oscEvent method. */ | |
void oscEvent(OscMessage theOscMessage) { | |
// theOscMessage.addrPattern() | |
// theOscMessage.typetag() | |
theOscMessage.print(); | |
for (int i = 0; i<num_parts; i++){ | |
pattern_strings[i] = theOscMessage.get(i).stringValue().trim(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
http://code.compartmental.net/minim/javadoc/ddf/minim/BufferedAudio.html can be used to visualize waves in Processing.