Skip to content

"Time" for event-sending vs. rendering - do we need amy.sequencer_ticks()? #1084

Description

@dpwe

When you schedule a single event for a specific time, as in:

amy.send(ticks=96, synth=1, note=60, vel=1)

the ticks time is interpreted as absolute, i.e. the number of sequencer ticks since the timebase was last reset. One common idiom is to establish a timebase for "now", then schedule relative to it:

now = amy.sequencer_ticks()
amy.send(ticks=now + 48, synth=1, note=60, vel=1)

This leads to some agita in the code, where a command to reset the timebase may or may not have been executed before a subsequent read of the system clock:

amy.send(reset=amy.RESET_TIMEBASE)
now = amy.sequencer_ticks()   # Has the timebase reset yet?

This led originally to a special-case for RESET_TIMEBASE (in 7e55ec6), and more recently to some work to ensure thread-safety in #1081.

I'm trying to think of a way to make this less uneven. I suspect there's no real use for the now = amy.sequencer_ticks() idiom - if you want to schedule things at a specific time relative to now, you should just RESET_TIMEBASE and specify when you want them to happen assuming that time just started. We wouldn't need to read sequencer_ticks at all.

This would be bad if you had two programs trying to use the timebase at the same time - if one reset the timebase, the other might be thrown off. But I'm not sure how realistic a problem this is.

The idiom shows up in "real-time" scheduling, where the illusion is that amy.send() commands are executed immediately (rather than being added to a queue), and the caller does "loose" scheduling by waiting for time to reach some particular point before issuing the commands. This is the common pattern for AMYboard sketches exemplified by acid_generator.py - the sketch counts how many times loop() has been called, then issue "immediate" amy.send commands pertinent to the current beat. This works because loop() is called in synchrony to the sequencer clock. Significantly, acid_generator never actually looks at sequencer_ticks().

If we deprecated sequencer_ticks, we could enforce "you don't know where the sequencer is because you're just sending commands asynchronously, but you can tell me when to play them relative to RESET_TIMEBASE". Then we could do without the the #1081 machinery to make reads of the clock correct even if the timebase reset hasn't yet kicked in -- it feels like "bending over backwards" to preserve a user's fantasy that everything happens immediately. I think it's more elegant to have an interface that implicitly reflects the engine's semantics that commands will be executed in order (or at the scheduled time on the engine's timebase), but the caller doesn't actually know when that is relative to when the commands are sent.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions