Here are the files for the final presentation:
Project description (PDF)
The code (text)
Tuesday, June 8, 2010
Slides for the final presentation
Monday, June 7, 2010
Tune-up
Today I was testing my (working!) system. I ended up with 5 response types functions, triggered accordingly to the input parameters and controlled by them in real time. More detailed explanation will be provided in the final paper. Beside that I implemented starting and stopping the whole system from the GUI, without the need to re-run the code.
Here's the close-to-the-final code:
Here's the close-to-the-final code:
(
var numinchannels, samplerate, controlrate, masterVol, bufferArray, longbufferLength, fftbuflength, muteInput, in, out, st_out, timeOffset, pathprefix, inputBoost, minimalEventDuration, currentEventNumber, onsetOffset, soundInNo, inputOffset, continuousDataFreq, maxEvents; //general variables
var recorder, play, straightPlayer, mainPlayer, rec1, time, playCnt, checkPlayers, checkDuration, phrasePlay, clear, init, getPhrases, phraseAnalyze, dirPhraseAnalyze, phraseRespond, grainsynth, makeResponse, grainTask; //synthdefs and functions
var startResp, stopResp, contResp; //responders
var longbuf, fftbuf; //buffers
var window1, midiNoteKnob, midiAvKnob, hasFreqAvKnob, ampAvKnob, loudnessAvKnob, amplitudeKnob, starttimeNumber, durationNumber, recButton, playButton, onsetNumber, playNumber, clearButton, phraseNumber, ampTrigLoKnob, ampTrigHiKnob, onsetThreshKnob, noteChangeTrigLoKnob, noteChangeTrigHiKnob, noteChangeKnob, pauseNumber, startButton, stopButton; //gui
var midiNotesAvKnob, midiNotesDirKnob, midiSlopeAvKnob, midiSlopeDirKnob, densityAvKnob, densityDirKnob, loudnAvKnob, loudnDirKnob, attitudeKnob, counterpointKnob, stabilityKnob, stablePhraseLengthKnob, durationsAvKnob, durationsDirKnob; //gui extracted params
//var midiNote, midiAv, hasFreqAv, ampAv, loudnessAv, amplitude, duration, oldstarttime, oldstoptime, pause; //changing values
var mainList, phraseList, durPhrases, pitchPhrases, durPitchPhrases, extractedEvents; //arrays with events and phrases
var response1, response2, response3, response4, response5, response6, response7;//phrase responses
var midiNotesAv, midiNotesDir, midiSlopeAv, midiSlopeDir, densityAv, densityDir, loudnAv, loudnDir, regularityAv, regularityDir, durationsAv, durationsDir, stability; //realtime parameters
var longEventThresh, highDensity, highMidiSlope, highRegister, stabilityThresh, highLoudness, highLoudnessDif, longPauseLo, longPauseHi;//responder conditions
var densSTime, loudSTime, densStab, loudStab, stabilityThreshTime, stabilityLoudThresh, stabilityDensThresh, stablePhrasesLengths, stablePhrasesLengthsMemory, stablePhraseSTime; //stability and stable phrase length calculation
//var attitude, counterpoint; //attitude 1 = continue on your own, 0 = stop right after input, counterpoin1 1 = use opposite paramteres to the input, 0 = use similar
var complexSlopeAvTresh, complexLengthAvTresh; //thresholds
// ----------- variables to set ------------------------- //
in = 0; //number of the input channel
st_out = 0; //number of the first channel of stereo out
masterVol = 0; //in dB
inputBoost = 0; //input amplification in dB
longbufferLength = 600; //length of rec buffers in seconds
onsetOffset = -0.08; //compensate for onset delay; negative values if onsets are too late; -0.08 seems to be fine at the moment
minimalEventDuration = 0.2; //in seconds - to avoid unnecessary retriggering
complexSlopeAvTresh = 2; //amount of midi slope average for the event above which event is treated as "complex" (multi-note)
complexLengthAvTresh = 1; //the shortest complex event
continuousDataFreq = 30; //Hz
stabilityThreshTime = 10; //time to consider the phrase too stable - seconds
stabilityLoudThresh = 0.3; //loudness variation +- above which the phrase isn't considered too stable anymore
stabilityDensThresh = 0.1; //density variation +- above which the phrase isn't considered too stable anymore
stablePhrasesLengthsMemory = 4; //number of stable phrases' lengths remembered back form the current
maxEvents = 10000; //maximum number of events to remember
//responder conditions
highDensity = 0.6;
longEventThresh = 0.4;
highMidiSlope = 1.9;
highRegister = 57;
stabilityThresh = 0.6;
highLoudness = 29;
highLoudnessDif = 10;
longPauseLo = 6;
longPauseHi = 10;
// ------------------------------------------------------ //
// INIT
currentEventNumber = 0; //init event numbering
samplerate = Server.default.sampleRate; //get samplerate
controlrate = samplerate / Server.default.options.blockSize; //get control rate
if (samplerate < 50000, {fftbuflength = 1024}, {if (samplerate < 100000, {fftbuflength = 2048}, {fftbuflength = 4096})}); // set the FFT buffer size // fftbuflength.postln;
longbuf = CtkBuffer.new(size: samplerate * longbufferLength).load(sync: true); //main recording buffer
fftbuf = Buffer.alloc(Server.default, fftbuflength, 1); //fft buffer
stablePhrasesLengths = 0 ! stablePhrasesLengthsMemory; //init stable phrase length and stime
stablePhraseSTime = 0;
//phraseList = Array.newClear(8) ! 1000; //list of detected phrases
//durPhrases = Array.new; //list of phrases with similar dur
//pitchPhrases = Array.new; //list of phrases with similar pitch
inputOffset = in + Server.default.options.numOutputBusChannels;
densSTime = 0;
loudSTime = 0;
//controlrate.reciprocal.postln;
// init/clear functions
init = {
mainList = Array.newClear(8) ! maxEvents; //set up the global event list for the buffer; values for each array inside: startTime, midiNote, midiSlopeAv, hasFreqAv, ampAv, loudnessAv, duration, pause
rec1 = recorder.new; //initialize recorder
rec1.play; //start recorder
timeOffset = thisThread.seconds; // get runtime time
startResp.add;
stopResp.add;
contResp.add;
makeResponse.start; //run THE Task!
};
clear = ({arg kill = 0; // free whatever possible, if kill == 1, completely stop execution
rec1.free;
startResp.remove;
stopResp.remove;
contResp.remove;
makeResponse.stop;
response1.stop;
response2.stop;
response3.stop;
response4.stop;
response5.stop;
response6.stop;
if(kill == 1, {
longbuf.free;
fftbuf.free;
AppClock.sched(0.3, {Task({51.do({arg i; {window1.alpha_(1 - (i * 0.02))}.defer; 0.007.wait;})}).play}); //fade out haha
AppClock.sched(1.03, {window1.close});
});
});
// SYNTHDEFS
// TODO - as of Jun 3rd - deprecated!!! - add freq event triggering to the recorder; then add temporal ges ture analysis; then create meaningful answer from the system...
recorder = CtkSynthDef(\recorder, {arg mute = 0, onsetTreshold = 0.24, threshHi = -36, threshLo = -49, averageDelay = 0.07, noteChangeTrigLo = 120.1, noteChangeTrigHi = 400, relaxtime = 0.1, floor = -30; //tresholds to be adjusted
var recTrigger, stopTrigger, ampTrigger, onsetTrigger, noteChange, noteTrigger, sumTrigger, negSumTrigger, negAmpTrigger, amplitude, timer, pauseTimer, pauseTimerLatch, durTimer, hasFreq, freq, trigger, freqSlope, sig, hasFreqAv, ampAv, freqAv, counter, recTrigger0, inGate, chain, localbuf, onsets, midiNote, midiSlope, midiAv, loudness, loudnessAv, controlReci, noteNoteGate, midiGated, chain2, loud;
inGate = Gate.ar(In.ar(inputOffset), mute.neg + 1) * (mute.neg + 1); //incoming sound with mute
amplitude = Lag3UD.ar(RunningSum.rms(inGate), 0, 0.05); //Amplitude.kr(LagUD.ar(inGate, 0.01, 0.4), 0.01, 1.8); //amplitude tracking with "smoothing" TRYYYY!!!!
// amplitude.ampdb.poll(25, \amplitude);
ampTrigger = Schmidt.kr(amplitude, threshLo.dbamp, threshHi.dbamp); //amplitude trigger
negAmpTrigger = ampTrigger.neg + 1;
// ampTrigger.poll(125, \ampTri);
sig = inGate;//Gate.ar(inGate, trigger); //not gated for now
controlReci = controlrate.reciprocal;
chain = FFT(fftbuf, inGate, 0.5); //start the chain
loudness = Clip.kr(Loudness.kr(chain), 0, 64); //loudness analysis
onsetTrigger = Ramp.kr(Onsets.kr(chain, onsetTreshold), 0.01); //onset detection lagged
// onsetTrigger = Onsets.kr(chain, onsetTreshold);
// onsetTrigger.poll(125, \onsetT);
RecordBuf.ar(sig, longbuf, 0, run: 1, loop: 0, trigger: 1); //recorder
#freq, hasFreq = Tartini.kr(sig); //pitch analysis
// midiNote = freq.cpsmidi;
midiNote = LagUD.kr(freq.cpsmidi, 0.03, 0.03); //convert to midi notes - lagged -------- !params
noteNoteGate = Schmidt.kr(hasFreq, 0.85, 0.9);
midiGated = Gate.kr(midiNote, noteNoteGate);
midiSlope = (midiGated - DelayL.kr(midiGated, 2, averageDelay)).abs; //amount of change of midi note
noteChange = (midiSlope - DelayL.kr(midiSlope, 2, averageDelay)).abs * controlrate; //speed of pitch change
noteTrigger = Ramp.kr(Schmidt.kr(noteChange, noteChangeTrigLo, noteChangeTrigHi), 0.01); //lagged
// noteTrigger.poll(125, \noteTrig);
sumTrigger = Clip.kr(Trig1.kr(noteTrigger + onsetTrigger, 0.11) * ampTrigger, 0, 1); //Trig1 to avoid double triggers?
// sumTrigger.poll(125, "sumTri----------");
negSumTrigger = SetResetFF.kr(sumTrigger, negAmpTrigger).neg + 1;
// negSumTrigger.poll(100, "negSumTrigger");
midiAv = AverageOutput.kr(midiSlope, DelayL.kr(sumTrigger, 2, averageDelay)); //average of midi note change
hasFreqAv = AverageOutput.kr(hasFreq, DelayL.kr(sumTrigger, 2, averageDelay)); //average amount of pitched sounds in the sample
ampAv = AverageOutput.kr(amplitude, DelayL.kr(sumTrigger, 2, averageDelay)).ampdb; //average amplitude of the sample
loudnessAv = AverageOutput.kr(loudness, DelayL.kr(sumTrigger, 2, averageDelay)); //average lodness
timer = Phasor.kr(1, controlrate.reciprocal, 0, longbufferLength); //timer to report rec time
// pauseTimer = Phasor.kr(negSumTrigger, controlrate.reciprocal, 0, longbufferLength) * DelayL.kr(negAmpTrigger, 2, 0.2); //send pause AFTER current event - with Delay2 it gets MOST pauses in tests...
pauseTimer = Phasor.kr(negAmpTrigger, controlrate.reciprocal, 0, longbufferLength) * DelayL.kr(negSumTrigger, 0.5, 0.02); //send pause AFTER current event - with Delay2 it gets MOST(?) pauses in tests...
pauseTimerLatch = pauseTimer + (Latch.kr(pauseTimer, Delay1.kr(sumTrigger)) * ampTrigger);
// pauseTimerLatch.poll(15, "timerlatch");
durTimer = Gate.kr(Phasor.kr(Delay1.kr(sumTrigger), controlrate.reciprocal, 0, longbufferLength), ampTrigger); //send dur of the current event
SendReply.kr(sumTrigger, 'start' , [timer, midiNote, midiAv, hasFreqAv, ampAv, loudnessAv, durTimer, pauseTimer]); //send start times for events
SendReply.kr(Impulse.kr(continuousDataFreq), 'cont' , [midiNote, midiAv, hasFreqAv, ampAv, loudnessAv, durTimer, pauseTimer, amplitude.ampdb, noteChange]); //test
SendReply.kr(negSumTrigger, 'stop' , [timer, midiNote, midiAv, hasFreqAv, ampAv, loudnessAv, durTimer, pauseTimer]); //send stop times for events
});
//main player works! - mod: playback speed mod, filter type: 0 - LPF, 1 - BPF, 2 - HPF,
mainPlayer = CtkSynthDef(\mainPlayer, {arg startPos = 0, dur = longbufferLength, grainDur = 0.05, warpToGrain = 1, grainFreq = 27, grainFreqRand = 4, grainPitchRand = 0.01,randPan = 1, filterAmt = 0, filterFreq = 4000, filterType = 2, buffer = longbuf, amp = 0, pan = 0, rate = 1, pitch = 1, modAmt = 0, modFreq = 0, att = 0.01, rel = 0.01, gate = 1;
var src, pointer, env, envGen, enc_w, enc_x, enc_y, enc_z, decoded, mod, secondLeg, scaledDur, warp, grain, trigger, triggeredPan, triggeredPitch, mix, filters;
scaledDur = dur * rate.reciprocal;
secondLeg = scaledDur - (scaledDur * att) - (scaledDur * rel);
envGen = EnvGen.kr(Env([0.001, 1, 1, 0.001], [att * scaledDur, secondLeg, rel * scaledDur], \exponential), gate, doneAction: 2);
mod = SinOsc.ar(modFreq, 0, modAmt) + modAmt;
pointer = Phasor.kr(1, rate * controlrate.reciprocal / longbufferLength, startPos / longbufferLength, (startPos + dur) / longbufferLength);
// pointer.poll(20, "pointer");
trigger = Impulse.kr(grainFreq + TRand.kr(grainFreqRand.neg, grainFreqRand, Impulse.kr(grainFreq)));
triggeredPan = TRand.kr(randPan.neg, randPan, trigger);
triggeredPitch = TRand.kr(grainPitchRand.neg, grainPitchRand, trigger);
warp = Pan2.ar(Warp1.ar(1, buffer, pointer, pitch, 0.14, -1, 4, 0.02, 4), pan);
grain = GrainBuf.ar(2, trigger, grainDur, buffer, Clip.kr(pitch + triggeredPitch, -10, 10), pointer, 4, Clip.kr(triggeredPan + pan, -1, 1));
mix = ((warp * warpToGrain) + (grain * (1 - warpToGrain))) * envGen;
filters = (LPF.ar(mix, filterFreq) * Clip.kr(1 - filterAmt, 0, 1)) + (BPF.ar(mix, filterFreq, 0.3) * Clip.kr(((filterAmt - 1).abs.neg + 1), 0, 1)) + (HPF.ar(mix, filterFreq) * Clip.kr(filterAmt - 1, 0, 1));
src = DelayC.ar((mix * (1 - filterAmt)) + (filters * filterAmt), 2, mod);
// az = (az * pi) / 180; //to use az in degrees
// el = (el * pi) / 180; //to use el in degrees
// # enc_w, enc_x, enc_y, enc_z = BFEncode1.ar(src, az, el, 1, 1, 0);
// Out.ar(st_out, B2UHJ.ar(enc_w, enc_x, enc_y, enc_z) * amp.dbamp * masterVol.dbamp); //stereo out
Out.ar(st_out, src * amp.dbamp * masterVol.dbamp); //stereo out
// Out.ar(out, [enc_w, enc_x, enc_y, enc_z] * amp.dbamp * masterVol.dbamp); //ambisonics encoded out
});
//mainPlayer.new(4).buffer_(longbuf).dur_(5).play;
// FUNCTIONS (~200 lines)
time = {thisThread.seconds - timeOffset}; //get time relative to start
checkDuration = {arg type, respTime;
if((type == 1), { //if this is startTime
// "mainList[currentEventNumber - 2]: ".post; mainList[currentEventNumber - 2].postln;
// "mainList[currentEventNumber - 1]: ".post; mainList[currentEventNumber - 1].postln;
// "mainList[currentEventNumber]: ".post; mainList[currentEventNumber].postln;
if((mainList[currentEventNumber][0].notNil) && (respTime > minimalEventDuration), {currentEventNumber = currentEventNumber + 1; mainList[currentEventNumber][1] = 0; mainList[currentEventNumber][2] = 0; mainList[currentEventNumber][3] = 0; mainList[currentEventNumber][4] = 0; mainList[currentEventNumber][5] = 0; mainList[currentEventNumber][6] = 0; mainList[currentEventNumber][7] = 0; }); //advance, exception for the first one, ommit too short events, get rid of nils
mainList.at(currentEventNumber).putEach(0, respTime); //put the start time into array
// "mainList[currentEventNumber] after advance: ".post; mainList[currentEventNumber].postln;
{recButton.value = 1;
starttimeNumber.value = respTime;
onsetNumber.value = currentEventNumber;}.defer;
}, {//if this is stopTime
// currentEventNumber = currentEventNumber + 1; //advance on stoptime
{recButton.value = 0}.defer
});
phraseAnalyze.value;
};
//array: startTime, midiNote, midiSlopeAv, hasFreqAv, ampAv, loudnessAv, duration, pause
phraseAnalyze = {arg lookBackEvents = 10;//number of events to get current "character" and current "direction" for certain parameters
var farthestEvent, midiAr, midiNoComplexAr, slopeAr, lastSlopes, pausesAr, dursAr, pausesSum, dursSum, loudnesAr;
// "lookBackEvents before if: ".post; lookBackEvents.postln;
// "current ev before: ".post; currentEventNumber.postln;
if(currentEventNumber < lookBackEvents, {lookBackEvents = currentEventNumber}); //decrease lookback at the beginning
farthestEvent = (currentEventNumber - lookBackEvents - 1).thresh(0); //to not include the actual item in calculations
// "lookBackEvents: ".post; lookBackEvents.postln;
// "current ev: ".post; currentEventNumber.postln;
// "farthestEvent: ".post; farthestEvent.postln;
extractedEvents = Array.new;
lookBackEvents.do({arg i; //get last number of events into a separate array
if(mainList.at(i + farthestEvent).at(0).notNil,
{extractedEvents = extractedEvents.add(mainList.at(i + farthestEvent))
});
});
// "extractedEvents: ".post; extractedEvents.postln;
// "extractedEvents[lookBackEvents - 1]: ".post; extractedEvents[lookBackEvents - 1].postln;
// "extractedEvents[lookBackEvents - 2]: ".post; extractedEvents[lookBackEvents - 2].postln;
// "extractedEvents[lookBackEvents - 3]: ".post; extractedEvents[lookBackEvents - 3].postln;
//create arrays
midiAr = Array.new;
slopeAr = Array.new;
midiNoComplexAr = Array.new;
lastSlopes = Array.new;
pausesAr = Array.new;
dursAr = Array.new;
loudnesAr = Array.new;
lookBackEvents.do({arg i;
if(extractedEvents[i].notNil, {
midiAr = midiAr.add(extractedEvents[i][1]); //extract array of midi notes
pausesAr = pausesAr.add(if(extractedEvents[i][7].notNil, extractedEvents[i][7], 0)); //extract array of pauses
dursAr = dursAr.add(if(extractedEvents[i][6].notNil, extractedEvents[i][6], 0)); //extract array of durations
loudnesAr = loudnesAr.add(if(extractedEvents[i][5].notNil, extractedEvents[i][5], 0)); //extract array of loudnesses
if((extractedEvents[i][2].notNil) && (extractedEvents[i][5].notNil), {
if((extractedEvents[i][2] < complexSlopeAvTresh) && (extractedEvents[i][5] < complexLengthAvTresh), //and array of notes without complex events
{midiNoComplexAr = midiNoComplexAr.add(extractedEvents[i][1]);
if(midiNoComplexAr.size > 1,
{slopeAr = slopeAr.add(absdif(midiNoComplexAr[midiNoComplexAr.size - 2], midiNoComplexAr[midiNoComplexAr.size - 1]))
});
}, { //add midiSlope from complex events
slopeAr = slopeAr.add(extractedEvents[i][2]);
};
);
});
});
});
// "midiAr: ".post; midiAr.postln;
midiNotesAv = midiAr.mean;
// "midiNotesAv: ".post; midiNotesAv.postln;
// "slopeAr: ".post; slopeAr.postln;
midiSlopeAv = slopeAr.mean;
// "midiSlopeAv: ".post; midiSlopeAv.postln;
// "pausesAr: ".post; pausesAr.postln;
// "dursAr: ".post; dursAr.postln;
if(pausesAr.includes(nil), {pausesSum = 0}, {pausesSum = pausesAr.sum}); //for density: sum pauses
if(dursAr.includes(nil), {dursSum = 0; durationsAv = 0}, {dursSum = dursAr.sum; durationsAv = dursAr.mean}); //sum durs and mean durs!
// "pausesSum: ".post; pausesSum.postln;
// "dursSum: ".post; dursSum.postln;
if((pausesSum == 0) && (dursSum == 0), {densityAv = 0}, {densityAv = ((dursSum - pausesSum) / (dursSum + pausesSum)).clip(0, 1)}); //if both of pauses and durs are 0, then density is 0, if not, then it's calculated
// "densityAv: ".post; densityAv.postln;
loudnAv = loudnesAr.mean;
// "loudnAv: ".post; loudnAv.postln;
{midiNotesAvKnob.value = midiNotesAv;
midiSlopeAvKnob.value = midiSlopeAv;
densityAvKnob.value = densityAv;
durationsAvKnob.value = durationsAv;
loudnAvKnob.value = loudnAv;
}.defer;
};
dirPhraseAnalyze = {arg dirLookBack = 3;//number of events to get current "character" and current "direction" for certain parameters
var slopeDirAr, dirFarthestEvent, midiNoComplexDirAr, pausesDirAr, dursDirAr, pausesDirSum, dursDirSum;//for live
if(currentEventNumber < dirLookBack, {dirLookBack = currentEventNumber}); //decrease dirlookback at the beginning
dirFarthestEvent = (currentEventNumber - dirLookBack).thresh(0); //
midiNoComplexDirAr = Array.new;
slopeDirAr = Array.new;
pausesDirAr = Array.new;
dursDirAr = Array.new;
dirLookBack.do({arg i; //get last number of events into a separate array
if(mainList[i + dirFarthestEvent][7].notNil, {
pausesDirAr = pausesDirAr.add(mainList[i + dirFarthestEvent][7]);
dursDirAr = dursDirAr.add(mainList[i + dirFarthestEvent][6]);
});
if(mainList[i + dirFarthestEvent][2].notNil, {
if((mainList[i + dirFarthestEvent][2] < complexSlopeAvTresh) && (mainList[i + dirFarthestEvent][5] < complexLengthAvTresh), //and array of notes without complex events
{midiNoComplexDirAr = midiNoComplexDirAr.add(mainList[i + dirFarthestEvent][1]);
if(midiNoComplexDirAr.size > 1,
{slopeDirAr = slopeDirAr.add(absdif(midiNoComplexDirAr[midiNoComplexDirAr.size - 2], midiNoComplexDirAr[midiNoComplexDirAr.size - 1])) //get slopes of those
})
}, { //add midiSlope from complex events
slopeDirAr = slopeDirAr.add(mainList[i + dirFarthestEvent][2]); //slope gets confused on long events!!! -------------- <<<<< ------ CHECK --------- !!!!!!!!! ------------
//"complex slope! ---------------".postln;
};
);
//lastSlopes = lastSlopes.add
//slopeDirAr = slopeDirAr.add(mainList[i + dirFarthestEvent][2]);
});
});
// "dursDirAr: ".post; dursDirAr.postln;
// "pausesDirAr: ".post; pausesDirAr.postln;
if(mainList[currentEventNumber][1].notNil, {midiNotesDir = mainList[currentEventNumber][1] - midiNotesAv});
// "midiNotesDir: ".post; midiNotesDir.postln;
midiSlopeDir = slopeDirAr.mean - midiSlopeAv;
// "midiSlopeDir: ".post; midiSlopeDir.postln;
if(pausesDirAr.includes(nil), {pausesDirSum = 0}, {pausesDirSum = pausesDirAr.sum}); //for density: sum pauses
if(dursDirAr.includes(nil), {dursDirSum = 0}, {dursDirSum = dursDirAr.sum}); //sum durs
if((dursDirSum == 0) && (pausesDirSum == 0), {densityDir = 0 - densityAv; durationsDir = 0}, {densityDir = ((dursDirSum - pausesDirSum) / (dursDirSum + pausesDirSum)) - densityAv; durationsDir = dursDirAr.mean - durationsAv});
// "((dursDirSum - pausesDirSum) / (dursDirSum + pausesDirSum)): ".post; ((dursDirSum - pausesDirSum) / (dursDirSum + pausesDirSum)).postln;
// "densityDir: ".post; densityDir.postln;
if(mainList[currentEventNumber][5].notNil, {loudnDir = mainList[currentEventNumber][5] - loudnAv}, {loudnDir = 0});
// "loudnDir: ".post; loudnDir.postln;
//stability calc - will use density and loudness for now - var densSTime, loudSTime, densStab, loudStab; stabilityThreshTime, stabilityLoudThresh, stabilityDensThresh
if((loudnDir < stabilityLoudThresh) && (loudnDir > stabilityLoudThresh.neg), {loudStab = ((time.value - densSTime) / stabilityThreshTime).clip2(1)}, {loudSTime = time.value; loudStab = 0});
if((densityDir < stabilityDensThresh) && (densityDir > stabilityDensThresh.neg), {densStab = ((time.value - densSTime) / stabilityThreshTime).clip2(1)}, {densSTime = time.value; densStab = 0});
if(loudStab > densStab, {stability = loudStab}, {stability = densStab});
//stable phrase length
if(((loudnDir < stabilityLoudThresh) && (loudnDir > stabilityLoudThresh.neg) || (densityDir < stabilityDensThresh) && (densityDir > stabilityDensThresh.neg)),{stablePhrasesLengths[stablePhrasesLengthsMemory - 1] = (time.value - stablePhraseSTime)}, {stablePhraseSTime = time.value; stablePhrasesLengths.shift(-1)});
//GUI control
{midiNotesDirKnob.value = midiNotesDir;
midiSlopeDirKnob.value = midiSlopeDir;
densityDirKnob.value = densityDir;
durationsDirKnob.value = durationsDir;
loudnDirKnob.value = loudnDir;
stabilityKnob.value = stability;
stablePhraseLengthKnob.value = stablePhrasesLengths[stablePhrasesLengthsMemory - 1];
}.defer;
};
//player for tests
phrasePlay = {arg phraseNo;
var startPos, dur;
startPos = mainList[phraseNo][0];
dur = mainList[phraseNo][6];
'PLAYER phrase no: '.post; phraseNo.post; ', startPos: '.post; startPos.post; ', duration: '.post; dur.postln;
// straightPlayer.new(0, dur).dur_(dur).startPos_(startPos).play;
mainPlayer.new(0, dur).dur_(dur).startPos_(startPos).play;
};
/*
phrase responses:
loudness - influencing amp and amount of response
stablePhrasesLengths influencing responses' length - mean of last three values? or of all the values of any of the analyzed?
for long and steady pitches, high density - resp1: long granulated answer + harmony? (long notes or stretched) OR - when too stable - short, scattered texture, (realtime parameters influence)
for long unsteady pitches - resp2: long stretched answer modulated OR many shorter notes
for short events, low density, LOUD - resp3: short/shortened scattered texture with filters OR long notes with matching pitch (?)
for short events, high density, LOUD - resp4: dense texture of short/shortened notes modulated OR many shorter notes
for very loud events - resp5: dense texture of short/shortened notes modulated
for long pause - generate random? - generate or not.. - resp6
--the above is being modified
//mainPlayer.new.startPos_(0).dur_(0).grainDur_(0).warpToGrain_(1).grainFreq_(27).grainFreqRand_(4).grainPitchRand_(0.01).randPan_(0).filterAmt_(0).filterFreq_(4000).filterType_(2).buffer_(longbuf).amp_(0).pan_(0).rate_(1).pitch_(1).modAmt_(0).modFreq_(0).att_(0.01).rel_(0.01); //synth will free automatically without supplying note length
realtime analyzed parameters:
midiNotesAv, midiNotesDir, midiSlopeAv, midiSlopeDir, densityAv, densityDir, loudnAv, loudnDir, durationsAv, durationsDir, stability;
and use parameters from the mainList: startTime, midiNote, midiSlopeAv, hasFreqAv, ampAv, loudnessAv, duration, pause
*/
//--------------!!!!!!!!!!!!------------ main brain of the response generation -------------------!!!!!!!!!!!!!!-----------------
makeResponse = Task.new({
var now, pauseResp; //task vars
now = 0; //init time
1.wait; //give some time to start
"I'm in the Task!".postln;
//------TASKS go here------
response1 = Task.new({ //for long and steady pitches, high density - resp1: long granulated answer + harmony? (long notes or stretched) OR - when too stable - short, scattered texture, (realtime parameters influence)
var length, respNow, respIncr, chosenEvAr1, chosenEvAr2, stretchEvents, thisEvent, pitch, rate;
//mappings: densityDir to grain dur and grainFreq, realtime loudness to harmony size, and rand Pan, realtime midiSlopeDir to events speed in the phrase
length = stablePhrasesLengths.mean * 14; //get initial length for the phrase
if(length < (longEventThresh * 6), {length = stablePhrasesLengths.sum * 12}); //if it's too short, get the longer one
if(length < (longEventThresh * 6), {length = longEventThresh * 14}); //if it's STILL too short, get the hardcoded longer one
"playing response1! length: ".post; length.postln;
respNow = 0; //start internal time
chosenEvAr1 = Array.new; //long events array
chosenEvAr2 = Array.new; //short events array
mainList.do({arg array, index; //get indexes of long events
if(array[6].notNil, {
if(array[6] > longEventThresh,
{chosenEvAr1 = chosenEvAr1.add(index)}, {chosenEvAr2 = chosenEvAr2.add(index)}
)
}
)
});
if((chosenEvAr1.size.notNil), {if(chosenEvAr1.size < 1, {stretchEvents = 1}, {stretchEvents = 0})}); //if there are no long events, stretch short ones
if((chosenEvAr1.size.notNil) && (chosenEvAr2.size.notNil), { //if they are not nil
if((chosenEvAr1.size > 0) || (chosenEvAr2.size > 0), { //proceed only, when we have something to play
while({respNow < length},
{
// "response1: I'm in the while loop!".postln;
if(stability < stabilityThresh, {//play longer notes unless the phrase is too stable
5.rand.round(1).do({arg i;
if(stretchEvents == 0, {thisEvent = chosenEvAr1.choose; rate = rrand(0.5, 1)}, {thisEvent = mainList.size.rand; rate = rrand(0.2, 0.7)}); //get this event number
if(i > 0, {pitch = rrand(0.5, (mainList[currentEventNumber][5] * 0.1) + 0.7)}; {pitch = 1});
mainPlayer.new.startPos_(mainList[thisEvent][0]).dur_(mainList[thisEvent][6]).grainDur_((densityDir * 0.02) + 0.2).warpToGrain_(0).grainFreq_((densityDir * 5) + 15).grainFreqRand_((densityDir * 8) + 8).grainPitchRand_((densityDir * 0.1) + 0.1).randPan_((mainList[currentEventNumber][5] * 0.07)).filterAmt_(0).filterFreq_(4000).filterType_(2).buffer_(longbuf).amp_((mainList[currentEventNumber][5] * 0.3) - 10).pan_(0.1.rand2).rate_(rate).pitch_(pitch).modAmt_(0).modFreq_(0).att_(rrand(0.1, 0.2)).rel_(rrand(0.3, 0.6)).play;
respIncr = 1.1 + (mainList[currentEventNumber][2] * 0.5);
if(respIncr<=0, {respIncr = 0.1});
respIncr.wait; //that's prett crucial...
respNow = respNow + respIncr;
// "respNow from less stability: ".post; respNow.postln
}); //do loop closing bracket
}, { //when phrase becomes too stable...
thisEvent = chosenEvAr2.choose;//get this event number
rate = rrand(1, 5); //set the rate
if(thisEvent.isNil, {thisEvent = mainList.size.rand}); //if the array would be empty
pitch = rrand(0.7, (mainList[currentEventNumber][5] * 0.1) + 0.7);
mainPlayer.new.startPos_(mainList[thisEvent][0]).dur_(mainList[thisEvent][6]).grainDur_((densityDir * 0.02) + 0.2).warpToGrain_(0.8).grainFreq_((densityDir * 5) + 15).grainFreqRand_((densityDir * 2) + 4).grainPitchRand_((densityDir * 0.05) + 0.05).randPan_((mainList[currentEventNumber][5] * 0.015)).filterAmt_(0).filterFreq_(4000).filterType_(2).buffer_(longbuf).amp_((mainList[currentEventNumber][5] * 0.3) - 8).pan_(0.1.rand2).rate_(rate).pitch_(pitch).modAmt_(0).modFreq_(0).att_(0.3.rand).rel_(0.3.rand).play;
respIncr = 0.2 + (mainList[currentEventNumber][2].abs * 0.25);
if(respIncr<=0, {respIncr = 0.1});
respIncr.wait; //that's prett crucial...
respNow = respNow + respIncr;
// "respNow from more stability: ".post; respNow.postln;
});
}); //while loop closing bracket
}); //if arr size > 0 ending bracket
}); //if sizes are not nil ending bracket
});
response2 = Task.new({ //for long unsteady pitches - resp2: long stretched answer modulated OR many shorter notes
var length, respNow, respIncr, chosenEvAr1, chosenEvAr2, stretchEvents, thisEvent, pitch, rate;
//mappings: densityDir to modAmt and modFreq, realtime loudness to harmony size, and rand Pan, realtime midiSlopeDir to events speed in the phrase
length = stablePhrasesLengths.mean * 14; //get initial length for the phrase
if(length < (longEventThresh * 6), {length = stablePhrasesLengths.sum * 12}); //if it's too short, get the longer one
if(length < (longEventThresh * 6), {length = longEventThresh * 14}); //if it's STILL too short, get the hardcoded longer one
"playing response2! length: ".post; length.postln;
respNow = 0; //start internal time
chosenEvAr1 = Array.new; //long events array
chosenEvAr2 = Array.new; //short events array
mainList.do({arg array, index; //get indexes of long and short events
if(array[6].notNil, {
if(array[6] > longEventThresh,
{chosenEvAr1 = chosenEvAr1.add(index)}, {chosenEvAr2 = chosenEvAr2.add(index)}
)
}
)
});
if((chosenEvAr1.size.notNil) && (chosenEvAr2.size.notNil), { //if they are not nil
if((chosenEvAr2.size > 0), { //proceed only, when we have something to play
while({respNow < length},
{
// "response2: I'm in the while loop!".postln;
if(stability < stabilityThresh, {//use short events and stretch them!
5.rand.round(1).do({arg i;
thisEvent = chosenEvAr2.choose;//get this event number
rate = rrand(0.1, 0.3); //set the rate
if(thisEvent.isNil, {thisEvent = mainList.size.rand});
if(i > 0, {pitch = rrand(0.5, (mainList[currentEventNumber][5] * 0.1) + 0.7)}; {pitch = 1});
mainPlayer.new.startPos_(mainList[thisEvent][0]).dur_(mainList[thisEvent][6]).grainDur_((densityDir * 0.02) + 0.02).warpToGrain_(1).filterAmt_(0).filterFreq_(4000).filterType_(2).buffer_(longbuf).amp_((mainList[currentEventNumber][5] * 0.1) - 10).pan_(0.1.rand2).rate_(rate).pitch_(pitch).modAmt_((densityDir * 0.005) + 0.01).modFreq_((densityDir * 6) + 58).att_(rrand(0.1, 0.3)).rel_(rrand(0.3, 0.4)).play;
respIncr = 1.1 + (mainList[currentEventNumber][2] * 0.5); //tempo
if(respIncr<=0, {respIncr = 0.1});
respIncr.wait; //that's prett crucial...
respNow = respNow + respIncr;
}); //do loop closing bracket
}, { //when phrase becomes too stable...
thisEvent = chosenEvAr2.choose;//get this event number
rate = rrand(1, 3); //set the rate
if(thisEvent.isNil, {thisEvent = mainList.size.rand}); //if the array would be empty
pitch = rrand(0.7, (mainList[currentEventNumber][5] * 0.1) + 0.7);
mainPlayer.new.startPos_(mainList[thisEvent][0]).dur_(mainList[thisEvent][6]).grainDur_((densityDir * 0.02) + 0.2).warpToGrain_(0.8).grainFreq_((densityDir * 5) + 15).grainFreqRand_((densityDir * 2) + 4).grainPitchRand_((densityDir * 0.05) + 0.05).randPan_((mainList[currentEventNumber][5] * 0.015)).filterAmt_(0).filterFreq_(4000).filterType_(2).buffer_(longbuf).amp_((mainList[currentEventNumber][5] * 0.1) - 8).pan_(0.1.rand2).rate_(rate).pitch_(pitch).modAmt_(0).modFreq_(0).att_(0.3.rand).rel_(0.3.rand).play;
respIncr = 0.04 + (mainList[currentEventNumber][2] * 0.07);
if(respIncr<=0, {respIncr = 0.1});
respIncr.wait; //that's prett crucial...
respNow = respNow + respIncr;
});
}); //while loop closing bracket
}); //if arr size > 0 ending bracket
}); //if sizes are not nil ending bracket
});
response3 = Task.new({ //for short events, low density - resp3: short/shortened scattered texture with filters OR long notes with matching pitch (?)
var length, respNow, respIncr, chosenEvAr1, chosenEvAr2, shortenEvents, thisEvent, pitch, rate;
//mappings: realtime midiSlopeDir to events speed in the phrase, average pitch to filter freq and modulation params, loudnDir influences warp to grain in alternative case
length = stablePhrasesLengths.mean * 10; //get initial length for the phrase
if(length < (longEventThresh * 6), {length = stablePhrasesLengths.sum * 10}); //if it's too short, get the longer one
if(length < (longEventThresh * 6), {length = longEventThresh * 10}); //if it's STILL too short, get the hardcoded longer one
"playing response3! length: ".post; length.postln;
respNow = 0; //start internal time
chosenEvAr1 = Array.new; //long events array
chosenEvAr2 = Array.new; //short events array
mainList.do({arg array, index; //get indexes of long and short events
if(array[6].notNil, {
if(array[6] > longEventThresh,
{chosenEvAr1 = chosenEvAr1.add(index)}, {chosenEvAr2 = chosenEvAr2.add(index)}
)
}
)
});
if((chosenEvAr2.size.notNil), {if(chosenEvAr2.size < 1, {shortenEvents = 1}, {shortenEvents = 0})}); //if there are no short events, shorten long ones
if((chosenEvAr1.size.notNil) && (chosenEvAr2.size.notNil), { //if they are not nil
if((chosenEvAr1.size > 0) || (chosenEvAr2.size > 0), { //proceed only, when we have something to play
while({respNow < length},
{
// "response3: I'm in the while loop!".postln;
if(stability < stabilityThresh, {//use short events + filter
// 5.rand.round(1).do({arg i;
// thisEvent = chosenEvAr2.choose;//get this event number
// rate = rrand(0.8, 1.2); //set the rate
if(shortenEvents == 0, {thisEvent = chosenEvAr2.choose; rate = rrand(1.9, 2.3)}, {thisEvent = chosenEvAr1.choose; rate = rrand(3, 6)}); //get this event number
if(thisEvent.isNil, {thisEvent = mainList.size.rand});
// if(i > 0, {pitch = rrand(0.5, (mainList[currentEventNumber][5] * 0.1) + 0.7)}; {pitch = 1});
pitch = 1;
if(durationsDir.isNil, {durationsDir = 0});
mainPlayer.new.startPos_(mainList[thisEvent][0]).dur_(mainList[thisEvent][6]).grainDur_(0.1).warpToGrain_(1).filterAmt_(1).filterFreq_(midiNotesAv * 20).filterType_(2.rand).buffer_(longbuf).amp_((mainList[currentEventNumber][5] * 0.1) - 7).pan_(0.1.rand2).rate_(rate).pitch_(pitch).modAmt_((durationsDir * 0.002) + 0.002).modFreq_(midiNotesAv).att_(0.17.rand).rel_(0.2.rand).play;
respIncr = 0.19 + (mainList[currentEventNumber][2] * 0.08); //tempo
if(respIncr<=0, {respIncr = 0.1});
respIncr.wait; //that's prett crucial...
respNow = respNow + respIncr;
// }); //do loop closing bracket
}, { //when phrase becomes too stable...
thisEvent = chosenEvAr1.choose;//get this event number
rate = rrand(0.2, 0.7); //set the rate
if(thisEvent.isNil, {thisEvent = mainList.size.rand}); //if the array would be empty
pitch = rrand(0.4, (mainList[currentEventNumber][5] * 0.1) + 0.9);
mainPlayer.new.startPos_(mainList[thisEvent][0]).dur_(mainList[thisEvent][6]).grainDur_((densityDir * 0.02) + 0.2).warpToGrain_((loudnDir * 0.5) + 0.5).grainFreq_((densityDir * 5) + 15).grainFreqRand_((densityDir * 2) + 4).grainPitchRand_((densityDir * 0.05) + 0.05).randPan_((mainList[currentEventNumber][5] * 0.015)).filterAmt_(1).filterFreq_(midiNotesAv * 20).filterType_(2.rand).buffer_(longbuf).amp_((mainList[currentEventNumber][5] * 0.1) - 8).pan_(0.1.rand2).rate_(rate).pitch_(pitch).modAmt_(0).modFreq_(0).att_(0.3.rand).rel_(0.3.rand).play;
respIncr = 0.34 + (mainList[currentEventNumber][2] * 0.27);
if(respIncr<=0, {respIncr = 0.1});
respIncr.wait; //that's prett crucial...
respNow = respNow + respIncr;
});
}); //while loop closing bracket
}); //if arr size > 0 ending bracket
}); //if sizes are not nil ending bracket
});
response4 = Task.new({ //for short events, high density - resp4: dense texture of short/shortened notes modulated OR many shorter notes
var length, respNow, respIncr, chosenEvAr1, chosenEvAr2, shortenEvents, thisEvent, pitch, rate;
//mappings: realtime midiSlopeDir to events speed in the phrase, densiyDir to modulation params, loudnDir influences warp to grain in alternative case
length = stablePhrasesLengths.mean * 10; //get initial length for the phrase
if(length < (longEventThresh * 6), {length = stablePhrasesLengths.sum * 20}); //if it's too short, get the longer one
if(length < (longEventThresh * 6), {length = longEventThresh * 10}); //if it's STILL too short, get the hardcoded longer one
"playing response4! length: ".post; length.postln;
respNow = 0; //start internal time
chosenEvAr1 = Array.new; //long events array
chosenEvAr2 = Array.new; //short events array
mainList.do({arg array, index; //get indexes of long and short events
if(array[6].notNil, {
if(array[6] > longEventThresh,
{chosenEvAr1 = chosenEvAr1.add(index)}, {chosenEvAr2 = chosenEvAr2.add(index)}
)
}
)
});
if((chosenEvAr2.size.notNil), {if(chosenEvAr2.size < 1, {shortenEvents = 1}, {shortenEvents = 0})}); //if there are no short events, shorten long ones
if((chosenEvAr1.size.notNil) && (chosenEvAr2.size.notNil), { //if they are not nil
if((chosenEvAr1.size > 0) || (chosenEvAr2.size > 0), { //proceed only, when we have something to play
while({respNow < length},
{
// "response4: I'm in the while loop!".postln;
if(stability < stabilityThresh, {//use short events + filter
// 5.rand.round(1).do({arg i;
// thisEvent = chosenEvAr2.choose;//get this event number
// rate = rrand(0.8, 1.2); //set the rate
if(shortenEvents == 0, {thisEvent = chosenEvAr2.choose; rate = rrand(1.9, 2.3)}, {thisEvent = chosenEvAr1.choose; rate = rrand(3, 6)}); //get this event number
if(thisEvent.isNil, {thisEvent = mainList.size.rand});
// if(i > 0, {pitch = rrand(0.5, (mainList[currentEventNumber][5] * 0.1) + 0.7)}; {pitch = 1});
pitch = midiNotesAv * 0.03;
if(durationsDir.isNil, {durationsDir = 0});
mainPlayer.new.startPos_(mainList[thisEvent][0]).dur_(mainList[thisEvent][6]).grainDur_(0.1).warpToGrain_(1).filterAmt_(1).filterFreq_(midiNotesAv * 20).filterType_(2.rand).buffer_(longbuf).amp_((mainList[currentEventNumber][5] * 0.1) - 7).pan_(0.1.rand2).rate_(rate).pitch_(pitch).modAmt_((densityDir * 0.0005) + 0.001).modFreq_((densityDir * 4) + 16).att_(0.07.rand).rel_(0.1.rand).play;
respIncr = 0.1 + (mainList[currentEventNumber][2] * 0.08); //tempo
if(respIncr<=0, {respIncr = 0.1});
respIncr.wait; //that's prett crucial...
respNow = respNow + respIncr;
// }); //do loop closing bracket
}, { //when phrase becomes too stable...
thisEvent = chosenEvAr1.choose;//get this event number
rate = rrand(3, 8); //set the rate
if(thisEvent.isNil, {thisEvent = mainList.size.rand}); //if the array would be empty
pitch = rrand(0.2, (mainList[currentEventNumber][5] * 0.8) + 1.9);
mainPlayer.new.startPos_(mainList[thisEvent][0]).dur_(mainList[thisEvent][6]).grainDur_((densityDir * 0.02) + 0.2).warpToGrain_((loudnDir * 0.5) + 0.5).grainFreq_((densityDir * 5) + 15).grainFreqRand_((densityDir * 2) + 4).grainPitchRand_((densityDir * 0.05) + 0.05).randPan_((mainList[currentEventNumber][5] * 0.015)).filterAmt_(1).filterFreq_(midiNotesAv * 20).filterType_(2.rand).buffer_(longbuf).amp_((mainList[currentEventNumber][5] * 0.1) - 8).pan_(0.1.rand2).rate_(rate).pitch_(pitch).modAmt_(0).modFreq_(0).att_(0.3.rand).rel_(0.3.rand).play;
respIncr = 0.24 + (mainList[currentEventNumber][2] * 0.17);
if(respIncr<=0, {respIncr = 0.1});
respIncr.wait; //that's prett crucial...
respNow = respNow + respIncr;
});
}); //while loop closing bracket
}); //if arr size > 0 ending bracket
}); //if sizes are not nil ending bracket
});
response5 = Task.new({ //for very loud events - resp5: dense texture of short/shortened notes modulated
var length, respNow, respIncr, chosenEvAr1, chosenEvAr2, shortenEvents, thisEvent, pitch, rate;
//mappings: realtime loudness to events speed in the phrase, densiyDir to modulation params, loudnDir influences warp to grain in alternative case
length = stablePhrasesLengths.mean * 6; //get initial length for the phrase
if(length < (longEventThresh * 2), {length = stablePhrasesLengths.sum * 3}); //if it's too short, get the longer one
if(length < (longEventThresh * 2), {length = longEventThresh * 6}); //if it's STILL too short, get the hardcoded longer one
"playing response5! length: ".post; length.postln;
respNow = 0; //start internal time
chosenEvAr1 = Array.new; //long events array
chosenEvAr2 = Array.new; //short events array
mainList.do({arg array, index; //get indexes of long and short events
if(array[6].notNil, {
if(array[6] > longEventThresh,
{chosenEvAr1 = chosenEvAr1.add(index)}, {chosenEvAr2 = chosenEvAr2.add(index)}
)
}
)
});
if((chosenEvAr2.size.notNil), {if(chosenEvAr2.size < 1, {shortenEvents = 1}, {shortenEvents = 0})}); //if there are no short events, shorten long ones
if((chosenEvAr1.size.notNil) && (chosenEvAr2.size.notNil), { //if they are not nil
if((chosenEvAr1.size > 0) || (chosenEvAr2.size > 0), { //proceed only, when we have something to play
while({respNow < length},
{
// "response5: I'm in the while loop!".postln;
if(shortenEvents == 0, {thisEvent = chosenEvAr2.choose; rate = rrand(0.9, 1.3)}, {thisEvent = chosenEvAr1.choose; rate = rrand(2, 4)}); //get this event number
if(thisEvent.isNil, {thisEvent = mainList.size.rand});
// if(i > 0, {pitch = rrand(0.5, (mainList[currentEventNumber][5] * 0.1) + 0.7)}; {pitch = 1});
pitch = rrand(0.1, 3);
if(durationsDir.isNil, {durationsDir = 0});
mainPlayer.new.startPos_(mainList[thisEvent][0]).dur_(mainList[thisEvent][6]).grainDur_(0.1).warpToGrain_(1).filterAmt_(0.6).filterFreq_(midiNotesAv * 60).filterType_(1).buffer_(longbuf).amp_((mainList[currentEventNumber][5] * 0.2) - 10).pan_(0.7.rand2).rate_(rate).pitch_(pitch).modAmt_((densityDir * 0.0005) + 0.003).modFreq_((densityDir * 3) + 18).att_(0.07.rand).rel_(0.1.rand).play;
respIncr = 0.09 - (mainList[currentEventNumber][5] * 0.001); //tempo
if(respIncr<=0, {respIncr = 0.1});
respIncr.wait; //that's prett crucial...
respNow = respNow + respIncr;
}); //while loop closing bracket
}); //if arr size > 0 ending bracket
}); //if sizes are not nil ending bracket
});
//decision part
while({now < longbufferLength},
{
//check conditions for respo1
if(durationsAv.isNil, {durationsAv = 0});
if(durationsDir.isNil, {durationsAv = 0});
if(densityAv.isNil, {densityAv = 0});
if(midiSlopeAv.isNil, {midiSlopeAv = 0});
if(mainList[currentEventNumber][6].isNil, {mainList[currentEventNumber][6] = 0});
// densityAv.postln;
if(
(response1.isPlaying.not) && (mainList[currentEventNumber][6] > longEventThresh) && (midiSlopeAv < highMidiSlope) && (densityAv > highDensity) && (mainList[currentEventNumber][7] < (longPauseHi * 1.5)),
{response1.start}/*, {(durationsAv > longEventThresh).postln; (midiSlopeAv < highMidiSlope).postln; (densityAv > highDensity).postln}*/
);
if(
(response2.isPlaying.not) && (mainList[currentEventNumber][6] > longEventThresh) && (midiSlopeAv > highMidiSlope) && (mainList[currentEventNumber][7] < (longPauseHi * 1.5)),
{response2.start}
);
if(
(response3.isPlaying.not) && (mainList[currentEventNumber][6] < longEventThresh) && (densityAv < highDensity) && (currentEventNumber > 3) && (mainList[currentEventNumber][7] < (longPauseHi * 1.5)),
{response3.start}
);
if(
(response4.isPlaying.not) && (mainList[currentEventNumber][6] < longEventThresh) && (densityAv > highDensity) && (mainList[currentEventNumber][7] < (longPauseHi * 1.5)),
{response4.start}
);
if(
(response5.isPlaying.not) && (mainList[currentEventNumber][5] > highLoudness) && ((mainList[currentEventNumber][5] - loudnAv) > highLoudnessDif),
{response1.stop; response2.stop; response3.stop; response4.stop; response5.start}
);
if(
(mainList[currentEventNumber][7] > rrand(longPauseLo, longPauseHi)) && (mainList[currentEventNumber][7] < (longPauseHi * 1.5)),
{if(0.04.coin,
{pauseResp = [response1, response2, response3, response4].choose; pauseResp.start; "Hey! Don't just stand like that! Play something!".postln;});
}
);
// if((response1.isPlaying.not), {response1.start}); //test
// if((response2.isPlaying.not), {response2.start}); //test
// if((response3.isPlaying.not), {response3.start}); //test
// if((response4.isPlaying.not), {response4.start}); //test
// if((response5.isPlaying.not), {response5.start}); //test
now = now + continuousDataFreq.reciprocal;
continuousDataFreq.reciprocal.wait;
}
);
});
//array: startTime, midiNote, midiAv, hasFreqAv, ampAv, loudnessAv, duration, pause;
// ------ a simple GUI --------
//var midiNotesAvKnob, midiNotesDirKnob, midiSlopeAvKnob, midiSlopeDirKnob, densityAvKnob, densityDirKnob, loudnAvKnob, loudnDirKnob, attitudeKnob, counterpointKnob;
window1 = Window.new('Improviser parameters', Rect(128, 64, 560, 620)).alpha_(0).front;
window1.view.decorator=FlowLayout(window1.view.bounds, Point(10, 10), Point(10, 6));
midiNoteKnob = EZKnob(window1, 60@100,"midiNote", [0, 127]);
midiAvKnob = EZKnob(window1, 60@100,"midiSlAv", [0, 6]);
hasFreqAvKnob = EZKnob(window1, 60@100,"hasFreq", [0, 1]);
ampAvKnob = EZKnob(window1, 60@100,"ampAv", [-120, 0]);
loudnessAvKnob = EZKnob(window1, 60@100,"loudAv", [0, 64]);
// loudnessAvKnob = EZKnob(window1, 50@90,"loudAv", [0, 1100]);
amplitudeKnob = EZKnob(window1, 60@100," amp ", [-120, 0]);
noteChangeKnob = EZKnob(window1, 60@100,"noteChange", [0, 1200]);
window1.view.decorator.nextLine;
starttimeNumber = EZNumber(window1, 150@20,"startTime", [0, longbufferLength]);
durationNumber = EZNumber(window1, 150@20,"event dur", [0, longbufferLength]);
pauseNumber = EZNumber(window1, 150@20,"pause", [0, longbufferLength]);
onsetNumber = EZNumber(window1, 150@20,"onset no", [0, 10000]);
// phraseNumber = EZNumber(window1, 150@20,"phrase no", [0, 10000]);
playNumber = EZNumber(window1, 150@20,"play ev", [0, 10000], {|ez| phrasePlay.value(ez.value)});
window1.view.decorator.nextLine;
recButton = Button(window1, Rect(20,20,50,30)).states_([["waiting", Color.gray, Color.black], ["event", Color.black, Color.green]]);
window1.view.decorator.nextLine;
midiNotesAvKnob = EZKnob(window1, 60@100,"PhrAvNote", [0, 127]);
midiNotesDirKnob = EZKnob(window1, 60@100,"NoteDir", [-40, 40]);
midiSlopeAvKnob = EZKnob(window1, 60@100,"PhrSlope", [0, 6]);
midiSlopeDirKnob = EZKnob(window1, 60@100,"SlopeDir", [-6, 6]);
durationsAvKnob = EZKnob(window1, 60@100,"DursAv", [0, 20]);
durationsDirKnob = EZKnob(window1, 60@100,"DursDir", [-5, 5]);
densityAvKnob = EZKnob(window1, 60@100,"PhrDensity", [0, 1]);
densityDirKnob = EZKnob(window1, 60@100,"DensityDir", [-1, 1]);
loudnAvKnob = EZKnob(window1, 60@100,"PhrLoudnss", [0, 64]);
loudnDirKnob = EZKnob(window1, 60@100,"LoudnssDir", [-40, 40]);
stabilityKnob = EZKnob(window1, 60@100,"Stability", [0, 1]);
stablePhraseLengthKnob = EZKnob(window1, 60@100,"StablePhrLgth", [0, longbufferLength]);
window1.view.decorator.nextLine;
ampTrigLoKnob = EZKnob(window1, 50@90,"AmpLo(dB)", [-120, 0], {|ez| rec1.threshLo_(ez.value)});
ampTrigHiKnob = EZKnob(window1, 50@90,"AmpHi(dB)", [-120, 0], {|ez| rec1.threshHi_(ez.value)});
onsetThreshKnob = EZKnob(window1, 50@90,"OnsetThr", [0, 1], {|ez| rec1.onsetTreshold_(ez.value)});
noteChangeTrigLoKnob = EZKnob(window1, 50@90,"NoteTLo", [0, 400], {|ez| rec1.noteChangeTrigLo_(ez.value)});
noteChangeTrigHiKnob = EZKnob(window1, 50@90,"NoteTHi", [0, 900], {|ez| rec1.noteChangeTrigHi_(ez.value)});
window1.view.decorator.nextLine;
startButton = Button(window1, Rect(20,20,70,30)).states_([["start", Color.green, Color.black]]).action_({ arg butt; init.value});
stopButton = Button(window1, Rect(20,20,70,30)).states_([["stop", Color.red, Color.black]]).action_({ arg butt; clear.value});
clearButton = Button(window1, Rect(20,20,70,30)).states_([["kill me", Color.gray, Color.black], ["AAAHHH!!!", Color.red, Color.black]]).action_({ arg butt; clear.value(1)});
//vars ampTrigLoKnob, ampTrigHiKnob, onsetThreshKnob, noteChangeTrigLoKnob, noteChangeTrigHiKnob
//rec1 mute = 0, onsetTreshold = 0.93, threshHi = -46, threshLo = -59, averageDelay = 0.1, noteChangeTrigLo = 20, noteChangeTrigHi = 40, relaxtime = 0.1, floor = -30
//window fade in...
AppClock.sched(0.05, {Task({26.do({arg i; {window1.alpha_(i * 0.04)}.defer; 0.003.wait;})}).play}); //fade in haha
// RESPONDERS
startResp = OSCresponder(nil, 'start', { arg t, r, msg; // msg.postln;
var msgNew, startTime;
// oldstarttime = startTime; //remember previous start time - to func
startTime = msg.at(3) + onsetOffset; //get actual start time
msgNew = msg.rotate(-4).clipExtend(7); //extract only useful values from the array
// 'new array: '.post; msgNew.postln;
msgNew.do{arg val, i; mainList.at(currentEventNumber).putEach(i+1, val)};
// "mainList[currentEventNumber] startResp: ".post; mainList[currentEventNumber].postln;
// "pause from the list in startResp: ".post; mainList[currentEventNumber][7].postln;
checkDuration.value(1, startTime); //do operations to current event
});
stopResp = OSCresponder(nil, 'stop', { arg t, r, msg;// msg.postln;
var msgNew, stopTime;
// oldstoptime = stopTime;
stopTime = msg.at(3); //get stop time
msgNew = msg.rotate(-4).clipExtend(7); //extract only useful values from the array
// 'new array: '.post; msgNew.postln;
msgNew.do{arg val, i; mainList.at(currentEventNumber).putEach(i+1, val)};
// "pause from the list in stopResp: ".post; mainList[currentEventNumber][7].postln;
checkDuration.value(0, stopTime); //put duration in the array - this also triggers response now
// 'stoptime: '.post; stopTime.postln;
});
contResp = OSCresponder(nil, 'cont', { arg t, r, msg;// msg.postln;
var freqAv, hasFreqAv, ampAv, msgNew;
// [midiNote, midiAv, hasFreqAv, ampAv, loudnessAv, dur, pause, amplitude, noteChange]
// from v28 pause is added after current, adding before was to unreliable
{
midiNoteKnob.value = msg.at(3);
midiAvKnob.value = msg.at(4);
hasFreqAvKnob.value = msg.at(5);
ampAvKnob.value = msg.at(6);
loudnessAvKnob.value = msg.at(7);
durationNumber.value = msg.at(8);
pauseNumber.value = msg.at(9);
amplitudeKnob.value = msg.at(10);
noteChangeKnob.value = msg.at(11);
}.defer; //gui control
// 'org array: '.post; msg.postln;
msgNew = msg.rotate(-3).clipExtend(7); //extract only useful values from the array
// 'new array: '.post; msgNew.postln;
msgNew.do{arg val, i; mainList.at(currentEventNumber).putEach(i+1, val)};//put parameters into the main array
// "pause from the list: ".post; mainList[currentEventNumber][7].postln;
// "current event no: ".post; currentEventNumber.postln;
dirPhraseAnalyze.value; //analyze phrase directions etc
});
)
Subscribe to:
Posts (Atom)


