Search code examples
puredata

Possible bug in Pd patch


I have made a very simple patch, by which when a bang is triggered, it is meant to trigger a unique number between 0-2, in other words, no numbers are repeated.

In the way that I set it up, it is meant to work in theory. Even my programming mentor said that it should work, in theory, and he's generally a very smart man. He's informally known as being the boffin of the academy.

A few more details:

This happens in both purr data and pure data, with the exact same setup.

There are no external libraries are used. Just plain Vanilla objects.

Since there doesn't seem to be a way to attach the actual file itself, I will instead post an image of the code:

Meow


Solution

  • The problem is with depth-first processing (as used by Pd) and the related stack-unrolling, as this might lead to setting the 2nd input of [select] to an old value (which you didn't expect).

    Example

    Note: select:in0 means the left-most inlet of [select],... The numbers generated by [random] are shown in bold (1) and the numbers output the patch are shown in bold italics (e.g. 3)

    Imagine the [select] is initialized to 0 and the [random 3] object outputs a list 2 0 0 2 0 2 ... (hint: [seed 96().

    The expected output would be 2 0 2 0 2 ..., however the output really is 2 0 2 2 2 ...

    Now this is what happens if you consecutively send [bang( to the random generator:

    1. random generates 2
      • 2 is sent to the sel:in0, which compares it to 0 (no match)
        • and sends it out of sel:out1 (the reject outlet), displaying the number 2
      • after that the number is sent to sel:in1, setting it's internal state to 2.
    2. random generates 0
      • 0 is sent to the sel:in0, which compares it to 2 (no match)
        • and sends it out of sel:out1, displaying the number 0
      • after that the number is sent to sel:1, setting it's internal state to 0.
    3. random generates 0
      • 0 is sent to the sel:in0, which compares it to 0 (match!)
        • and sends a bang through sel:out0 (the match outlet)
          • triggering a new call to random, which now generates 2
          • 2 is sent to the sel:in0, which compares it to 0 (no match)
            • and sends it out of sel:out1, displaying the number 2
          • after that the number is sent to sel:1, setting it's internal state to 2.
      • after that the number 0 (still pending in the trigger:out0) is sent to sel:1, setting it's internal state to 0!!!
    4. random generates 0
      • 0 is sent to the sel:in0, which compares it to 0 (match!)
        • and sends a bang through sel:out0
          • triggering a new call to random, which now generates 2
          • 2 is sent to the sel:in0, which compares it to 0
            • and sends it out of sel:out1, displaying the number 2
          • after that the number is sent to sel:1, setting it's internal state to 2.
      • after that the number 0 (still pending in the trigger:out0) is sent to sel:1, setting it's internal state to 0!!!

    As you can see, at the end of #3 the internal state of [select] is 0, even though the last number generated by [random] was 2 (because the left-most outlet of [trigger] will only send to 0 after it has sent the 2, due to stack-unrolling).

    Solution

    The solution is simple: make sure that the state of [select] contains the last displayed value, rather than the last one generated on the stack. avoid feedback when modifying the internal state.

    E.g (using local send/receive to for nicer ASCII-art)

    [r $0-again]
    |
    [bang(
    |
    [random 3]
    |
    |      [r $0-last]
    |      |
    [select]
    |      |
    |      [t f f]
    |      |     |
    |      |     [s $0-last]
    |      |
    |      [print]
    |
    [s $0-again]