AlexM posted Sep 27 '16, 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 '16, 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, 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 '16, 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 '16, 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 '16, 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,
piano_note%midinote_voicenumber%voice.wav
organ_note%midinote_voicenumber%voice.wav
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
# ACTUALLY LOAD
...
defaultparams = {'midinote': '0', 'velocity': '127', 'notename': '', 'voice': '1'}
...
.replace(r"\%voice", r"(?P<voice>\d+)")\
...
voice = int(info.get('voice', defaultparams['voice']))
voices.append(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
# MIDI CALLBACK
...
try:
playingnotes.setdefault(midinote, []).append(samples[midinote, SelectVelocity, current_voice].play(midinote, velocity))
except:
pass
...
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 '16, 18:41:
That's clever! Really neat solution,thanks for sharing.
HansEhv posted Oct 2 '16, 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