1. IMPORTANT:
    We launched a new online community and this space is now closed. This community will be available as a read-only resources until further notice.
    JOIN US HERE

Lock variable for writing

Discussion in 'Scripting Workshop' started by Reid115, Dec 22, 2021.

  1. Reid115

    Reid115 NI Product Owner

    Messages:
    40
    Say this is my code:
    Code:
    on init
        declare const $MAX_FOO := 8
        declare %foo[$MAX_FOO] := (0)
        declare polyphonic $looping
        declare polyphonic $idx
        declare polyphonic $i
    end on
    
    on note
        ignore_event($EVENT_ID)
       
        { ... }
       
        $i := 0
        $idx := -1
        while ($i < $MAX_FOO and $idx = -1)
            if (%foo[$i] = 0)
                %foo[$i] := $EVENT_ID
                $idx := $i
            end if
            inc($i)
        end while
       
        $looping := 1
        while ($looping = 1)
            { ... }
            wait(1000)
        end while
    end on
    Now say you play two notes at the exact same time. My guess is that both note IDs would be placed at %foo index 0, because they both see it as empty at the same time. I don't want this to happen -- one note should be placed at index 0, the other at index 1. Note that I'm including the bottom while loop with the wait() because I read a post from many years ago that CBs that don't have wait statements will be executed in batch (which might solve my problem). But that wouldn't be the case here. I thought of creating a lock variable like so...
    Code:
    while ($lock = 1)
       wait(100)
    end while
    $lock := 1
    $i := 0
    $idx := -1
    while ($i < $MAX_FOO and $idx = -1)
       if (%foo[$i] = 0)
           %foo[$i] := $EVENT_ID
           $idx := $i
       end if
       inc($i)
    end while
    $lock := 0
    ...but both NCBs will probably surpass the lock loop at the same time before one sets $lock to 1. I also don't like the idea of waiting some non-exact arbitrary amount of time to see if $lock has changed. Is there a simple solution to having two separate NCBs read/write to a global array without conflict? Thanks.
     
  2. EvilDragon

    EvilDragon Well-Known Member

    Messages:
    19,938
    Don't use a polyphonic variable for $idx in this case then. It will start at -1 for each and every played voice.
     
  3. Reid115

    Reid115 NI Product Owner

    Messages:
    40
    I don't see how that would have any impact on the issue. Regardless whether $idx is polyphonic or not, "%foo[$i] := $EVENT_ID" is going to happen at the same time at the same index in both NCBs. Maybe it was a bad example -- this would hypothetically be an ongoing process, where the contents of %foo are constantly changing from 0 to non-zero and back to 0 randomly. The real scenario involves keeping track of keys and associated voices:
    Code:
    $active_idx := -1
    { if cut duplicates is on, check if key already playing }
    if ($cut_duplicates_button = 1)
       $nv := 0
       while ($nv < $max_keys and $active_idx = -1)
           if (%active_keys[$nv] # 0 and $EVENT_NOTE = get_event_par(%active_keys[$nv], $EVENT_PAR_NOTE))
               $active_idx := $nv
           end if
           inc($nv)
       end while
    end if
    { if active_idx still not found, check if any free key slots }
    if ($active_idx = -1)
       $nv := 0
       while ($nv < $max_keys and $active_idx = -1)
           if (%active_keys[$nv] = 0)
               $active_idx := $nv
           end if
           inc($nv)
       end while
    end if
    { if active_idx still not found, replace oldest key }
    if ($active_idx = -1)
       $nv := 0
       $oldest_timestamp := -1
       while ($nv < $max_keys)
           if ($oldest_timestamp = -1 or get_event_par_arr(%active_keys[$nv], $EVENT_PAR_CUSTOM, 3) < $oldest_timestamp)
               $oldest_timestamp := get_event_par_arr(%active_keys[$nv], $EVENT_PAR_CUSTOM, 3)
               $active_idx := $nv
           end if
           inc($nv)
       end while
    end if
    
    $this_mark := %marks[$active_idx]
    change_vol(by_marks($this_mark), -200000, 1)
    fade_out(by_marks($this_mark), 4000, 1)
    set_event_par_arr(by_marks($this_mark), $EVENT_PAR_CUSTOM, -1, 1)
    set_event_par_arr(by_marks($this_mark), $EVENT_PAR_CUSTOM, 0, 2)
    %active_keys[$active_idx] := $EVENT_ID { what if two notes happen at exact same time? }
    set_event_mark($EVENT_ID, $this_mark)
     
  4. EvilDragon

    EvilDragon Well-Known Member

    Messages:
    19,938
    There is no such thing as "same time" in KSP. It's always a sequential order of operations. So even if two notes have the same exact position in MIDI ticks, they will be processed one after another, sequentially.

    If you simply need a "number of keys pressed" variable, you do it like this:

    Code:
    on init
        declare $keys_pressed
    end on
    
    on note
        inc($keys_pressed)
    end on
    
    on release
        if ($keys_pressed > 0)
           dec($keys_pressed)
        end if
    end on
    Then use this as your index into the %foo array.


    Unsure why you're doing your own %active_keys array, when built in %KEY_DOWN array exists.
     
  5. Reid115

    Reid115 NI Product Owner

    Messages:
    40
    When you say sequentially, are you referring to the individual instructions or the whole callbacks? If you're saying the latter, then I have no issue. My concern is that two notes being played at the same time will result in the code being run like this:
    Code:
    instruction 1: if (%foo[$i] = 0) { in NCB 1 }
    instruction 2: if (%foo[$i] = 0) { in NCB 2 } { will evaluate to true because NCB 1 hasn't gotten to instruction 3 yet }
    instruction 3: %foo[$i] := $EVENT_ID { in NCB 1 }
    instruction 4: %foo[$i] := $EVENT_ID { in NCB 2 }
    When I need it to run like this:
    Code:
    { in NCB 1 }
    { ... }
    if (%foo[$i] = 0)
       %foo[$i] := $EVENT_ID
    { ... }
    
    { in NCB 2 }
    { ... }
    if (%foo[$i] = 0) { will be false, because NCB 1 has already put something there }
       %foo[$i] := $EVENT_ID
    { ... }
    Maybe I'm just overthinking and getting flashbacks to my MIPS assembly days...

    %KEY_DOWN wouldn't work for what I'm doing in my project -- I need to keep track of the associated $EVENT_IDs, there could be multiple instances of the same key being pressed, I need to restrict the %active_keys size to 8, etc... probably better named "%active_ids". This is why I try to create dummy examples for these questions so I don't create added confusion :(
     
    Last edited: Dec 22, 2021
  6. EvilDragon

    EvilDragon Well-Known Member

    Messages:
    19,938
    Nope, they're not. Callbacks are executed sequentially always, that's what I meant.
     
  7. Reid115

    Reid115 NI Product Owner

    Messages:
    40
    Awesome, thanks.