Hi there, I’m trying to write a Pattern that will scramble Events (reorder them in time). Of course, this needs to be “look ahead”, so I do like Pfindur and ask for events up to a specified duration, store them, then scramble them and return.
I’ve got it mostly working, but it fails when I afterwards combine the scrambled events with other patterns (e.g. chaining).
Here’s an example of what I’ve got:
// (see definition of Pscramble and Pattern.scramble below)
// Works as expected (random Event order)
p = Pbind(\degree, Pseq((0..3)), \dur, 0.25, \amp, Pseq((1..4) / 4)); // degree and amp Pbind
p.scramble.asStream.nextN(4, ()).do(_.postln)
// ( 'degree': 0, 'dur': 0.25, 'amp': 0.25 )
// ( 'degree': 3, 'dur': 0.25, 'amp': 1.0 )
// ( 'degree': 1, 'dur': 0.25, 'amp': 0.5 )
// ( 'degree': 2, 'dur': 0.25, 'amp': 0.75 )
// Also works as expected with Pchain (scrambling after the chaining)
d = Pbind(\degree, Pseq((0..3)), \dur, 0.25); // degree Pbind
a = Pbind(\amp, Pseq((1..4) / 4), \dur, 0.25); // amp Pbind
(d <> a).scramble.asStream.nextN(4, ()).do(_.postln)
// ( 'degree': 1, 'dur': 0.25, 'amp': 0.5 )
// ( 'degree': 2, 'dur': 0.25, 'amp': 0.75 )
// ( 'degree': 0, 'dur': 0.25, 'amp': 0.25 )
// ( 'degree': 3, 'dur': 0.25, 'amp': 1.0 )
// Doesn't work when you scramble and then Pchain, it's not
// taking events from the chained stream (all amps are the same).
// (would want/expect a random degree followed by a sequential order of amps: 0.25,0.5,0.75,1)
(d.scramble <> a).asStream.nextN(4, ()).do(_.postln)
// ( 'degree': 0, 'dur': 0.25, 'amp': 0.25 )
// ( 'degree': 3, 'dur': 0.25, 'amp': 0.25 )
// ( 'degree': 2, 'dur': 0.25, 'amp': 0.25 )
// ( 'degree': 1, 'dur': 0.25, 'amp': 0.25 )
If anyone is willing to take a look, here is my work in progress on Pscramble (I’m not very familiar with Patterns and Streams, so any suggestions/critiques are welcome):
// Pscramble.sc
// Event Pattern that takes a duration of elements and looks ahead to scramble the result
Pscramble : FilterPattern {
var <>dur;
var <>randSeed;
const tolerance = 0.001;
*new { arg pattern, dur = 1, randSeed;
^super.new(pattern).dur_(dur).randSeed_(randSeed)
}
storeArgs { ^[pattern,dur,randSeed] }
prReturnEvents { arg allEvents, seed;
var lastEvent;
// Scramble and yield all the Events in our list before exiting
"Scrambling % Events".format(allEvents.size).postln;
if (seed.notNil) {
"Setting seed to %".format(seed).postln;
thisThread.randSeed = seed;
};
allEvents = allEvents.scramble;
allEvents.do{ arg ev; lastEvent = yield(ev.debug("yield")) }
^lastEvent
}
embedInStream { arg event;
var delta, elapsed = 0.0, nextElapsed, inevent;
var localdur = dur.value(event);
var stream = pattern.asStream;
var seedStream = randSeed.asStream;
var cleanup = EventStreamCleanup.new;
var allEvents = [];
loop {
inevent = stream.next(event).asEvent ?? {
^cleanup.exit(this.prReturnEvents(allEvents, seedStream.next(event)).debug("returning after playing loop"));
};
cleanup.update(inevent);
delta = inevent.delta;
nextElapsed = elapsed + delta;
if (nextElapsed.roundUp(tolerance) >= localdur) {
// must always copy an event before altering it.
// fix remaining time and yield all the events.
inevent = inevent.copy.put(\dur, localdur - elapsed);
"Adding last: % (elapsed %)".format(inevent, localdur).postln;
allEvents = allEvents.add(inevent);
^cleanup.exit(this.prReturnEvents(allEvents, seedStream.next(event))).debug("returning after dur");
};
elapsed = nextElapsed;
"Adding % (elapsed %)".format(inevent, elapsed).postln;
allEvents = allEvents.add(inevent.copy);
event = inevent;
}
}
}
+Pattern {
scramble { arg dur = 1, randSeed;
^Pscramble(this, dur, randSeed)
}
}
Thanks,
Glen.