-
Notifications
You must be signed in to change notification settings - Fork 56
Description
Below is a script that is meant to do something simple:
- Create a finite PSequence (
pat) from 10 hardcoded notes with repeats=1 - Create another pattern (
dur) using PScaleLinLin to scale the notes inpatto the range 0.2 to 1.2 - Create reversed copies of each (
rpatandrdur) - Schedule
patnotes to play for durationdur - Schedule
rpatnotes to play for durationrdur - Run timeline.
You would expect both tracks to finish at the same time, because the sums of dur and rdur are the same forwards or backwards. But the first track finishes well before the second, as you can hear and see from the logging messages:
[2022-03-17 14:37:24,183] Timeline: Scheduled new track (total tracks: 1)
[2022-03-17 14:37:24,183] Timeline: Scheduled new track (total tracks: 2)
[2022-03-17 14:37:24,183] Timeline: Starting
[2022-03-17 14:37:27,624] Timeline: Track finished, removing from scheduler (total tracks: 1)
[2022-03-17 14:37:35,014] Timeline: Track finished, removing from scheduler (total tracks: 0)
[2022-03-17 14:37:35,014] Timeline: Finished
I found a workaround which is to pre-iterate through dur:
dur = iso.PSequence(dur.all(), 1) # <-- workaround!When I uncomment that line, the tracks finish in the same millisecond:
[2022-03-17 14:52:35,214] Timeline: Scheduled new track (total tracks: 1)
[2022-03-17 14:52:35,214] Timeline: Scheduled new track (total tracks: 2)
[2022-03-17 14:52:35,214] Timeline: Starting
[2022-03-17 14:52:46,046] Timeline: Track finished, removing from scheduler (total tracks: 1)
[2022-03-17 14:52:46,046] Timeline: Track finished, removing from scheduler (total tracks: 0)
[2022-03-17 14:52:46,046] Timeline: Finished
But I am wondering why this happens. I looked at the code for PScaleLinLin and PMap which it calls, but nothing jumps out at me. It seems that when the PScaleLinLin pattern is iterated "live" (while scheduled on the timeline) something happens and it finishes early.
Not a huge deal since I have a workaround but thought it was worth reporting, and I'm curious what this turns out to be.
I'm on an M1 Mac Mini running macOS 12.3/Monterey and Python 3.10.2 (native ARM build) and the latest version of isobar from PyPI (0.1.1) (but it also happens when I install the latest version from GitHub).
Here is the full script:
#!/usr/bin/env python3
import isobar as iso
import logging
logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(message)s")
patnotes = [74, 1, 76, 76, 52, 30, 83, 13, 97, 9]
pat = iso.PSequence(patnotes, 1)
dur = iso.PScaleLinLin(pat, min(patnotes), max(patnotes), 0.2, 1.2)
# dur = iso.PSequence(dur.all(), 1) # <-- workaround!
rpat = iso.PReverse(pat.copy())
rdur = iso.PReverse(dur.copy())
# all have the same length:
print(len(pat))
print(len(dur))
print(len(rpat))
print(len(rdur))
timeline = iso.Timeline(40)
timeline.stop_when_done = True
timeline.schedule(dict(
note=pat,
duration=dur,
amplitude=100,
gate=1,
channel=0
))
timeline.schedule(dict(
note=rpat,
duration=rdur,
amplitude=100,
gate=1,
channel=0
))
try:
timeline.run()
except KeyboardInterrupt:
timeline.output_device.all_notes_off()