Hi all,
An age-old problem that has been annoying me for a while is the one of writing a pattern and trying to manually remember if the total duration of your durations in a Pseq for example are equivalent to a certain number (let’s say 4 beats in this (bad) example):
Pbind(
\dur, Pseq([1,1/4,1/8,1/12,Rest(2)], inf) // Is this 4 beats?
).play
It would be a more musical/performative workflow for me if I could call something equivalent to .normalizeSum
but instead of normalizing all items to 1.0 (and then what to do about rests??), I want all durations (including rests) to be scaled.
I couldn’t immediately find a good function for this in the class library, so I wrote a first attempt:
+ Array{
// return the total duration of an array of numbers and Rests
totalDuration{
^this.sanitizeRests.sum;
}
sanitizeRests{
^this.collect{|x| x.isRest.if({x.dur}, x) };
}
// FIXME: WORK IN PROGRESS
// FIXME: How to make it round properly and not return zeroes?
// Take an array of durations / Rests and normalize their total duration (for Pseq etc)
// Not 100% accurate but mostly works
normalizeDurations{|sumDurations=8.0, roundTo|
var returnArray = this;
// Get the indices of all Rests
var areRests = this.collect{|x| x.isRest};
// Convert to durations
returnArray = returnArray.sanitizeRests();
// Normalize and scale
returnArray = returnArray.normalizeSum().collect{|x| x * sumDurations };
// Round
roundTo.notNil.if({
returnArray = returnArray.collect{|x|
// The rounding algos expect x to to not be 0 (and so does \dur in Pbind)
if(x == 0.0, {
x = 0.00001
});
x.roundUp(roundTo).postln;
};
});
// Convert rests back to rests
returnArray = returnArray.collect{|x, index| areRests[index].if({Rest(x)}, x)};
^returnArray;
}
}
With the example above:
Pbind(
\dur, Pseq([1,1/4,1/8,1/12,Rest(2)].normalizeDurations(4), inf) // Is this 4 beats? Yes it is
).play
Now, this seemingly works OK, but there is a problem, if we start rounding/quantizing the durations, the returned array will always be too long.
a = [1,1/4,1/8,1/12,Rest(2)].normalizeDurations(4, roundTo: 0.125).postln;
a.totalDuration.postln;
This leads to the big question: What to do about this? Do we shave off bits of duration from the Rests? Do we scale it back and forth recursively until it snaps into place (this could be very taxing on the CPU)? Any advice?