Skip to content

subtle timing issue with PScaleLinLin #26

@dtenenba

Description

@dtenenba

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 in pat to the range 0.2 to 1.2
  • Create reversed copies of each (rpat and rdur)
  • Schedule pat notes to play for duration dur
  • Schedule rpat notes to play for duration rdur
  • 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()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions