Sample Start Modulation

Discussion in 'Scripting Workshop' started by Johnny C, Jun 12, 2019.

  1. Johnny C

    Johnny C New Member

    Messages:
    18
    I have a few questions/statements about the sample start modulation options via KSP.

    1) Changing the sample start position via KSP involves a group level option of using an External -> Constant modulator in the source pane. There is no way to do this per Zone via KSP, right? (EDIT: By "do this" I mean modulate sample start per Zone via KSP)

    2) Using ENGINE_PAR_MOD_INTENSITY requires a lookup table to map it to a linear range, a la the one provided by EvilDragon here: https://pastebin.com/raw/11e2sDkV

    3) There is no way to modulate sample end position. Is there any way to approximate/work around this, perhaps with looping markers? This would require some way to count the number of loops and stop as soon as the first loop is over.

    The idea is that I have sampled words of which I would like the instrument user to be able to dial in specific pieces (vowels, consonants, very small slices). Without being able to move the sample end marker this is likely just impossible, but I'm still curious about the correctness of the statements/questions above.

    Thanks in advance!
     
  2. EvilDragon

    EvilDragon Moderator Moderator

    Messages:
    14,969
    1. It's possible to do this directly in play_note() command. It's the third argument.

    2. Correct.

    3. You could do this by changing the length parameter of play_note (fourth argument).
     
  3. Johnny C

    Johnny C New Member

    Messages:
    18
    Ah! I had a thought that tracking duration would be the way to implement sample end just after I started the thread, but this is great news to know that I don't have to do that tracking manually!

    So just to be clear for anyone finding this in the search results later, I can avoid the modulation/ENGINE_PAR_MOD_INTENSITY approach as long as I don't want to have different sample start positions for multiple groups playing one the same note at the same time.

    I just need 1) a table of the length of all relevant samples in microseconds (I can build this using get_sample_length and save it as an NKA, for example) and 2) a math conversion from start/stop knobs that can operate with percent as units (or alternatively getting the range in microseconds from the table mentioned in requirement 1). Plus some logic to make sure that start is always at least a microsecond before the stop position.

    This is much simpler than I had feared it might be. Thanks ED!
     
  4. EvilDragon

    EvilDragon Moderator Moderator

    Messages:
    14,969
    You can even have that. Just a matter of allowing/disallowing groups per play_note() event, by using set_event_par_arr(<event ID>, $EVENT_PAR_ALLOW_GROUP, 0 or 1, <group ID>)

    Regarding point 2, just declare your start/end knobs with these ranges: 0-990 for start, and 10-1000 for end. This way there's always gonna be 1% difference between start and end. Of course, make sure when start is larger than end, that end moves to the start value, and vice versa when end is lower than start.

    After this all you need to do is a little bit of math, convert the start/end values to float for simpler calculation, divide by 1000, multiply by sample length, that's it.
     
  5. Johnny C

    Johnny C New Member

    Messages:
    18
    Ah, wow, I hadn't realized that I could do more than one play_note per event, but IIUC you are saying I can just do a play_note for each enabled group? Awesome :)

    Has anyone created a generic script for getting Zone IDs and storing them in an NKA or is it better to always roll your own? (I've seen some snippets here in the forum during searches that should help with that, at the very least). Unless I'm mistaken, I'll need to do that as a first step in order to get the sample lengths anyway.
     
  6. EvilDragon

    EvilDragon Moderator Moderator

    Messages:
    14,969
    The correct way to state it is: play_note() generates a new note event. For each of those created events you can have completely different sets of groups allowed, or just a single one or a few. Entirely up to you.

    If you're using Kontakt 6, the easiest way would be by using Creator Tools. However you'd need to do it in batches of around 1000, since right now that's the limit of number of lines that can be printed in CT instrument editor console, which is the only way you can export text from it (Lua scripts in CT cannot yet write to files unfortunately).
     
  7. Johnny C

    Johnny C New Member

    Messages:
    18
    I see what you mean here, that's definitely the more correct way to say it. I meant "for each event" in terms of how it looks from the 'on note' callback. For some reason I had the expectation that each time the callback runs it should only do a single 'play_note' (ie generate only a single note event per callback "event").

    Thanks for the tips with the knob values, forgot to mention that in my last post. (Also, for any future reader clarity: By "enabled group" I meant each group that is enabled via my instrument UI. Disallow all groups and then loop over each enabled group and play_note for it with the custom start/stop values, one by one.)
     
  8. EvilDragon

    EvilDragon Moderator Moderator

    Messages:
    14,969
    No need for such a hefty solution. Rather do a single play_note() and then iterate over set_event_par_arr() to allow groups (before the loop use set_event_par_arr() to disallow all groups, by doing set_event_par_arr($event_ID, $EVENT_PAR_ALLOW_GROUPS, 0, $ALL_GROUPS)).
     
  9. Johnny C

    Johnny C New Member

    Messages:
    18
    That would work if all the groups to be played will share the same sample start and duration values, but I thought we were discussing the edge case of wanting different start/duration values for each group that is assigned to play on a given note.

    Also, shouldn't the play_note() in your example come after the loop allowing groups via set_event_par_arr()? That's how I've currently got it set up and working, at least.
     
  10. EvilDragon

    EvilDragon Moderator Moderator

    Messages:
    14,969
    No, when you use set_event_par_arr(), you need the event ID, and you get that event ID by storing play_note() in a variable. So you gotta dis)allow groups after playing the event with this method.
     
  11. Johnny C

    Johnny C New Member

    Messages:
    18
    Ah, ok so that's clear, but I guess I'm still fuzzy on how this would allow different sample start/duration values per group.

    Here's some psuedo-code in SublimeKSP style from what I interpreted in your first reply.

    Code:
    // TOTAL_GROUPS : 2 (6 per note in my actual instrument but let's go easy)
    // groups[2] : 2 groups exist on a single note (only dealing with one note)
    // sampleStart[2] : 2 sample start values (MS value from start based on knobs but omitting the calculations here)
    // sampleEnd[2] : 2 sample end values (MS value from end, same caveat as above)
    // sampleLength[2] : 2 sample lengths
    // thisEvent : storage for the relevant event ID
    on note
      ignore_event(EVENT_ID)
      for idx := 0 to TOTAL_GROUPS-1
        thisEvent := play_note(EVENT_NOTE, EVENT_VELOCITY, sampleStart[idx], sampleLength[idx] - sampleStart[idx] - sampleEnd[idx])
        set_event_par_arr(thisEvent, EVENT_PAR_ALLOW_GROUP, 0, ALL_GROUPS)
        set_event_par_arr(thisEvent, EVENT_PAR_ALLOW_GROUP, 1, groups[idx])
      end for
    end on
    
    Otherwise I don't see how per-group values for sample start and duration can be provided to a single play_note() call. This would mean that all groups played on the EVENT_NOTE key would have the same sample start and duration values. That's an option, certainly, but again I thought we were talking about the edge case for having different sample start/duration settings for each group on each note played.
     
  12. EvilDragon

    EvilDragon Moderator Moderator

    Messages:
    14,969
    Yeah that'd work. I thought you had something like 100 groups, sweeping through all of them would be madness :D
     
  13. Johnny C

    Johnny C New Member

    Messages:
    18
    Yes indeed that would be :D

    Thanks for your help, especially breaking me of the notion that only one play_note can happen per 'on note' callback!
     
  14. EvilDragon

    EvilDragon Moderator Moderator

    Messages:
    14,969
    Actually no, you can have multiple play_note() commands in note callback. I think you may have misunderstood something I've said above.

    Your for loop is basically creating multiple play_note()... But there's nothing that forbids you from doing:

    Code:
    on note
        play_note($EVENT_NOTE+4, $EVENT_VELOCITY, 0, -1)
        play_note($EVENT_NOTE+7, $EVENT_VELOCITY, 0, -1)
        play_note($EVENT_NOTE+12, $EVENT_VELOCITY, 0, -1)
    end on
     
  15. Johnny C

    Johnny C New Member

    Messages:
    18
    Oh no, I understand you! I was just under the false impression that the above wasn't allowed, for some reason (probably because I never tried/only had experience with using the default $EVENT_ID so far). You've cleared me up on that point and I'm very grateful for that.

    By the way, are these separate play_note() events guaranteed to be played in sync? I assume they all begin together after the callback has finalized?
     
  16. EvilDragon

    EvilDragon Moderator Moderator

    Messages:
    14,969
    You can test with $KSP_TIMER how much difference there is between every play_note() event. It's in very low microseconds, so definitely close enough to be considered "in sync".
     
  17. Johnny C

    Johnny C New Member

    Messages:
    18
    I've got this all up and running now :D
    Thanks again for the help. Just a heads up to anyone who might land here in the future: the duration value might not have the effect you are expecting unless you disable the default envelope -> volume modulation in your groups.
     
  18. EvilDragon

    EvilDragon Moderator Moderator

    Messages:
    14,969
    You don't need to disable the envelope. Just reduce the envelope release time to something like 1-5 ms. It's good to have the envelope there to round off the starts and ends of the sample so they don't create potential clicks on note on/off.
     
  19. Johnny C

    Johnny C New Member

    Messages:
    18
    Great to know! I had it set to AHD and set it up to perform the entire sample as a one shot prior to starting scripting, so those settings seem to (understandably) disrupt the duration sent to play_note(). I have disabled AHD and set the release to a small ms value and its working wonderfully.