HISE Logo Forum
    • Categories
    • Register
    • Login

    Fun with Regex

    Scheduled Pinned Locked Moved Blog Entries
    13 Posts 3 Posters 2.4k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Christoph HartC
      Christoph Hart
      last edited by Christoph Hart

      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.selectSamplerSounds(regexWildCard);
      Sampler.getNumSelectedSamplerSounds();
      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:

      Sample_3_50_1_127.wav
      

      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:

      ^.*_2_.*
      

      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:

      ^.*_.*_36_.*
      

      Examples

      Automapper

      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
          Sampler.selectSounds(name);
      
           // 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
              Sampler.selectSounds(RRNames[i]);
              
              if(i < amount)
              {
                  // Unpurge the samples (22 = 'SampleState', 0 = Normal)
                  Sampler.setSoundPropertyForSelection(22, 0);
              }
              else
              {
                  // Purge the samples (22 = 'SampleState', 1 = Purged)
                  Sampler.setSoundPropertyForSelection(22, 1);
              }
          }
      }
      
      1 Reply Last reply Reply Quote 1
      • d.healeyD
        d.healey
        last edited by

        Powerful stuff!

        Libre Wave - Freedom respecting instruments and effects
        My Patreon - HISE tutorials
        YouTube Channel - Public HISE tutorials

        1 Reply Last reply Reply Quote 0
        • d.healeyD
          d.healey
          last edited by

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

          Libre Wave - Freedom respecting instruments and effects
          My Patreon - HISE tutorials
          YouTube Channel - Public HISE tutorials

          1 Reply Last reply Reply Quote 0
          • Christoph HartC
            Christoph Hart
            last edited by

            What about Sampler.Root ?

            d.healeyD 1 Reply Last reply Reply Quote 0
            • d.healeyD
              d.healey @Christoph Hart
              last edited by

              @Christoph-Hart Perfect, thank you.

              Libre Wave - Freedom respecting instruments and effects
              My Patreon - HISE tutorials
              YouTube Channel - Public HISE tutorials

              1 Reply Last reply Reply Quote 0
              • d.healeyD
                d.healey
                last edited by

                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?

                Libre Wave - Freedom respecting instruments and effects
                My Patreon - HISE tutorials
                YouTube Channel - Public HISE tutorials

                1 Reply Last reply Reply Quote 0
                • d.healeyD
                  d.healey
                  last edited by

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

                  Libre Wave - Freedom respecting instruments and effects
                  My Patreon - HISE tutorials
                  YouTube Channel - Public HISE tutorials

                  1 Reply Last reply Reply Quote 0
                  • Christoph HartC
                    Christoph Hart
                    last edited by

                    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).

                    d.healeyD 1 Reply Last reply Reply Quote 0
                    • d.healeyD
                      d.healey @Christoph Hart
                      last edited by

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

                      Libre Wave - Freedom respecting instruments and effects
                      My Patreon - HISE tutorials
                      YouTube Channel - Public HISE tutorials

                      d.healeyD 1 Reply Last reply Reply Quote 0
                      • d.healeyD
                        d.healey @d.healey
                        last edited by

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

                        const selection = Sampler1.createSelection(".*");
                        
                        for (s in selection)
                            Console.print(s.get(Sampler.SampleState));
                        

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

                        Libre Wave - Freedom respecting instruments and effects
                        My Patreon - HISE tutorials
                        YouTube Channel - Public HISE tutorials

                        1 Reply Last reply Reply Quote 0
                        • ?
                          A Former User
                          last edited by

                          Is the point of this Regex stuff to allow end users to modify something like the velocity mapping of a sampler in a compiled plugin?

                          1 Reply Last reply Reply Quote 0
                          • Christoph HartC
                            Christoph Hart
                            last edited by

                            You could do that, but it will be super complex to implement a data model that stores this information as the sample maps will be restored to their original state.

                            The prime use case for this is to automate tasks while sample editing, but you can also do simple stuff in the end user plugin.

                            ? 1 Reply Last reply Reply Quote 0
                            • ?
                              A Former User @Christoph Hart
                              last edited by

                              @Christoph-Hart Yeh sweet all good, sample start modulator and the note functions can still do what I need :) thanks for clarifying

                              1 Reply Last reply Reply Quote 0
                              • HISEnbergH HISEnberg referenced this topic on
                              • First post
                                Last post

                              60

                              Online

                              1.7k

                              Users

                              11.7k

                              Topics

                              102.1k

                              Posts