( // 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; 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); }); 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.postln.notNil) { ~msg.postln; ~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, { "here".postln; 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; //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; 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); ledger = ledger.drop(-1).add(curUID); 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, {arg msg, time, addr, port; ~transcribe.value(seq, dir, addr); }, \transcribe); ) ~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, hierarchicalDust; hierarchicalDust = ( TIRand.kr(0, 1, Impulse.kr(100)) * TIRand.kr(0, 1, Impulse.kr(10)) * TIRand.kr(0, 1, Impulse.kr(1)) ); 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; ) */