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.selectSamplerSounds(regexWildCard); Sampler.getNumSelectedSamplerSounds(); Sampler.setSampleSoundsProperty(propertyIndex, propertyValue);
The procedure to change the sounds would be like this:
- 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). - Check if the selection worked by checking the number with
Sampler.getNumSelectedSamplerSounds();
- 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 exactly2
, 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); } } }
-
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
andsetSoundProperty
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 togetSoundProperty
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) Console.print(s.get(Sampler.SampleState));
I'm thinking we can use this for a purge all until played type of functionality.
-
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?
-
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.
-
@Christoph-Hart Yeh sweet all good, sample start modulator and the note functions can still do what I need :) thanks for clarifying
-