As far as functionality goes:
I managed to extract some of the musical paramters. In the code below, I did a test with simple fm "grain" synthesizer. I used played events' average pitch to inform synth's grains time and tempo and events' average density to inform modulation frequency and amunt. Final version will still use recorded events, this is just for the test.
This is a basic framework for the final system:
Input analysis (event recognition) - Musical data extraction - a constantly running Task generating sounds, using extracted data.
(
var numinchannels, samplerate, controlrate, masterVol, bufferArray, longbufferLength, fftbuflength, muteInput, in, out, st_out, timeOffset, pathprefix, inputBoost, minimalEventDuration, currentEventNumber, onsetOffset, soundInNo, inputOffset; //general variables
var recorder, play, straightPlayer, warpPlayer, rec1, time, playCnt, checkPlayers, checkDuration, phrasePlay, clear, getPhrases, phraseAnalyze, 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 startTime, stopTime, midiNote, midiAv, hasFreqAv, ampAv, loudnessAv, amplitude, duration, oldstarttime, oldstoptime, pause; //changing values
var mainList, phraseList, durPhrases, pitchPhrases, durPitchPhrases, extractedPhrase; //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
// ------------------------------------------------------ //
// 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, 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); //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;
trigger = 1;
localbuf = LocalBuf.new(fftbuflength, 1); //fft buffer
// chain = FFT(localbuf, inGate, 0.5); //start the chain
chain = FFT(fftbuf, inGate, 0.5); //start the chain
// chain2 = FFT.new(LocalBuf.new(1024), inGate, 0.5);
loudness = Clip.kr(Loudness.kr(chain), 0, 64); //loudness analysis
// loudness = 0;
// onsets = Gate.kr(Onsets.kr(chain, onsetTreshold, relaxtime: relaxtime, floor: floor.dbamp), 1); //onset detection, gated and automaticaly "muted"
// onsetTrigger = Lag3UD.kr(Onsets.kr(chain, onsetTreshold, relaxtime: relaxtime, floor: floor.dbamp), 0, 0.1); //onset detection
// onsetTrigger = Onsets.kr(chain, onsetTreshold); //onset detection
onsetTrigger = Lag3UD.kr(Onsets.kr(chain, onsetTreshold), 0, 0.01); //onset detection lagged
// onsetTrigger.poll(25, \onsetT);
// stopTrigger = onsets.neg + 1;
RecordBuf.ar(sig, longbuf, 0, run: 1, loop: 0, trigger: 1); //recorder
#freq, hasFreq = Tartini.kr(sig); //pitch analysis
// midiNote = Gate.kr(freq.cpsmidi, 1); //convert to midi notes
// midiNote = freq.cpsmidi;
midiNote = Lag3UD.kr(freq.cpsmidi, 0.1, 0.1); //lagged -------- !params
// midiNote = freq.cpsmidi.round(0.01);
// midiNote.poll(5, \midiNote);
// noteChange = Lag3.kr((((midiNote - Delay1.kr(midiNote)).abs / controlrate) - ((Delay1.kr(midiNote) - Delay2.kr(midiNote)).abs / controlrate)).abs / controlrate, 0.1);
// d = (midiNote - Delay1.kr(midiNote)).abs;
// d.poll(25);
// e = d / controlrate;
// e.poll(25);
// f = (Delay1.kr(midiNote) - Delay2.kr(midiNote)).abs;
// g = (f - e).abs;
// g.poll(25);
// freqSlope = Slope.kr(freq).abs; //kind of amount of change of freq
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
// midiSlope = (midiGated - DelayL.kr(midiGated, 2, averageDelay)).abs; //lagged amount of change of midi note
// noteChange = Lag3UD.kr((midiSlope - Delay1.kr(midiSlope)).abs * controlrate, 0, 0); //speed of pitch change
// noteChange = Lag3UD.kr(Clip.kr((midiSlope - DelayL.kr(midiSlope, 2, averageDelay)).abs * controlrate, 0, noteChangeTrigHi * 2), 0.0001, 0.001); //speed of pitch change
noteChange = (midiSlope - DelayL.kr(midiSlope, 2, averageDelay)).abs * controlrate; //speed of pitch change
// noteChange.poll(125, \noteChange);
// noteTrigger = Schmidt.kr(noteChange, noteChangeTrigLo, noteChangeTrigHi);
noteTrigger = Lag3UD.kr(Schmidt.kr(noteChange, noteChangeTrigLo, noteChangeTrigHi), 0, 0.01); //lagged
// noteTrigger.poll(125, \noteTrig);
sumTrigger = Clip.kr((noteTrigger + onsetTrigger) * ampTrigger, 0, 1);
negSumTrigger = ampTrigger.neg + 1;
// sumTrigger = Lag3UD.kr((noteTrigger + onsetTrigger) * ampTrigger, 0, 0.01); //lagged
// sumTrigger.poll(125, "sumTri----------");
// midiAv = Gate.kr(AverageOutput.kr(midiSlope, DelayL.kr(sumTrigger, 2, averageDelay)), trigger); //average of midi note change
// midiAv = AverageOutput.kr(midiSlope, DelayL.kr(sumTrigger, 2, averageDelay)); //average of midi note change
midiAv = AverageOutput.kr(midiSlope, DelayL.kr(sumTrigger, 2, averageDelay)); //average of midi note change
// freqAv = Gate.kr(AverageOutput.kr(freqSlope, DelayL.kr(sumTrigger, 2, averageDelay)), trigger); //average base freq in the sample
// hasFreqAv = Gate.kr(AverageOutput.kr(hasFreq, DelayL.kr(sumTrigger, 2, averageDelay)), trigger); //average amount of pitched sounds in the sample
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) * Delay2.kr(negAmpTrigger); //send pause AFTER current event - with Delay2 it gets MOST pauses in tests...
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(sumTrigger, 'start' , [timer, amplitude.ampdb, ampTrigger, midiNote, noteChange, noteTrigger, hasFreq]); //test trigger
// SendReply.kr(Impulse.kr(10), 'cont' , [midiNote, midiAv, hasFreqAv, ampAv, loudnessAv, amplitude]); //send continous values for events
SendReply.kr(Impulse.kr(30), '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
// counter.poll(1, label: \counter);
// trigger.poll(1, label: \trigger);
// env.poll(1, label: \env);
// hasFreqAv.poll(1, label: \hasFreqAv);
// stopTrigger.poll(1, label: \stop);
// recTrigger4.poll(1, label: \recTrigger4);
// amplitude.ampdb.poll(1, label: \amp);
// Out.ar(1, inGate);
});
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; //response player inlined
if((type == 1), { //if this is startTime
"mainList[currentEventNumber]: ".post; mainList[currentEventNumber].postln;
// "mainList[currentEventNumber - 1]: ".post; mainList[currentEventNumber - 1].postln;
if(mainList[currentEventNumber][0].notNil, {currentEventNumber = currentEventNumber + 1}); //advance, except for the first event
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
{recButton.value = 0}.defer
});
phraseAnalyze.value; //move to continuous responder afterwards!!!!!!! only part of it!!!, remaining should be triggered only on note offs
};
// -------- 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
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, dirLookBack = 3;//number of events to get current "character" and current "direction" for certain parameters
var farthestEvent, midiAr, midiNoComplexAr, slopeAr, lastSlopes, pausesAr, dursAr, pausesSum, dursSum;
var slopeDirAr, dirFarthestEvent, midiNoComplexDirAr, pausesDirAr, dursDirAr, pausesDirSum, dursDirSum;//for live
// "lookBackEvents before if: ".post; lookBackEvents.postln;
// "current ev before: ".post; currentEventNumber.postln;
if(currentEventNumber < lookBackEvents, {lookBackEvents = currentEventNumber}); //decrease lookback at the beginning
if(currentEventNumber < dirLookBack, {dirLookBack = currentEventNumber}); //decrease dirlookback at the beginning
"current ev: ".post; currentEventNumber.postln;
"lookBackEvents: ".post; lookBackEvents.postln;
farthestEvent = (currentEventNumber - lookBackEvents - 1).thresh(0); //to not include the actual item in calculations
dirFarthestEvent = (currentEventNumber - dirLookBack).thresh(0); //probably move to live... dirlookback part
"farthestEvent: ".post; farthestEvent.postln;
extractedPhrase = Array.new;
lookBackEvents.do({arg i; //get last number of events into a separate array
if(mainList.at(i + farthestEvent).at(0).notNil,
{extractedPhrase = extractedPhrase.add(mainList.at(i + farthestEvent))
});
});
"extractedPhrase: ".post; extractedPhrase.postln;
//create arrays
midiAr = Array.new;
slopeAr = Array.new;
midiNoComplexAr = Array.new;
lastSlopes = Array.new;
pausesAr = Array.new;
dursAr = Array.new;
lookBackEvents.do({arg i;
if(extractedPhrase[i].notNil, {
midiAr = midiAr.add(extractedPhrase[i][1]); //extract array of midi notes
pausesAr = pausesAr.add(if(extractedPhrase[i][7].notNil, extractedPhrase[i][7], 0)); //extract array of pauses
dursAr = dursAr.add(if(extractedPhrase[i][6].notNil, extractedPhrase[i][6], 0)); //extract array of durations
if((extractedPhrase[i][2].notNil) && (extractedPhrase[i][5].notNil), {
if((extractedPhrase[i][2] < complexSlopeAvTresh) && (extractedPhrase[i][5] < complexLengthAvTresh), //and array of notes without complex events
{midiNoComplexAr = midiNoComplexAr.add(extractedPhrase[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(extractedPhrase[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;
if(pausesAr.includes(nil), {pausesSum = 0}, {pausesSum = pausesAr.sum}); //for density: sum pauses
if(dursAr.includes(nil), {dursSum = 0}, {dursSum = dursAr.sum}); //sum durs
// if(pausesSum.isNil, {pausesSum = 0});
// if(dursSum.isNil, {dursSum = 0});
"pausesSum: ".post; pausesSum.postln;
"dursSum: ".post; dursSum.postln;
if((pausesSum == 0) && (dursSum == 0), {densityAv = 0}, {densityAv = (dursSum / (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;
// "here!--------------------------------".postln;
//to realtime part -- ( ADD CASES WHEN IT'S NIL ) nil - done I think
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]);
};
);
//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.isNil, {pausesDirSum = 0});
// if(dursDirSum.isNil, {dursDirSum = 0});
if((pausesDirSum == 0) && (pausesDirSum == 0), {densityDir = 0 - densityAv}, {densityDir = (dursDirSum / (dursDirSum + pausesDirSum)) - densityAv});
"densityDir: ".post; densityDir.postln;
};
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 --------
window1 = Window.new('Improviser parameters').front;
window1.view.decorator=FlowLayout(window1.view.bounds);
midiNoteKnob = EZKnob(window1, 50@90,"midiNote", [0, 127]);
midiAvKnob = EZKnob(window1, 50@90,"midiSlAv", [0, 6]);
hasFreqAvKnob = EZKnob(window1, 50@90,"hasFreq", [0, 1]);
ampAvKnob = EZKnob(window1, 50@90,"ampAv", [-120, 0]);
loudnessAvKnob = EZKnob(window1, 50@90,"loudAv", [0, 64]);
// loudnessAvKnob = EZKnob(window1, 50@90,"loudAv", [0, 1100]);
amplitudeKnob = EZKnob(window1, 50@90," amp ", [-120, 0]);
noteChangeKnob = EZKnob(window1, 50@90,"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;
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;
// 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)};
// "pause from the list in startResp: ".post; mainList[currentEventNumber][7].postln;
checkDuration.value(1, startTime); //check if we need to take duration from it - this also triggers response now
// mainList.at(currentEventNumber).putEach(0, startTime); //put start time into the current phrase array
// 'starttrigger starttime: '.post; startTime.postln;
// {recButton.value = 1;
// starttimeNumber.value = startTime;
// onsetNumber.value = currentEventNumber;
// }.defer; //gui control
}).add;
stopResp = OSCresponder(nil, 'stop', { arg t, r, msg;// msg.postln;
var msgNew;
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]
{
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;
}).add;
)
No comments:
Post a Comment