adding one shot playback and custom module cleanup
							parent
							
								
									21611e92b8
								
							
						
					
					
						commit
						e3d1b7c0a3
					
				| @ -0,0 +1,796 @@ | ||||
| ( | ||||
| // helper funcs | ||||
| var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; | ||||
| 
 | ||||
| // score funcs | ||||
| var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; | ||||
| 
 | ||||
| // subroutines | ||||
| var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc; | ||||
| 
 | ||||
| // 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, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| motifEdited, orderEdited; | ||||
| 
 | ||||
| // model aux vars | ||||
| var entrancesDurFunc, passagesDurFunc, exitsDurFunc; | ||||
| 
 | ||||
| // 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) | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| }; | ||||
| 
 | ||||
| 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 = 1/pow(hdSum.value(voices.deepCopy.put(ins, candidate)), 2); | ||||
| 		//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}); | ||||
| 	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; | ||||
| 		var clumps, hdScores, freqs, fDurs; | ||||
| 		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( | ||||
| 			\instrument, \test, | ||||
| 			\group, group, | ||||
| 			\freq, Pseq(freqs, 1), | ||||
| 			\dur, Pseq(fDurs, 1), | ||||
| 			\sustain, Pseq(fDurs, 1) | ||||
| 		) | ||||
| 	}); | ||||
| 	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; | ||||
| 		}, { | ||||
| 			//res.postln; | ||||
| 			if(res.every({arg char; char.isDecDigit}), {res = res.asInteger}); | ||||
| 		}); | ||||
| 	}); | ||||
| 	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, passagesWeights, 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; | ||||
| 	// order really matters!!!! | ||||
| 	# seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 	entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 	ranges, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| 	motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); | ||||
| 	dict | ||||
| }; | ||||
| 
 | ||||
| globalVarsToDict = { | ||||
| 	var modelItems, dict; | ||||
| 	// order really matters!!!! | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, 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; | ||||
| 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", "passages_weights", "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); | ||||
| 
 | ||||
| 	popSize = ranges.size; | ||||
| 
 | ||||
| 	//refUID.postln; | ||||
| 
 | ||||
| 	loadLedgerFile.value(ledgerPath); | ||||
| 	if(ledger == nil, {ledger = ["tmp"]}); | ||||
| 	if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); | ||||
| 
 | ||||
| 	lastXChanges = if(refUID == nil, { | ||||
| 		[initVoices.value().deepCopy]; | ||||
| 	}, { | ||||
| 		var file; | ||||
| 		file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); | ||||
| 		msgInterpret.value(file.readAllString.parseJSON["last_changes"]); | ||||
| 	}); | ||||
| 
 | ||||
| 	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)); | ||||
| 	}); | ||||
| 	seq = seedFunc.value(genMotif, motifSeed).value; | ||||
| 
 | ||||
| 	dict = globalVarsToDict.value; | ||||
| 	modelString = writeResources.value(path, dict); | ||||
| 
 | ||||
| 	//addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); | ||||
| 	addr.sendMsg("/generated", path, modelString); | ||||
| }, \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, { | ||||
| 		player.stop; | ||||
| 		group.set(\gate, 0); | ||||
| 	}, { | ||||
| 		// 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; | ||||
| 			}); | ||||
| 		}, { | ||||
| 			"here".postln; | ||||
| 			pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; | ||||
| 
 | ||||
| 		}); | ||||
| 		patterns = genPatterns.value(pSeq, addr); | ||||
| 		player = Pfset(pattern: patterns, cleanupFunc: { | ||||
| 			addr.sendMsg("/transport", 0); | ||||
| 		}); | ||||
| 		player = player.play | ||||
| 	}); | ||||
| }, \transport); | ||||
| 
 | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; | ||||
| 	var trig, exc, sig1, sig2, noHarms; | ||||
| 	noHarms = 30; | ||||
| 	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.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)); | ||||
| 	//Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); | ||||
| }).add; | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| 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; | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| "{\"a\": 1}".parseYAML["a"].asInteger; | ||||
| "{\"a\": 1}".parseJSON["a"].isNumber; | ||||
| 
 | ||||
| 1223423434123.asHexString.toLower | ||||
| 
 | ||||
| Date.getDate.rawSeconds | ||||
| Date.seed.asHexString.toLower | ||||
| 
 | ||||
| n = NetAddr("localhost", 8080); | ||||
| n.sendMsg("/GET/#", (NetAddr.localAddr.hostname ++ ":" ++ NetAddr.localAddr.port), "/passage_probs_vals"); | ||||
| @ -0,0 +1,55 @@ | ||||
| { | ||||
| "music_data": | ||||
| [ | ||||
|  [ | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 16.75 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.125 ], | ||||
|     [ [ [ 1, -1, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.5 ], | ||||
|     [ [ [ 0, 1, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0 ], | ||||
|     [ [ [ 0, 0, 0, 1, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.375 ], | ||||
|     [ [ [ 1, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.25 ], | ||||
|     [ [ [ 2, 0, 0, -1, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.25 ], | ||||
|     [ [ [ 2, 0, 0, 0, 0, -1 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.25 ], | ||||
|     [ [ [ 2, -1, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.375 ], | ||||
|     [ [ [ 1, 1, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.125 ], | ||||
|     [ [ [ 2, 0, -1, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 3.25 ] | ||||
|   ], | ||||
|   [ | ||||
|     [ [ [ 2, 0, -1, 0, 0, 0 ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 0 ], | ||||
|     [ [ [ 2, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 9.375 ], | ||||
|     [ [ [ 2, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 3.125 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 3.25 ] | ||||
|   ] | ||||
|  ] | ||||
| ], | ||||
| "last_changes": | ||||
| [ | ||||
|   [ [ 2, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], | ||||
|   [ [ 2, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], | ||||
|   [ [ 1, 1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], | ||||
|   [ [ 2, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], | ||||
|   [ [ 2, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ] | ||||
| ], | ||||
| "cur_uid": "46f2010f", | ||||
| "ref_uid": "nil", | ||||
| "order_seed": 209033, | ||||
| "dur_seed": 797162, | ||||
| "motifs_seed": 128998, | ||||
| "entrances_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.26424870466321, 0.75675675675676, 0.5, 0.5, 0.58549222797927, 0.72635135135135, 1, 0.5 ], | ||||
| "passages_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], | ||||
| "exits_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], | ||||
| "ranges": [ [ -384, 2400 ], [ -507, 2400 ], [ -282, 2237 ], [ -1200, 2053 ] ], | ||||
| "passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], | ||||
| "order": | ||||
| [ | ||||
|   [ [ 2 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], [ 3, 1 ] ], | ||||
|   [ [ 1 ], [ 0 ], [ 2, 3 ] ] | ||||
| ], | ||||
| "sus_weights": [ 0.75, 0.69, 0.75 ], | ||||
| "order_size": [ 2, 6 ], | ||||
| "passages_size": [ 0, 10 ], | ||||
| "motif_edited": "true", | ||||
| "order_edited": "false" | ||||
| } | ||||
| @ -0,0 +1,794 @@ | ||||
| ( | ||||
| // helper funcs | ||||
| var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; | ||||
| 
 | ||||
| // score funcs | ||||
| var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; | ||||
| 
 | ||||
| // subroutines | ||||
| var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc; | ||||
| 
 | ||||
| // 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, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| motifEdited, orderEdited; | ||||
| 
 | ||||
| // model aux vars | ||||
| var entrancesDurFunc, passagesDurFunc, exitsDurFunc; | ||||
| 
 | ||||
| // 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) | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| }; | ||||
| 
 | ||||
| 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 = 1/pow(hdSum.value(voices.deepCopy.put(ins, candidate)), 2); | ||||
| 		//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}); | ||||
| 	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; | ||||
| 		var clumps, hdScores, freqs, fDurs; | ||||
| 		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( | ||||
| 			\instrument, \test, | ||||
| 			\group, group, | ||||
| 			\freq, Pseq(freqs, 1), | ||||
| 			\dur, Pseq(fDurs, 1), | ||||
| 			\sustain, Pseq(fDurs, 1) | ||||
| 		) | ||||
| 	}); | ||||
| 	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; | ||||
| 		}, { | ||||
| 			//res.postln; | ||||
| 			if(res.every({arg char; char.isDecDigit}), {res = res.asInteger}); | ||||
| 		}); | ||||
| 	}); | ||||
| 	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, passagesWeights, 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; | ||||
| 	// order really matters!!!! | ||||
| 	# seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 	entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 	ranges, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| 	motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); | ||||
| 	dict | ||||
| }; | ||||
| 
 | ||||
| globalVarsToDict = { | ||||
| 	var modelItems, dict; | ||||
| 	// order really matters!!!! | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, 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; | ||||
| 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", "passages_weights", "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); | ||||
| 
 | ||||
| 	popSize = ranges.size; | ||||
| 
 | ||||
| 	//refUID.postln; | ||||
| 
 | ||||
| 	loadLedgerFile.value(ledgerPath); | ||||
| 	if(ledger == nil, {ledger = ["tmp"]}); | ||||
| 	if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); | ||||
| 
 | ||||
| 	lastXChanges = if(refUID == nil, { | ||||
| 		[initVoices.value().deepCopy]; | ||||
| 	}, { | ||||
| 		var file; | ||||
| 		file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); | ||||
| 		msgInterpret.value(file.readAllString.parseJSON["last_changes"]); | ||||
| 	}); | ||||
| 
 | ||||
| 	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)); | ||||
| 	}); | ||||
| 	seq = seedFunc.value(genMotif, motifSeed).value; | ||||
| 
 | ||||
| 	dict = globalVarsToDict.value; | ||||
| 	modelString = writeResources.value(path, dict); | ||||
| 
 | ||||
| 	//addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); | ||||
| 	addr.sendMsg("/generated", path, modelString); | ||||
| }, \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, { | ||||
| 		player.stop; | ||||
| 		group.set(\gate, 0); | ||||
| 	}, { | ||||
| 		// 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; | ||||
| 			}); | ||||
| 		}, { | ||||
| 			pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; | ||||
| 		}); | ||||
| 		patterns = genPatterns.value(pSeq, addr); | ||||
| 		player = Pfset(pattern: patterns, cleanupFunc: { | ||||
| 			addr.sendMsg("/transport", 0); | ||||
| 		}); | ||||
| 		player = player.play | ||||
| 	}); | ||||
| }, \transport); | ||||
| 
 | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; | ||||
| 	var trig, exc, sig1, sig2, noHarms; | ||||
| 	noHarms = 30; | ||||
| 	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.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)); | ||||
| 	//Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); | ||||
| }).add; | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| 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; | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| "{\"a\": 1}".parseYAML["a"].asInteger; | ||||
| "{\"a\": 1}".parseJSON["a"].isNumber; | ||||
| 
 | ||||
| 1223423434123.asHexString.toLower | ||||
| 
 | ||||
| Date.getDate.rawSeconds | ||||
| Date.seed.asHexString.toLower | ||||
| 
 | ||||
| n = NetAddr("localhost", 8080); | ||||
| n.sendMsg("/GET/#", (NetAddr.localAddr.hostname ++ ":" ++ NetAddr.localAddr.port), "/passage_probs_vals"); | ||||
| @ -0,0 +1,794 @@ | ||||
| ( | ||||
| // helper funcs | ||||
| var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; | ||||
| 
 | ||||
| // score funcs | ||||
| var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; | ||||
| 
 | ||||
| // subroutines | ||||
| var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc; | ||||
| 
 | ||||
| // 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, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| motifEdited, orderEdited; | ||||
| 
 | ||||
| // model aux vars | ||||
| var entrancesDurFunc, passagesDurFunc, exitsDurFunc; | ||||
| 
 | ||||
| // 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) | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| }; | ||||
| 
 | ||||
| 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 = 1/pow(hdSum.value(voices.deepCopy.put(ins, candidate)), 2); | ||||
| 		//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}); | ||||
| 	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; | ||||
| 		var clumps, hdScores, freqs, fDurs; | ||||
| 		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( | ||||
| 			\instrument, \test, | ||||
| 			\group, group, | ||||
| 			\freq, Pseq(freqs, 1), | ||||
| 			\dur, Pseq(fDurs, 1), | ||||
| 			\sustain, Pseq(fDurs, 1) | ||||
| 		) | ||||
| 	}); | ||||
| 	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; | ||||
| 		}, { | ||||
| 			//res.postln; | ||||
| 			if(res.every({arg char; char.isDecDigit}), {res = res.asInteger}); | ||||
| 		}); | ||||
| 	}); | ||||
| 	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, passagesWeights, 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; | ||||
| 	// order really matters!!!! | ||||
| 	# seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 	entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 	ranges, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| 	motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); | ||||
| 	dict | ||||
| }; | ||||
| 
 | ||||
| globalVarsToDict = { | ||||
| 	var modelItems, dict; | ||||
| 	// order really matters!!!! | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, 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; | ||||
| 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", "passages_weights", "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); | ||||
| 
 | ||||
| 	popSize = ranges.size; | ||||
| 
 | ||||
| 	//refUID.postln; | ||||
| 
 | ||||
| 	loadLedgerFile.value(ledgerPath); | ||||
| 	if(ledger == nil, {ledger = ["tmp"]}); | ||||
| 	if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); | ||||
| 
 | ||||
| 	lastXChanges = if(refUID == nil, { | ||||
| 		[initVoices.value().deepCopy]; | ||||
| 	}, { | ||||
| 		var file; | ||||
| 		file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); | ||||
| 		msgInterpret.value(file.readAllString.parseJSON["last_changes"]); | ||||
| 	}); | ||||
| 
 | ||||
| 	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)); | ||||
| 	}); | ||||
| 	seq = seedFunc.value(genMotif, motifSeed).value; | ||||
| 
 | ||||
| 	dict = globalVarsToDict.value; | ||||
| 	modelString = writeResources.value(path, dict); | ||||
| 
 | ||||
| 	//addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); | ||||
| 	addr.sendMsg("/generated", path, modelString); | ||||
| }, \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, { | ||||
| 		player.stop; | ||||
| 		group.set(\gate, 0); | ||||
| 	}, { | ||||
| 		// 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; | ||||
| 			}); | ||||
| 		}, { | ||||
| 			pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; | ||||
| 		}); | ||||
| 		patterns = genPatterns.value(pSeq, addr); | ||||
| 		player = Pfset(pattern: patterns, cleanupFunc: { | ||||
| 			addr.sendMsg("/transport", 0); | ||||
| 		}); | ||||
| 		player = player.play | ||||
| 	}); | ||||
| }, \transport); | ||||
| 
 | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; | ||||
| 	var trig, exc, sig1, sig2, noHarms; | ||||
| 	noHarms = 30; | ||||
| 	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.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)); | ||||
| 	//Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); | ||||
| }).add; | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| 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; | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| "{\"a\": 1}".parseYAML["a"].asInteger; | ||||
| "{\"a\": 1}".parseJSON["a"].isNumber; | ||||
| 
 | ||||
| 1223423434123.asHexString.toLower | ||||
| 
 | ||||
| Date.getDate.rawSeconds | ||||
| Date.seed.asHexString.toLower | ||||
| 
 | ||||
| n = NetAddr("localhost", 8080); | ||||
| n.sendMsg("/GET/#", (NetAddr.localAddr.hostname ++ ":" ++ NetAddr.localAddr.port), "/passage_probs_vals"); | ||||
| @ -0,0 +1,796 @@ | ||||
| ( | ||||
| // helper funcs | ||||
| var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; | ||||
| 
 | ||||
| // score funcs | ||||
| var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; | ||||
| 
 | ||||
| // subroutines | ||||
| var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc; | ||||
| 
 | ||||
| // 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, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| motifEdited, orderEdited; | ||||
| 
 | ||||
| // model aux vars | ||||
| var entrancesDurFunc, passagesDurFunc, exitsDurFunc; | ||||
| 
 | ||||
| // 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) | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| }; | ||||
| 
 | ||||
| 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 = 1/pow(hdSum.value(voices.deepCopy.put(ins, candidate)), 2); | ||||
| 		//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}); | ||||
| 	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; | ||||
| 		var clumps, hdScores, freqs, fDurs; | ||||
| 		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( | ||||
| 			\instrument, \test, | ||||
| 			\group, group, | ||||
| 			\freq, Pseq(freqs, 1), | ||||
| 			\dur, Pseq(fDurs, 1), | ||||
| 			\sustain, Pseq(fDurs, 1) | ||||
| 		) | ||||
| 	}); | ||||
| 	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; | ||||
| 		}, { | ||||
| 			//res.postln; | ||||
| 			if(res.every({arg char; char.isDecDigit}), {res = res.asInteger}); | ||||
| 		}); | ||||
| 	}); | ||||
| 	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, passagesWeights, 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; | ||||
| 	// order really matters!!!! | ||||
| 	# seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 	entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 	ranges, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| 	motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); | ||||
| 	dict | ||||
| }; | ||||
| 
 | ||||
| globalVarsToDict = { | ||||
| 	var modelItems, dict; | ||||
| 	// order really matters!!!! | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, 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; | ||||
| 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", "passages_weights", "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); | ||||
| 
 | ||||
| 	popSize = ranges.size; | ||||
| 
 | ||||
| 	//refUID.postln; | ||||
| 
 | ||||
| 	loadLedgerFile.value(ledgerPath); | ||||
| 	if(ledger == nil, {ledger = ["tmp"]}); | ||||
| 	if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); | ||||
| 
 | ||||
| 	lastXChanges = if(refUID == nil, { | ||||
| 		[initVoices.value().deepCopy]; | ||||
| 	}, { | ||||
| 		var file; | ||||
| 		file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); | ||||
| 		msgInterpret.value(file.readAllString.parseJSON["last_changes"]); | ||||
| 	}); | ||||
| 
 | ||||
| 	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)); | ||||
| 	}); | ||||
| 	seq = seedFunc.value(genMotif, motifSeed).value; | ||||
| 
 | ||||
| 	dict = globalVarsToDict.value; | ||||
| 	modelString = writeResources.value(path, dict); | ||||
| 
 | ||||
| 	//addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); | ||||
| 	addr.sendMsg("/generated", path, modelString); | ||||
| }, \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, { | ||||
| 		player.stop; | ||||
| 		group.set(\gate, 0); | ||||
| 	}, { | ||||
| 		// 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; | ||||
| 			}); | ||||
| 		}, { | ||||
| 			"here".postln; | ||||
| 			pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; | ||||
| 
 | ||||
| 		}); | ||||
| 		patterns = genPatterns.value(pSeq, addr); | ||||
| 		player = Pfset(pattern: patterns, cleanupFunc: { | ||||
| 			addr.sendMsg("/transport", 0); | ||||
| 		}); | ||||
| 		player = player.play | ||||
| 	}); | ||||
| }, \transport); | ||||
| 
 | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; | ||||
| 	var trig, exc, sig1, sig2, noHarms; | ||||
| 	noHarms = 30; | ||||
| 	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.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)); | ||||
| 	//Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); | ||||
| }).add; | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| 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; | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| "{\"a\": 1}".parseYAML["a"].asInteger; | ||||
| "{\"a\": 1}".parseJSON["a"].isNumber; | ||||
| 
 | ||||
| 1223423434123.asHexString.toLower | ||||
| 
 | ||||
| Date.getDate.rawSeconds | ||||
| Date.seed.asHexString.toLower | ||||
| 
 | ||||
| n = NetAddr("localhost", 8080); | ||||
| n.sendMsg("/GET/#", (NetAddr.localAddr.hostname ++ ":" ++ NetAddr.localAddr.port), "/passage_probs_vals"); | ||||
| @ -0,0 +1,55 @@ | ||||
| { | ||||
| "music_data": | ||||
| [ | ||||
|  [ | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 7.125 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 1, 0, 0, 0 ], [ "Rest" ] ], 0.25 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ "Rest" ] ], 0.375 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ "Rest" ] ], 0.125 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ "Rest" ] ], 7.25 ] | ||||
|   ], | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ "Rest" ] ], 0.125 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], 5 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, -1, 0, 1 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.25 ], | ||||
|     [ [ [ "Rest" ], [ -1, 0, 1, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ 1, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.125 ], | ||||
|     [ [ [ "Rest" ], [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.5 ], | ||||
|     [ [ [ "Rest" ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], 10.25 ], | ||||
|     [ [ [ "Rest" ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, 1 ], [ "Rest" ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 1 ], [ "Rest" ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 6.125 ] | ||||
|   ] | ||||
|  ] | ||||
| ], | ||||
| "last_changes": | ||||
| [ | ||||
|   [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, -1, 0, 1 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 0, 0, 0, 0, 0 ], [ -1, 0, 1, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ] | ||||
| ], | ||||
| "cur_uid": "64085471", | ||||
| "ref_uid": "nil", | ||||
| "order_seed": 815237, | ||||
| "dur_seed": 146432, | ||||
| "motifs_seed": 680955, | ||||
| "entrances_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.26424870466321, 0.75675675675676, 0.5, 0.5, 0.58549222797927, 0.72635135135135, 1, 0.5 ], | ||||
| "passages_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], | ||||
| "exits_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], | ||||
| "ranges": [ [ -384, 2400 ], [ -507, 2400 ], [ -282, 2237 ], [ -1200, 2053 ] ], | ||||
| "passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], | ||||
| "order": | ||||
| [ | ||||
|   [ [ 1, 0 ], [ 2, 2, 2, 2 ], [ 3 ] ], | ||||
|   [ [ 2, 3 ], [ 1, 1, 1, 1, 1 ], [ 0 ] ] | ||||
| ], | ||||
| "sus_weights": [ 0.75, 0.69, 0.75 ], | ||||
| "order_size": [ 2, 6 ], | ||||
| "passages_size": [ 0, 10 ], | ||||
| "motif_edited": "false", | ||||
| "order_edited": "false" | ||||
| } | ||||
| @ -0,0 +1,825 @@ | ||||
| ( | ||||
| // helper funcs | ||||
| var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; | ||||
| 
 | ||||
| // score funcs | ||||
| var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; | ||||
| 
 | ||||
| // subroutines | ||||
| var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc; | ||||
| 
 | ||||
| // 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, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| motifEdited, orderEdited; | ||||
| 
 | ||||
| // model aux vars | ||||
| var entrancesDurFunc, passagesDurFunc, exitsDurFunc; | ||||
| 
 | ||||
| // 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) | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| }; | ||||
| 
 | ||||
| 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 = 1/pow(hdSum.value(voices.deepCopy.put(ins, candidate)), 2); | ||||
| 		//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}); | ||||
| 	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; | ||||
| 	var voices, durs, patterns, res, indices, sectionDurs, msg, ids, seq; | ||||
| 	seq = inSeq.collect({arg mSeq; mSeq[0]}); | ||||
| 	# voices, durs =  seq.flatten2(seq.maxDepth - 5).flop; | ||||
| 	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}); | ||||
| 	res = Ppar( | ||||
| 		voices.flop.collect({arg voice; | ||||
| 			var clumps, hdScores, freqs, fDurs; | ||||
| 			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( | ||||
| 				\instrument, \test, | ||||
| 				\group, group, | ||||
| 				\freq, Pseq(freqs, 1), | ||||
| 				\dur, Pseq(fDurs, 1), | ||||
| 				\sustain, Pseq(fDurs, 1) | ||||
| 			) | ||||
| 		}) ++ | ||||
| 		[ | ||||
| 			Pbind( | ||||
| 				\type, \osc, | ||||
| 				\addr, addr, | ||||
| 				//\indexPath, "/cur_play_index", | ||||
| 				//\indexMsg, Pseq(indices, 1), | ||||
| 				//\seqPath, "/mus_seq", | ||||
| 				//\seqMsg, Pseq(seq, 1), | ||||
| 				\path, "/playing", | ||||
| 				\msg, Pseq(msg, 1), | ||||
| 				\dur, Pseq(sectionDurs, 1) | ||||
| 			); | ||||
| 		] | ||||
| 	); | ||||
| 	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; | ||||
| 		}, { | ||||
| 			//res.postln; | ||||
| 			if(res.every({arg char; char.isDecDigit}), {res = res.asInteger}); | ||||
| 		}); | ||||
| 	}); | ||||
| 	res | ||||
| }; | ||||
| 
 | ||||
| /* | ||||
| writeResources = {arg path; | ||||
| 	var file, modelItems, resString; | ||||
| 	file = File(path,"w"); | ||||
| 
 | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| 		motifEdited, orderEdited | ||||
| 	]; | ||||
| 
 | ||||
| 	resString = [nameSpaces, modelItems].flop.collect({arg item; | ||||
| 		var nameSpace, modelItem, depth = 0, insert = " "; | ||||
| 		# nameSpace, modelItem = item; | ||||
| 		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") && (modelItem == nil), {modelItem = "nil"}); | ||||
| 		"\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(modelItem, depth) | ||||
| 	}).join(",\n"); | ||||
| 
 | ||||
| 	resString = "{\n" ++ resString ++ "\n}"; | ||||
| 
 | ||||
| 	file.write(resString); | ||||
| 	file.close; | ||||
| 	resString | ||||
| }; | ||||
| */ | ||||
| 
 | ||||
| writeResources = {arg path, dict; | ||||
| 	var file, modelItems, resString; | ||||
| 	file = File(path,"w"); | ||||
| 
 | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, 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; | ||||
| 	//model = File(path, "r").readAllString.parseJSON; | ||||
| 	dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); | ||||
| 	dict | ||||
| }; | ||||
| 
 | ||||
| setGlobalVars = {arg dict; | ||||
| 	// order really matters!!!! | ||||
| 	# seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 	entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 	ranges, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| 	motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); | ||||
| 	dict | ||||
| }; | ||||
| 
 | ||||
| globalVarsToDict = { | ||||
| 	var modelItems, dict; | ||||
| 	// order really matters!!!! | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, 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; | ||||
| 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", "passages_weights", "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); | ||||
| 
 | ||||
| 	popSize = ranges.size; | ||||
| 
 | ||||
| 	//refUID.postln; | ||||
| 
 | ||||
| 	loadLedgerFile.value(ledgerPath); | ||||
| 	if(ledger == nil, {ledger = ["tmp"]}); | ||||
| 	if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); | ||||
| 
 | ||||
| 	lastXChanges = if(refUID == nil, { | ||||
| 		[initVoices.value().deepCopy]; | ||||
| 	}, { | ||||
| 		var file; | ||||
| 		file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); | ||||
| 		msgInterpret.value(file.readAllString.parseJSON["last_changes"]); | ||||
| 	}); | ||||
| 
 | ||||
| 	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)); | ||||
| 	}); | ||||
| 	seq = seedFunc.value(genMotif, motifSeed).value; | ||||
| 
 | ||||
| 	dict = globalVarsToDict.value; | ||||
| 	modelString = writeResources.value(path, dict); | ||||
| 
 | ||||
| 	//addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); | ||||
| 	addr.sendMsg("/generated", path, modelString); | ||||
| }, \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, { | ||||
| 		player.stop; | ||||
| 		group.set(\gate, 0); | ||||
| 	}, { | ||||
| 		// the cued sequence can now be read from file, so this can be cleaned up | ||||
| 		var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; | ||||
| 		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"); | ||||
| 				//"here".postln; | ||||
| 				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); | ||||
| 		player = Pfset(pattern: patterns, cleanupFunc: { | ||||
| 			addr.sendMsg("/transport", 0); | ||||
| 		}); | ||||
| 		player = player.play | ||||
| 	}); | ||||
| }, \transport); | ||||
| 
 | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; | ||||
| 	var trig, exc, sig1, sig2, noHarms; | ||||
| 	noHarms = 30; | ||||
| 	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.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)); | ||||
| 	//Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); | ||||
| }).add; | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| 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; | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| "{\"a\": 1}".parseYAML["a"].asInteger; | ||||
| "{\"a\": 1}".parseJSON["a"].isNumber; | ||||
| 
 | ||||
| 1223423434123.asHexString.toLower | ||||
| 
 | ||||
| Date.getDate.rawSeconds | ||||
| Date.seed.asHexString.toLower | ||||
| 
 | ||||
| n = NetAddr("localhost", 8080); | ||||
| n.sendMsg("/GET/#", (NetAddr.localAddr.hostname ++ ":" ++ NetAddr.localAddr.port), "/passage_probs_vals"); | ||||
| @ -0,0 +1,73 @@ | ||||
| { | ||||
| "music_data": | ||||
| [ | ||||
|  [ | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 10.25 ], | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 3 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ 1, 0, 0, 0, 0, -1 ], [ -1, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ -1, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ -1, 1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ -1, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 1.625 ] | ||||
|   ], | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ -1, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.25 ], | ||||
|     [ [ [ "Rest" ], [ 1, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ 1, 0, 0, -1, 0, 0 ], [ -1, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ 1, 0, 0, -1, 0, 0 ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.125 ], | ||||
|     [ [ [ "Rest" ], [ 1, -1, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ 0, 1, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.25 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 1 ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ] ], 8.125 ] | ||||
|   ], | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 1 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 4.625 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ "Rest" ], [ "Rest" ], [ 0, 0, 1, 0, -1, 0 ] ], 0.25 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ "Rest" ], [ "Rest" ], [ -1, 0, 1, 0, 1, 0 ] ], 0.25 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ "Rest" ], [ "Rest" ], [ -1, 1, 1, 0, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ "Rest" ], [ "Rest" ], [ 0, -1, 1, 0, 0, 0 ] ], 7.375 ] | ||||
|   ], | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ 0, -1, 1, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ 1, 0, 0, 0, 0, -1 ], [ 0, -1, 1, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ 2, -1, 0, 0, 0, -1 ], [ 1, 0, 0, 0, 0, -1 ], [ 0, -1, 1, 0, 0, 0 ] ], 3.375 ], | ||||
|     [ [ [ "Rest" ], [ 2, -1, 0, 0, 0, -1 ], [ "Rest" ], [ 0, -1, 1, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ 2, -1, 0, 0, 0, -1 ], [ "Rest" ], [ "Rest" ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 3.625 ] | ||||
|   ] | ||||
|  ] | ||||
| ], | ||||
| "last_changes": | ||||
| [ | ||||
|   [ [ 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, 1, 0, -1, 0 ] ], | ||||
|   [ [ 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ 1, 0, 0, 0, 0, -1 ], [ -1, 0, 1, 0, 1, 0 ] ], | ||||
|   [ [ 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ 1, 0, 0, 0, 0, -1 ], [ -1, 1, 1, 0, 0, 0 ] ], | ||||
|   [ [ 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ 1, 0, 0, 0, 0, -1 ], [ 0, -1, 1, 0, 0, 0 ] ], | ||||
|   [ [ 0, 0, 1, 0, 0, 0 ], [ 2, -1, 0, 0, 0, -1 ], [ 1, 0, 0, 0, 0, -1 ], [ 0, -1, 1, 0, 0, 0 ] ] | ||||
| ], | ||||
| "cur_uid": "6506161e", | ||||
| "ref_uid": "nil", | ||||
| "order_seed": 629173, | ||||
| "dur_seed": 841732, | ||||
| "motifs_seed": 647828, | ||||
| "entrances_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.26424870466321, 0.75675675675676, 0.5, 0.5, 0.58549222797927, 0.72635135135135, 1, 0.5 ], | ||||
| "passages_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], | ||||
| "exits_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], | ||||
| "ranges": [ [ -384, 2400 ], [ -507, 2400 ], [ -282, 2237 ], [ -1200, 2053 ] ], | ||||
| "passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], | ||||
| "order": | ||||
| [ | ||||
|   [ [ 2, 3 ], [ 1, 0, 1, 0, 1, 1 ], [  ] ], | ||||
|   [ [ 3 ], [ 1, 2, 2, 1, 1, 1 ], [ 0 ] ], | ||||
|   [ [ 0 ], [ 3, 3, 3, 3 ], [ 2, 1 ] ], | ||||
|   [ [ 2, 3 ], [ 1 ], [ 0 ] ] | ||||
| ], | ||||
| "sus_weights": [ 0.75, 0.69, 0.75 ], | ||||
| "order_size": [ 2, 6 ], | ||||
| "passages_size": [ 0, 10 ], | ||||
| "motif_edited": "true", | ||||
| "order_edited": "false" | ||||
| } | ||||
| @ -0,0 +1,825 @@ | ||||
| ( | ||||
| // helper funcs | ||||
| var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; | ||||
| 
 | ||||
| // score funcs | ||||
| var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; | ||||
| 
 | ||||
| // subroutines | ||||
| var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc; | ||||
| 
 | ||||
| // 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, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| motifEdited, orderEdited; | ||||
| 
 | ||||
| // model aux vars | ||||
| var entrancesDurFunc, passagesDurFunc, exitsDurFunc; | ||||
| 
 | ||||
| // 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) | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| }; | ||||
| 
 | ||||
| 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 = 1/pow(hdSum.value(voices.deepCopy.put(ins, candidate)), 2); | ||||
| 		//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}); | ||||
| 	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; | ||||
| 	var voices, durs, patterns, res, indices, sectionDurs, msg, ids, seq; | ||||
| 	seq = inSeq.collect({arg mSeq; mSeq[0]}); | ||||
| 	# voices, durs =  seq.flatten2(seq.maxDepth - 5).flop; | ||||
| 	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}); | ||||
| 	res = Ppar( | ||||
| 		voices.flop.collect({arg voice; | ||||
| 			var clumps, hdScores, freqs, fDurs; | ||||
| 			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( | ||||
| 				\instrument, \test, | ||||
| 				\group, group, | ||||
| 				\freq, Pseq(freqs, 1), | ||||
| 				\dur, Pseq(fDurs, 1), | ||||
| 				\sustain, Pseq(fDurs, 1) | ||||
| 			) | ||||
| 		}) ++ | ||||
| 		[ | ||||
| 			Pbind( | ||||
| 				\type, \osc, | ||||
| 				\addr, addr, | ||||
| 				//\indexPath, "/cur_play_index", | ||||
| 				//\indexMsg, Pseq(indices, 1), | ||||
| 				//\seqPath, "/mus_seq", | ||||
| 				//\seqMsg, Pseq(seq, 1), | ||||
| 				\path, "/playing", | ||||
| 				\msg, Pseq(msg, 1), | ||||
| 				\dur, Pseq(sectionDurs, 1) | ||||
| 			); | ||||
| 		] | ||||
| 	); | ||||
| 	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; | ||||
| 		}, { | ||||
| 			//res.postln; | ||||
| 			if(res.every({arg char; char.isDecDigit}), {res = res.asInteger}); | ||||
| 		}); | ||||
| 	}); | ||||
| 	res | ||||
| }; | ||||
| 
 | ||||
| /* | ||||
| writeResources = {arg path; | ||||
| 	var file, modelItems, resString; | ||||
| 	file = File(path,"w"); | ||||
| 
 | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| 		motifEdited, orderEdited | ||||
| 	]; | ||||
| 
 | ||||
| 	resString = [nameSpaces, modelItems].flop.collect({arg item; | ||||
| 		var nameSpace, modelItem, depth = 0, insert = " "; | ||||
| 		# nameSpace, modelItem = item; | ||||
| 		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") && (modelItem == nil), {modelItem = "nil"}); | ||||
| 		"\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(modelItem, depth) | ||||
| 	}).join(",\n"); | ||||
| 
 | ||||
| 	resString = "{\n" ++ resString ++ "\n}"; | ||||
| 
 | ||||
| 	file.write(resString); | ||||
| 	file.close; | ||||
| 	resString | ||||
| }; | ||||
| */ | ||||
| 
 | ||||
| writeResources = {arg path, dict; | ||||
| 	var file, modelItems, resString; | ||||
| 	file = File(path,"w"); | ||||
| 
 | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, 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; | ||||
| 	//model = File(path, "r").readAllString.parseJSON; | ||||
| 	dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); | ||||
| 	dict | ||||
| }; | ||||
| 
 | ||||
| setGlobalVars = {arg dict; | ||||
| 	// order really matters!!!! | ||||
| 	# seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 	entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 	ranges, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| 	motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); | ||||
| 	dict | ||||
| }; | ||||
| 
 | ||||
| globalVarsToDict = { | ||||
| 	var modelItems, dict; | ||||
| 	// order really matters!!!! | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, 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; | ||||
| 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", "passages_weights", "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); | ||||
| 
 | ||||
| 	popSize = ranges.size; | ||||
| 
 | ||||
| 	//refUID.postln; | ||||
| 
 | ||||
| 	loadLedgerFile.value(ledgerPath); | ||||
| 	if(ledger == nil, {ledger = ["tmp"]}); | ||||
| 	if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); | ||||
| 
 | ||||
| 	lastXChanges = if(refUID == nil, { | ||||
| 		[initVoices.value().deepCopy]; | ||||
| 	}, { | ||||
| 		var file; | ||||
| 		file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); | ||||
| 		msgInterpret.value(file.readAllString.parseJSON["last_changes"]); | ||||
| 	}); | ||||
| 
 | ||||
| 	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)); | ||||
| 	}); | ||||
| 	seq = seedFunc.value(genMotif, motifSeed).value; | ||||
| 
 | ||||
| 	dict = globalVarsToDict.value; | ||||
| 	modelString = writeResources.value(path, dict); | ||||
| 
 | ||||
| 	//addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); | ||||
| 	addr.sendMsg("/generated", path, modelString); | ||||
| }, \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, { | ||||
| 		player.stop; | ||||
| 		group.set(\gate, 0); | ||||
| 	}, { | ||||
| 		// the cued sequence can now be read from file, so this can be cleaned up | ||||
| 		var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; | ||||
| 		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"); | ||||
| 				//"here".postln; | ||||
| 				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); | ||||
| 		player = Pfset(pattern: patterns, cleanupFunc: { | ||||
| 			addr.sendMsg("/transport", 0); | ||||
| 		}); | ||||
| 		player = player.play | ||||
| 	}); | ||||
| }, \transport); | ||||
| 
 | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; | ||||
| 	var trig, exc, sig1, sig2, noHarms; | ||||
| 	noHarms = 30; | ||||
| 	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.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)); | ||||
| 	//Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); | ||||
| }).add; | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| 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; | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| "{\"a\": 1}".parseYAML["a"].asInteger; | ||||
| "{\"a\": 1}".parseJSON["a"].isNumber; | ||||
| 
 | ||||
| 1223423434123.asHexString.toLower | ||||
| 
 | ||||
| Date.getDate.rawSeconds | ||||
| Date.seed.asHexString.toLower | ||||
| 
 | ||||
| n = NetAddr("localhost", 8080); | ||||
| n.sendMsg("/GET/#", (NetAddr.localAddr.hostname ++ ":" ++ NetAddr.localAddr.port), "/passage_probs_vals"); | ||||
| @ -0,0 +1,91 @@ | ||||
| { | ||||
| "music_data": | ||||
| [ | ||||
|  [ | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 7.375 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, -1 ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 1, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 0.25 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, -1, 0, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, 0 ] ], 0.25 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, -1, 0, 0, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 1, 0, 0, 0 ] ], 0.125 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 1, 0, 0, 0, 0 ] ], 9.125 ] | ||||
|   ], | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, 1, 0, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 7.125 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, -1, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 1, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ "Rest" ], [ "Rest" ] ], 0.375 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0.125 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, -1, 0 ], [ "Rest" ], [ "Rest" ] ], 0.5 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ "Rest" ], [ "Rest" ] ], 0.375 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 1, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0.375 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, -1, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0.125 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0.375 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 9 ] | ||||
|   ], | ||||
|   [ | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 4.75 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, 0, -1, 0 ] ], 0 ], | ||||
|     [ [ [ 0, 1, 0, -1, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, 0, -1, 0 ] ], 0.25 ], | ||||
|     [ [ [ 0, 1, 0, -1, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 1, 0 ] ], 0.375 ], | ||||
|     [ [ [ 0, 1, 0, -1, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, -1, 0, 0, 0, 0 ] ], 5 ] | ||||
|   ], | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, -1, 0, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.5 ], | ||||
|     [ [ [ "Rest" ], [ 2, 0, 0, -1, 0, 0 ], [ 1, 0, -1, -1, 0, 0 ], [ "Rest" ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, -1, 0, 1 ], [ "Rest" ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ 2, 0, 0, -1, 0, 0 ], [ 1, 0, 0, -1, 0, 0 ], [ "Rest" ] ], 0.25 ], | ||||
|     [ [ [ "Rest" ], [ 2, 0, 0, -1, 0, 0 ], [ 2, 0, 0, -2, 0, 0 ], [ "Rest" ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ 2, 0, 0, -1, 0, 0 ], [ 2, -1, 0, -1, 0, 0 ], [ "Rest" ] ], 7 ] | ||||
|   ], | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ 2, -1, 0, -1, 0, 1 ], [ 2, -1, 0, -1, 0, 0 ], [ "Rest" ] ], 0.25 ], | ||||
|     [ [ [ "Rest" ], [ 3, -1, -1, -1, 0, 0 ], [ 2, -1, 0, -1, 0, 0 ], [ "Rest" ] ], 0.25 ], | ||||
|     [ [ [ "Rest" ], [ 3, -1, 0, -1, -1, 0 ], [ 2, -1, 0, -1, 0, 0 ], [ "Rest" ] ], 0.5 ], | ||||
|     [ [ [ "Rest" ], [ 3, -2, 0, -1, 0, 0 ], [ 2, -1, 0, -1, 0, 0 ], [ "Rest" ] ], 6.5 ], | ||||
|     [ [ [ "Rest" ], [ 3, -2, 0, -1, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0.25 ], | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 1.375 ] | ||||
|   ] | ||||
|  ] | ||||
| ], | ||||
| "last_changes": | ||||
| [ | ||||
|   [ [ 0, 1, 0, -1, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ], [ 2, -1, 0, -1, 0, 0 ], [ 2, -1, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 1, 0, -1, 0, 0 ], [ 2, -1, 0, -1, 0, 1 ], [ 2, -1, 0, -1, 0, 0 ], [ 2, -1, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 1, 0, -1, 0, 0 ], [ 3, -1, -1, -1, 0, 0 ], [ 2, -1, 0, -1, 0, 0 ], [ 2, -1, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 1, 0, -1, 0, 0 ], [ 3, -1, 0, -1, -1, 0 ], [ 2, -1, 0, -1, 0, 0 ], [ 2, -1, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 1, 0, -1, 0, 0 ], [ 3, -2, 0, -1, 0, 0 ], [ 2, -1, 0, -1, 0, 0 ], [ 2, -1, 0, 0, 0, 0 ] ] | ||||
| ], | ||||
| "cur_uid": "65c4abeb", | ||||
| "ref_uid": "nil", | ||||
| "order_seed": 792993, | ||||
| "dur_seed": 209090, | ||||
| "motifs_seed": 340840, | ||||
| "entrances_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.26424870466321, 0.75675675675676, 0.5, 0.5, 0.58549222797927, 0.72635135135135, 1, 0.5 ], | ||||
| "passages_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], | ||||
| "exits_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], | ||||
| "ranges": [ [ -384, 2400 ], [ -507, 2400 ], [ -282, 2237 ], [ -1200, 2053 ] ], | ||||
| "passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], | ||||
| "order": | ||||
| [ | ||||
|   [ [ 2, 1 ], [ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 ], [ 0 ] ], | ||||
|   [ [ 0 ], [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], [ 2, 3 ] ], | ||||
|   [ [ 1, 2 ], [ 3, 0, 3, 3 ], [  ] ], | ||||
|   [ [ 1 ], [ 2, 2, 2, 2, 2 ], [ 0, 3 ] ], | ||||
|   [ [ 2 ], [ 1, 1, 1, 1 ], [ 3, 0 ] ] | ||||
| ], | ||||
| "sus_weights": [ 0.75, 0.69, 0.75 ], | ||||
| "order_size": [ 2, 6 ], | ||||
| "passages_size": [ 0, 10 ], | ||||
| "motif_edited": "false", | ||||
| "order_edited": "false" | ||||
| } | ||||
| @ -0,0 +1,796 @@ | ||||
| ( | ||||
| // helper funcs | ||||
| var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; | ||||
| 
 | ||||
| // score funcs | ||||
| var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; | ||||
| 
 | ||||
| // subroutines | ||||
| var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc; | ||||
| 
 | ||||
| // 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, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| motifEdited, orderEdited; | ||||
| 
 | ||||
| // model aux vars | ||||
| var entrancesDurFunc, passagesDurFunc, exitsDurFunc; | ||||
| 
 | ||||
| // 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) | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| }; | ||||
| 
 | ||||
| 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 = 1/pow(hdSum.value(voices.deepCopy.put(ins, candidate)), 2); | ||||
| 		//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}); | ||||
| 	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; | ||||
| 		var clumps, hdScores, freqs, fDurs; | ||||
| 		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( | ||||
| 			\instrument, \test, | ||||
| 			\group, group, | ||||
| 			\freq, Pseq(freqs, 1), | ||||
| 			\dur, Pseq(fDurs, 1), | ||||
| 			\sustain, Pseq(fDurs, 1) | ||||
| 		) | ||||
| 	}); | ||||
| 	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; | ||||
| 		}, { | ||||
| 			//res.postln; | ||||
| 			if(res.every({arg char; char.isDecDigit}), {res = res.asInteger}); | ||||
| 		}); | ||||
| 	}); | ||||
| 	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, passagesWeights, 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; | ||||
| 	// order really matters!!!! | ||||
| 	# seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 	entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 	ranges, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| 	motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); | ||||
| 	dict | ||||
| }; | ||||
| 
 | ||||
| globalVarsToDict = { | ||||
| 	var modelItems, dict; | ||||
| 	// order really matters!!!! | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, 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; | ||||
| 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", "passages_weights", "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); | ||||
| 
 | ||||
| 	popSize = ranges.size; | ||||
| 
 | ||||
| 	//refUID.postln; | ||||
| 
 | ||||
| 	loadLedgerFile.value(ledgerPath); | ||||
| 	if(ledger == nil, {ledger = ["tmp"]}); | ||||
| 	if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); | ||||
| 
 | ||||
| 	lastXChanges = if(refUID == nil, { | ||||
| 		[initVoices.value().deepCopy]; | ||||
| 	}, { | ||||
| 		var file; | ||||
| 		file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); | ||||
| 		msgInterpret.value(file.readAllString.parseJSON["last_changes"]); | ||||
| 	}); | ||||
| 
 | ||||
| 	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)); | ||||
| 	}); | ||||
| 	seq = seedFunc.value(genMotif, motifSeed).value; | ||||
| 
 | ||||
| 	dict = globalVarsToDict.value; | ||||
| 	modelString = writeResources.value(path, dict); | ||||
| 
 | ||||
| 	//addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); | ||||
| 	addr.sendMsg("/generated", path, modelString); | ||||
| }, \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, { | ||||
| 		player.stop; | ||||
| 		group.set(\gate, 0); | ||||
| 	}, { | ||||
| 		// 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; | ||||
| 			}); | ||||
| 		}, { | ||||
| 			"here".postln; | ||||
| 			pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; | ||||
| 
 | ||||
| 		}); | ||||
| 		patterns = genPatterns.value(pSeq, addr); | ||||
| 		player = Pfset(pattern: patterns, cleanupFunc: { | ||||
| 			addr.sendMsg("/transport", 0); | ||||
| 		}); | ||||
| 		player = player.play | ||||
| 	}); | ||||
| }, \transport); | ||||
| 
 | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; | ||||
| 	var trig, exc, sig1, sig2, noHarms; | ||||
| 	noHarms = 30; | ||||
| 	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.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)); | ||||
| 	//Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); | ||||
| }).add; | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| 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; | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| "{\"a\": 1}".parseYAML["a"].asInteger; | ||||
| "{\"a\": 1}".parseJSON["a"].isNumber; | ||||
| 
 | ||||
| 1223423434123.asHexString.toLower | ||||
| 
 | ||||
| Date.getDate.rawSeconds | ||||
| Date.seed.asHexString.toLower | ||||
| 
 | ||||
| n = NetAddr("localhost", 8080); | ||||
| n.sendMsg("/GET/#", (NetAddr.localAddr.hostname ++ ":" ++ NetAddr.localAddr.port), "/passage_probs_vals"); | ||||
| @ -0,0 +1,55 @@ | ||||
| { | ||||
| "music_data": | ||||
| [ | ||||
|  [ | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 7.125 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 1, 0, 0, 0 ], [ "Rest" ] ], 0.25 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ "Rest" ] ], 0.375 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ "Rest" ] ], 0.125 ], | ||||
|     [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ "Rest" ] ], 7.25 ] | ||||
|   ], | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ "Rest" ] ], 0.125 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], 5 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, -1, 0, 1 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.25 ], | ||||
|     [ [ [ "Rest" ], [ -1, 0, 1, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ 1, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.125 ], | ||||
|     [ [ [ "Rest" ], [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.5 ], | ||||
|     [ [ [ "Rest" ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], 10.25 ], | ||||
|     [ [ [ "Rest" ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, 1 ], [ "Rest" ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 1 ], [ "Rest" ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 6.125 ] | ||||
|   ] | ||||
|  ] | ||||
| ], | ||||
| "last_changes": | ||||
| [ | ||||
|   [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, -1, 0, 1 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 0, 0, 0, 0, 0 ], [ -1, 0, 1, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ] | ||||
| ], | ||||
| "cur_uid": "661a850b", | ||||
| "ref_uid": "nil", | ||||
| "order_seed": 815237, | ||||
| "dur_seed": 146432, | ||||
| "motifs_seed": 680955, | ||||
| "entrances_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.26424870466321, 0.75675675675676, 0.5, 0.5, 0.58549222797927, 0.72635135135135, 1, 0.5 ], | ||||
| "passages_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], | ||||
| "exits_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], | ||||
| "ranges": [ [ -384, 2400 ], [ -507, 2400 ], [ -282, 2237 ], [ -1200, 2053 ] ], | ||||
| "passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], | ||||
| "order": | ||||
| [ | ||||
|   [ [ 1, 0 ], [ 2, 2, 2, 2 ], [ 3 ] ], | ||||
|   [ [ 2, 3 ], [ 1, 1, 1, 1, 1 ], [ 0 ] ] | ||||
| ], | ||||
| "sus_weights": [ 0.75, 0.69, 0.75 ], | ||||
| "order_size": [ 2, 6 ], | ||||
| "passages_size": [ 0, 10 ], | ||||
| "motif_edited": "false", | ||||
| "order_edited": "false" | ||||
| } | ||||
| @ -0,0 +1,825 @@ | ||||
| ( | ||||
| // helper funcs | ||||
| var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; | ||||
| 
 | ||||
| // score funcs | ||||
| var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; | ||||
| 
 | ||||
| // subroutines | ||||
| var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc; | ||||
| 
 | ||||
| // 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, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| motifEdited, orderEdited; | ||||
| 
 | ||||
| // model aux vars | ||||
| var entrancesDurFunc, passagesDurFunc, exitsDurFunc; | ||||
| 
 | ||||
| // 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) | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| }; | ||||
| 
 | ||||
| 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 = 1/pow(hdSum.value(voices.deepCopy.put(ins, candidate)), 2); | ||||
| 		//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}); | ||||
| 	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; | ||||
| 	var voices, durs, patterns, res, indices, sectionDurs, msg, ids, seq; | ||||
| 	seq = inSeq.collect({arg mSeq; mSeq[0]}); | ||||
| 	# voices, durs =  seq.flatten2(seq.maxDepth - 5).flop; | ||||
| 	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}); | ||||
| 	res = Ppar( | ||||
| 		voices.flop.collect({arg voice; | ||||
| 			var clumps, hdScores, freqs, fDurs; | ||||
| 			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( | ||||
| 				\instrument, \test, | ||||
| 				\group, group, | ||||
| 				\freq, Pseq(freqs, 1), | ||||
| 				\dur, Pseq(fDurs, 1), | ||||
| 				\sustain, Pseq(fDurs, 1) | ||||
| 			) | ||||
| 		}) ++ | ||||
| 		[ | ||||
| 			Pbind( | ||||
| 				\type, \osc, | ||||
| 				\addr, addr, | ||||
| 				//\indexPath, "/cur_play_index", | ||||
| 				//\indexMsg, Pseq(indices, 1), | ||||
| 				//\seqPath, "/mus_seq", | ||||
| 				//\seqMsg, Pseq(seq, 1), | ||||
| 				\path, "/playing", | ||||
| 				\msg, Pseq(msg, 1), | ||||
| 				\dur, Pseq(sectionDurs, 1) | ||||
| 			); | ||||
| 		] | ||||
| 	); | ||||
| 	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; | ||||
| 		}, { | ||||
| 			//res.postln; | ||||
| 			if(res.every({arg char; char.isDecDigit}), {res = res.asInteger}); | ||||
| 		}); | ||||
| 	}); | ||||
| 	res | ||||
| }; | ||||
| 
 | ||||
| /* | ||||
| writeResources = {arg path; | ||||
| 	var file, modelItems, resString; | ||||
| 	file = File(path,"w"); | ||||
| 
 | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| 		motifEdited, orderEdited | ||||
| 	]; | ||||
| 
 | ||||
| 	resString = [nameSpaces, modelItems].flop.collect({arg item; | ||||
| 		var nameSpace, modelItem, depth = 0, insert = " "; | ||||
| 		# nameSpace, modelItem = item; | ||||
| 		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") && (modelItem == nil), {modelItem = "nil"}); | ||||
| 		"\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(modelItem, depth) | ||||
| 	}).join(",\n"); | ||||
| 
 | ||||
| 	resString = "{\n" ++ resString ++ "\n}"; | ||||
| 
 | ||||
| 	file.write(resString); | ||||
| 	file.close; | ||||
| 	resString | ||||
| }; | ||||
| */ | ||||
| 
 | ||||
| writeResources = {arg path, dict; | ||||
| 	var file, modelItems, resString; | ||||
| 	file = File(path,"w"); | ||||
| 
 | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, 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; | ||||
| 	//model = File(path, "r").readAllString.parseJSON; | ||||
| 	dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); | ||||
| 	dict | ||||
| }; | ||||
| 
 | ||||
| setGlobalVars = {arg dict; | ||||
| 	// order really matters!!!! | ||||
| 	# seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 	entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 	ranges, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| 	motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); | ||||
| 	dict | ||||
| }; | ||||
| 
 | ||||
| globalVarsToDict = { | ||||
| 	var modelItems, dict; | ||||
| 	// order really matters!!!! | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, 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; | ||||
| 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", "passages_weights", "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); | ||||
| 
 | ||||
| 	popSize = ranges.size; | ||||
| 
 | ||||
| 	//refUID.postln; | ||||
| 
 | ||||
| 	loadLedgerFile.value(ledgerPath); | ||||
| 	if(ledger == nil, {ledger = ["tmp"]}); | ||||
| 	if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); | ||||
| 
 | ||||
| 	lastXChanges = if(refUID == nil, { | ||||
| 		[initVoices.value().deepCopy]; | ||||
| 	}, { | ||||
| 		var file; | ||||
| 		file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); | ||||
| 		msgInterpret.value(file.readAllString.parseJSON["last_changes"]); | ||||
| 	}); | ||||
| 
 | ||||
| 	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)); | ||||
| 	}); | ||||
| 	seq = seedFunc.value(genMotif, motifSeed).value; | ||||
| 
 | ||||
| 	dict = globalVarsToDict.value; | ||||
| 	modelString = writeResources.value(path, dict); | ||||
| 
 | ||||
| 	//addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); | ||||
| 	addr.sendMsg("/generated", path, modelString); | ||||
| }, \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["motif_edited"] = 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, { | ||||
| 		player.stop; | ||||
| 		group.set(\gate, 0); | ||||
| 	}, { | ||||
| 		// the cued sequence can now be read from file, so this can be cleaned up | ||||
| 		var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; | ||||
| 		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"); | ||||
| 				//"here".postln; | ||||
| 				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); | ||||
| 		player = Pfset(pattern: patterns, cleanupFunc: { | ||||
| 			addr.sendMsg("/transport", 0); | ||||
| 		}); | ||||
| 		player = player.play | ||||
| 	}); | ||||
| }, \transport); | ||||
| 
 | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; | ||||
| 	var trig, exc, sig1, sig2, noHarms; | ||||
| 	noHarms = 30; | ||||
| 	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.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)); | ||||
| 	//Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); | ||||
| }).add; | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| 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; | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| "{\"a\": 1}".parseYAML["a"].asInteger; | ||||
| "{\"a\": 1}".parseJSON["a"].isNumber; | ||||
| 
 | ||||
| 1223423434123.asHexString.toLower | ||||
| 
 | ||||
| Date.getDate.rawSeconds | ||||
| Date.seed.asHexString.toLower | ||||
| 
 | ||||
| n = NetAddr("localhost", 8080); | ||||
| n.sendMsg("/GET/#", (NetAddr.localAddr.hostname ++ ":" ++ NetAddr.localAddr.port), "/passage_probs_vals"); | ||||
| @ -0,0 +1,57 @@ | ||||
| { | ||||
| "music_data": | ||||
| [ | ||||
|  [ | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 2 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ -1, 0, 0, 0, 0, 1 ] ], 0.125 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ] ], 0.25 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], 10.375 ] | ||||
|   ], | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, -2, 0, 0, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], 0.125 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, -1, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 1, 0, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], 0.125 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], 0.125 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 1, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], 0.125 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 1, 0, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], 0.25 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 2, -2, 0, 0, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, -1, 0, 0, 0, 0 ] ], 0.375 ], | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ 0, -1, 0, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 3.875 ] | ||||
|   ] | ||||
|  ] | ||||
| ], | ||||
| "last_changes": | ||||
| [ | ||||
|   [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 1, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 1, 0, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, -2, 0, 0, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], | ||||
|   [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ] ] | ||||
| ], | ||||
| "cur_uid": "69a72421", | ||||
| "ref_uid": "nil", | ||||
| "order_seed": 921464, | ||||
| "dur_seed": 715865, | ||||
| "motifs_seed": 525056, | ||||
| "entrances_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.26424870466321, 0.75675675675676, 0.5, 0.5, 0.58549222797927, 0.72635135135135, 1, 0.5 ], | ||||
| "passages_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], | ||||
| "exits_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], | ||||
| "ranges": [ [ -384, 2400 ], [ -507, 2400 ], [ -282, 2237 ], [ -1200, 2053 ] ], | ||||
| "passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], | ||||
| "order": | ||||
| [ | ||||
|   [ [ 1, 2 ], [ 3, 3, 3 ], [ 0 ] ], | ||||
|   [ [ 3, 1 ], [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ], [ 0 ] ] | ||||
| ], | ||||
| "sus_weights": [ 0.75, 0.69, 0.75 ], | ||||
| "order_size": [ 2, 6 ], | ||||
| "passages_size": [ 0, 10 ], | ||||
| "motif_edited": "true", | ||||
| "order_edited": "false" | ||||
| } | ||||
| @ -0,0 +1,822 @@ | ||||
| ( | ||||
| // helper funcs | ||||
| var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; | ||||
| 
 | ||||
| // score funcs | ||||
| var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; | ||||
| 
 | ||||
| // subroutines | ||||
| var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc; | ||||
| 
 | ||||
| // 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, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| motifEdited, orderEdited; | ||||
| 
 | ||||
| // model aux vars | ||||
| var entrancesDurFunc, passagesDurFunc, exitsDurFunc; | ||||
| 
 | ||||
| // 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) | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| }; | ||||
| 
 | ||||
| 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 = 1/pow(hdSum.value(voices.deepCopy.put(ins, candidate)), 2); | ||||
| 		//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}); | ||||
| 	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; | ||||
| 	var voices, durs, patterns, res, indices, sectionDurs, msg, ids, seq; | ||||
| 	seq = inSeq.collect({arg mSeq; mSeq[0]}); | ||||
| 	# voices, durs =  seq.flatten2(seq.maxDepth - 5).flop; | ||||
| 	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}); | ||||
| 	res = Ppar( | ||||
| 		voices.flop.collect({arg voice; | ||||
| 			var clumps, hdScores, freqs, fDurs; | ||||
| 			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( | ||||
| 				\instrument, \test, | ||||
| 				\group, group, | ||||
| 				\freq, Pseq(freqs, 1), | ||||
| 				\dur, Pseq(fDurs, 1), | ||||
| 				\sustain, Pseq(fDurs, 1) | ||||
| 			) | ||||
| 		}) ++ | ||||
| 		[ | ||||
| 			Pbind( | ||||
| 				\type, \osc, | ||||
| 				\addr, addr, | ||||
| 				//\indexPath, "/cur_play_index", | ||||
| 				//\indexMsg, Pseq(indices, 1), | ||||
| 				//\seqPath, "/mus_seq", | ||||
| 				//\seqMsg, Pseq(seq, 1), | ||||
| 				\path, "/playing", | ||||
| 				\msg, Pseq(msg, 1), | ||||
| 				\dur, Pseq(sectionDurs, 1) | ||||
| 			); | ||||
| 		] | ||||
| 	); | ||||
| 	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; | ||||
| 		}, { | ||||
| 			//res.postln; | ||||
| 			if(res.every({arg char; char.isDecDigit}), {res = res.asInteger}); | ||||
| 		}); | ||||
| 	}); | ||||
| 	res | ||||
| }; | ||||
| 
 | ||||
| /* | ||||
| writeResources = {arg path; | ||||
| 	var file, modelItems, resString; | ||||
| 	file = File(path,"w"); | ||||
| 
 | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| 		motifEdited, orderEdited | ||||
| 	]; | ||||
| 
 | ||||
| 	resString = [nameSpaces, modelItems].flop.collect({arg item; | ||||
| 		var nameSpace, modelItem, depth = 0, insert = " "; | ||||
| 		# nameSpace, modelItem = item; | ||||
| 		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") && (modelItem == nil), {modelItem = "nil"}); | ||||
| 		"\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(modelItem, depth) | ||||
| 	}).join(",\n"); | ||||
| 
 | ||||
| 	resString = "{\n" ++ resString ++ "\n}"; | ||||
| 
 | ||||
| 	file.write(resString); | ||||
| 	file.close; | ||||
| 	resString | ||||
| }; | ||||
| */ | ||||
| 
 | ||||
| writeResources = {arg path, dict; | ||||
| 	var file, modelItems, resString; | ||||
| 	file = File(path,"w"); | ||||
| 
 | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, 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; | ||||
| 	//model = File(path, "r").readAllString.parseJSON; | ||||
| 	dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); | ||||
| 	dict | ||||
| }; | ||||
| 
 | ||||
| setGlobalVars = {arg dict; | ||||
| 	// order really matters!!!! | ||||
| 	# seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 	entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 	ranges, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| 	motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); | ||||
| 	dict | ||||
| }; | ||||
| 
 | ||||
| globalVarsToDict = { | ||||
| 	var modelItems, dict; | ||||
| 	// order really matters!!!! | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, 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; | ||||
| 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", "passages_weights", "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); | ||||
| 
 | ||||
| 	popSize = ranges.size; | ||||
| 
 | ||||
| 	//refUID.postln; | ||||
| 
 | ||||
| 	loadLedgerFile.value(ledgerPath); | ||||
| 	if(ledger == nil, {ledger = ["tmp"]}); | ||||
| 	if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); | ||||
| 
 | ||||
| 	lastXChanges = if(refUID == nil, { | ||||
| 		[initVoices.value().deepCopy]; | ||||
| 	}, { | ||||
| 		var file; | ||||
| 		file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); | ||||
| 		msgInterpret.value(file.readAllString.parseJSON["last_changes"]); | ||||
| 	}); | ||||
| 
 | ||||
| 	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)); | ||||
| 	}); | ||||
| 	seq = seedFunc.value(genMotif, motifSeed).value; | ||||
| 
 | ||||
| 	dict = globalVarsToDict.value; | ||||
| 	modelString = writeResources.value(path, dict); | ||||
| 
 | ||||
| 	//addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); | ||||
| 	addr.sendMsg("/generated", path, modelString); | ||||
| }, \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; | ||||
| 	dict.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, {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, { | ||||
| 		player.stop; | ||||
| 		group.set(\gate, 0); | ||||
| 	}, { | ||||
| 		// the cued sequence can now be read from file, so this can be cleaned up | ||||
| 		var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; | ||||
| 		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"); | ||||
| 				//"here".postln; | ||||
| 				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); | ||||
| 		player = Pfset(pattern: patterns, cleanupFunc: { | ||||
| 			addr.sendMsg("/transport", 0); | ||||
| 		}); | ||||
| 		player = player.play | ||||
| 	}); | ||||
| }, \transport); | ||||
| 
 | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; | ||||
| 	var trig, exc, sig1, sig2, noHarms; | ||||
| 	noHarms = 30; | ||||
| 	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.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)); | ||||
| 	//Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); | ||||
| }).add; | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| 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; | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| "{\"a\": 1}".parseYAML["a"].asInteger; | ||||
| "{\"a\": 1}".parseJSON["a"].isNumber; | ||||
| 
 | ||||
| 1223423434123.asHexString.toLower | ||||
| 
 | ||||
| Date.getDate.rawSeconds | ||||
| Date.seed.asHexString.toLower | ||||
| 
 | ||||
| n = NetAddr("localhost", 8080); | ||||
| n.sendMsg("/GET/#", (NetAddr.localAddr.hostname ++ ":" ++ NetAddr.localAddr.port), "/passage_probs_vals"); | ||||
| @ -0,0 +1,84 @@ | ||||
| { | ||||
| "music_data": | ||||
| [ | ||||
|  [ | ||||
|   [ | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0.625 ], | ||||
|     [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 1, 0, 0, 0 ] ], 0.25 ], | ||||
|     [ [ [ -1, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 1, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ -1, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, -1, 0, 0, 0, 0 ] ], 0.125 ], | ||||
|     [ [ [ -1, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, -1, 0, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ -1, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 1, 0, 0, 0, 0 ] ], 0.25 ], | ||||
|     [ [ [ 1, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 1, 0, 0, 0, 0 ] ], 0.125 ], | ||||
|     [ [ [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 1, 0, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 1 ] ], 0.375 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 1 ] ], 3 ] | ||||
|   ], | ||||
|   [ | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ -1, 0, 1, 0, 0, 1 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 1 ] ], 0 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ -1, 0, 1, 0, 0, 1 ], [ "Rest" ], [ 0, 0, 1, 0, 1, 0 ] ], 0 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ -1, 0, 1, 0, 0, 1 ], [ -1, 1, 1, 0, 0, 0 ], [ 0, 0, 1, 0, 1, 0 ] ], 0.25 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ -1, 0, 1, 0, 0, 1 ], [ -1, 0, 1, 0, 1, 0 ], [ 0, 0, 1, 0, 1, 0 ] ], 0 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ -1, 0, 1, 0, 0, 1 ], [ -1, 0, 1, 0, 1, 0 ], [ 1, -1, 1, 0, 0, 0 ] ], 0.25 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ -1, 0, 1, 0, 0, 1 ], [ -1, 0, 1, 0, 1, 0 ], [ 0, 0, 2, 0, 0, 0 ] ], 5.125 ] | ||||
|   ], | ||||
|   [ | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ -1, 0, 1, 0, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ 0, 0, 2, 0, 0, 0 ] ], 0 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ -1, 0, 1, 0, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ 0, 0, 0, 0, 0, 1 ] ], 0.25 ], | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ -1, 0, 1, 0, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], 3.375 ] | ||||
|   ], | ||||
|   [ | ||||
|     [ [ [ 0, 0, 1, 0, 0, 0 ], [ -1, 0, 1, 1, 0, 0 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], 0 ], | ||||
|     [ [ [ -1, -1, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 0 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], 0.375 ], | ||||
|     [ [ [ -2, 1, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 0 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], 0 ], | ||||
|     [ [ [ -2, 1, 1, 1, 0, 1 ], [ -2, 0, 2, 1, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], 0.25 ], | ||||
|     [ [ [ -1, 0, 0, 1, 0, 1 ], [ -2, 0, 2, 1, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], 1.5 ] | ||||
|   ], | ||||
|   [ | ||||
|     [ [ [ -2, 0, 1, 2, 0, 1 ], [ -2, 0, 2, 1, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], 0.375 ], | ||||
|     [ [ [ -1, -1, 2, 1, 0, 1 ], [ -2, 0, 2, 1, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], 0.375 ], | ||||
|     [ [ [ -2, 0, 1, 1, 0, 2 ], [ -2, 0, 2, 1, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], 0.25 ], | ||||
|     [ [ [ -2, 0, 3, 1, 0, 1 ], [ -2, 0, 2, 1, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], 0.25 ], | ||||
|     [ [ [ -1, -1, 1, 1, 0, 1 ], [ -2, 0, 2, 1, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], 0.375 ], | ||||
|     [ [ [ -1, 0, 1, 1, 0, 0 ], [ -2, 0, 2, 1, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], 0.375 ], | ||||
|     [ [ [ -2, 1, 1, 1, 0, 1 ], [ -2, 0, 2, 1, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], 0.125 ], | ||||
|     [ [ [ -1, -1, 2, 1, 0, 1 ], [ -2, 0, 2, 1, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], 9.25 ], | ||||
|     [ [ [ -1, -1, 2, 1, 0, 1 ], [ -2, 0, 2, 1, 0, 1 ], [ "Rest" ], [ -1, 0, 1, 1, 0, 1 ] ], 0 ], | ||||
|     [ [ [ -1, -1, 2, 1, 0, 1 ], [ "Rest" ], [ "Rest" ], [ -1, 0, 1, 1, 0, 1 ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ -1, 0, 1, 1, 0, 1 ] ], 0 ], | ||||
|     [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 9.625 ] | ||||
|   ] | ||||
|  ] | ||||
| ], | ||||
| "last_changes": | ||||
| [ | ||||
|   [ [ -2, 0, 3, 1, 0, 1 ], [ -2, 0, 2, 1, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], | ||||
|   [ [ -1, -1, 1, 1, 0, 1 ], [ -2, 0, 2, 1, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], | ||||
|   [ [ -1, 0, 1, 1, 0, 0 ], [ -2, 0, 2, 1, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], | ||||
|   [ [ -2, 1, 1, 1, 0, 1 ], [ -2, 0, 2, 1, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ], | ||||
|   [ [ -1, -1, 2, 1, 0, 1 ], [ -2, 0, 2, 1, 0, 1 ], [ -2, 0, 1, 1, 0, 1 ], [ -1, 0, 1, 1, 0, 1 ] ] | ||||
| ], | ||||
| "cur_uid": "7000ae3e", | ||||
| "ref_uid": "nil", | ||||
| "order_seed": 992409, | ||||
| "dur_seed": 284618, | ||||
| "motifs_seed": 483475, | ||||
| "entrances_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.26424870466321, 0.75675675675676, 0.5, 0.5, 0.58549222797927, 0.72635135135135, 1, 0.5 ], | ||||
| "passages_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], | ||||
| "exits_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], | ||||
| "ranges": [ [ -384, 2400 ], [ -507, 2400 ], [ -282, 2237 ], [ -1200, 2053 ] ], | ||||
| "passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], | ||||
| "order": | ||||
| [ | ||||
|   [ [ 1 ], [ 3, 0, 3, 0, 3, 0, 0, 3, 0 ], [ 2 ] ], | ||||
|   [ [ 0 ], [ 1, 3, 2, 2, 3, 3 ], [  ] ], | ||||
|   [ [ 0, 1 ], [ 2, 3, 3 ], [  ] ], | ||||
|   [ [ 2, 3 ], [ 1, 0, 0, 1, 0 ], [  ] ], | ||||
|   [ [ 3, 1, 2 ], [ 0, 0, 0, 0, 0, 0, 0, 0 ], [  ] ] | ||||
| ], | ||||
| "sus_weights": [ 0.75, 0.69, 0.75 ], | ||||
| "order_size": [ 2, 6 ], | ||||
| "passages_size": [ 0, 10 ], | ||||
| "motif_edited": "true", | ||||
| "order_edited": "false" | ||||
| } | ||||
| @ -0,0 +1,794 @@ | ||||
| ( | ||||
| // helper funcs | ||||
| var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; | ||||
| 
 | ||||
| // score funcs | ||||
| var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; | ||||
| 
 | ||||
| // subroutines | ||||
| var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc; | ||||
| 
 | ||||
| // 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, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| motifEdited, orderEdited; | ||||
| 
 | ||||
| // model aux vars | ||||
| var entrancesDurFunc, passagesDurFunc, exitsDurFunc; | ||||
| 
 | ||||
| // 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) | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| }; | ||||
| 
 | ||||
| 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 = 1/pow(hdSum.value(voices.deepCopy.put(ins, candidate)), 2); | ||||
| 		//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}); | ||||
| 	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; | ||||
| 		var clumps, hdScores, freqs, fDurs; | ||||
| 		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( | ||||
| 			\instrument, \test, | ||||
| 			\group, group, | ||||
| 			\freq, Pseq(freqs, 1), | ||||
| 			\dur, Pseq(fDurs, 1), | ||||
| 			\sustain, Pseq(fDurs, 1) | ||||
| 		) | ||||
| 	}); | ||||
| 	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; | ||||
| 		}, { | ||||
| 			//res.postln; | ||||
| 			if(res.every({arg char; char.isDecDigit}), {res = res.asInteger}); | ||||
| 		}); | ||||
| 	}); | ||||
| 	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, passagesWeights, 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; | ||||
| 	// order really matters!!!! | ||||
| 	# seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 	entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 	ranges, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| 	motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); | ||||
| 	dict | ||||
| }; | ||||
| 
 | ||||
| globalVarsToDict = { | ||||
| 	var modelItems, dict; | ||||
| 	// order really matters!!!! | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, 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; | ||||
| 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", "passages_weights", "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); | ||||
| 
 | ||||
| 	popSize = ranges.size; | ||||
| 
 | ||||
| 	//refUID.postln; | ||||
| 
 | ||||
| 	loadLedgerFile.value(ledgerPath); | ||||
| 	if(ledger == nil, {ledger = ["tmp"]}); | ||||
| 	if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); | ||||
| 
 | ||||
| 	lastXChanges = if(refUID == nil, { | ||||
| 		[initVoices.value().deepCopy]; | ||||
| 	}, { | ||||
| 		var file; | ||||
| 		file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); | ||||
| 		msgInterpret.value(file.readAllString.parseJSON["last_changes"]); | ||||
| 	}); | ||||
| 
 | ||||
| 	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)); | ||||
| 	}); | ||||
| 	seq = seedFunc.value(genMotif, motifSeed).value; | ||||
| 
 | ||||
| 	dict = globalVarsToDict.value; | ||||
| 	modelString = writeResources.value(path, dict); | ||||
| 
 | ||||
| 	//addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); | ||||
| 	addr.sendMsg("/generated", path, modelString); | ||||
| }, \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, { | ||||
| 		player.stop; | ||||
| 		group.set(\gate, 0); | ||||
| 	}, { | ||||
| 		// 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; | ||||
| 			}); | ||||
| 		}, { | ||||
| 			pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; | ||||
| 		}); | ||||
| 		patterns = genPatterns.value(pSeq, addr); | ||||
| 		player = Pfset(pattern: patterns, cleanupFunc: { | ||||
| 			addr.sendMsg("/transport", 0); | ||||
| 		}); | ||||
| 		player = player.play | ||||
| 	}); | ||||
| }, \transport); | ||||
| 
 | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; | ||||
| 	var trig, exc, sig1, sig2, noHarms; | ||||
| 	noHarms = 30; | ||||
| 	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.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)); | ||||
| 	//Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); | ||||
| }).add; | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| 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; | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| "{\"a\": 1}".parseYAML["a"].asInteger; | ||||
| "{\"a\": 1}".parseJSON["a"].isNumber; | ||||
| 
 | ||||
| 1223423434123.asHexString.toLower | ||||
| 
 | ||||
| Date.getDate.rawSeconds | ||||
| Date.seed.asHexString.toLower | ||||
| 
 | ||||
| n = NetAddr("localhost", 8080); | ||||
| n.sendMsg("/GET/#", (NetAddr.localAddr.hostname ++ ":" ++ NetAddr.localAddr.port), "/passage_probs_vals"); | ||||
| @ -0,0 +1,794 @@ | ||||
| ( | ||||
| // helper funcs | ||||
| var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; | ||||
| 
 | ||||
| // score funcs | ||||
| var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; | ||||
| 
 | ||||
| // subroutines | ||||
| var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc; | ||||
| 
 | ||||
| // 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, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| motifEdited, orderEdited; | ||||
| 
 | ||||
| // model aux vars | ||||
| var entrancesDurFunc, passagesDurFunc, exitsDurFunc; | ||||
| 
 | ||||
| // 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) | ||||
| }; | ||||
| 
 | ||||
| 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); | ||||
| }; | ||||
| 
 | ||||
| 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 = 1/pow(hdSum.value(voices.deepCopy.put(ins, candidate)), 2); | ||||
| 		//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}); | ||||
| 	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; | ||||
| 		var clumps, hdScores, freqs, fDurs; | ||||
| 		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( | ||||
| 			\instrument, \test, | ||||
| 			\group, group, | ||||
| 			\freq, Pseq(freqs, 1), | ||||
| 			\dur, Pseq(fDurs, 1), | ||||
| 			\sustain, Pseq(fDurs, 1) | ||||
| 		) | ||||
| 	}); | ||||
| 	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; | ||||
| 		}, { | ||||
| 			//res.postln; | ||||
| 			if(res.every({arg char; char.isDecDigit}), {res = res.asInteger}); | ||||
| 		}); | ||||
| 	}); | ||||
| 	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, passagesWeights, 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; | ||||
| 	// order really matters!!!! | ||||
| 	# seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 	entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 	ranges, passagesWeights, orders, susWeights, orderSize, passagesSize, | ||||
| 	motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); | ||||
| 	dict | ||||
| }; | ||||
| 
 | ||||
| globalVarsToDict = { | ||||
| 	var modelItems, dict; | ||||
| 	// order really matters!!!! | ||||
| 	modelItems = [ | ||||
| 		seq, lastXChanges, | ||||
| 		curUID, refUID, orderSeed, durSeed, motifSeed, | ||||
| 		entrancesProbVals, passagesProbVals, exitsProbVals, | ||||
| 		ranges, passagesWeights, 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; | ||||
| 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", "passages_weights", "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); | ||||
| 
 | ||||
| 	popSize = ranges.size; | ||||
| 
 | ||||
| 	//refUID.postln; | ||||
| 
 | ||||
| 	loadLedgerFile.value(ledgerPath); | ||||
| 	if(ledger == nil, {ledger = ["tmp"]}); | ||||
| 	if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); | ||||
| 
 | ||||
| 	lastXChanges = if(refUID == nil, { | ||||
| 		[initVoices.value().deepCopy]; | ||||
| 	}, { | ||||
| 		var file; | ||||
| 		file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); | ||||
| 		msgInterpret.value(file.readAllString.parseJSON["last_changes"]); | ||||
| 	}); | ||||
| 
 | ||||
| 	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)); | ||||
| 	}); | ||||
| 	seq = seedFunc.value(genMotif, motifSeed).value; | ||||
| 
 | ||||
| 	dict = globalVarsToDict.value; | ||||
| 	modelString = writeResources.value(path, dict); | ||||
| 
 | ||||
| 	//addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); | ||||
| 	addr.sendMsg("/generated", path, modelString); | ||||
| }, \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, { | ||||
| 		player.stop; | ||||
| 		group.set(\gate, 0); | ||||
| 	}, { | ||||
| 		// 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; | ||||
| 			}); | ||||
| 		}, { | ||||
| 			pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; | ||||
| 		}); | ||||
| 		patterns = genPatterns.value(pSeq, addr); | ||||
| 		player = Pfset(pattern: patterns, cleanupFunc: { | ||||
| 			addr.sendMsg("/transport", 0); | ||||
| 		}); | ||||
| 		player = player.play | ||||
| 	}); | ||||
| }, \transport); | ||||
| 
 | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; | ||||
| 	var trig, exc, sig1, sig2, noHarms; | ||||
| 	noHarms = 30; | ||||
| 	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.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)); | ||||
| 	//Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); | ||||
| }).add; | ||||
| ) | ||||
| 
 | ||||
| ( | ||||
| 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; | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| "{\"a\": 1}".parseYAML["a"].asInteger; | ||||
| "{\"a\": 1}".parseJSON["a"].isNumber; | ||||
| 
 | ||||
| 1223423434123.asHexString.toLower | ||||
| 
 | ||||
| Date.getDate.rawSeconds | ||||
| Date.seed.asHexString.toLower | ||||
| 
 | ||||
| n = NetAddr("localhost", 8080); | ||||
| n.sendMsg("/GET/#", (NetAddr.localAddr.hostname ++ ":" ++ NetAddr.localAddr.port), "/passage_probs_vals"); | ||||
					Loading…
					
					
				
		Reference in New Issue