Voices within presets

AlexM posted Sep 27, 12:39:

Hey guys,

Wondering if anyone can help me with this idea.

So sometimes in a live set I'll have a song that has a 2 or more voices, eg verses=synth, choruses=mellotron, etc. Currently I use buttons on my MIDI controller to switch between instruments in combinator devices in Reason (for those familiar with the program). As you know with SamplerBox it can take a few seconds when switching between presets, which is fine in between songs but definitely not during.

How would I go about using a new keyword variable (let's say %voice) to the definition.txt to achieve this? In python I would imagine somehow disabling and enabling sample sets depending on which voice button is toggled, but I'm having trouble figuring out where to start...

HansEhv posted Sep 27, 14:06:

You can also use another approach as midi offers 128 notes, which is 10 octaves. If you give the samples alternative midi note numbers, it is possible to have 2 to 4 voices in 1 sample. By using the octave buttons on the keyboard you can map it quite fast on the right range=voice.

AlexM posted Sep 27, 16:19:

Ah ok good idea. What if the voice's midinote values are shifted instead?

-When voice is enabled, range is the usual 1-128
-Then disable all other voices by pushing their midinote ranges way out of the way 1000-1128

I know that simply assigning all required samples notes across 1-128 is easier to achieve programmatically, but I'm also thinking about easing the headaches caused when making sample sets ;)

HansEhv posted Sep 27, 23:51:

That's the royal way indeed, but it will take some programming :-)
It will also set SB's requirements to PI3 because of the memory usage.

AlexM posted Sep 28, 08:35:

That's in the case that the combined size of the samples might be massive, yeah? In my case, sample sets (or a voice) are rarely larger than 100mb so I wouldn't be worrying about that anyway - but it's definitely something worth bearing in mind

AlexM posted Sep 28, 17:59:

This ended up being as easy as I initially thought - although it still took me half a day to find this solution haha!

Added a new %voice variable to definition.txt. For example,

I've condensed the code to mostly just my additions/modifications. The main thing was adding voice to the samples dictionary (dictionary, right?), and modifying the current_voice variable with MIDI buttons

defaultparams = {'midinote': '0', 'velocity': '127', 'notename': '', 'voice': '1'}
.replace(r"\%voice", r"(?P<voice>\d+)")\
voice = int(info.get('voice', defaultparams['voice']))
samples[midinote, velocity, voice] = Sound(os.path.join(dirname, fname), midinote, velocity)
initial_keys = set(samples.keys())
voices = list(set(voices)) # Remove duplicates by converting to a set
for voice in xrange(len(voices)):
    for midinote in xrange(128):
        lastvelocity = None
        for velocity in xrange(128):
            if (midinote, velocity, voice) not in initial_keys:

Some MIDI buttons will change the current_voice variable which determines which samples are active

    playingnotes.setdefault(midinote, []).append(samples[midinote, SelectVelocity, current_voice].play(midinote, velocity))

Sustain pedal also works as intended: samples will keep sustaining even after a voice change.

I also discovered that this opens the door to layering samples which for me could be super useful, but I can also think of a handful of potential issues off the top of my head

HansEhv posted Sep 28, 18:41:

That's clever! Really neat solution,thanks for sharing.

HansEhv posted Oct 2, 17:43:

Some minor things I had to do to get it working:

  • "for voice in xrange(len(voices)):" changed to "for voice in voices:"
  • all occurrences of samples[] will have to get the extra parameter (obvious of course, but pretty hard to debug if you forget one :-))
  • current voice is a global variable

  (not published)
  I want to post as guest