# Dynamic NKA-based preset system



## magneto538 (Mar 15, 2017)

Hi all.
I am setting up an NKA-based presets management system. In order to easily import NKAs, I will have the user triggering manually the scan using the File Selector (just like it happens with the scan in Output Exhale).

I have two UI elements here, a button (presets_scan) and a File Selector (presets_list). When presets_scan is clicked, it triggers presets_list's UI callback using fs_navigate.

presets_list's callback acts like a big 'for' loop. Within PRESETS_BUFFER (which is the name of the array containing the current preset's data) is a unique ID which identifies each NKA preset. After loading PRESETS_BUFFER from disk, this UID is compared to the ones which are saved inside a UID buffer: if the UID is NOT in the buffer yet, the array data are added to some menus (basically, I am loading the name of the array in the menus so I can load the preset easily).

When the data has been loaded, another fs_navigate is triggered, so the callback is triggered again (just like a 'for' loop). The callback is triggered as long as the UID of the loaded preset is NOT in the UID buffer: when this happens, it means that the file selector has completed a full round of loading, so it stops.

This is a good solution in my mind, the problem is that every now and then - with no specific pattern - the UID is not loaded correctly. Sometimes I get an UID from a previously loaded array instead of the one in the array I am currently loading, so the loop stops before finishing the scan. I thought about a failed array load, but I checked the async status for each cycle and the array is loaded correctly everytime. I really don't know where the problem might be, because everything seems to be OK but sometimes the wrong UID is retrieved from the array. I am sure that all the UIDs are different in each source NKA because I have manually checked them.

Here's (part of) the script:


```
on ui_control (presets_scan)    // button
    // Reset everything and trigger the real scan (which happens in presets_list callback)
    if presets_scan = ON
        // Reset UID buffer. I will load the current UID in this array.
        for i := 0 to num_elements(uid_buffer)
            uid_buffer[i] := -1
        end for

        // ...Additional reset operations...

        // fs_navigate triggers the File Selector callback by default.
        // This one is here in order to trigger it.
        fs_navigate(get_ui_id(presets_list), 1)
    end if
end on

on ui_control (presets_list)
    // After setting up everything in presets_scan, this callback is triggered.
    if presets_scan = ON
        // Load next preset data, then load it from disk
        @sel_preset_name := fs_get_filename(get_ui_id(presets_list), 0)
        @sel_preset_path := fs_get_filename(get_ui_id(presets_list), 2) & '.nka'

        // PRESET_BUFFER is the array where I store all the presets data, including an unique ID for each preset.
        P_ASYNC_ID := load_array_str(PRESET_BUFFER, @sel_preset_path)
       
        // Wait for Async green light. P_ASYNC_ID is processed by async callback.
        while P_ASYNC_ALLOW # ON 
            if P_ASYNC_ALLOW = -1
                P_ASYNC_ALLOW := OFF
                exit
            end if
            wait(1000)
        end while   
        P_ASYNC_ALLOW := OFF

        // If the current UID is not in the buffer yet, it means that the current .nka has not been imported, so import it.
        // If the current UID is already in the buffer, it means that the scan is complete, so get the hell out.
        if search(uid_buffer, PRESET_BUFFER[P_UID_IDX]) = -1
            uid_buffer[pc] := PRESET_BUFFER[P_UID_IDX]

            // ...here's where I place the script to load the presets to some menus...

            inc(pc)
            // Goto the next file in the File Selector. fs_navigate triggers this callback again, so it's like being in a big for loop.
            // This pseudo-loop ends when the condition above is not met (aka the 'else' statement below is triggered).
            fs_navigate(get_ui_id(presets_list), 1)
        else
            presets_scan := OFF
            exit
        end if
    end if
end on
```

Any ideas?


----------



## magneto538 (Mar 15, 2017)

Ok, I think I came up with something about this.

I am using Sam Windell's fork of SublimeKSP. As you may know, it features a live logging system thru its activate_logger() function. This function adds some script at the end of the compiled Persistence callback:


```
while (9=9)
    if ($logger_previous_count # $logger_count)
      $LOGGER_ASYNC_ID := save_array_str(!_log,@logger_filepath)
    end if
    $logger_previous_count := $logger_count
    wait(200000)
  end while
```

Basically, the Persistence callback never exits: the while loop is triggered every 2 seconds and a save_array_str is performed at every round.

The script I am working on (the one I posted above) performs a large amount of load_array_str, which do not seem to be related to the issue. When I am using the logger, though, I noticed that some NKAs on the disk get messed up: it seems that some data are actually WRITTEN on them when they are loaded. The result is that some arrays get copied to each other, which causes the UIDs to be overwritten and my whole system gets screwed. This only happens when I am using the logger.

It seems like there are some serious issues when using save_ and load_array_str simultaneously.


----------



## EvilDragon (Mar 15, 2017)

Yeah I think making persistence_changed never end is not really a good idea... Thankfully you wouldn't use the logger in the final product.

Also, I am not sure how would you expect saving and loading to work simultaneously - it's bound to create issues, no?


----------



## magneto538 (Mar 15, 2017)

EvilDragon said:


> Yeah I think making persistence_changed never end is not really a good idea... Thankfully you wouldn't use the logger in the final product.


Yeah, but I have to be careful not to mess up all the presets once they are 'official'... The logger should have its own dedicated callback, but until NI won't let us define custom callbacks I don't think there are other solutions.



EvilDragon said:


> Also, I am not sure how would you expect saving and loading to work simultaneously - it's bound to create issues, no?


I am not saving and loading the same array. The logger is saving its own array and I am sweeping through a set of NKAs (which point to PRESETS_BUFFER[], not to the logger array) and loading them. Honestly I don't see how they could interact with each other, since I'm working on two different arrays.


----------



## EvilDragon (Mar 15, 2017)

magneto538 said:


> but until NI won't let us define custom callbacks



Snowball's chance in hell  Best scenario is we get better logging possibilities than just message().


----------



## Flaneurette (Mar 22, 2017)

It's asynchronous, so each callback or reference is probably re-instantiated when you try to retrieve it. Have you tried using the _async_complete_ callback? The _async_complete_ callback is triggered after load_array and save_array, and returns the updated (built-in) $NI_ASYNC_ID. I wrote a presets system a few years ago. A snippet from from it:


```
declare $sync_id

on async_complete
    if ($NI_ASYNC_ID = $sync_id)
    $sync_id := -1
    end if
end on

$sync_id := load_array (%save,0)
while ($sync_id # -1)
   wait (1)
end while
```

But I haven't coded any KSP in 3 years. So I am a little rusty.

You can find most of the code here:  or download the synthesizer with full code: http://www.mediafire.com/?7dno9om5dw6opnq


----------



## magneto538 (Mar 24, 2017)

Flaneurette said:


> It's asynchronous, so each callback or reference is probably re-instantiated when you try to retrieve it. Have you tried using the _async_complete_ callback? The _async_complete_ callback is triggered after load_array and save_array, and returns the updated (built-in) $NI_ASYNC_ID. I wrote a presets system a few years ago. A snippet from from it:
> ...



Eventually I worked it out by using the async callback, but the issue persists. Now I just have to be careful not to use the logger inside the scan function.


----------



## KrisY (Sep 17, 2021)

magneto538 said:


> Eventually I worked it out by using the async callback, but the issue persists. Now I just have to be careful not to use the logger inside the scan function.


Hello from the future  Did you ever get this one to work properly? I'd very much appreciate taking a look at a few lines of the preset method if you don't mind.


----------



## davidnaroth (Sep 20, 2021)

KrisY said:


> Hello from the future  Did you ever get this one to work properly? I'd very much appreciate taking a look at a few lines of the preset method if you don't mind.


Ive never been able to succesfully "refresh" the file selectors, so I ended up just building my own with buttons and a slider that will scroll through, updating the buttons to load the correct preset when selected.


----------



## EvilDragon (Sep 20, 2021)

File selectors can now be refreshed from any callback since K6.6, simply by setting $CONTROL_PAR_BASEPATH again.


----------



## gregjazz (Sep 22, 2021)

Just to double-check, the fs_navigate function only works once an item in the file selector has been selected, right? And there's no way to programmatically make that first selection?


----------



## EvilDragon (Sep 22, 2021)

You can, by setting $CONTROL_PAR_FILEPATH


----------



## KrisY (Sep 24, 2021)

EvilDragon said:


> You can, by setting $CONTROL_PAR_FILEPATH


The manual does seem to indicate this is the way to "remember" your last use and position of the file browser upon on init. Will try it out. Also, it was introduced in K5.0, so backwards compatible, in theory.


----------

