Action events
Action events trigger Python functions. Set the action
property to a function or lambda, and it will be executed at the time of the event:
timeline.schedule({
"action": lambda: print("Hello world")
})
Observe that, when you run the above, it will print Hello world
indefinitely, once per beat. Why is this?
Just as for notes and other event types, the duration
parameter of the event template defaults to an infinitely-repeating pattern generated by PConstant
. To limit the number of repeats that an action performs, use the count
argument:
timeline.schedule({
"action": lambda: print(round(timeline.current_time))
}, count=4)
Action arguments
For more complex functions, custom named keyword arguments can be passed to the function using the args
property
This executes an action every 4 beats to change the global key of the piece, using the Globals variables:
def set_key(k):
iso.Globals.set("key", iso.Key(k))
timeline.schedule({
"action": set_key,
"args": {
"k": iso.PWhite(8)
},
"duration": 4
})
timeline.schedule({
"degree": 0,
"key": iso.PGlobals("key"),
"octave": 4
})
Avoid Mixing EVENT_ACTION and EVENT_NOTE or EVENT_DEGREE
Mixing them incorrectly can lead to unexpected behavior. Including both EVENT_ACTION and EVENT_NOTE events in the same schedule results in an empty track.
Incorrect mixing:
def record_time():
times.append(time.time())
events = {
iso.EVENT_NOTE: iso.PSequence([1, 1], 1),
iso.EVENT_ACTION: record_time,
iso.EVENT_DURATION: 0.1
}
timeline.schedule(events, delay=0.1)
t0 = time.time()
timeline.run()
The above code results in an empty track:
>>> timeline.output_device.miditrack
MidiTrack()
While removing EVENT_ACTION events
events = {
iso.EVENT_NOTE: iso.PSequence([1, 1], 1),
iso.EVENT_DURATION: 0.1
}
Program returns:
>>> timeline.output_device.miditrack
MidiTrack([
Message('note_on', channel=0, note=1, velocity=64, time=48),
Message('note_off', channel=0, note=1, velocity=64, time=48),
Message('note_on', channel=0, note=1, velocity=64, time=0),
Message('note_off', channel=0, note=1, velocity=64, time=48)])
Workaround for this is to use 2 separate schedules.
def record_time():
times.append(time.time())
events_action = {
iso.EVENT_ACTION: record_time,
iso.EVENT_DURATION: 0.1
}
events_notes = {
iso.EVENT_NOTE: iso.PSequence([1, 1], 1),
iso.EVENT_DURATION: 0.1
}
timeline.schedule(events_action, delay=0.1)
timeline.schedule(events_notes, delay=0.1)
timeline.run()