Stepwise patterning (experimental)
This is a developing area of strudel, and behaviour might change or be renamed in future versions. Feedback and ideas are welcome!
Introduction
Usually in strudel, the only reference point for most pattern transformations is the cycle. Now it is possible to also work with steps, via a growing range of functions.
For example usually when you fastcat
two patterns together, the cycles will be squashed into half a cycle each:
fastcat("bd hh hh", "bd hh hh cp hh").sound()
With the new stepwise stepcat
function, the steps of the two patterns will be evenly distributed across the cycle:
stepcat("bd hh hh", "bd hh hh cp hh").sound()
By default, steps are counted according to the ‘top level’ in mini-notation. For example "a [b c] d e"
has five events in it per cycle, but is counted as four steps, where [b c]
is counted as a single step.
However, you can mark a different metrical level to count steps relative to, using a ^
at the start of a sub-pattern. If we do this to the subpattern in our example: "a [^b c] d e"
, then the pattern is now counted as having eight steps. This is because ‘b’ and ‘c’ are each counted as single steps, and the events in the pattenr are twice as long, and so counted as two steps each.
Pacing the steps
Some stepwise functions don’t appear to do very much on their own, for example these two examples of the expand
function sound exactly the same despite being expanded by different amounts:
"c a f e".expand(2).note().sound("folkharp")
"c a f e".expand(4).note().sound("folkharp")
The number of steps per cycle is being changed behind the scenes, but on its own, that doesn’t do anything. You will hear a difference however, once you use another stepwise function with it, for example stepcat
:
stepcat("c a f e".expand(2), "g d").note() .sound("folkharp")
stepcat("c a f e".expand(4), "g d").note() .sound("folkharp")
You should be able to hear that expand
increases the duration of the steps of the first subpattern, proportionally to the second one.
You can also change the speed of a pattern to match a given number of steps per cycle, with the pace
function:
stepcat("c a f e".expand(2), "g d").note() .sound("folkharp") .pace(8)
stepcat("c a f e".expand(4), "g d").note() .sound("folkharp") .pace(8)
The first example has ten steps, and the second example has 18 steps, but are then both played a rate of 8 steps per cycle.
The argument to expand
can also be patterned, and will be treated in a stepwise fashion. This means that the patterns from the changing values in the argument will be stepcat
ted together:
note("c a f e").sound("folkharp").expand("3 2 1 1 2 3")
This results in a dense pattern, because the different expanded versions are squashed into a single cycle. pace
is again handy here for slowing down the pattern to a particular number of steps per cycle:
note("c a f e").sound("folkharp").expand("3 2 1 1 2 3").pace(8)
Earlier versions of many of these functions had s_
prefixes, and the pace
function was previously known as steps
. These still exist as aliases, but may have changed behaviour and will soon be removed. Please update your patterns!
Stepwise functions
pace
Experimental
Speeds a pattern up or down, to fit to the given number of steps per cycle.
sound("bd sd cp").pace(4) // The same as sound("{bd sd cp}%4") or sound("<bd sd cp>*4")
stepcat
timeCat, timecat
'Concatenates' patterns like fastcat
, but proportional to a number of steps per cycle.
The steps can either be inferred from the pattern, or provided as a [length, pattern] pair.
Has the alias timecat
.
stepcat([3,"e3"],[1, "g3"]).note() // the same as "e3@3 g3".note()
stepcat("bd sd cp","hh hh").sound() // the same as "bd sd cp hh hh".sound()
stepalt
Experimental
Concatenates patterns stepwise, according to an inferred 'steps per cycle'.
Similar to stepcat
, but if an argument is a list, the whole pattern will alternate between the elements in the list.
stepalt(["bd cp", "mt"], "bd").sound() // The same as "bd cp bd mt bd".sound()
expand
Experimental
Expands the step size of the pattern by the given factor.
sound("tha dhi thom nam").bank("mridangam").expand("3 2 1 1 2 3").pace(8)
contract
Experimental
Contracts the step size of the pattern by the given factor. See also expand
.
sound("tha dhi thom nam").bank("mridangam").contract("3 2 1 1 2 3").pace(8)
repeat
Experimental
repeat
is similar to fast
in that it 'speeds up' the pattern, but it also increases the step count
accordingly. So stepcat("a b".repeat(2), "c d")
would be the same as "a b a b c d"
, whereas
stepcat("a b".fast(2), "c d")
would be the same as "[a b] [a b] c d"
.
stepcat( sound("bd bd - cp").repeat(2), sound("bd - sd -") ).pace(8)
take
Experimental
Takes the given number of steps from a pattern (dropping the rest). A positive number will take steps from the start of a pattern, and a negative number from the end.
"bd cp ht mt".take("2").sound() // The same as "bd cp".sound()
"bd cp ht mt".take("1 2 3").sound() // The same as "bd bd cp bd cp ht".sound()
"bd cp ht mt".take("-1 -2 -3").sound() // The same as "mt ht mt cp ht mt".sound()
drop
Experimental
Drops the given number of steps from a pattern. A positive number will drop steps from the start of a pattern, and a negative number from the end.
"tha dhi thom nam".drop("1").sound().bank("mridangam")
"tha dhi thom nam".drop("-1").sound().bank("mridangam")
"tha dhi thom nam".drop("0 1 2 3").sound().bank("mridangam")
"tha dhi thom nam".drop("0 -1 -2 -3").sound().bank("mridangam")
polymeter
pm
Experimental
Aligns the steps of the patterns, to match the steps per cycle of the first pattern, creating polymeters. See polymeterSteps
to set the target steps explicitly.
// The same as note("{c eb g, c2 g2}") polymeter("c eb g", "c2 g2").note()
polymeterSteps
Experimental
Aligns the steps of the patterns, to match the given number of steps per cycle, creating polymeters.
- steps (number): how many items are placed in one cycle
- patterns (Array.<any>): one or more patterns
// the same as "{c d, e f g}%4" polymeterSteps(4, "c d", "e f g").note()
shrink
Experimental
Progressively shrinks the pattern by 'n' steps until there's nothing left, or if a second value is given (using mininotation list syntax with :
),
that number of times.
A positive number will progressively drop steps from the start of a pattern, and a negative number from the end.
"tha dhi thom nam".shrink("1").sound() .bank("mridangam")
"tha dhi thom nam".shrink("-1").sound() .bank("mridangam")
"tha dhi thom nam".shrink("1 -1").sound().bank("mridangam").pace(4)
note("0 1 2 3 4 5 6 7".scale("C:ritusen")).sound("folkharp") .shrink("1 -1").pace(8)
grow
Experimental
Progressively grows the pattern by 'n' steps until the full pattern is played, or if a second value is given (using mininotation list syntax with :
),
that number of times.
A positive number will progressively grow steps from the start of a pattern, and a negative number from the end.
"tha dhi thom nam".grow("1").sound() .bank("mridangam")
"tha dhi thom nam".grow("-1").sound() .bank("mridangam")
"tha dhi thom nam".grow("1 -1").sound().bank("mridangam").pace(4)
note("0 1 2 3 4 5 6 7".scale("C:ritusen")).sound("folkharp") .grow("1 -1").pace(8)
tour
Experimental
Inserts a pattern into a list of patterns. On the first repetition it will be inserted at the end of the list, then moved backwards through the list
on successive repetitions. The patterns are added together stepwise, with all repetitions taking place over a single cycle. Using pace
to set the
number of steps per cycle is therefore usually recommended.
"[c g]".tour("e f", "e f g", "g f e c").note() .sound("folkharp") .pace(8)
zip
Experimental
'zips' together the steps of the provided patterns. This can create a long repetition, taking place over a single, dense cycle.
Using pace
to set the number of steps per cycle is therefore usually recommended.
zip("e f", "e f g", "g [f e] a f4 c").note() .sound("folkharp") .pace(8)