Last active
November 2, 2021 20:01
-
-
Save scztt/87bf6542e3cd60844113fd201258a82a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Play many patterns according to their own delta's', composing the events from bottom to top. | |
// Deltas of first pattern are responsible for timing of output events | |
// | |
// ( | |
// Pdef(\ptchain).clear; | |
// Pdef(\ptchain, PTChain( | |
// Pbind( | |
// \dur, Pseg([1/32, 1/2, 1/32], [16, 16], \exponential, inf), | |
// \velocity, Pwhite(40, 127), | |
// \strum, Pkey(\dur) / 2 | |
// ), | |
// Pbind( | |
// \degree, Ptuple([Pkey(\degreeA), Pkey(\degreeB)]) | |
// ), | |
// Pbind( | |
// \dur, 1/3, | |
// \degreeA, Ptuple([ | |
// Pseq([0, 2, 6, 8, 5], inf), | |
// ]) | |
// ), | |
// Pbind( | |
// \dur, 2, | |
// \degreeB, Ptuple([ | |
// 12 + Pseq([0, -6], inf), | |
// ]) | |
// ), | |
// )).play | |
// ) | |
PTChain : Pattern { | |
var <>patterns; | |
*new { arg ... patterns; | |
^super.newCopyArgs(patterns); | |
} | |
*durs { arg durs ... patterns; | |
^super.newCopyArgs( | |
[Pbind(\dur, durs)] ++ patterns | |
); | |
} | |
<< { arg aPattern; | |
var list; | |
list = patterns.copy.add(aPattern); | |
^this.class.new(*list) | |
} | |
embedInStream { arg inval; | |
var structureStream = patterns[0].asStream; | |
var startTime = thisThread.beats; | |
// Store the value streams, their current time and latest Events | |
var valueStreams = patterns[1..].collect{ |p| [p.asStream, (), ThreadStateScope() ] }; | |
var inevent, cleanup = EventStreamCleanup.new; | |
var timeEpsilon = 0.0001; | |
loop { | |
var structureEvent; | |
var cumulativeEvent = inevent = inval.copy; | |
// inevent.debug("inevent at start of loop"); | |
valueStreams.reverseDo { |strData, i| | |
var valueStream, nextValueEvent, threadState; | |
#valueStream, nextValueEvent, threadState = strData; | |
while { | |
// "stream: % current time: % stream time: % timeToCheck: %".format( | |
// valueStream, | |
// thisThread.beats - startTime, | |
// threadState.beats - startTime, | |
// (thisThread.beats - startTime) + timeEpsilon | |
// ).debug; | |
threadState.beats <= (thisThread.beats + timeEpsilon); | |
} { | |
var delta; | |
threadState.use { | |
if (inevent !== cumulativeEvent) { | |
inevent.parent_(cumulativeEvent); | |
}; | |
nextValueEvent = valueStream.next(inevent); | |
// "\tpulled new value: %".format(nextValueEvent).postln; | |
}; | |
// nextValueEvent.debug("nextValueEvent"); | |
// Q: Should we exit for value streams that end, or just the structure stream? | |
// A: Will have to look at concrete examples, for now: yes, we exit when | |
// any of the streams ends... | |
if (nextValueEvent.isNil) { ^cleanup.exit(inval) }; | |
delta = nextValueEvent.delta.value; | |
if (delta.notNil) { | |
threadState.beats = threadState.beats + delta; | |
} { | |
// There is no time information, just use our next value | |
// for the next structure Event (as regular Pchain would do) | |
threadState.beats = threadState.beats + (timeEpsilon * 2); | |
}; | |
// nextValueTime.debug("nextValueTime updated"); | |
// inevent feeds from one into the next, gathering/replacing values | |
strData[1] = nextValueEvent; | |
}; | |
// Combine the contributions of all the "current" value events | |
// that came before the main structure event. | |
cumulativeEvent = cumulativeEvent.composeEvents(nextValueEvent); | |
// cumulativeEvent.debug("updated cumulativeEvent"); | |
}; | |
structureEvent = structureStream.next(cumulativeEvent); | |
if (structureEvent.isNil) { ^cleanup.exit(inval) }; | |
cleanup.update(structureEvent); | |
// structureEvent.debug("yielded structureEvent"); | |
inval = yield(structureEvent); | |
// structureTime.debug("structureTime"); | |
}; | |
} | |
storeOn { arg stream; | |
stream << "("; | |
patterns.do { |item,i| if(i != 0) { stream << " <> " }; stream <<< item; }; | |
stream << ")" | |
} | |
} | |
+Pattern { | |
<< { arg aPattern; | |
// time-based pattern key merging | |
^PTChain(this, aPattern) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ThreadStateScope { | |
var beats, endBeat; | |
*new { | |
^super.newCopyArgs(thisThread.beats, thisThread.endBeat); | |
} | |
use { | |
|function| | |
var oldValues; | |
if (beats.isNil) { beats = thisThread.beats }; | |
if (endBeat.isNil) { endBeat = thisThread.endBeat }; | |
protect { | |
oldValues = [thisThread.beats, thisThread.endBeat]; | |
thisThread.beats = beats; | |
thisThread.endBeat = endBeat; | |
function.() | |
} { | |
this.beats = thisThread.beats; | |
endBeat = thisThread.endBeat; | |
thisThread.beats = oldValues[0]; | |
thisThread.endBeat = oldValues[1]; | |
} | |
} | |
beats_{ | |
|value| | |
if (value.isNil) { | |
"setting beats to nil... weird".postln; | |
}; | |
beats = value; | |
} | |
beats { | |
^(beats ?? { 0 }) | |
} | |
endBeat { | |
^(endBeat ?? { 0 }) | |
} | |
} |
I believe this behavior is correct. One subtlety here is that the second Pbind
has an implicit \dur
of 1. You can more clearly see what's going on by adding some traces:
(
Pdef(\ptchain).clear;
Pdef(\ptchain, PTChain(
Pbind(
\dur, Pseg([1/32, 1/2, 1/32], [16, 16], \exponential, inf),
\velocity, Pwhite(40, 127),
\strum, Pkey(\dur) / 2
),
Pbind(
\degree, Ptuple([Pkey(\degreeA), Pkey(\degreeB)]).trace(prefix:"Ptuple")
),
Pbind(
\dur, 2,
\degreeA, Ptuple([
Pseq([0, 2, 6, 8, 5], inf),
]).trace(prefix:"A")
),
Pbind(
\dur, 1/3,
\degreeB, Ptuple([
12 + Pseq([0, -6], inf),
]).trace(prefix:"B")
),
)).play
)
Oh right, now i see what's going on. Thanks for the clarifying :)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for this very useful class! If i exchange the \dur values of the last two Pbinds in the example the \degreeB never changes. Maybe i misunderstood how it is supposed to work, or maybe it is a small bug, i will try to figure out the code.
Here is the example that doesn't work like i had expected: