---
An exploration of different sampling methods for sonification of functions
Upload a CSV file containing the data you want to sonify. Two columns: x values in the first column, y values in the second column.
Continuous sonification plays every data point.
Variable Pitch Interval matches equal intervals in x.
Variable Tempo matches equal intervals in y.
//**** VARIABLE LEGEND **** // nVals: number of values // x: x values (normalized between 0 and 1) // y: y values (normalized between 0 and 1) // dx, dy // maxdy: maximum dy // absdYSum: total range of y movement // fs: sampling frequency for user equation // totalSecCont: number of seconds for playing equation // noteDuration: duration of each note for csv input 50::ms => now; //**** AUDIO SETUP **** SinOsc m => ADSR e => dac; e.set( 10::ms, 8::ms, .5, 10::ms ); // attack, decay, sustain, release //**** SONIFICATION PARAMETERS **** 50 => float minMIDI; // highest pitch 80 => float maxMIDI; // lowest pitch 0.8 => float onFraction; // on-off duty cycle of note //**** PRE-SONIFICATION COMPUTATION **** // Goal: to compute arrays of t, pitch, dt, dpitch for chuck float t[0]; float pitch[0]; float dt[0]; float dpitch[0]; // if source is csv with continous sonification, keep each data point if(source == 0){ // for each data value for(0 => int i; i < x.size(); i++){ // compute t t << i * noteDuration; // compute pitch pitch << y[i]*(maxMIDI - minMIDI)+minMIDI; } } // if user defined functon with continuous sonification, keep every unit step if(source == 1 && mode == "cont"){ // for each data value for(0 => int i; i < x.size(); i++){ // compute t t << i * totalSecCont / x.size(); // compute pitch pitch << y[i]*(maxMIDI - minMIDI)+minMIDI; } } // if user defined functon with variable tempo sonification, compute every binned change in y if(source == 1 && mode == "y"){ float prevNote; // for each data value for(0 => int i; i < x.size(); i++){ // compute discretized note to floored chromatic y[i]*(maxMIDI - minMIDI)+minMIDI => float note; // if the note had changed if(note >= prevNote + 1 || note <= prevNote - 1){ // save this note t << i * totalSecCont / x.size(); pitch << note; note => prevNote; } } } // if user defined functon with variable pitch interval, match n of variable tempo sonification if(source == 1 && mode == "x"){ 0 => int counter; float prevNote; // To count how many notes would have been played with equal Tempo // for each data value for(0 => int i; i < x.size(); i++){ // compute discretized note to floored chromatic y[i]*(maxMIDI - minMIDI)+minMIDI => float note; // if the note had changed if(note >= prevNote + 1 || note <= prevNote - 1){ // count the note counter++; note => prevNote; } } // compute size of each step by dividing second to play with number of notes to play Std.ftoi(Math.round(1000 * totalSecCont / (counter))) => int xStep; // save note at each xStep for(0 => int i; i <= x.size() - xStep; i + xStep => i){ t << i * totalSecCont / x.size(); pitch << y[i]*(maxMIDI - minMIDI)+minMIDI; } } // compute difference array for x and y for (1 => int i; i < t.size(); i++) { dt << t[i] - t[i - 1]; dpitch << pitch[i] - pitch[i - 1]; } // compute how much pitch traverses total 0 => float absdPitchSum; // as well as the highest dPitch 0 => float maxdPitch; for(0 => int i; i < dpitch.size(); i++){ Math.fabs(dpitch[i]) => float absVal; absdPitchSum + absVal => absdPitchSum; if(absVal > maxdPitch){ absVal => maxdPitch; } } //**** SONIFYING Different Options **** // for user specified functions if(source == 1){ if(mode == "cont"){ continuousSonify(dt, pitch); } else { discreteSonify(dt, pitch); } } if(source == 0 && mode == "cont"){ continuousSonify(dt, pitch); } if(source == 0 && mode == "x"){ Math.round(absdPitchSum / t.size()) => float xMiniSteps; xInterpSonify(dt, pitch, dpitch, xMiniSteps); } if(source == 0 && mode == "y"){ yInterpSonify(dt, pitch, dpitch); } //**** HELPER FUNCTIONS **** // sonifies according to dx (timing) and y (pitch) // requires updated dx and y private void continuousSonify(float dt[], float pitch[]){ 0.9 => m.gain; e.keyOn(); // looping through each value to sonify for (0 => int i; i < dt.size(); i++) { // set main note frequency and play Std.mtof(pitch[i]) => m.freq; // data values in range 0 - 1 dt[i] ::second => now; } e.keyOff(); } // sonifies according to dt (timing) and pitch // requires dt and pitch private void discreteSonify(float dt[], float pitch[]){ // looping through each value to sonify for (0 => int i; i < dt.size() ; i++) { 0.9 => m.gain; // set main note frequency and play Std.mtof(pitch[i]) => m.freq; // data values in range 0 - 1 // play note playNote(dt[i]); } } // sonifies with equal interval x interpolations // requires updated dx, y, dy, xMiniSteps private void xInterpSonify(float dt[], float pitch[], float dpitch[], float xMiniSteps){ // looping through each value to sonify for (0 => int i; i < nVals; i++) { // set main note frequency and play Std.mtof(pitch[i]) => m.freq; // data values in range 0 - 1 0.9 => m.gain; // if not the last note if(i < pitch.size() - 1){ // if there are more than one steps if(xMiniSteps < 1) { 1 => xMiniSteps; } // play main note playNote(dt[i] / xMiniSteps); // for the midi notes to be played for (1 => int j; j < xMiniSteps; j++){ // assign smaller gain to mini notes 0.1 => m.gain; // compute pitch Std.mtof(pitch[i] + Math.sgn(dpitch[i]) * j) => m.freq; // play mini note playNote(dt[i] / xMiniSteps); } } } // hold primary note one last time playNote(dt[dt.size() - 1]); } // sonifies with equal interval y interpolations // requires updated dx, y, dy private void yInterpSonify(float dt[], float pitch[], float dpitch[]){ // looping through each main note to sonify for (0 => int i; i < pitch.size(); i++) { // set main note frequency and play Std.mtof(pitch[i]) => m.freq; 0.9 => m.gain; // if not the last note if(i < pitch.size() - 1){ // compute number of mini-steps until next note Math.fabs(Math.floor(dpitch[i])) => float miniSteps; if(miniSteps < 1){ 1 => miniSteps; } // play main note playNote(dt[i] / miniSteps); // for the midi notes to be played for (1 => int j; j < miniSteps; j++){ // assign smaller gain to mini notes 0.1 => m.gain; // compute pitch Std.mtof(pitch[i] + Math.sgn(dpitch[i]) * j) => m.freq; // play mini note playNote(dt[i] / miniSteps); } } } // hold primary note one last time playNote(dt[dt.size() - 1]); } // handles playing notes, which involves toggling envelope on and off // noteDuration: seconds private void playNote(float noteDuration){ e.keyOn(); onFraction * noteDuration ::second => now; e.keyOff(); (1 - onFraction) * noteDuration ::second => now; }