Anatomy of a Reaktor Project, Part III
In this third part, I will continue on with our MIDI implementation. In future tutorials, I will tackle other subjects such as an event bus, specialized knobs that can hold up to 128 values at once, and more. I have written at length that a project must be properly conceptualized from the ground up. It is the purpose of these tutorials to show this idea in action.
FINDING A VOICE
Towards the end of last week’s tutorial, I introduced a simple macro, named Timer, that took as it’s inputs the eventual Gate output of MIDI handler. To test Timer, I simply used a polyphonic Gate module to feed it’s inputs (Our handlers will function nearly identically to the Built-in Note Pitch and Gate modules, only we will be able to pick and choose which MIDI notes are actually sent to each handler).
The output of Timer is a polyphonic signal which gives the number of display clocks that have occurred since the last note on each voice. This is a simple way to create a time count that uses basically no CPU (remember that we are going to use around 8 of these MIDI handlers, so it’s important that we not waste CPU needlessly).
The next step I’d like to implement is that we need to identify the voice with the largest value, and any new incoming Note/Gate information will be added to that voice. To that end, I created a new macro, named Max Voice. Max Voice receives the output of Timer, and any new Gate values greater than zero as inputs.
Note that while the Event Voice Combiner Max module will give us the greatest value in a signal we actually don’t what the actual value is, just which voice holds the greatest value, something Event V.C. Max module doesn’t give us. I toyed with a few methods of using that module but none were satisfactory, however it is certainly possible I missed something.
In any event, I chose to try some other ways to find the voice with the largest value. To begin, we’ll need an iterator to let us read the Timer value of each voice individually and determine which is the greatest:
This Iteration gives us an output that runs from 1 to the number of voices in the ensemble. Using the outputs of the Iteration, we can create the rest of the macro:
Here, the Separator is fed by the G output of the Iteration, and the Order by ‘Out’. I’ve done my best to re-arrange things to make it clear what flows where.
The order of events is important to keep in mind here. The first thing that happens on a new event arriving at the Iteration module is that the G output gets set to one. We take this opportunity to set the Value module that is fed by the merge to be equal to zero. This Value will hold the largest value in the ‘Sig’ Input (fed by the output from Timer), so we initialize it to zero so we get an accurate reading.
Next, the ‘Out’ terminal of the Iteration sends out values from 1 to 4 (or more, depending on the instrument properties). Using the Order module, we store the voice number, then read out that voice from the Sig input. That number is then compared to our Value module storing the largest voice. If it’s larger than the stored value, both the voice number and the length are stored.
Finally, when the Iteration finishes, we use the off value from the G output to read out the voice number we stored previously.
CONNECTING IT ALL TOGETHER
Okay, the process I have been explaining so far has dealt exclusively with adding new voices to the voice handler. In this section, I’ll show how we can wire these disparate macros together, and how to handle removing voices (IE when a note is released).
Unfortunately, while the process of doing this is not so complicated, the code that it creates ends up looking a little bit ugly. Those of you who are regular readers know that I put code readability towards the top of my priorities when working on complicated structures like this. In this case, there is not much to be done. I’ve taken a few pictures with different modules selected so you can see the wiring as clearly as possible.
Both of the modules that I selected flow back into modules to their left, a practice I like to avoid that was necessary in this instance. Event loops are avoided by making sure that both modules are stored in Value modules until the next event arrives.
Remembering that Pitch inputs arrive before Gates, we can follow the order here. The Separator module filters out whether incoming Gates are on or off. On a new Gate, we trigger the Max Voice macro, and use it to funnel incoming Mono signals to their placement in the polyphonic stream. The Gate only controls on values, so it must be merged with another stream to create Gate off events. Finally, these values are used to feed the Timer macro.
The lower half of the structure turns off Gate values, when a Gate off arrives for a value that is currently set to on.
Okay, that wraps up the aspect of this series that deals with handling MIDI. In later tutorials, I’ll show how we’ll use these structures in conjunction with an event bus to control our drum machine, giving us a multi-timbral structure with up to 128 sounds.
Have a Question or Comment About This Tutorial?
Want to ask a question about this tutorial or perhaps you have something to add?
Click through to our forum post about this tutorial and join the conversation!