Evaluators
Evaluators
Evaluators are expressions that are valid for any value in a Slot. They can be used to achieve controllable per-step randomness, pull values for a Data Pools or pull notes from one track into another.
Use of evaluators allows Warp to produce expressive, highly evolving compositions full of emergent behavior, operating off a very small amount of data, and often zero custom programming. As the UI in Warp is built, evaluators will be fully exposed to the user interface, and not remain exclusively a programming feature.
Probability #
In the example below, the 4th note has a 40% chance of being silenced:
api.patterns.add(name='foo', slots=[
Slot(degree=1),
Slot(degree=2),
Slot(degree=3),
Slot(degree=4, rest=Probability(0.4,True,False)
])
api.patterns.add(name='foo', slots=[
Slot(degree=1),
Slot(degree=2),
Slot(degree=3),
Slot(degree=4, octave_shift=Probability(0.4,-2,0)
])
RandomChoice #
In the example below, the note is chosen from a limited set of possible notes:
api.patterns.add(name='foo', slots=[
Slot(degree=RandomChoice(1,2,4,7),
Slot(degree=4),
Slot(degree=5)
])
RandomRange #
In the example below, the velocity is selected within a narrow range, implementing humanization
api.patterns.add(name='foo', slots=[
Slot(degree=1, velocity=RandomRange(80,90),
Slot(degree=2, velocity=RandomRange(80,95),
Slot(degree=3, velocity=RandomRange(85,95),
])
LoadVariable #
Variables are global and can be shared between tracks. As a result, one pattern, perhaps even playing on a muted track, can influence another. Here is a trivial self-contained example:
api.patterns.add(name='foo', slots=[
Slot(degree=1, variables=dict(a=1, b=2)),
Slot(degree=2),
Slot(degree=LoadVariable('a')),
])
TrackGrab #
Track Grab listens to what note is playing on another track and replaces the current note with that value. See this hocket example.
api.patterns.add(name='foo', slots=[
Slot(track_grab='conductor')
])
DataGrab #
Somewhat like a TrackGrab, a Data Grab pulls the next value from a Data Pool. This could be a fun way to interlace one melody into another. J.S. Bach, here we come!
api.patterns.add(name='foo', slots=[
Slot(degree=DataGrab('some_pool'),
Slot(degree=4),
Slot(degree=5),
Slot(degree=DataGrab('some_pool'),
])
Negate #
If you need to negate the value from a DataGrab you can't use the minus sign because a DataGrab is some kind of magical timey-whimey object. You need to use Negate
api.transforms.add(name='octave_shifter', slots=[
Slot(octave_shift=1),
Slot(octave_shift=Negate(DataGrab('mcelligots_pool')),
Slot(octave_shift=1),
])
This is a preview of this next thing...
Nesting Evaluators #
Technically you can nest Evaluators at infinite level. If you need to randomly decide to use a DataGrab or a constant, for example:
api.transforms.add(name='maximum_chaos', slots=[
Slot(octave_shift=1),
Slot(octave_shift=Probability(0.5, DataGrab('the_deep_end'), 2),
Slot(octave_shift=1),
])
Any parameter to any evaluator can be subject to nesting.
Suggestions & Tips #
- Per-step controllable randomness is often more interesting than lots of randomness throughout a track
- Using DataGrab may sometimes be more interesting than randomness