Friday, June 4, 2010

On the way... not quite there yet

I finished extraction of most of phrase's parameters: average pitch, pitch change slope, rhythm density and loudness. I'll try to implement one more parameter, based on time flow: stability. The way it would be acquired is still in process of developing. The parameter will generally express how much of "the same" is going on and thus will help system make a decision to change.

The next step is to build the actual Task responsible for generating "accompaniment".

I updated the GUI so all extracted parameters are visible in real time:
( //v30
var numinchannels, samplerate, controlrate, masterVol, bufferArray, longbufferLength, fftbuflength, muteInput, in, out, st_out, timeOffset, pathprefix, inputBoost, minimalEventDuration, currentEventNumber, onsetOffset, soundInNo, inputOffset, continuousDataFreq; //general variables
var recorder, play, straightPlayer, warpPlayer, rec1, time, playCnt, checkPlayers, checkDuration, phrasePlay, clear, getPhrases, phraseAnalyze, dirPhraseAnalyze, phraseRespond, grainsynth, makeGrains, grainTask; //synthdefs and functions
var startResp, stopResp, contResp; //responders
var longbuf, fftbuf; //buffers
var durationTriggerValue, dursMult, dursMultRand, stimes, stimesRand, timesEnv, timesRand, rates, ratesRand, pitches, pitchesRand, az, azRand, az2diff, el, elRand, el2diff, modAmts, modAmtsRand, modFreqs, modFreqsRand, atts, attsRand, rels, relsRand, amps, ampsRand; //envelopes
var window1, midiNoteKnob, midiAvKnob, hasFreqAvKnob, ampAvKnob, loudnessAvKnob, amplitudeKnob, starttimeNumber, durationNumber, recButton, playButton, onsetNumber, playNumber, clearButton, phraseNumber, ampTrigLoKnob, ampTrigHiKnob, onsetThreshKnob, noteChangeTrigLoKnob, noteChangeTrigHiKnob, noteChangeKnob, pauseNumber; //gui
var midiNotesAvKnob, midiNotesDirKnob, midiSlopeAvKnob, midiSlopeDirKnob, densityAvKnob, densityDirKnob, loudnAvKnob, loudnDirKnob, attitudeKnob, counterpointKnob; //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 midiNotesAv, midiNotesDir, midiSlopeAv, midiSlopeDir, densityAv, densityDir, loudnAv, loudnDir, regularityAv, regularityDir, durationsAv, durationsDir; //realtime parameters
var attitude, counterpoint; //attitude 1 = continue on your own, 0 = stop right after input, counterpoing 1 = use opposite paramteres to the input, 0 = use similar
var complexSlopeAvTresh, complexLengthAvTresh; //thresholds

// ----------- variables to set ------------------------- // 
in = 0; //number of the input channel
out = 42; //number of the first channel of Ambisonics B-format output
st_out = 0; //number of the first channel of stereo out
masterVol = 0; //in dB
inputBoost = 0; //input amplification in dB
minimalEventDuration = 0.2; //in seconds - to avoid unnecessary retriggering //not active currently??
longbufferLength = 90; //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
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 = 3; //Hz
// ------------------------------------------------------ //

// INIT

numinchannels = 1; //currently only one input channel is supported
//inputBoost = inputBoost.dbamp; //convert to amp
currentEventNumber = 0; //init event numbering
//startTime = 0; //init start time
//oldstoptime = 0; //init
//oldstarttime = 0; //init
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
mainList = Array.newClear(8) ! 10000; //set up the global event list for the buffer; values for each array inside: startTime, midiNote, midiSlopeAv, hasFreqAv, ampAv, loudnessAv, duration, pause
//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
timeOffset = thisThread.seconds; // get runtime time
inputOffset = in + Server.default.options.numOutputBusChannels;
//stopTime = 0;
//controlrate.postln;
fftbuf = Buffer.alloc(Server.default, fftbuflength, 1);

// 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 = -26, threshLo = -39, 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) * inputBoost.dbamp, mute.neg + 1) * (mute.neg + 1);
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 = SinOsc.kr(0, 0, 1, if(sumTrigger, 0,{if(ampTrigger.neg + 1, 1, 0)}));
// negSumTrigger = Latch.kr(sumTrigger + negAmpTrigger, sumTrigger + negAmpTrigger);
// SinOsc.kr(0, 0, 1, if(sumTrigger, 0,{if(ampTrigger.neg + 1, 1, 0)}));
// negSumTrigger = SinOsc.kr(0, 0, 1, if(ampTrigger, {if(sumTrigger, 0, 0)},{if(ampTrigger.neg + 1, 1, 0)}));
// negSumTrigger = SinOsc.kr(0, 0, 1, if(ampTrigger, 0, 1));
// Out.ar(0, SinOsc.ar(if(LFNoise1.kr(1.0,0.5,0.5), 440, 660), 0, 0.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
});

straightPlayer = CtkSynthDef(\straightPlayer, {arg dur, buffer = longbuf, startPos, amp = 0, rate = 1, az = 0, el = 0, dir=1, modAmt = 0, modFreq = 0, att = 0.001, rel = 0.001; 
var src, env, enc_w, enc_x, enc_y, enc_z, decoded, mod, secondLeg;
secondLeg = dur - (dur * att) - (dur * rel);
env = EnvGen.kr(Env([0.001, 1, 1, 0.001], [att * dur, secondLeg, rel * dur], \exponential));
az = (az * pi) / 180; //to use az in degrees
el = (el * pi) / 180; //to use el in degrees
mod = SinOsc.ar(modFreq, modAmt);
// src = PlayBuf.ar(numinchannels, buffer, (rate + mod) * BufRateScale.kr(buffer), 1, startPos * numinchannels * samplerate, 0) * env;
src = PlayBuf.ar(numinchannels, buffer, rate * BufRateScale.kr(buffer), 1, startPos * samplerate, 0) * env;
// src = PlayBuf.ar(1, buffer);
# 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(out, [enc_w, enc_x, enc_y, enc_z] * amp.dbamp * masterVol.dbamp); //ambisonics encoded out
});

//temp player
grainsynth = CtkSynthDef(\inPausesPlayer4, {arg dur, buffer, speed = 1, craziness = 0, amp = 0, pan = 0, modFreq = 0, modAmt = 0, soundToNoise = 1, output = 0, sendout, sendamp = -inf, sendout2, send2amp = -inf, sendout3, send3amp = -inf, perc = 0.95;
var src, snd, noise, mod, out, pointer, speedMod, trigger, startingPointMax = 0, startingPoint, env;
startingPointMax = BufDur.kr(buffer) - dur;
startingPoint = Rand(0.0, startingPointMax);
// env = EnvGen.kr(Env.new([0.0001, 1, 1, 0.0001], [dur * (1 - perc), dur * (perc * 0.3), dur * (perc * 0.7)], \exp)); //optional envelope... perc works better anyway I think
env = EnvGen.kr(Env.perc(dur * (1 - perc), dur * perc));
//speedMod = LFDNoise3.kr(modFreq, modAmt);
speedMod = SinOsc.ar(modFreq, 0, modAmt * 300);
//src = LPF.ar((PlayBuf.ar(1, buffer, (speed + speedMod) * BufRateScale.kr(buffer), 1, startingPoint * samplerate, 0) * soundToNoise) + BPF.ar(WhiteNoise.ar(soundToNoise.neg + 1), Clip.kr(speed * 330, 0, 20000), 0.2), (18000 - (12000 * modAmt))) * env;
src = SinOsc.ar(578 + speedMod) * env;
// src = PlayBuf.ar(1, buffer, speed + speedMod); //for testing
Out.ar(0, Pan2.ar(src, pan, amp.dbamp * masterVol.dbamp));
// Out.ar(sendout, src * sendamp.dbamp);
// Out.ar(sendout2, src * send2amp.dbamp);
// Out.ar(sendout3, src * send3amp.dbamp);
});



//warpPlayer = CtkSynthDef(\warpPlayer, {arg dur, buffer, amp = 0, length, rate = 1, pitch = 1, az = 0, el = 0, dir=1, modAmt = 0, modFreq = 0, att = 0.001, rel = 0.001; //AM!!!
// var src, pointer, env, enc_w, enc_x, enc_y, enc_z, decoded, mod, secondLeg;
// secondLeg = dur - (dur * att) - (dur * rel);
// env = EnvGen.kr(Env([0.001, 1, 1, 0.001], [att * dur, secondLeg, rel * dur], \exponential));
//// pointer = EnvGen.kr(Env([0, length / bufferslength], [rate * length / bufferslength]));
//// pointer = RedPhasor2.ar(Impulse.kr(0), rate / samplerate, 0, length / bufferslength, 1);
// mod = SinOsc.ar(modFreq, modAmt) + 1;
// pointer = Phasor.ar(Impulse.kr(0), rate * bufferslength / samplerate, 0, length / bufferslength);
//// pointer.poll(2, label: \pointer);
// az = (az * pi) / 180; //to use az in degrees
// el = (el * pi) / 180; //to use el in degrees
// src = Warp1.ar(numinchannels, buffer, pointer, pitch, 0.12, -1, 4, 0.2, 4) * env * mod;
// # 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(out, [enc_w, enc_x, enc_y, enc_z] * amp.dbamp * masterVol.dbamp); //ambisonics encoded out
// });


// RECORDER CONTROL

rec1 = recorder.new; //initialize recorder
rec1.play; //start recorder


// 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), {currentEventNumber = currentEventNumber + 1}); //advance, exception for the first one
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; 
};

// -------- IDEA: 1. get realtime musical data, 2. provide list of available resources, 3. build synth-----------!!!!!!!!!!!!--------<<<<<<<<<----------

// ------------- -------- -------- NEXT --------- ---------- ---------

//gather remaining parameters (loudness, regularity, durs probably not), opt put them to the gui, move parts to the noteoff or realtime, then make playing func
//get time of unchanged events!!!
/*
*** plan as of June 3rd: ***
1. finish gathering realtime data - loudness, regularity - no, leaving regularity for now 
2. gather time data: density, loudness, regularity, pitch?
3. try decision-making mechanisms
4. build fynction & synth(s) for playing

TO FIX:
- pause sum is negative...
*/

//array: startTime, midiNote, midiSlopeAv, hasFreqAv, ampAv, loudnessAv, duration, pause
/*
var midiNotesAv, midiNotesDir, midiSlopeAv, midiSlopeDir, densityAv, densityDir, loudnAv, loudnDir, regularityAv, regularityDir, durationsAv, durationsDir; //realtime parameters
var attitude, counterpoint; //attitude 1 = continue on your own, 0 = stop right after input, counterpoing 1 = use opposite paramteres to the input, 0 = use similar
var complexSlopeAvTresh; //thresholds
complexSlopeAvTresh = 2;
*/
//move part of that to realtime!!!
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]);
};
);
});
});
});
// "I'm here!".postln;
// "midiAr: ".post; midiAr.postln;
midiNotesAv = midiAr.mean; //notes are often recognized wrong (?) -----------------------------------------
// "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}, {dursSum = dursAr.sum}); //sum 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;
loudnAvKnob.value = loudnAv;
}.defer;
// "here!--------------------------------".postln;
};
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); //probably move to live... dirlookback part
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]);
//"complex slope! ---------------".postln;
};
);
//lastSlopes = lastSlopes.add
//slopeDirAr = slopeDirAr.add(mainList[i + dirFarthestEvent][2]);
});
});
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((pausesDirSum == 0) && (pausesDirSum == 0), {densityDir = 0 - densityAv}, {densityDir = ((dursDirSum - pausesDirSum) / (dursDirSum + pausesDirSum)) - densityAv});
// "densityDir: ".post; densityDir.postln;
if(mainList[currentEventNumber][5].notNil, {loudnDir = mainList[currentEventNumber][5] - loudnAv}, {loudnDir = 0});
// "loudnDir: ".post; loudnDir.postln;
{midiNotesDirKnob.value = midiNotesDir;
midiSlopeDirKnob.value = midiSlopeDir;
densityDirKnob.value = densityDir;
loudnDirKnob.value = loudnDir;
attitudeKnob.value = 0; //for now
counterpointKnob.value = 0; //for now
}.defer;
};

phrasePlay = {arg phraseNo;
var pos, dur;
'phrase no: '.post; phraseNo.postln;
pos = mainList[phraseNo][0];
dur = mainList[phraseNo][6];
// 'PLAYER startPos: '.post; pos.post; ', duration: '.post; dur.postln;
straightPlayer.new(0, dur).dur_(dur).startPos_(pos).play;
};

clear = { // free whatever possible = stop execution
rec1.free;
longbuf.free;
fftbuf.free;
startResp.remove;
stopResp.remove;
contResp.remove;
grainTask.stop;
AppClock.sched(0.53, {window1.close});
// Server.default.quit;
// Server.default.boot;
};

phraseRespond = { //find similar phrases and play them... to be rebuild to track directions in current playing and use buffer data to resynthesize (?) meaningful output
durPhrases = Array.new; //list of phrases with similar dur
pitchPhrases = Array.new; //list of phrases with similar pitch
durPitchPhrases = Array.new; //similar dur and pitch combined
(currentEventNumber - 1).do({arg i;
// "dur from phraseRespond: ".post; mainList.at(i).at(6).postln;
// "list at i: ".post; mainList.at(i).postln;
// if(mainList.at(currentEventNumber).at(6).notNil, {if(((mainList.at(i).at(6) / mainList.at(currentEventNumber).at(6)).abs > 0.9) && ((mainList.at(i).at(6) / mainList.at(currentEventNumber).at(6)).abs < 1.1), {durPhrases = durPhrases.add(i)})}); //find similar note durations
// if(mainList.at(currentEventNumber).at(1).notNil, {if(mainList.at(i).at(1).notNil, {if((mainList.at(i).at(1) - mainList.at(currentEventNumber).at(1)).abs < 1, {pitchPhrases = pitchPhrases.add(i)})})}); //find similar pitches
});
"durPhrases: ".post; durPhrases.postln;
"pitchPhrases: ".post; pitchPhrases.postln;
if(durPhrases.size>0, {phrasePlay.value(durPhrases.at(durPhrases.size.rand))}); //play for similar durations
if(pitchPhrases.size>0, {phrasePlay.value(pitchPhrases.at(pitchPhrases.size.rand))}); //play for similar pitches
// if((pitchPhrases.size>0) && (durPhrases.size>0), {
// durPitchPhrases = pitchPhrases.select({ arg item, i; durPhrases.includes(item) });
// "durPitchPhrases: ".post; durPitchPhrases.postln;
// if(durPitchPhrases.size>0, {phrasePlay.value(durPitchPhrases.at(durPitchPhrases.size.rand))}); //play for similar duration AND pitches
// });
};

makeGrains = {arg stime = 1, duration = 100, graindur = 0.1, graindurRand = 0, timeBetweenGrains = 0.05, timeBetweenGrainsRand = 0.02, perc = 0.7, speed = 1, modFreq = 2000, modAmt = 0.1, soundToNoise = 1, amp = 0, pan = 0, send1 = -90, send2 = -90, send3 = -90, buffer = longbuf;
var now, dur, increment, ratio, grainSpeed, grainSoundToNoise, grainModAmt, currentGraindur, currentGraindurRand, currentTimeBetweenGrains, currentTimeBetweenGrainsRand, grainModFreq, grainAmp, grainPan, percussiveness, grainSend1, grainSend2, grainSend3, proc;
// proc = ProcMod.new;
now = 0.0;
// proc.function_({arg group, envbus, server;
grainTask = Task.new({
stime.wait;
while({now < duration},
{
ratio = now/duration;
// "playing! --------------------------".postln;
// currentGraindur = graindur.at(ratio);
if(midiNotesAv>0, {currentGraindur = (midiNotesAv - 30) / 60;}, {currentGraindur = 0.1});
currentGraindurRand = graindurRand.at(ratio);
// currentTimeBetweenGrains = timeBetweenGrains.at(ratio);
if(midiNotesAv>0, {currentTimeBetweenGrains = (midiNotesAv - 35) / 90}, {currentTimeBetweenGrains = 0.1});
currentTimeBetweenGrainsRand = timeBetweenGrainsRand.at(ratio);
percussiveness = perc.at(ratio);
// grainModFreq = modFreq.at(ratio);
grainModFreq = 40 + (200 * densityAv);
// grainModAmt = modAmt.at(ratio);
grainModAmt = densityAv * 0.8;
grainAmp = amp.at(ratio);
grainPan = pan.at(ratio);
grainSend1 = send1.at(ratio);
grainSend2 = send2.at(ratio);
grainSend3 = send3.at(ratio);
dur = currentGraindur + currentGraindurRand.rand2;
increment = currentTimeBetweenGrains + currentTimeBetweenGrainsRand.rand2;
grainSpeed = speed.at(ratio, \betaRand);
grainSoundToNoise = soundToNoise.at(ratio, \meanRand);
grainsynth.new(0, dur).dur_(dur).buffer_(buffer).speed_(grainSpeed).modFreq_(grainModFreq).modAmt_(grainModAmt).soundToNoise_(grainSoundToNoise).amp_(grainAmp).pan_(grainPan).output_(0).sendout_(0).sendamp_(grainSend1).send2amp_(grainSend2).perc_(percussiveness).sendout2_(0).sendout3_(0).send3amp_(grainSend3).play;
if(increment < 0, {increment = increment.abs});
if(increment == 0, {increment = 0.0001});
now = now + increment;
increment.wait;
}
);
}).play;
// });
};
//makeGrains.value; //run it!

//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)).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]);
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]);
attitudeKnob = EZKnob(window1, 60@100,"Attitude", [0, 1]);
counterpointKnob = EZKnob(window1, 60@100,"Cpt", [-1, 1]);
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;
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});
//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

// 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
}).add;

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;

}).add;
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
}).add;
)

No comments:

Post a Comment