# K 6 Import your own samples help



## Claud9 (Feb 16, 2020)

Hello,
I'm doing some experiments with the new function in K6 that lets you drag and drop your own samples.
I have scripted a very basic instrument and the script is based on the example I found on the KSP manual.

I have two questions please:

1) I have noticed that if I want to save it as a preset I have to save it as a .nki instrument, if I try to save it as a Snapshot is not working.
So for example, if I import Sample A and save it as Snapshot A and then import Sample B and save it as Snapshot B when I recall Snapshot A is loaded with the latest sample I have imported (Sample B). The only way to save them is a .nki instrument. Is there a way with a script to save them also as "Snapshots"?

2) With my script Kontakt will point to the samples I import where are located in my system.. Is there a way with a script to force Kontakt to copy those samples to a location I want?

Thanks in advance for any help!


----------



## EvilDragon (Feb 16, 2020)

Install Creator Tools, there is a better example that comes with it (Ambient Escapes instrument) which works fine with snapshots.


----------



## Claud9 (Feb 16, 2020)

EvilDragon said:


> Install Creator Tools, there is a better example that comes with it (Ambient Escapes instrument) which works fine with snapshots.


Thanks, Evil I have that one but is really complicated for my skill scripting level. Can you please indicate me which is the section in that script that is related to make it work also with snapshots?


----------



## EvilDragon (Feb 16, 2020)

Everything is in persistence_changed, along with associated arrays therein. Can't really explain you better than that, you'll have to dig in a bit yourself


----------



## Claud9 (Feb 16, 2020)

Ok thanks, but I think the Ambient Escape scenario is different compared to my instrument... I have two Layers, the first is dedicated only to pre-mapped samples (without the possibility to drag and drop samples), the second Layer instead is dedicated only to drag and drop samples with 1 group only.


----------



## EvilDragon (Feb 17, 2020)

So just use one half of that script, stuff related to one layer only. The principle is the same.


----------



## soundtrax (Feb 18, 2020)

Claud9 said:


> 1) I have noticed that if I want to save it as a preset I have to save it as a .nki instrument, if I try to save it as a Snapshot is not working.


I just ran into the same problem. Basically, what you need to do is to store the path of your drag/dropped user sample into a string variable (or an array if you have more than one user sample in your nki) and then load it back with the help of the on persistence CB.

1. declare a string variable:

```
declare @user_sample_path
make_persistent(@user_sample_path)
```
2. store the sample path into the variable. It will be stored when you drop a new sample into the nki . Simply put it after the wait_async DND line (on ui_control or function):

```
wait_async(set_sample(%NI_USER_ZONE_IDS[$i],!NI_DND_ITEMS_AUDIO[0]))
@user_sample_path := !NI_DND_ITEMS_AUDIO[0]
```
3. and finally this will load the user sample into user zone 0 when loading a snapshot (on persistence_changed):

```
wait_async(set_sample(%NI_USER_ZONE_IDS[0],@user_sample_path ))
```


----------



## Claud9 (Feb 18, 2020)

soundtrax said:


> I just ran into the same problem. Basically, what you need to do is to store the path of your drag/dropped user sample into a string variable (or an array if you have more than one user sample in your nki) and then load it back with the help of the on persistence CB.
> 
> 1. declare a string variable:
> 
> ...


Thanks a lot, I tried to use your example in a test instrument and It works, but the problem is that when I save the snapshot It store and recall correctly only the sample path, how can I recall also the pitch detection and the root key? I tried to apply the same principle you applied to "set sample" to the line I have in the ui_control:

```
wait_async(set_zone_par(%NI_USER_ZONE_IDS[0], $ZONE_PAR_ROOT_KEY,real_to_int(round(~pitch_result))))
```
but i can't make it work.


----------



## EvilDragon (Feb 19, 2020)

You need to store all the zone properties into persistent variables then load that in persistence_changed too.


----------



## soundtrax (Feb 19, 2020)

Yes, no string variables for pitch dectection / root key, but I haven't tried this in my own test instrument yet. If you check out the 'Ambient Escapes' demo nki you get the idea how to store the root key:

```
%asyncIDs[($b * 7) + 2] := set_zone_par($temp_zone_ID, $ZONE_PAR_ROOT_KEY, real_to_int(round(?user_zone_key_root[($layer_pointer * $MAX_ZONES_PER_LAYER) + $b])))
```


Another thing about the stored sample paths: I noticed they're always absolute paths, so it won't be possible to distribute nkis with user samples and snapshots - or does anyone know a way how to get a relative path?


----------



## EvilDragon (Feb 19, 2020)

You don't need string vars for root key, just a float.



soundtrax said:


> or does anyone know a way how to get a relative path?



get_folder($GET_FOLDER_PATCH_DIR), then point to samples relatively from there. User zones are really more about users using their own samples rather than distributing content, so absolute paths are fine.


----------



## Claud9 (Feb 19, 2020)

soundtrax said:


> Yes, no string variables for pitch dectection / root key, but I haven't tried this in my own test instrument yet. If you check out the 'Ambient Escapes' demo nki you get the idea how to store the root key:
> 
> ```
> %asyncIDs[($b * 7) + 2] := set_zone_par($temp_zone_ID, $ZONE_PAR_ROOT_KEY, real_to_int(round(?user_zone_key_root[($layer_pointer * $MAX_ZONES_PER_LAYER) + $b])))
> ...


I went through that script several times but it is too complicated for my scripting skill level and also it is about a completely different situation compared to my instrument... I have only one group dedicated only to "user samples" and I can drop only one sample, so basically I need to store only 1 number that is the value of the Root key and recall it... 
I tried with this in the ui_control:

```
wait_async(set_zone_par(%NI_USER_ZONE_IDS[0], $ZONE_PAR_ROOT_KEY,real_to_int(round(~pitch_result))))
$root := set_zone_par(%NI_USER_ZONE_IDS[0], $ZONE_PAR_ROOT_KEY,real_to_int(round(~pitch_result)))
```

And this in the persistence_changed:

```
wait_async(set_zone_par(%NI_USER_ZONE_IDS[0], $ZONE_PAR_ROOT_KEY,$root)
```

But is not working.


----------



## soundtrax (Feb 19, 2020)

unfortunately my coding skills are quite limited too - my guess is that you'd have to convert $root back with int_to_real in the persistence CB. Maybe EvilDragon or some other experienced guy can help out here...


----------



## EvilDragon (Feb 19, 2020)

The Ambient Escapes script really just shows everything. It stores all user zone data in arrays and then recalls that in persistence_changed. That's how it should be done.

Dealing with user zones is definitely not beginner level KSP, though. It's upper intermediate level, by my assumption.


----------



## Claud9 (Feb 19, 2020)

EvilDragon said:


> The Ambient Escapes script really just shows everything.


Unfortunately shows everything only to skilled scripters like you. For me is too complex. Hopefully, NI or somebody will post a simpler script that just explains the basics for our skill level, how to do it with 1 group dedicated only to user sample, 1 drop sample/zone at a time.


----------



## EvilDragon (Feb 19, 2020)

Everything is already in there, man.

$NUM_LAYERS := 2
$MAX_ZONES_PER_LAYER := 32

Change both of those to 1. Then remove everything that has anything to do with second layer. Voila. You can't say that is too complex.


----------



## soundtrax (Feb 19, 2020)

@ claud9: I just took a closer look at your code. Your first line definitely works and sets the detected root key in your user zone:

```
wait_async(set_zone_par(%NI_USER_ZONE_IDS[0], $ZONE_PAR_ROOT_KEY,real_to_int(round(~pitch_result))))
```
But you don't need the second line at all (all you need is the ~pitch_result variable, where the root key info is stored) - so delete the second line.
In the persistence CB just repeat the first line and everything should work.


----------



## Claud9 (Feb 20, 2020)

soundtrax said:


> @ claud9: I just took a closer look at your code. Your first line definitely works and sets the detected root key in your user zone:
> 
> ```
> wait_async(set_zone_par(%NI_USER_ZONE_IDS[0], $ZONE_PAR_ROOT_KEY,real_to_int(round(~pitch_result))))
> ...


Yes, thanks that's what I did yesterday and it seems to work! Basically, in the persistence CB I recall only the sample path and then force Kontakt to re-detect the pitch and set the root key again... I know that is not the "academic" way to do it but the "Ambient Escape" code is still too complex for me.


----------



## Claud9 (Feb 20, 2020)

I have a slightly "off-topic' question: is it possible to apply the same concept to the "learn midi CC# automation" function? I mean if I assign the MW to control for example the Volume Knob of my GUI is it possible to store that assignment when I save the snapshot and then recall it when I open that snapshot again?


----------



## EvilDragon (Feb 20, 2020)

Claud9 said:


> but the "Ambient Escape" code is still too complex for me.



Yet I mentioned exactly what you need to do to it to make it work. It's easy and done in a minute or so. You remove all the layer B controls from the performance view file (using Creator Tools' GUI Designer), and remove any related code from the script and change those constants. Easy. At least try it out, don't be lazy and expect full solutions written out every time here.



Claud9 said:


> I have a slightly "off-topic' question: is it possible to apply the same concept to the "learn midi CC# automation" function? I mean if I assign the MW to control for example the Volume Knob of my GUI is it possible to store that assignment when I save the snapshot and then recall it when I open that snapshot again?



No, MIDI learn and host automation are global at instrument level, they're not stored in snapshots.


----------



## Claud9 (Feb 26, 2020)

I have a question about the "path" of the user samples:
I have created in my instrument folder (that is located on an external HHD) a sub-folder named "user samples" with some .wav in it. I have loaded in the "user sample" area of my instrument some of those samples and saved the snapshots. All is good and when I recall the snapshot the sample is loaded correctly. I tried to move the HHD to another computer and when I recall those snapshots no sample is loaded... Is that ok or am I missing something?


----------



## EvilDragon (Feb 26, 2020)

That's how snapshots are for non-Kontakt Player encoded libraries. But also, when you load a sample through user zones, the path to the sample is stored as absolute path. It won't work on any other computer except yours.


----------



## soundtrax (Feb 26, 2020)

The only workaround is to load the string variable of the path into a ui_text_edit field and delete the beginning of the path to make it a relative path, then save the snapshot.
For example, if your absolute path is: "/disk/samples/testsample.wav" you'll have to change it to: "samples/testsample.wav" Then Kontakt will look in a folder called "samples" next to your nki (on any computer).


----------



## EvilDragon (Feb 26, 2020)

Well no, you can use get_sample() command to query just the filename from samples loaded to user zones, then use get_folder($GET_FOLDER_PATCH_DIR) then access that sample relatively.


----------



## soundtrax (Feb 26, 2020)

well, $GET_FOLDER_PATCH_DIR retrieves the directory where the nki is saved. But is it also possible to access a sub directory with this method? (You don't want to have all the samples in the same folder with the nki)


----------



## EvilDragon (Feb 26, 2020)

Yes sure, you use ../ to go one folder back. Add it multiple times if necessary to go multiple levels back.


```
get_folder($GET_FOLDER_PATCH_DIR) & "../Samples/"
```


----------



## Claud9 (Feb 26, 2020)

soundtrax said:


> The only workaround is to load the string variable of the path into a ui_text_edit field and delete the beginning of the path to make it a relative path, then save the snapshot.
> For example, if your absolute path is: "/disk/samples/testsample.wav" you'll have to change it to: "samples/testsample.wav" Then Kontakt will look in a folder called "samples" next to your nki (on any computer).


I'm trying to apply your example. I have created a ui_text_edit field (@relative_path)
in the ui_control CB after the line : @user_sample_path := !NI_DND_ITEMS_AUDIO[0]
I added this line: @relative_path := @user_sample_path 

the text filed now when I drop a sample show the path, I change it to relative and save the snapshot but is not working... do I have to change something also in the persistence_changed CB?


----------



## soundtrax (Feb 26, 2020)

EvilDragon said:


> Yes sure, you use ../ to go one folder back. Add it multiple times if necessary to go multiple levels back.


Ah okay, yes!
But I guess it should be:

```
get_folder($GET_FOLDER_PATCH_DIR) & "Samples/"
```
because the sample folder is inside the nki folder, we don't want to go on a folder level below but one above.


----------



## soundtrax (Feb 26, 2020)

Claud9 said:


> the text filed now when I drop a sample show the path, I change it to relative and save the snapshot but is not working... do I have to change something also in the persistence_changed CB?



No you don't have to change anything in the persistence CB.
Yes your first line works:

```
@relative_path := @user_sample_path
```

and then this as separate ui_control:

```
on ui_control (@relative_path)
@user_sample_path := @relative_path
end on
```

then it should work...

edit:
when you save a snapshot with a relative path, make sure to also delete the first / (slash) before the folder, like "samples/test.wav"


----------



## Claud9 (Feb 26, 2020)

soundtrax said:


> No you don't have to change anything in the persistence CB.
> Yes your first line works:
> 
> ```
> ...


I tried it in a new instrument and it worked perfectly, but when I insert the code in my existing instrument (where I have several other lines of code) I can't edit the text anymore... I know It's weird but the text shows the path correctly but I can't make any change to the text, I can't delate or write anything new, any idea what could cause this?


----------



## soundtrax (Feb 26, 2020)

Claud9 said:


> any idea what could cause this?


Not really...maybe there could be another UI element in the way?


----------



## soundtrax (Feb 26, 2020)

or are you doing on_listener stuff in this nki? then it's not possible to do text edits.


----------



## Claud9 (Feb 26, 2020)

soundtrax said:


> Not really...maybe there could be another UI element in the way?


nope, that crazy! I put it where nothing is behind, I can see the cursor but if I try to delete the text the cursor just move but nothing is deleted..


----------



## EvilDragon (Feb 26, 2020)

soundtrax said:


> Ah okay, yes!
> But I guess it should be:
> 
> ```
> ...



Totally up to you how you organize your library. I always put NKIs in "Instruments" folder, and samples in "Samples" folder, even if it's not a Kontakt Player library.


----------



## soundtrax (Feb 27, 2020)

EvilDragon said:


> I always put NKIs in "Instruments" folder, and samples in "Samples" folder


Yes, makes sense then!


----------



## soundtrax (Feb 27, 2020)

Claud9 said:


> nope, that crazy! I put it where nothing is behind, I can see the cursor but if I try to delete the text the cursor just move but nothing is deleted..


Then mabye try what ED suggested...


----------



## Claud9 (Feb 27, 2020)

soundtrax said:


> Then mabye try what ED suggested...


Ok I will try but I found the problem and It's really strange, It looks like if you have in your script an "on listener' CB you can't edit the text... Take a look at this example, for me is impossible to edit the text because of the on listener CB. The on listener code is taken directly with no changes from KSP manual so nothing strange or wrong there... If you then try the same code deleting the on listener CB you will see that you can edit the text..


```
on init
make_perfview
set_ui_width_px(970)
set_ui_height_px(120)

declare ui_text_edit @label_name
make_persistent(@label_name)
set_control_par_str(get_ui_id(@label_name),$CONTROL_PAR_TEXT,"empty")
set_control_par(get_ui_id(@label_name),$CONTROL_PAR_FONT_TYPE,25)
set_control_par(get_ui_id(@label_name),$CONTROL_PAR_POS_X,273)
set_control_par(get_ui_id(@label_name),$CONTROL_PAR_POS_Y,20)

declare ui_label $pattern_lbl(1,1)
set_text($pattern_lbl,"")
move_control_px($pattern_lbl,243,20)

declare ui_knob $Test (0,99,1)
declare $direction
declare $tick_counter
set_listener($NI_SIGNAL_TIMER_MS,10000)
end on

on listener
if ($NI_SIGNAL_TYPE = $NI_SIGNAL_TIMER_MS)
if ($direction = 0)
inc($tick_counter)
else
dec($tick_counter)
end if
$Test := $tick_counter
if ($tick_counter = 99)
$direction := 1
end if
if ($tick_counter = 0)
$direction := 0
end if
end if
end on
```


----------



## soundtrax (Feb 27, 2020)

Yeah it's a known issue (and already reported by ED) - not sure if the devs can fix this...


----------



## EvilDragon (Feb 27, 2020)

It's a really complicated issue - GUI refreshes are killing text input, so if you're doing something that refreshes the GUI continuously in listener callback (i.e. moving a control or changing a knob value), you can't input text. Known problem but no immediate solution for it, other than just not typing anything into text edit while listener is running.


----------



## Claud9 (Feb 27, 2020)

EvilDragon said:


> It's a really complicated issue - GUI refreshes are killing text input, so if you're doing something that refreshes the GUI continuously in listener callback (i.e. moving a control or changing a knob value), you can't input text. Known problem but no immediate solution for it, other than just not typing anything into text edit while listener is running.


Is there maybe a way to pause the listener while you are typing (so you can do it), maybe in the "on ui_control (@label_name)" CB?


----------



## EvilDragon (Feb 28, 2020)

No. That callback is executed only when you press Enter (stop typing), not when you start typing. You just need to make sure that your listener callback doesn't refresh GUI all the time. If there are no voices playing, there's no real need for listener to be executed either.


----------



## vewilya (Oct 6, 2022)

EvilDragon said:


> You don't need string vars for root key, just a float.
> 
> 
> 
> get_folder($GET_FOLDER_PATCH_DIR), then point to samples relatively from there. User zones are really more about users using their own samples rather than distributing content, so absolute paths are fine.


Hey @EvilDragon! How do you do the pointing to the samples with that get_folder($GET_FOLDER_PATCH_DIR)? Because this will get you the patch directory, right? But since I don't have string manipulation in KSP, how do I get to my samples folder?

So this is where I'm at. Of course the &"../" is just concatenating the strings and not performing the operation needed... A bit lost on the KSP syntax here!

`for k := 0 to 1
random_samples[k] := get_folder($GET_FOLDER_PATCH_DIR) & "../" & !sample_list[randZone]
end for`

Thx, urs


----------



## EvilDragon (Oct 6, 2022)

Is the list of samples in !sample_list partial (relative) paths from a certain point, or absolute paths? Obviously absolute paths should be a no-no here since they would only work on your machine.

At any rate yes you use ../ to go a folder level back from where the patch is. But I cannot suggest anything further without knowing what exactly is writtein inside !sample_list.


----------



## vewilya (Oct 6, 2022)

EvilDragon said:


> Is the list of samples in !sample_list partial (relative) paths from a certain point, or absolute paths? Obviously absolute paths should be a no-no here since they would only work on your machine.
> 
> At any rate yes you use ../ to go a folder level back from where the patch is. But I cannot suggest anything further without knowing what exactly is writtein inside !sample_list.


Yeah... The !sample_list just contains the file names... I need to populate USER GROUPS with these, so...

I have it working now with the following:

`declare @FACTORY_SAMPLE_BASE_PATH
@FACTORY_SAMPLE_BASE_PATH := get_folder($GET_FOLDER_PATCH_DIR) & "../Samples"` 

and then I string the 2 together...


----------



## EvilDragon (Oct 6, 2022)

Yep that should be fine.


----------

