Another fork() problem

I wrote a Cycle class that lets me cycle trough a list. When it comes to the end of the list it starts again at the beginning.

I use a function pphase1 to play through the pattern 4 times. It all works fine when I call the function but when I pass the function to fork() it just plays the 1st note and then hangs.
(I usually have the class in a separate file but for this I just copied into the py file)

from scamp import *

class Cycle:
    def __init__(self, cyc):
        self.data = cyc
        self.curr = -1
        self.len = len(cyc)

    def next(self):
        self.curr = self.curr + 1
        if self.curr >= self.len:
            self.curr = 0
        return self.data[self.curr]

    def curr(self):
        if self.curr == -1:
            return None
        return self.data[self.curr]

    def reset(self):
        self.curr = -1

s = Session(tempo=72)

piano = s.new_part("piano")

phasing_trope = [64, 66, 71, 73, 74, 66, 64, 73, 71, 66, 74, 73]
trope = Cycle(phasing_trope)
rate = 1/6
reps = 4

def pphase1(pat, reps, amp, inst):
    for _ in range(pat.len * reps):
        inst.play_note(pat.next(), amp, rate * 1.5, blocking=False)
        s.wait(rate)

pphase1(trope, 4, 0.5, piano)
s.wait(1.0)

s.fork(pphase1, args=[trope, 4, 0.5, piano])
s.wait_for_children_to_finish()
s.wait(0.5)

I haven’t tested this, but I think the issue is that you’re calling s.wait, when what you really want is just wait.

When you fork a function, it runs on a new, child clock. Calling s.wait is asking it to wait within the parent clock (the Session, which is acting as master clock), which confuses things. When you call wait, it’s an alias for current_clock().wait, which is what you want. When you call pphase1 directly, current_clock() is the Session, but when you fork pphase1, current_clock() gives you the clock of the child process.

By the way, for cycling lists, and working with patterns in general, it might be worth looking into itertools (specifically itertools.cycle) and writing generator functions. :slight_smile:

Thanks Marc, that was it!
Of course I had written the function and used it first without the fork so the s.wait() was fine.
I did have a quick look at iterators and generator functions but decided to try a class. PCycle is only one of a number of pattern classes all implementing the next() function. Others are PLine, PHeap, PPalindorome and PRand at present. They all derive now from a Pattern class and each iterates differently through a list with additional arguments like reverse and no repetitions(for the random selections)

Oh, that’s great—sounds like a useful set of tools! I wonder if you could use some sort of parsing expression grammar (like Arpeggio · PyPI) to create a shorthand to parse your classes from strings!