Fun with Regex

  • This is an advanced feature that I just implemented (and you need at least version 0.984 for this). You can now change the properties of individual sample sounds with API calls. This allows many interesting things:

    • Write your own Automapper (if the filename parser is not enough)
    • Dynamically purge individual samples
    • Change the microtuning with a script

    How it works

    It uses the filename and a regex parser to create a selection of sounds that can be changed by using a property index and the new value.
    In order for this to work, you need consistent filenames - but I strongly suggest you do this anyway.

    There are three new API calls:

    Sampler.setSampleSoundsProperty(propertyIndex, propertyValue);

    The procedure to change the sounds would be like this:

    1. Select all sounds that you want to change with Sampler.selectSamplerSounds(regexWildCard);. You can use the same regex expressions that can be entered in the Sampler table editor. So you can check which expression does the job by entering the regex in the table search field and copy the right one into the parameter slot of this function. It will create and internal selection (and this selection will be independant from the user interface selection to prevent cross interference).
    2. Check if the selection worked by checking the number with Sampler.getNumSelectedSamplerSounds();
    3. Change the sound with Sampler.setSampleSoundsProperty(propertyIndex, propertyValue);

    Property IDS

    The propertys are accessed by an index which can be looked up here:

    Name Index Description
    KeyHigh 4 the highest mapped key
    KeyLow 5 the lowest mapped key
    VeloLow 6 the lowest mapped velocity
    VeloHigh 7 the highest mapped velocity
    RRGroup 8 the group index for round robin / random group start behaviour
    Volume 9 the gain in decibels.
    Pan 10 The stereo balance (-100 = left, 100 = right
    Normalized 11 enables / disables Autogain to 0dB for all samples.
    Pitch 12 the pitch factor in cents (+- 100 . This is for fine tuning, for anything else, use RootNote.
    SampleStart 13 the start of the sample.
    SampleEnd 14 the end sample
    SampleStartMod 15 The amount of samples that the sample start can be modulated.
    LoopStart 16 The loop start in samples. This is independent from the sample start / end (so 0 is the SampleStart value and not the beginning of the file , but it checks the bounds.
    LoopEnd 17 The loop end in samples. This is independent from the sample start / end, but it checks the bounds.
    LoopXFade 18 the loop crossfade at the end of the loop (using a recalculated buffer) LoopEnabled 19 true if the sample should be looped.
    LowerVelocityXFade 20 the length of the lower velocity crossfade (0 if there is no crossfade) .
    UpperVelocityXFade 21 the length of the upper velocity crossfade (0 if there is no crossfade) .
    SampleState 22 This property allows to set the state of samples between Normal(0) and Purged (1)

    Regex tricks

    That's it. Now there is only one catch: regex is ugly and a beast. If you try to read a regex tutorial you will give up soon, I promise.
    Fortunately we don't have to master our regex skills for this task, so here are some useful tricks:

    • Use the regex parser as a normal wildcard search engine until it does not do what you want.
    • Select all with .
    • if you want to subtract something from the selection, use the prefix sub: (this is not official regex, but I added it for simpler handling)
    • if you want to add something to the selection but keep the other sounds selected, use the prefix add:
    • the end of the filename can be checked with $, the start of the filename can be checked with ^.

    If you want to check one specific token only (tokens are supposed to be parts of the filename that are divided by the separation character (eg. _), you can use this expression:

    ^.*_.*[Interesting part].*_.*
    1  2                      3

    Whatever you enter into [Interesting part] will be checked only against the second token (if _ is our separation character of course). If you have a (rather bad) filename structure like this:


    where there are only numbers, you can use this trick to still get the samples you want. Let's say the first number is the round robin group and we want to change the volume of the second round robin group by -6db. Just using 2 would cause all kinds of mayhem. Using _2_ would be a little bit better, but there is no guarantee that the other tokens are not this value. The solution for this case would be:


    There are two regex elements used in this expression: the beginning of the filename character ^ and the combination .* which simply means "an arbitrary amount of any character". Translated into english this expression would be "Any amount of characters after the beginning of the file up to the first underscore, then exactly 2, then another underscore followed by anything."

    The next thing we want to do is to change the pitch of the lowest C note (let's say 36) by 20 cent. The note number is the second number token, so we use this regex:




    Well this can get complicated pretty quickly, but lets consider one pretty simple use case.

    You have some samples with the root note saved into the filename as note name (eg. "D#3"). This snippet sets the root note, the lower and the upper key to the correct value for every sample:

    for(var i = 0; i < 127; i++)
        // API call to get the midi note name in the form "C#4"
        var name = Engine.getMidiNoteName(i);
        // Select all samples with this notename
         // save some effort if no sample is found...
        if(Sampler.getNumSelectedSounds() != 0)
            Sampler.setSoundPropertyForSelection(3, i); // Root note
            Sampler.setSoundPropertyForSelection(4, i); // Lower key
            Sampler.setSoundPropertyForSelection(5, i); // Upper key

    Dynamically purge individual samples

    Sometimes having too much round robin samples is just a waste of memory.

    A neat way of handling the round robin amount is to simply purge all unneeded groups. This function disables all groups above the specified group number. The round robin token is supposed to be "RR1" to "RRx" in this case.

    // The tokens used to identify all round robin groups...
    var RRNames = ["RR1", "RR2", "RR3", "RR4", "RR5", "RR6", "RR7"];
    function useRoundRobinGroups(amount)
        for(var i = 0; i < RRNames.length; i++)
            // Select the RR group
            if(i < amount)
                // Unpurge the samples (22 = 'SampleState', 0 = Normal)
                Sampler.setSoundPropertyForSelection(22, 0);
                // Purge the samples (22 = 'SampleState', 1 = Purged)
                Sampler.setSoundPropertyForSelection(22, 1);

  • Powerful stuff!

  • The sound property indexes no longer seem to be correct, is there an enum we can use?

  • What about Sampler.Root ?

  • @Christoph-Hart Perfect, thank you.

  • I've noticed a discrepency between getSoundProperty and setSoundProperty that has been confusing me for the last hour or so.

    The sound index passed setSoundProperty relates to the sample selection, while the sound index passed to getSoundProperty relates to the entire sample map. I think it would make more sense if both related to the sound selection. I can implement this and make a pull request or do you think it would be better as a separate function?

  • I've made a pull request, it's a tiny change.

  • Actually I have redesigned that entire system to use a more "modern" approach last year. You can create an array of sample objects and then just iterate over them to avoid all this index mess:

    for(sample in Sampler.createSelection(".*"))
        sample.set(Sampler.RootKey, sample.get(Sampler.RootKey);

    There are a bunch of calls that create these arrays (for example you can use Sampler.createListFromGUISelection() in order to create custom workflows and macro operations).

  • @Christoph-Hart Ooo I shall try this out, thanks!

  • @d-healey Sampler.SampleState always seems to return 0.

    const selection = Sampler1.createSelection(".*");
    for (s in selection)

    I'm thinking we can use this for a purge all until played type of functionality.

Log in to reply