//-- // name: DroneBar.ck // desc: feedback- // // relies on modified Tracking.ck (Ge Wang + Rebecca Fiebrink, 2007) // // author: Andy Stuhl // date: 2013 // -- 3 => int nTones; //should sustained loudness from the guitar drive up the number of tones? (will need to add a maxTones parameter for array generation) 4 => int nPTones; 0.005 => float SILENCE_THRESHOLD; 200::ms => dur SWITCHER_THRESHOLD; 0.25 => float PTONE_THRESHOLD; 1000 => float filterBase; 200::ms => dur baseGap; baseGap => dur gapFactor; dur silenceTracker; time start; false => int exiting; adc.chan(0) => Dyno d => JCRev r => dac; d.compress(); 10::ms => d.attackTime; 2000::ms => d.releaseTime; 0.2 => d.thresh; 0.33 => d.slopeAbove; 1.3 => d.gain; 0.05 => r.mix; 1.2 => r.gain; TriOsc active[nTones]; Pan2 p[nTones]; Envelope e[nTones]; BPF filter[nTones]; for (0 => int i; i < nTones; i++) { active[i] => e[i] => filter[i] => p[i] => dac; 1 => active[i].gain; 50::ms => e[i].duration; 1 => filter[i].Q; 1 => filter[i].gain; filterBase => filter[i].freq; } 0 => int nActive; float activeTones[nTones]; false => int match; 0 => float lastFreq; pTone pTones[nPTones]; 0 => int activePTones; 0 => int totalRecurrence; int currentPTones[nTones]; int markov[nPTones][nPTones]; int markovTotals[nPTones]; for (0 => int x; x < nPTones; x++) { for (0 => int y; y < nPTones; y++) nPTones => markov[x][y]; nPTones * nPTones => markovTotals[x]; } 0 => int lastPToneDetected; 0 => int lastPToneActivated; //main control loop: spork ~ switcher(); spork ~ filterSweep(); spork ~ KeyListener(); while( true ) { false => match; Tracking.the_event => now; now => start; if (!exiting && Tracking.the_gain > SILENCE_THRESHOLD) { //should a recent replacement temporarily lower the threshold? (for chord changes) 0::ms => silenceTracker; baseGap => gapFactor; for (0 => int i; i < nActive; i++) { if (activeTones[i] == Tracking.the_freq) { amplifyActive(i); true => match; } } if (!match) { if (Tracking.the_freq == lastFreq) makeActive(Tracking.the_freq, false); else { Tracking.the_freq => lastFreq; } } } else { 5::ms => now; silenceTracker + (now - start) => silenceTracker; if (silenceTracker % baseGap == 0::ms) { gapFactor / 2 => gapFactor; <<< "gapFactor halved to ", gapFactor >>>; } //<<< "silence detected for ", silenceTracker >>>; } decay(); } fun int makeActive(float f, int isPTone) { //replace quietest tone 0 => int replace; 0.0 => float targetGain; if (nActive < nTones) { nActive => replace; nActive ++; } else { if (isPTone) Math.rand2(0, nActive - 1) => replace; else for (1 => int i; i < nTones; i++) { if (e[i].value() < e[replace].value()) i => replace; } e[replace].value() => targetGain; e[replace].keyOff(); e[replace].duration() => now; } f => active[replace].freq; f => activeTones[replace]; if (isPTone) { true => currentPTones[replace]; 500::ms => e[replace].duration; } else { false => currentPTones[replace]; 50::ms => e[replace].duration; } Math.rand2f(-1, 1) => p[replace].pan; e[replace].target(targetGain); e[replace].duration() => now; <<< "newly active: ", f, "; pan = ", p[replace].pan() >>>; return replace; } fun void amplifyActive(int i) { (e[i].value() + .01) / 1.01 => e[i].value; //<<< "amplified tone at ", activeTones[i], " to ", e[i].value() >>>; //amplfication shouldn't be linear, should be quicker for tones that have recurred a lot //update pTones false => int toneMatch; if (e[i].value() > PTONE_THRESHOLD) { for (0 => int j; !toneMatch && j < nPTones; j++) { if (j >= activePTones) { activeTones[i] => pTones[activePTones].freq; activePTones ++; true => toneMatch; <<< "new pTone at freq ", activeTones[i], "; ", activePTones, "total" >>>; } else if (pTones[j].freq == activeTones[i]) { pTones[j].recurrence ++; totalRecurrence ++; //<<< "total recurrence at ", totalRecurrence >>>; //update Markov if (j != lastPToneDetected) { markov[lastPToneDetected][j] ++; markovTotals[lastPToneDetected] ++; j => lastPToneDetected; } true => toneMatch; } } } } fun void decay() { //each active tone has its own decay rate influenced by algorithm, shared fundamentals? for (0 => int i; i < nActive; i++) { if (exiting || !(currentPTones[i] && silenceTracker > SWITCHER_THRESHOLD)) //(e[i].value() + .0008) / 1.0008 => e[i].value; //else e[i].value() / 1.01 => e[i].value; } } fun void switcher() { int flip; int sum; int toneIndex; while (true) { //sustained silence from the guitar kicks off algorithmic foolery if (silenceTracker > SWITCHER_THRESHOLD) { <<< "SWITCHING!" >>>; if (activePTones < nPTones) { Math.rand2(0, totalRecurrence) => flip; 0 => sum; for (0 => int i; i < activePTones; i++) { if (flip < pTones[i].recurrence + sum) { makeActive(pTones[i].freq, true) => toneIndex; <<< "pTone", i, "picked at ", pTones[i].freq, " with recurrence ", pTones[i].recurrence, " out of ", totalRecurrence, "; flip =", flip >>>; e[toneIndex].target((e[toneIndex].value() + (pTones[i].recurrence / totalRecurrence)) / 1.1); break; } sum + pTones[i].recurrence => sum; } } else { Math.rand2(0, markovTotals[lastPToneActivated]) => flip; 0 => sum; for (0 => int i; i < activePTones; i++) { if (flip < markov[lastPToneActivated][i] + sum) { makeActive(pTones[i].freq, true) => toneIndex; <<< "pTone", i, "picked at ", pTones[i].freq, "from Markov" >>>; e[toneIndex].target((e[toneIndex].value() + (markov[lastPToneActivated][i] / markovTotals[lastPToneActivated])) / 1.1); i => lastPToneActivated; break; } sum + markov[lastPToneActivated][i] => sum; } } } ((nPTones - activePTones) + Math.rand2(1, 4)) * gapFactor => now; } } fun void filterSweep() { int index; float speedFactor; float target; float flip; while (true) { if (nActive > 0) { Math.rand2(0, nActive - 1) => index; Math.rand2f(6.0, 16.0) => speedFactor; Math.rand2f(-1.0, 1.0) => flip; <<< "sweeping tone", index, "with speed factor", speedFactor, flip >>>; if (flip < 0) { while (filter[index].freq() > 20) { filter[index].freq() - speedFactor => filter[index].freq; 1::ms => now; } while (filter[index].freq() < filterBase) { filter[index].freq() + speedFactor => filter[index].freq; 1::ms => now; } } else { while (filter[index].freq() < 20000) { filter[index].freq() + speedFactor => filter[index].freq; 5::ms => now; } while (filter[index].freq() > filterBase) { filter[index].freq() - speedFactor => filter[index].freq; 5::ms => now; } } } 1::second => now; } } fun void KeyListener() { // the device number to open 0 => int deviceNum; // instantiate a HidIn object HidIn hi; // structure to hold HID messages HidMsg msg; // open keyboard if( !hi.openKeyboard( deviceNum ) ) me.exit(); // successful! print name of device <<< "keyboard '", hi.name(), "' ready" >>>; // infinite event loop while( true ) { // wait on event hi => now; // get one or more messages while( hi.recv( msg ) ) { // check for action type if( msg.isButtonDown() ) { if( msg.which == 44 ) true => exiting; } } } } class pTone { float freq; int recurrence; } 1::day => now;