Question about clocks #447
-
I've noticed something I can't quite explain. I've built a 16-step sequencer, and have made it possible to change the quantization. I've been testing with a quantization of 8th and 16th notes. I've sequenced the same notes as 1/4 notes when quantizing to either 16th notes or 8th notes (a simple four-on-the-floor 909 kick drum pattern). Like this: As 16th notes: As 8th notes: 1-and-2-and
As expected, when I examine the times of the notes, I see that they have the same times (in units of the As 16th notes:
As 8th notes:
I would expect that when I play back the kick drum pattern at the same BPM, and when setting the clock's callback Am I misunderstanding something? |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 9 replies
-
The quantization is only for cueing when a callback is initially started. It doesn't quantize any of the subsequent events. |
Beta Was this translation helpful? Give feedback.
-
Damn, it's late and I'm brain fried. I didn't do a good of a job pulling my
code out of the system and making it work in the same way. So this example
isn't showing what I want. Ugh. Sorry.
I don't use globals in any serious code. Don't worry.
I didn't generalize the callbacks to make my point more explicit, but like
I said, I messed up my whole point. In my code the issue isn't that the
callbacks take different lengths of time to execute for the same number of
steps, but that the playback of the samples is occuring at a much slower
rate. So a kick drum on every down beat is playing more frequently when the
delta is 0.0625 than when the delta is 0.125. That shouldn't be happening
since they are both just playing on every down beat. Anyway, I'm totally
exhausted, and will try to come up with a better example tomorrow.
…On Tue, Feb 25, 2025, 8:59 PM Joséphine Wolf Oberholtzer < ***@***.***> wrote:
Thanks for the code example.
1.
I'm not sure I understand what the problem is. A delta of 1/8 * 16
events is going to take twice as long as a delta of 1/16 * 16 events.
2.
Please let me talk you out of your use of globals. You'll have shorter
and simpler code if you just pass arguments / return values and assign them
in local namespaces.
3.
I'm gonna rewrite your example, removing globals, dead code (e.g.
Server and times_dict), etc....
import sys
from concurrent.futures import Future
from supriya.clocks import Clock, ClockContext
from supriya.clocks.ephemera import TimeUnit
BPM: int = 120
STEPS: int = 17 # let's do 17 steps so we loop around to the next downbeat
def set_up_clock() -> Clock:
clock = Clock()
clock.change(beats_per_minute=BPM)
clock.start()
return clock
def clock_callback(
context: ClockContext,
delta: float,
future: Future,
timings: list[float],
) -> tuple[float, TimeUnit]:
"""
We can make this function generic and pass in the delta and timing list at
cue time.
"""
print(delta, context.event.invocations, context.current_moment.seconds)
if context.event.invocations == 0:
timings.append(context.current_moment.seconds)
elif context.event.invocations == STEPS - 1:
timings.append(context.current_moment.seconds)
future.set_result(True)
return None # We've reached the last step.
return (
delta,
TimeUnit.BEATS,
)
def main() -> None:
# setup lists for storing the timings
quantization_8_timings: list[float] = []
quantization_16_timings: list[float] = []
# setup futures to wait on
quantization_8_future = Future()
quantization_16_future = Future()
# setup the clock
clock = set_up_clock()
# cue the clock callbacks, passing in kwargs to customize how each works
clock.cue(
procedure=clock_callback,
kwargs=dict(
delta=1 / 8, future=quantization_8_future, timings=quantization_8_timings
),
)
clock.cue(
procedure=clock_callback,
kwargs=dict(
delta=1 / 16, future=quantization_16_future, timings=quantization_16_timings
),
)
# await the futures
quantization_8_future.result()
quantization_16_future.result()
# pring out the results
quantization_8_time = quantization_8_timings[1] - quantization_8_timings[0]
quantization_16_time = quantization_16_timings[1] - quantization_16_timings[0]
print(f"{quantization_8_time=}")
print(f"{quantization_16_time=}")
if __name__ == "__main__":
main()
sys.exit()
➜ python discussion447.py
0.125 0 1740513501.0896
0.0625 0 1740513501.0896
0.0625 1 1740513501.215277
0.125 1 1740513501.340211
0.0625 2 1740513501.340211
0.0625 3 1740513501.4652412
0.125 2 1740513501.5902421
0.0625 4 1740513501.5902421
0.0625 5 1740513501.715581
0.125 3 1740513501.8398879
0.0625 6 1740513501.8398879
0.0625 7 1740513501.965145
0.125 4 1740513502.090654
0.0625 8 1740513502.090654
0.0625 9 1740513502.215522
0.125 5 1740513502.340033
0.0625 10 1740513502.340033
0.0625 11 1740513502.46579
0.125 6 1740513502.590525
0.0625 12 1740513502.590525
0.0625 13 1740513502.714777
0.125 7 1740513502.8398662
0.0625 14 1740513502.8398662
0.0625 15 1740513502.9654489
0.125 8 1740513503.0904682
0.0625 16 1740513503.0904682
0.125 9 1740513503.339875
0.125 10 1740513503.5905979
0.125 11 1740513503.8401768
0.125 12 1740513504.090258
0.125 13 1740513504.340682
0.125 14 1740513504.589966
0.125 15 1740513504.84062
0.125 16 1740513505.0901172
quantization_8_time=4.000517129898071
quantization_16_time=2.000868082046509
—
Reply to this email directly, view it on GitHub
<#447 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACVNNEQHWAVNVKMG6VHHLH32RTDTLAVCNFSM6AAAAABX27HGN6VHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTEMZRG4ZTQNY>
.
You are receiving this because you authored the thread.Message ID:
***@***.***
com>
|
Beta Was this translation helpful? Give feedback.
-
Also wonder if you might rethink how you're approaching the entire problem. A step sequencer is often represented as a grid. Columns are time steps (e.g. all the 16th notes in a 4/4 bar) and rows are instruments. If a cell in the grid is true, it indicates that instrument should play when the sequencer reaches that column. With that model, you only need one callback, always with the same delta. It marches through the columns in the grid, checks each row, and plays each instrument's cell if true. A lot of old-school drum machines work this way. |
Beta Was this translation helpful? Give feedback.
The quantization is only for cueing when a callback is initially started. It doesn't quantize any of the subsequent events.