public class Voice{ // name of this voice string name; // stores the voice as note steps int stepArr[][]; // includes the tempo int tempo; // since it's by steps, the last duration is dropped. Include it here int last_dur; // initialize based on a voice array in the form of [[midiNum, dur]*] fun void init(string name,int voice[][], int temp){ voiceToStep(voice) @=> stepArr; voice[voice.cap()-1][1] => last_dur; temp => tempo; } fun int[][] voiceToStep(int voice[][]){ if(voice.cap() <= 1) return [[-1,-1]]; int foo[voice.cap()-1][2]; int note1, note2; for(0 => int i; i < voice.cap() -1; i++){ voice[i][0] => note1; voice[i+1][0] => note2; (note2 - note1) => foo[i][0]; voice[i][1] => foo[i][1]; } return foo; } fun void printSteps(){ for(0 => int i; i < stepArr.cap(); i++){ <<< stepArr[i][0] >>>; } } fun float[] findBestMatch(int segment[][]){ [-1.0,-1] @=> float result[]; voiceToStep(segment) @=> int segAsSteps[][]; segAsSteps.cap() => int size; // only works if given at least 4 notes... if(size < 3) return result; toStepVector(segAsSteps) @=> int segVector[]; //int segTempoVector[0]; //for(0 => int i; i < size; i++){ // segTempoVector << segment[i][1]; //} for(0 => int i; i < stepArr.cap() - size; i++){ int stepVector[0]; //int tempoVector[0]; for(0 => int j; j < size; j++){ stepVector << stepArr[i+j][0]; //tempoVector << stepArr[i+j][1]; } getAngleBetweenVectors(segVector, stepVector) + 1.0 => float stepAngle; //getAngleBetweenVectors(segTempoVector, tempoVector) + 1.0 => float tempoAngle; //stepAngle * tempoAngle => float angle; stepAngle => float angle; //<<< "stepAngle: " + stepAngle + " tempoAngle: " + tempoAngle + " angle: " + angle + " index: " + i >>>; if(result[0] == -1.0 || angle < result[0]){ angle => result[0]; i => result[1]; } } return result; } fun int[] toStepVector(int s[][]){ int foo[s.cap()]; for(0 => int i; i< s.cap(); i++){ s[i][0] => foo[i]; } return foo; } // calculate the angle between two vectors based on law of cosine fun float getAngleBetweenVectors(int a[], int b[]){ a.cap() => int size; 0 => int dotProduct; 0.0 => float normA; 0.0 => float normB; for(0 => int i; i < size; i++){ dotProduct + a[i]*b[i] => dotProduct; normA + a[i]*a[i] => normA; normB + b[i]*b[i] => normB; } Math.sqrt(normA) => normA; Math.sqrt(normB) => normB; // if there's a zero vector (??), then assume matches perfectly (??) if(normA == 0 && normB == 0) return 0.0; (dotProduct / (normA * normB)) => float ratio; if(Math.fabs(ratio - 1.0) < 0.0001 ) return 0.0; // float round-off error causes nan-value //<<< "ratio:", ratio, "angle:", Math.acos(ratio)>>>; return Math.acos(ratio); } fun dur[] getDurations(int tempo){ dur duration[9]; 240000::ms / ( 1 * tempo ) => duration[1]; // whole 240000::ms / ( 2 * tempo ) => duration[2]; // half 240000::ms / ( 4 * tempo ) => duration[4]; // quarter 240000::ms / ( 8 * tempo ) => duration[8]; // eighth (duration[4] + duration[8]) => duration[5]; // dotted quarter (duration[2] + duration[4]) => duration[3]; // dotted half return duration; } // startNote is the midi key number of the first note // can be taken from the segment's first note fun void playVoice(StkInstrument m, int startNote, int startIndex) { Event finish; getDurations(tempo) @=> dur duration[]; // plays first note std.mtof(startNote) => m.freq; m.noteOn(1.0); duration[stepArr[0][1]] => now; // add the difference back for each subsequent note startNote => int note; for( startIndex => int i; i < stepArr.cap(); i++) { note + stepArr[i][0] => note; std.mtof(note) => m.freq; m.noteOn(1.0); if(i < stepArr.cap() -1) duration[stepArr[i+1][1]] => now; } duration[last_dur] => now; finish.broadcast(); } }