( // helper funcs var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; // score funcs var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; // subroutines var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc, genStepFunc; // primary routines var genMotif, genSecondarySeq; // audition funcs var genPatterns, genMidiPatterns; // resource management funcs var seedFunc, genUID, writeResources, stringifyToDepth, setSeeds, sanityCheck, msgInterpret, loadLedgerFile, loadLedgerJSON, loadModelFile, loadModelJSON, setGlobalVars, globalVarsToDict, saveLedger; // model vars //(model and global vars mostly set by OSC funcs var seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, entrancesProbVals, passagesProbVals, exitsProbVals, ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, orders, susWeights, orderSize, passagesSize, motifEdited, orderEdited; // model aux vars var entrancesDurFunc, passagesDurFunc, exitsDurFunc, stepFunc; // other global vars var popSize, exPath, dir, primes, dims, tuples, group, player, ledgerPath, ledger, currentlyPlayingUID, nameSpaces; // install JSON quark if(Quarks.isInstalled("JSONlib").not, { Quarks.install("https://github.com/musikinformatik/JSONlib.git"); thisProcess.recompile; //HelpBrowser.openHelpFor("Classes/JSONlib"); }); //------helper funcs hsArrayToCents = { arg hsArray; hsArray.collect({arg dist, p; dist * 1200 * log2(primes[p][0]/primes[p][1])}).sum }; pDist = { arg array1, array2, signed = false; var pDistance; pDistance = hsArrayToCents.value(array1) - hsArrayToCents.value(array2); if(signed, {pDistance}, {abs(pDistance)}) }; hdSum = { arg hsArrays; var size, distances, mean; size = hsArrays.size; distances = (size - 1).collect({arg i; ((i + 1)..(size - 1)).collect({arg j; abs(hsArrays[i] - hsArrays[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum }); }).flat; mean = distances.sum / distances.size; distances.sum //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) }; hsChordalDistance = { arg hsArrays1, hsArrays2; var size, distances, mean; size = hsArrays1.size; distances = hsArrays1.size.collect({arg i; hsArrays2.size.collect({arg j; abs(hsArrays1[i] - hsArrays2[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum }); }).flat; mean = distances.sum / distances.size; distances.sum //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) }; hsArrayToFreq = { arg array; array.collect({arg dim, d; pow(primes[d][0]/primes[d][1], dim)}).product }; //------score funcs /* isInRange = { arg hsArray, min, max; var cents; cents = hsArrayToCents.value(hsArray); (cents >= min) && (cents <= max) }; */ spacingScore = { arg hsArrays, min; var centsArray; centsArray = hsArrays.collect({arg hsArray; hsArrayToCents.value(hsArray)}).sort({arg a, b; a < b}); centsArray.differentiate.drop(1).collect({arg pDistance; if(pDistance >= min, {1}, {0.01})}).sum; }; rangeScore = { arg hsArray1, hsArray2, min, max, low, signed = false; var pDistance; pDistance = pDist.value(hsArray1, hsArray2, signed); if((pDistance >= min) && (pDistance <= max), {1}, {low}); }; intervalScore = { arg hsArray1, hsArray2, mean, sd, signed = false; var pDistance; pDistance = pDist.value(hsArray1, hsArray2, signed); //pDistance.gaussCurve(1, mean, sd) stepFunc.value(pDistance); }; inclusionScore = { arg array, test, min = 0.01; if(array.collect({arg v; v.hash}).includes(test.hash), {min}, {1}); }; //------subroutines genTuples = { var tuples; tuples = dims.collect({[-1, 0, 1]}).allTuples.select({arg tuple; (abs(tuple.drop(1)).sum <= 1) && (tuple[0] == 0)}); tuples = tuples ++ tuples.collect({arg tuple; [-3, -2, -1, 1, 2, 3].collect({arg octTrans; tuple.deepCopy.put(0, octTrans)})}).flatten; }; initVoices = { var init, voicesInit; voicesInit = popSize.collect({dims.collect({0})}); /* voicesInit = [dims.collect({0})]; (popSize - 1).do({ arg rep, new; rep = dims.rand; new = voicesInit.last.deepCopy; new[rep] = new[rep] + [-1, 1].choose(); voicesInit = voicesInit.add(new); }); */ voicesInit.deepCopy; }; genDurFunc = {arg chordProb, minPad, maxPad, minDur, maxDur, envData, seed; var env, pTable, durFunc; env = Env.pairs([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).asSignal(256).asList.asArray; pTable = env.asRandomTable; [chordProb, minPad, maxPad, minDur, maxDur, envData].postln; durFunc = {arg allowChord, pad = false; var res; res = if(allowChord.not, { pTable.tableRand * (maxDur - minDur) + minDur }, { if(1.0.rand < chordProb, {0}, {pTable.tableRand * (maxDur - minDur) + minDur}); }).round(0.125); if(pad, {res = res + rrand(minPad.asFloat, maxPad.asFloat).round(0.125)}); if(res.asInteger == res, {res = res.asInteger}); res }; seedFunc.value(durFunc, seed); }; genStepFunc = {arg minStep, maxStep, envData, seed; var envDataNorm, env, pTable, stepFunc; [minStep, maxStep, envData].postln; envDataNorm = ([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).flop; envDataNorm = [envDataNorm[0].normalize(minStep, maxStep), envDataNorm[1]].flop; env = Env.pairs(envDataNorm); stepFunc = {arg pDist; env.at(pDist).clip(0.001, 1); }; seedFunc.value(stepFunc, seed); }; genOrders = {arg minMotifLength = 1, maxMotifLength = 5, minProgLength = 0, maxProgLength = 5; ((maxMotifLength - minMotifLength).rand + minMotifLength).collect({ var noProgIns, noSusIns, noSilentIns, prog, sus, silent, order; noSusIns = [1, 2, 3].wchoose(susWeights.normalizeSum); noProgIns = (popSize - noSusIns).rand + 1; noSilentIns = popSize - noSusIns - noProgIns; # prog, sus, silent = (0..(popSize-1)).scramble.clumps([noProgIns, noSusIns, noSilentIns]); prog = (prog.scramble ++ ((maxProgLength - minProgLength).rand + minProgLength).collect({prog.choose}).scramble); if(silent == nil, {silent = []}); [sus.scramble, prog, silent.scramble] }); }; updateVoices = {arg ins, sus; var voices, candidates, nWeights, nProbs, sel; voices = lastXChanges.deepCopy.last; candidates = sus.collect({arg v; tuples.collect({arg t; voices[v] + t})}).flatten; candidates = difference(candidates.asSet, voices.asSet).asList; nProbs = candidates.collect({arg candidate; var stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore; //stepScore = intervalScore.value(voices[ins], candidate, 30, 400, 0.1); stepScore = intervalScore.value(voices[ins], candidate, 100, 100); recentlySoundedScore = inclusionScore.value(lastXChanges.flop[ins], candidate, 0); isInRangeScore = rangeScore.value(candidate, candidate.collect({0}), ranges[ins][0], ranges[ins][1], 0, true); regScore = spacingScore.value(voices.deepCopy.put(ins, candidate), 300); hdScore = pow(hdSum.value(voices.deepCopy.put(ins, candidate)), hdExp); if(hdInvert == 0, {hdScore = 1/hdScore}); //maybe what you want here is a vector to another root and then favoring movement towards it. //distScore = pow(hsChordalDistance.value(voices, voices.put(ins, candidate)), 2); [stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore] }); nWeights = passagesWeights; //this handles nWeights of 0; mainly for testing nProbs = nProbs.flop.select({arg scores, s; nWeights[s] != 0}).flop; nWeights = nWeights.select({arg weight; weight != 0}); nProbs = nProbs.flop.collect({arg scores, s; if(scores.sum == 0, {scores}, {scores.normalizeSum * nWeights[s]}) }); nProbs = nProbs.flop.collect({arg scores, s; scores.product}).normalizeSum; sel = candidates.wchoose(nProbs); voices[ins] = sel; lastXChanges = lastXChanges.add(voices).keep(-5); }; genSubMotif = {arg order, orderIndex, lastState, repeatLast = false, startFromLast = false, isLastOrder = false; var sus, prog, silent, flatOrder, res, isInChord, allowChord, pad, lastXChangesHold, voices, adder; # sus, prog, silent = order; flatOrder = silent ++ sus ++ prog; lastXChangesHold = lastXChanges.deepCopy; voices = lastState.deepCopy; isInChord = popSize.collect({false}); allowChord = false; pad = false; res = []; "------generating motif".postln; //need to figure out here if voices move between motifs flatOrder.do({arg ins, i; if(prog.includes(ins) && repeatLast.not, {updateVoices.value(ins, sus)}); adder = if(silent.includes(ins), {["Rest"]}, {lastXChanges.last.deepCopy[ins]}); if(voices[ins] != adder, { var dur; if((sus ++ silent).includes(ins), { allowChord = (ins != sus.last); pad = (ins == sus.last); }, { if(i < (flatOrder.size - 1), { allowChord = (isInChord[flatOrder[i + 1]] || (ins == flatOrder[i + 1])).not; pad = false; }, { allowChord = false; pad = true }); }); if((orderIndex == 0) && sus.includes(ins), { dur = entrancesDurFunc.value(allowChord, pad); }, { dur = passagesDurFunc.value(allowChord, pad); }); if(dur == 0, {isInChord[ins] = true}, {isInChord = popSize.collect({false})}); voices[ins] = adder; res = res.add([voices.deepCopy.postln, dur]); }); }); // pad ending if(orderIndex == (orders.size - 1), { (0..(popSize-1)).scramble.do({arg ins; if(res.last.first[ins] != ["Rest"], { var dur; voices[ins] = ["Rest"]; allowChord = (voices != popSize.collect({["Rest"]})); pad = allowChord.not; dur = exitsDurFunc.value(allowChord, pad); res = res.add([voices.deepCopy.postln, dur]); }); }); }); //format and return if(startFromLast, {lastXChanges = lastXChangesHold.deepCopy}); res; }; //------primary routines genMotif = { var repeats, fSeq, fDur, durAdd; repeats = 1; fSeq = []; repeats.do({arg index; var motif; motif = []; orders.do({arg order, o; var lastState, subMotif; lastState = if(o == 0, {popSize.collect({["Rest"]})}, {motif.last.last.first}); subMotif = genSubMotif.value(order, o, lastState, isLastOrder: o == (orders.size - 1)); motif = motif.add(subMotif); }); sanityCheck.value(motif, index); fSeq = fSeq.add(motif); }); //round last duration to measure fDur = fSeq.flatten.flatten.slice(nil, 1).sum; durAdd = fDur.round(4) - fDur; if(durAdd < 0, {durAdd = 4 - durAdd}); fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] = fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] + durAdd; fSeq }; genSecondarySeq = {arg seq; var curdles, fSeq; curdles = []; while({curdles.sum < seq.size}, {curdles = curdles ++ [3.rand + 1]}); fSeq = seq.clumps(curdles).collect({arg clump, m; var repeats, paddedSeq; //add rest paddedSeq = clump.add([[[popSize.collect({["Rest"]}), 0.5.rand]]]); //implement repeats repeats = [0.rand + 1, 1].wchoose([1, 0].normalizeSum); repeats.collect({paddedSeq}); }); fSeq }; //------audition funcs /* Event.addEventType(\osc, { if (~addr.postln.notNil) { ~addr.sendMsg(~indexPath, ~indexMsg); ~addr.sendMsg(~seqPath, stringifyToDepth.value(~seqMsg, 3)); //~addr.sendMsg("/STATE/OPEN", (dir.replace("supercollider", "resources") +/+ ~idMsg +/+ ~idMsg ++ "_gui_state" ++ ".state").standardizePath.postln); }; }); */ Event.addEventType(\osc, { if (~addr.notNil) { ~msg; ~addr.sendMsg(~path, *~msg); }; }); genPatterns = {arg inSeq, addr, oneShot = false; var voices, durs, pbinds, res, indices, sectionDurs, msg, ids, seq; seq = inSeq.collect({arg mSeq; mSeq[0]}); # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; pbinds = voices.flop.collect({arg voice, v; var clumps, hdScores, freqs, fDurs, attacks, rels, amps; clumps = voice.separate({arg a, b; a != b }); freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); //attacks = 2.collect({rrand(1, 3)}) ++ freqs.drop(2).collect({rrand(0.3, 0.5)}); attacks = fDurs.collect({arg dur; dur * rrand(0.2, 0.4)}); //rels = freqs.drop(2).collect({rrand(0.3, 0.5)}) ++ 2.collect({rrand(1, 3)}); rels = (clumps.size - 1).collect({arg c; if(clumps[c + 1][0] == ["Rest"], {rrand(1.0, 3.0)}, {rrand(0.3, 0.5)}); }); rels = rels.add(rrand(1.0, 3.0)); amps = freqs.collect({rrand(0.6, 0.99)}); [ Pbind( \instrument, \string_model, \group, group, \freq, Pseq(freqs, 1), \dur, Pseq(fDurs, 1), \attack, Pseq(attacks, 1), \sustain, Pseq(fDurs, 1), \release, Pseq(rels, 1), //\amp, Pseq(amps, 1), \amp, Pbrown(0.5, 1, 0.5), \busIndex, v ), Pbind( \instrument, \sine, \group, group, \freq, Pseq(freqs, 1), \dur, Pseq(fDurs, 1), \sustain, Pseq(fDurs, 1), \busIndex, v ) ] }).flatten; if(oneShot.not, { msg = inSeq.collect({arg mSeq, m; mSeq[1..]}); //ids = inSeq.collect({arg mSeq, m; mSeq[2]}); sectionDurs = seq.collect({arg mSeq; mSeq.flatten2(mSeq.maxDepth - 5).flop[1].sum}); pbinds = pbinds ++ [ Pbind( \type, \osc, \addr, addr, \path, "/playing", \msg, Pseq(msg, 1), \dur, Pseq(sectionDurs, 1) ); ] }); res = Ppar(pbinds); res }; /* genMidiPatterns = {arg seq; var voices, durs, patterns, res, mOut, pbRange; pbRange = 1; //semitones - change this as needed for your situation mOut = MIDIOut.newByName("TiMidity", "TiMidity port 0").latency_(Server.default.latency); # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; res = Ppar( voices.flop.collect({arg voice, v; var clumps, hdScores, freqs, fDurs; mOut.program(v, 70); clumps = voice.separate({arg a, b; a != b }); freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); Pbind( \type, \midi, \chan, v, \noteval, Pseq(freqs.cpsmidi - 24, 1), \note, Pfunc({ | event | event[\noteval].floor }), \dur, Pseq(fDurs, 1), \midiout, mOut, \amp, 1, \bend, Pfunc({ | event | if (event[\note].isRest.not) { var pitchbendvalue = event[\noteval].frac.linlin(0, pbRange, 8192, 8192*2).asInteger; m.bend(v, pitchbendvalue); }; 0; // return something other than nil to avoid stopping the pattern }), ); }); ); res }; */ //------resource management funcs genUID = {Date.seed.asHexString.toLower}; seedFunc = {arg func, seed; var funcArgs, next; next = Routine({loop{func.valueArray(funcArgs).yield }}); next.randSeed_(seed); {arg ...args; funcArgs = args; next.value} }; stringifyToDepth = {arg data, maxDepth = 1; var prettyString = "", rCount = 0, writeArray, indent; if(maxDepth == 0, { data.asCompileString }, { indent = {arg size; size.collect({" "}).join("")}; writeArray = {arg array; prettyString = prettyString ++ indent.value(rCount) ++ "[\n"; rCount = rCount + 1; if(rCount < maxDepth, { array.do({arg subArray; writeArray.value(subArray)}); }, { prettyString = prettyString ++ array.collect({arg subArray; indent.value(rCount + 1) ++ subArray.asCompileString }).join(",\n"); }); rCount = rCount - 1; prettyString = prettyString ++ "\n" ++ indent.value(rCount) ++ "],\n"; }; writeArray.value(data); prettyString.replace(",\n\n", "\n").drop(-2); }) }; sanityCheck = {arg motif, index; //print functions = very helpful ("----------" + index + "------------").postln; motif.flatten.do({arg val, v; if(v > 0, { if(motif.flatten[v-1][0].hammingDistance(val[0]) > 1, {"problem 1".postln}); if(motif.flatten[v-1][0].hammingDistance(val[0]) == 0, {"problem 2".postln}); }); val.postln }); "***********".postln; }; msgInterpret = {arg in, escapeDoubleQuotes = true, escapeSingleQuotes = true; var res; res = in; if(res.isNil.not, { if((res.isArray && res.isString.not), { res = res.asCompileString; res = res.replace(" ", "").replace("\n", "").replace("\t", ""); if(escapeSingleQuotes, {res = res.replace("\'", "")}); if(escapeDoubleQuotes, {res = res.replace("\"", "")}); res = res.replace("Rest", "\"Rest\""); res = res.interpret; }, { var tmpRes; if(res.every({arg char; char.isDecDigit}), {tmpRes = res.asInteger}); if(res.contains("."), {tmpRes = res.asFloat}); if(tmpRes != nil, {res = tmpRes}); }); }); res }; writeResources = {arg path, dict; var file, modelItems, resString; file = File(path,"w"); modelItems = [ seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, entrancesProbVals, passagesProbVals, exitsProbVals, ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, orders, susWeights, orderSize, passagesSize, motifEdited, orderEdited ]; resString = nameSpaces.collect({arg nameSpace; var depth = 0, insert = " "; if(nameSpace == "music_data", {depth = 3; insert = "\n"}); if(nameSpace == "last_changes", {depth = 1; insert = "\n"}); if(nameSpace == "order", {depth = 1; insert = "\n"}); if((nameSpace == "ref_uid") && (dict[nameSpace] == nil), {dict[nameSpace] = "nil"}); "\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(dict[nameSpace], depth) }).join(",\n"); resString = "{\n" ++ resString ++ "\n}"; file.write(resString); file.close; resString }; loadModelFile = {arg path; loadModelJSON.value(File(path, "r").readAllString.parseJSON)}; loadModelJSON = {arg jsonObject; var dict; dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); dict }; setGlobalVars = {arg dict, skipLastXChanges = false; var tmpLastXChanges; tmpLastXChanges = lastXChanges.deepCopy; // order really matters!!!! # seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, entrancesProbVals, passagesProbVals, exitsProbVals, ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, orders, susWeights, orderSize, passagesSize, motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); if(skipLastXChanges, {lastXChanges = tmpLastXChanges}); dict }; globalVarsToDict = { var modelItems, dict; // order really matters!!!! modelItems = [ seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, entrancesProbVals, passagesProbVals, exitsProbVals, ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, orders, susWeights, orderSize, passagesSize, motifEdited, orderEdited ]; dict = Dictionary.with(*nameSpaces.collect({arg nS, n; nS->modelItems[n]})); }; loadLedgerFile = {arg path; ledgerPath = path; loadLedgerJSON.value(File(ledgerPath, "r").readAllString.parseJSON) }; loadLedgerJSON = {arg jsonObject; ledger = jsonObject["ledger"]}; saveLedger = {arg ledger, path; var file; file = File(path, "w"); file.write("{\n\"ledger\":\n" ++ stringifyToDepth.value(ledger, 1) ++ "\n}"); file.close; }; //------global vars primes = [[2, 1], [3, 2], [5, 4], [7, 4], [11, 8], [13, 8]]; //ranges = [[-2400, 0], [-1200, 1200], [0, 2400], [0, 2400]]; exPath = thisProcess.nowExecutingPath; dir = exPath.dirname; //popSize = 4; dims = primes.size; tuples = genTuples.value(); //refUID = nil; group = Group.new; ~group = group; loadLedgerFile.value(dir +/+ ".." +/+ "resources" +/+ "piece_ledger.json"); //passagesWeights = [1, 1, 1, 1, 1]; //susWeights = [1, 1, 1]; // order really matters!!!! nameSpaces = [ "music_data", "last_changes", "cur_uid", "ref_uid", "order_seed", "dur_seed", "motifs_seed", "entrances_probs_vals","passages_probs_vals", "exits_probs_vals", "ranges", "step_probs_vals", "passages_weights", "hd_exp", "hd_invert", "order", "sus_weights", "order_size", "passages_size", "motif_edited", "order_edited" ]; //------OSC funcs OSCdef(\load_ledger, {arg msg, time, addr, port; loadLedgerFile.value(msg[1].asString); }, \load_ledger); OSCdef(\load_model, {arg msg, time, addr, port; var dict; dict = loadModelFile.value(msg[1].asString); setGlobalVars.value(dict); }, \load_model); OSCdef(\save_ledger, {arg msg, time, addr, port; msg.postln; ledger = msgInterpret.value(msg[1].asString.parseJSON["ledger"], false).postln; //loadLedgerJSON.value(msg[0]) saveLedger.value(ledger, msg[2].asString); //loadLedgerFile.value(msg[1].asString); }, \save_ledger); OSCdef(\generate, {arg msg, time, addr, port; var path, dict, durSeeds, musPath, modelString; msg.postln; path = msg[1].asString; dict = loadModelFile.value(path); setGlobalVars.value(dict, true); popSize = ranges.size; //refUID.postln; loadLedgerFile.value(ledgerPath); if(ledger == nil, {ledger = ["tmp"]}); if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); if(refUID == nil, {lastXChanges = [initVoices.value().deepCopy]}); if((refUID != nil) && (refUID != "tmp"), { var file; file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); lastXChanges = msgInterpret.value(file.readAllString.parseJSON["last_changes"]); }); refUID.postln; lastXChanges.collect({arg item; item.postln}); durSeeds = seedFunc.value({3.collect({rrand(100000, 999999)})}, durSeed).value.postln; entrancesDurFunc = genDurFunc.valueArray(entrancesProbVals[..4] ++ [entrancesProbVals[5..]] ++ [durSeeds[0]]); passagesDurFunc = genDurFunc.valueArray(passagesProbVals[..4] ++ [passagesProbVals[5..]] ++ [durSeeds[1]]); exitsDurFunc = genDurFunc.valueArray(exitsProbVals[..4] ++ [exitsProbVals[5..]] ++ [durSeeds[2]]); if(orders == nil, { orders = seedFunc.value(genOrders, orderSeed).valueArray(orderSize ++ passagesSize); //addr.sendMsg("/order", stringifyToDepth.value(orders, 1)); }); stepFunc = genStepFunc.valueArray(stepProbsVals[..1] ++ [stepProbsVals[2..]] ++ [motifSeed]); seq = seedFunc.value(genMotif, motifSeed).value; lastXChanges.collect({arg item; item.postln}); dict = globalVarsToDict.value; modelString = writeResources.value(path, dict); //addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); //~seq = seq; addr.sendMsg("/generated", path, modelString, ledgerPath); }, \generate); OSCdef(\commit, {arg msg, time, addr, port; var musicData, musicChanged, dict, newLedger, modelPath, musString, musFile, test1, test2, lastCurUID, commitType, commitPos; //msg.postln; /* test1 = msg[1].asString.parseJSON; test2 = (dir +/+ ".." +/+ "resources/tmp/tmp_music" ++ ".json").standardizePath.parseJSONFile; msgInterpret.value(test1["music"])[0][0][0][1].class.postln; msgInterpret.value(test2["music_data"])[0][0][0][1].class.postln; (test1["music"] == test2["music_data"]).postln; */ musicData = loadModelJSON.value(msg[1].asString.parseJSON)["music_data"].postln; musicChanged = (musicData != seq).postln; commitType = msg[2].asString; commitPos = msg[3].postln.asInteger; lastCurUID = curUID.deepCopy; curUID = genUID.value; File.mkdir((dir +/+ ".." +/+ "resources" +/+ curUID).standardizePath); File.copy(exPath, (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_code" ++ ".scd").standardizePath); modelPath = (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_mus_model" ++ ".json").standardizePath; dict = globalVarsToDict.value; if(musicChanged, { seq = musicData; dict["music_data"] = seq; dict["motif_edited"] = "true" }); dict["cur_uid"] = curUID; writeResources.value(modelPath, dict); File.delete(ledgerPath ++ "_bak"); File.copy(ledgerPath, ledgerPath ++ "_bak"); File.delete(ledgerPath); /* if(commitType == "add", { if(lastCurUID == "tmp", { ledger = ledger.drop(-1).add(curUID); }, { ledger = ledger.add(curUID); }) }); */ if(commitType == "add", {ledger = ledger.add(curUID)}); if(commitType == "insert", {ledger = ledger.insert(commitPos - 1, curUID)}); if(commitType == "replace", {ledger = ledger.put(commitPos, curUID)}); if(ledger.includes("tmp"), {ledger = ledger.removeAt(ledger.indexOf("tmp"))}); saveLedger.value(ledger, ledgerPath); addr.sendMsg("/committed", curUID, ledgerPath); //refUID = curUID; }, \commit); OSCdef(\transport, {arg msg, time, addr, port; msg.postln; if(msg[1] == 0, { group.set(\release, 2); group.set(\gate, 0); player.stop; }, { // the cued sequence can now be read from file, so this can be cleaned up var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; if(msg[1] == 1, { pSeq = []; cuedSeek = (seq != nil); indexStart = msg[2].asInteger; indexEnd = ledger.size - if(cuedSeek, {2}, {1}); //ledger.postln; if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { ledger[indexStart..indexEnd].do({arg uid, index; var path, file; path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; file = File(path, "r"); pSeq = pSeq.add([msgInterpret.value(file.readAllString.postln.parseJSON["music_data"]), path, indexStart + index, uid]); file.close; }); }); if(cuedSeek, { var path, file; path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; file = File(path, "r"); pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); file.close; }); patterns = genPatterns.value(pSeq, addr); }, { pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; patterns = genPatterns.value(pSeq, addr, true); }); player = Pfset(pattern: patterns, cleanupFunc: { addr.sendMsg("/transport", 0); addr.sendMsg("/one_shot", 0); }); player = player.play }); }, \transport); OSCdef(\transcribe_motif, {arg msg, time, addr, port; var tSeq, refChord, refUID; msg.postln; tSeq = [loadModelJSON.value(msg[1].asString.parseJSON)["music_data"]]; refUID = msg[2].asString.postln; if((refUID != "nil") && (refUID != "tmp"), { var file; file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; file.close; }, { refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; }); ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "lilypond" +/+ "includes").standardizePath, addr, "/transcribe_motif"); }, \transcribe_motif); OSCdef(\transcribe_all, {arg msg, time, addr, port; var cSize, patterns, cuedSeek, indexStart, indexEnd, tmpLedger; if(true, { cuedSeek = (seq != nil); indexStart = msg[1].asInteger; indexEnd = ledger.size - if(cuedSeek, {2}, {1}); //tmp for testing transcription indexEnd = (indexStart+5); //ledger.postln; if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { var lilyPartLedgerFiles; lilyPartLedgerFiles = 4.collect({arg p; File((dir +/+ ".." +/+ "lilypond" +/+ "includes" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly").standardizePath, "w"); }); ledger[indexStart..indexEnd].do({arg uid, index; var path, file, fileString, tSeq, refUID, refChord; path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; file = File(path, "r"); fileString = file.readAllString; tSeq = msgInterpret.value(fileString.parseJSON["music_data"]); refUID = msgInterpret.value(fileString.parseJSON["ref_uid"]); file.close; //uid.postln; //(refUID == "nil").postln; refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; if(refUID != "nil", { path = (dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath; file = File(path, "r"); refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; file.close; }); if(index != indexEnd, { ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath); }, { ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath, addr, "/transcribe_all"); }); lilyPartLedgerFiles.do({arg f, p; f.write("\\include \".." +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly\"\n"); }); }); lilyPartLedgerFiles.do({arg f; f.close }); }); /* if(cuedSeek, { var path, file; path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; file = File(path, "r"); pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); file.close; }); */ }, { }); }, \transcribe_all); ) ~transcribe.value(~seq, dir); ( //synthdefs ~stringModelBusArray = 4.collect({Bus.audio(s, 1)}); ~sineBusArray = 4.collect({Bus.audio(s, 1)}); ~bassBusArray = 1.collect({Bus.audio(s, 1)}); ~hdustBusArray = 1.collect({Bus.audio(s, 1)}); ~samplerBusArray = 2.collect({Bus.audio(s, 1)}); ~sBuf = Buffer.alloc(s, 10, 2); SynthDef(\string_model, {arg freq, gate = 1, sustain, amp, dur, attack, release = 1, busIndex = 0; var trig, exc, sig1, sig2, noHarms; noHarms = rrand(20, 40); exc = Saw.ar(freq, TRand.ar(0.5, 1, Impulse.ar(freq))) * 0.001 + Dust.ar(10000, 0.01); sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), Array.fill(noHarms, {rrand(1, 2)}) ], exc) * 0.8).softclip; //sig1 = HPF.ar(sig1, 300); Out.ar(Select.kr(busIndex, ~stringModelBusArray), sig1 * amp * EnvGen.kr(Env.adsr(attack, 0.3, 0.9, release, 0.9, -3), gate, doneAction: 2)); //Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); }).add; SynthDef(\sine, {arg freq, gate = 1, sustain, amp, dur, busIndex = 0; var sig; sig = SinOsc.ar(freq); Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.asr(0.3, 0.4, 0.3), gate, timeScale: dur, doneAction: 2)); //Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.sine(dur), gate, doneAction: 2)); }).add; SynthDef(\mixer, {arg freq, gate = 1, sustain, amp, dur, out; var nameSpaces, sigs; sigs = [~stringModelBusArray, ~sineBusArray/*, ~bassBusArray, ~hdustBusArray, ~samplerBusArray*/].collect({arg busArray, i; var nameSpace, sig; nameSpace = ['string', 'sine', 'bass', 'hdust', 'sampler'][i]; sig = busArray.collect({arg bus, c; In.ar(bus, 1) * NamedControl.kr(\ ++ nameSpace ++ '_volume_' ++ c, 1, 0.1)}); sig = sig.collect({arg channel, c; Pan2.ar(channel, NamedControl.kr(\ ++ nameSpace ++ '_pan_' ++ c, i / (busArray.size - 1), 0.1) * 2 - 1)}); sig = sig.collect({arg channel, c; channel * NamedControl.kr(\ ++ nameSpace ++ '_mute_' ++ c, 1, 0.1)}); sig = Mix.ar(sig) * pow(NamedControl.kr(\ ++ nameSpace ++ '_volume_master', 1, 0.1), 2); }); sigs = Mix.ar(sigs / 4); Out.ar(0, sigs) }).add; SynthDef(\bass, { var switches, drone; switches = {|i| Dust.kr(0.1)} ! 9; drone = {|i| var harm = pow(2, 2 - (i / 3).trunc), amp = (1 / pow(harm, 2)); SinOsc.ar(60 * harm + TRand.kr(-3, 3, switches[i]), 0, amp)} ! 9; Out.ar(~bassBusArray[0], Mix.new(drone) * 0.2); }).add; SynthDef(\sampler, { Out.ar(~samplerBusArray, PlayBuf.ar(2, ~sBuf, BufRateScale.kr(~sBuf), doneAction: 2)) }).add; // main routine SynthDef(\hdust, { arg gate = 0; var hierarchical_dust, low_sine, high_sine, brown_noise, white_noise; // this triggers the combinations of sources // it is similar to the Supercollider UGen called dust but with a hierarchical structure hierarchical_dust = ( TIRand.kr(0, 1, Impulse.kr(100)) * TIRand.kr(0, 1, Impulse.kr(10)) * TIRand.kr(0, 1, Impulse.kr(1)) * TIRand.kr(0, 1, Impulse.kr(0.1)) ); // adjust the multiplier at the end of each line for adjusting levels // note with each trigger, each source has a 1 in 3 chance of sounding low_sine = SinOsc.ar(76.midicps / 16) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.1; high_sine = SinOsc.ar(76.midicps * 8) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.01; brown_noise = BrownNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.025; white_noise = WhiteNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.02; Out.ar(~hdustBusArray[0], ((low_sine + high_sine + brown_noise + white_noise) ) ); }).add; ) ( var bass, hdust, sampler, mixer; /* bass = Synth.tail(~group, \bass); hdust = Synth.tail(~group, \hdust); sampler = Synth.head(~group, \sampler); */ mixer = Synth.tail(~group, \mixer); OSCdef(\mixer, {arg msg, time, addr, port; mixer.set((msg[1] ++ '_' ++ msg[2] ++ '_' ++ msg[3]), msg[4]) }, \mixer); /* OSCdef(\sampler, {arg msg, time, addr, port; msg.postln; sampler.free; ~sBuf.free; ~sBuf = Buffer.read(s, msg[1].asString.postln, action: {sampler = Synth.head(~group, \sampler)}); }, \sampler); */ ) /* old something ( SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; var trig, exc, sig1, sig2, noHarms, freqFinal, start, end; noHarms = 30; freq = WhiteNoise.ar * 3 + freq; freqFinal = Duty.ar((1/freq), 0, freq); trig = Changed.ar(freqFinal); start = Demand.ar(trig, 0, Dwhite(-1, -0.75)); end = Demand.ar(trig, 0, Dwhite(0.75, 1)); exc = Phasor.ar(trig, (end - start) * freqFinal / SampleRate.ir, start, end, 0) * 0.001 + Dust.ar(10000, 0.01); sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), Array.fill(noHarms, {rrand(2, 3)}) ], exc) * 0.5).softclip; sig1 = HPF.ar(sig1, 300); Out.ar([0, 1], sig1 * EnvGen.kr(Env.adsr(0.3, 0.3, 0.9, 0.5, 0.9), gate, doneAction: 2)); }).add; ) */