I need to create a Hold module in core.

Discussion in 'REAKTOR' started by Michael O'Hagan, Jun 13, 2019.

  1. Michael O'Hagan

    Michael O'Hagan NI Product Owner

    Messages:
    846
    I need to create a Hold module in core, I've been playing with a few possibilities and none of them are behaving as I would want them to.

    does anyone here have any ideas about how to build a core hold module that behaves the same as the primary one.

    I need to trigger a gate and have the 1 value held for a set duration of milliseconds.

    Thanks.
     
  2. Laureano Lopez

    Laureano Lopez NI Product Owner

    Messages:
    176
    you need it to output primary events, like the primary hold?
     
  3. Michael O'Hagan

    Michael O'Hagan NI Product Owner

    Messages:
    846
    Yes, I need it to duplicate the behavior of the primary module as closely as possible.
     
  4. Laureano Lopez

    Laureano Lopez NI Product Owner

    Messages:
    176
    this seems to make it
     

    Attached Files:

    • hold.ens
      File size:
      697.7 KB
      Views:
      31
    • Like Like x 1
  5. Michael O'Hagan

    Michael O'Hagan NI Product Owner

    Messages:
    846
    These are awesome.

    I'm really more interested in learning to build my own though, I've always had some confusion with read and write modules in core and I only partially understand how this is working.

    Did you make these?

    if so could you explain some of the structure to me where there are 2 read and write modules in serial operation.
     
  6. Laureano Lopez

    Laureano Lopez NI Product Owner

    Messages:
    176
    yep i made them. actually i just slightly modified a timer macro i had around. there are two memories. the lower one is a descending counter. the upper one is a flag to stop reading the lower one when the count has finished. when an event arrives, the flag is turned on and the counter is set to H. for every tick it's decreased until it reaches zero, then the flag is turned off. this flagging is similar to how the lin smoother [a] works (the version that stops outputting when it reaches the target).

    i've modified a bit more because it didn't quite work. the descending counter can't deal with a change of H while counting, so i've made it ascending (set to zero at start, check for <H). the conversion of H to samples seems to use truncation, not rounding. also, in the original i set the flag and the counter before the next read. this compared inconsistently with the primary version, so now i set them after. i still get an occasional difference, but can't figure why.

    btw, obc order is just this: if a read and a write happen at the same time, they must be done in the order they're connected. a read before a write means that if they happen simultaneously, the read will output before the write is done. in a feedback loop you can't write before you read, because the output of the read is used to calculate the input of the write. you have to read first, make the calculations, then write the result. if you need to set an initial value for a feedback process, usually you'd use a write-read-write -the first write for the initial value. this way, if a tick comes at the same time the process starts right away -otherwise it would start on the next tick. this is tricky with primary events though, because they're not simultaneous, except in the rare cases when they are -it twists my mind each time i have to figure it out.
     

    Attached Files:

    Last edited: Jun 14, 2019
    • Like Like x 3
  7. Quietschboy

    Quietschboy NI Product Owner

    Messages:
    368
    Last edited: Jun 15, 2019
    • Like Like x 2
  8. Laureano Lopez

    Laureano Lopez NI Product Owner

    Messages:
    176
    it's almost the same, plus the break function. the upper memory is the flag, the lower is the counter. the check for =N (instead of <N) is not good if N can change while holding. it sets the memories before reading as in my first version, which is the logical thing to do -i changed that later because it didn't match the primary hold. it does save some unnecessary reads of the counter though -i added that tweak to match.
     

    Attached Files:

  9. Quietschboy

    Quietschboy NI Product Owner

    Messages:
    368
    Hi Laureano,
    many thanks to pointing to the = comparison in my Hold Macro.
    i just replaced it by a < comparison to make it capable of decreasing delay time. It works well. It works now in the same manner than yours.

    This is also true for the difference i found to the Primary Hold Module:
    When triggering the button fast, at some point the output time is another then the Primary Hold´s one:
    In this example,
    Primary Hold sends 0 at 12s 15599 samples,
    our Holds send 0 at 12s 15719 samples
    upload_2019-6-18_0-44-7.png

    ens attached
     

    Attached Files:

    Last edited: Jun 18, 2019
    • Like Like x 1
  10. Laureano Lopez

    Laureano Lopez NI Product Owner

    Messages:
    176
    yap, that's the difference i couldn't figure. anyway, testing with a fixed hold time, every time they're different ours are closer, so it's actually a weird bug in the primary hold. otoh, i had replaced the w-r-w with latch[-1] because they failed at init, when the trigger and cr are simultaneous -you can see that yours is too short at init, then it goes well. i don't like that solution though, it would fail with a synced trigger. so i put the w-r-w back, and discarded the first cr tick instead. then i put all the "from primary" stuff on a separate macro (including the truncation, which is a weird choice i think), so the rest works in the general case.
     

    Attached Files:

    • Like Like x 1
  11. Quietschboy

    Quietschboy NI Product Owner

    Messages:
    368
    I made further investigations. It is getting delicate and very interesting now :D

    I did not change anything, just looking on the timing of the three macros still using Primary CR.
    My Sample Rate is 48kHz, CR is 400Hz, so each CR Tick needs exactly 2,5ms, which means 120 samples.
    If the Hold time is set to 10ms static, everybody would expect 4 clock ticks until the macros send the 0 value, agreed?

    So, this is what happens at
    FULL INIT:
    upload_2019-6-18_23-48-43.png

    First thing to be mentioned is that CR is always 1 sample too early counted from the init bang. 119 should be 120 samples 239 should be 240 and so on. But that doesn´t matter here.
    More important: The Primary Hold and yours add one clock tick. The Delay time is then 12,5ms (-1 sample) instead of 10ms what is clearly wrong, agreed?
    Making a 1:1 copy of the Primary Hold you have to take this into account, of course.

    looking at
    RE-INIT:
    upload_2019-6-18_23-59-7.png

    I added a switch to force re-init and also a structure to reset ACEW´s time stamp at re-init.
    In this case, the Primary Hold is wrong again (599 samples, 12,5ms) whereas ours a right (479 samples, 10ms).

    To reset ACEW @ re-init i made this:
    upload_2019-6-19_0-3-5.png

    upload_2019-6-19_0-3-36.png


    Now let´s look at
    NORMAL EVENTS:
    upload_2019-6-19_0-11-7.png


    Hmm, five clock counts.
    2519-1920 samples = 599 samples = 12,5ms...
    One may say that is due to asynchronity of Trigger to Clock, because the counting begins at 0 with next clock tick.
    Yes, this is how it works.
    And yes, the attentive viewer will recognize that the button´s event leaves 1 sample after the last CR tick was sent!
    In the screenshot the last CR comes at 1919 samples and the button event at 1920 samples.

    If i´m right, Mouse actions are quantized to DClk not to CR as it may look like here.
    That said, DClk, and with that Mouse events, seem to follow CR! The relationship of DClk freq (which seems to run at full 25Hz in this small test ens) to CR freq obviously leads to synchronity of them.

    A SR.C based Core Hold is definitely more accurate, as it may add max 1 extra sample to the delay time.
     

    Attached Files:

  12. Z Gabr

    Z Gabr NI Product Owner

    Messages:
    162
    Another hold module..
     

    Attached Files:

  13. Michael O'Hagan

    Michael O'Hagan NI Product Owner

    Messages:
    846
    This turned out a lot more interesting than I expected.

    Thank you to everyone for contributing so much.
     
  14. Quietschboy

    Quietschboy NI Product Owner

    Messages:
    368
    Hi Z Gabr!
    Nice to see you diving in!
    Your approach with SR.C uses a ramp adding the fractions of 1 divided by samples which leads to inaccuracy, unfortunately.
    At 32bit depth (default setting) the zero output comes to early (should be 1sec and 0 samples at 1 sec. Hold time):
    upload_2019-6-24_22-39-5.png

    When setting your macro to 64bit depth, it´s looking mostly fine but is still one sample too early:
    upload_2019-6-24_22-43-56.png
     
    • Like Like x 1
  15. Quietschboy

    Quietschboy NI Product Owner

    Messages:
    368
    I am just wondering what the apparently loved oscilloscopes in the previous examples can tell us ? ;)
     
  16. Z Gabr

    Z Gabr NI Product Owner

    Messages:
    162
    Visually, everything seems without errors, but ...:D
     
    • Funny Funny x 2
  17. Laureano Lopez

    Laureano Lopez NI Product Owner

    Messages:
    176
    sorry for the silence, i've been away. yap that's what i saw, ours are on time at init and late afterwards, the primary one is always late. i hadn't thought of re-init, heh. it seems indeed that gui events follow CR, that's why changing the place of the trigger's write (before or after the CR read) does nothing, they're not simultaneous. there's more though. i set up a SR counter and latched it with three triggers: a gui event, its next or simultaneous CR tick (w-r-w), and its next (not simultaneous) CR tick (r-w). at init, both the event and w-r-w read 0, and r-w reads 109. at re-init, they all read the same. in any other case, both w-r-w and r-w read 110 more than the event. this seems to indicate that init is simultaneous with CR, normal events follow CR, but CR follows re-init. what a jungle! :D
     

    Attached Files:

    • Like Like x 1
  18. Quietschboy

    Quietschboy NI Product Owner

    Messages:
    368
    Yeah, what a jungle!
    For a moment it looked to me that re-init is synced to CR, coming one sample later but that was just by chance. Otoh, the appearing of re-init seems to be quantized to some grid. Maybe depending on fractions of CR?!?
    Fact is, that re-init introduces an additional CR tick.
    Otoh, re-init seems not to reset the CR counter. So, the clock stays runing in the same phase than before re-init. There is just this annoying additional re-init CR tick which let´s stumble CR for a short moment. I think this re-init clock tick is necessary and good in other cases. Maybe it is blockable as a workaround.
    All this let´s me think about the init table i posted some years ago, and yes, i see that CR was not mentioned in that. :eek:
    I will catch up.

    Laureano, did you also check how CR behaves at full init with lower CR rates than 400Hz (also higher rates)? No? Then do it. :p
    The first tick after init dances out of the series (except for 400Hz...). Here after 9 samples. Following ticks are based on that:
    upload_2019-6-27_20-23-10.png

    Hold_6.ens was just expanded by the CR numerics for f, T(smp) and T(ms).
    I also made the ms_to_N converter in front of my Hold to always round up.

    PS:
    The DClk´s behaviour at init is unreliable too! But for DClk it is more worse. Don´t have the details in mind for now, just remember it concerns the moment of the first two DClk ticks appearing. Maybe also their values (o vs. 1).
    My conclusion was that already existing "Late init trigger" macros using DClk weren´t stable enough. A bit more complicated solution using DClk had to come.
     

    Attached Files:

    Last edited: Jun 27, 2019
    • Like Like x 2
  19. Quietschboy

    Quietschboy NI Product Owner

    Messages:
    368
    I´m just diving into at which time a re-init occours.
    But this is so off-topic here that i just want to say for now that re-init seems to be quantized into a 100Hz grid. Independently of the sample rate or CR setting. When the re-init exactly occours after the trigger impulse was sent in structure is uncertain. This could be the next 100Hz impulse or some pulses later. In fact the 100Hz clock is startet with initialization and stable.
    Maybe i´ll explain that in another thread.
     
    • Like Like x 1
  20. Michael O'Hagan

    Michael O'Hagan NI Product Owner

    Messages:
    846

    You guys went deep on this thing, this has been very interesting so thank you, and lets keep this conversation going here.