Analog Modular Control of BFD or EZDrummer with the Alesis Trigger IO
I had a lot of fun building out the [Probabilistic Trigger Sequencer](nw2s.net/probabilistic-triggers/). It's a great compositional tool when you need some spacey, almost predictable beats, and it got me thinking about how I could use the same analog control for something a little more dynamic. I am a BFD user, but there are quite a few other equivalent MIDI-based drum systems that work the same way, and are surprisingly realistic when used/programmed properly.
In theory, something like the Alesis Trigger IO should be a perfect trigger to MIDI converter. It is velocity sensitive, so as long as the nw2s::b CV outputs can operate in a range that will give sufficient velocity resolution in the Trigger IO, then we'll have some interesting possibilities. The first step was to test this. Luckily, with the Alesis gain value set to the default value of 15, we are able to get a solid, linear response as shown in the following chart:
So this means that we'll get a full range of drum hit velocities with CV output values from 50 to 850. Now all we need to do is build a device that will generate similarly stochastic patterns to the Probabilistic Trigger Sequencer, but instead of just yes or no, now we can also randomly vary the velocity.
What would be nice is an efficient algorithm for a normal distribution for the velocity to give a slightly more natural feel. I'll be on the lookout.
Obviously, there's a lot more that you could do with this beyond some wonky rock kit, given some orchestral or even theatrical samples, it could be a great tool to bridge the analog back into the digital in true hybrid form.
In the mean time, here's what the sketch looks like.
First, we set up the probabilities just like we do for the probabilistic triggers:
int snaretriggers[16] = { 0, 0, 1, 1, 75, 1, 1, 0, 0, 1, 20, 1, 75, 1, 1, 15 };
int snarevelocities[16] = { 0, 0, 75, 75, 400, 75, 75, 0, 0, 75, 200, 75, 400, 1, 1, 75 };
int kicklist[16] = { 90, 1, 1, 5, 0, 1, 10, 1, 75, 1, 1, 1, 0, 1, 15, 1 };
int kickvelocities[16] = { 500, 75, 75, 75, 0, 75, 200, 75, 400, 75, 75, 75, 0, 75, 150, 75 };
int hatlist[16] = { 85, 25, 85, 25, 85, 25, 85, 50, 25, 85, 25, 85, 25, 85, 25, 85 };
int hatvelocities[16] = { 100, 250, 100, 250, 100, 250, 100, 200, 250, 100, 250, 100, 250, 100, 200, 100 };
int crashlist[16] = { 10 };
int crashvelocities[16] = { 400 };
As you can see, for each trigger probability, we have a corresponding velocity value. We follow that up with our standard memory allocation - we just have more of them now:
TriggerSequenceData* snaretriggerdata = new TriggerSequenceData(snaretriggers, snaretriggers + 16);
TriggerSequenceData* snarevelocitydata = new TriggerSequenceData(snarevelocities, snarevelocities + 16);
TriggerSequenceData* kicktriggerdata = new TriggerSequenceData(kicklist, kicklist + 16);
TriggerSequenceData* kickvelocitydata = new TriggerSequenceData(kickvelocities, kickvelocities + 16);
TriggerSequenceData* hattriggerdata = new TriggerSequenceData(hatlist, hatlist + 16);
TriggerSequenceData* hatvelocitydata = new TriggerSequenceData(hatvelocities, hatvelocities + 16);
TriggerSequenceData* crashtriggerdata = new TriggerSequenceData(crashlist, crashlist + 1);
TriggerSequenceData* crashvelocitydata = new TriggerSequenceData(crashvelocities, crashvelocities + 1);
Then we create the actual sequencers:
ProbabilityDrumTriggerSequencer* snaresequencer = ProbabilityDrumTriggerSequencer::create(snaretriggerdata, snarevelocitydata, 25, DIV_SIXTEENTH, DUE_SPI_4822_00);
ProbabilityDrumTriggerSequencer* kicksequencer = ProbabilityDrumTriggerSequencer::create(kicktriggerdata, kickvelocitydata, 10, DIV_SIXTEENTH, DUE_SPI_4822_05);
ProbabilityDrumTriggerSequencer* hatsequencer = ProbabilityDrumTriggerSequencer::create(hattriggerdata, hatvelocitydata, 50, DIV_SIXTEENTH, DUE_SPI_4822_02);
ProbabilityDrumTriggerSequencer* crashsequencer = ProbabilityDrumTriggerSequencer::create(crashtriggerdata, crashvelocitydata, 100, DIV_WHOLE, DUE_SPI_4822_03);
Note that the sequencers have a new parameter in addition to providing two sets of sequence data (probabilities and velocities), another integer is provided. This integer is used as a range by which the velocity (or rather the amplitude of the trigger hit) will vary for any given trigger event.
We also wire up our probability modifier analog inputs. Note that these are completely optional, but way more fun.
snaresequencer->setProbabilityModifier(DUE_IN_A01);
kicksequencer->setProbabilityModifier(DUE_IN_A02);
hatsequencer->setProbabilityModifier(DUE_IN_A03);
crashsequencer->setProbabilityModifier(DUE_IN_A04);
Then register the devices with their respective master devices and away we go.
vclock->registerDevice(snaresequencer);
vclock->registerDevice(kicksequencer);
vclock->registerDevice(hatsequencer);
vclock->registerDevice(crashsequencer);
EventManager::registerDevice(vclock);
[The full sketch can be downloaded from github.](https://github.com/nw2s/b/blob/master/sketches/nw2s/bDemoDrumTrigger/bDemoDrumTrigger.ino)