Skip to content

Instantly share code, notes, and snippets.

@zeffii
Last active December 28, 2015 00:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeffii/7416350 to your computer and use it in GitHub Desktop.
Save zeffii/7416350 to your computer and use it in GitHub Desktop.
// 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++;
}
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();
}
}
@zeffii
Copy link
Author

zeffii commented Nov 15, 2013

http://code.compartmental.net/minim/javadoc/ddf/minim/BufferedAudio.html can be used to visualize waves in Processing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment