Equation Composer

Documentation and Examples

Creating Modules with Multiple Outputs

Modules that have more than one output must contain ModuleOutputs. In essense, you use modules within modules. Although that might sound scary, it's extremely easy to do.

ModuleDrumSequencer is a good example of a module with multiple outputs.

Step #1: Include ModuleOutput.h and declare the outputs in the .h file:

// =====================================================
// This is ModuleDrumSequencer.h
// =====================================================

#ifndef ModuleDrumSequencer_h
#define ModuleDrumSequencer_h

#include "Arduino.h"
#include "Module.h"
#include "ModuleOutput.h"

class ModuleDrumSequencer : public Module
{
  
  public:
    ModuleDrumSequencer();
    uint16_t compute();
    
    // Inputs
    Module *clock_input;
    Module *kick_pattern_input;
    Module *snare_pattern_input;
    Module *hihat_pattern_input;
    
    // Outputs
    ModuleOutput *kick_output;
    ModuleOutput *snare_output;
    ModuleOutput *hihat_output;    

  private:
    int bank; 
    int step;
    int patterns[3][8];  // 3 banks, 8 patterns per bank
    uint32_t old_clock;
    boolean clocked;
};

#endif

Step #2: Instantiate the outputs in the constructor

If you forget to instantiate the outputs, the Equation Composer will crash when your module's compute() method is called.


// =====================================================
// This is ModuleDrumSequencer.cpp
// =====================================================

ModuleDrumSequencer::ModuleDrumSequencer()
{
  this->clocked = false;
  this->bank = bank;
  this->step = 0;
  
  // this->patterns = { ... Removed for brevity

  // Initialize all inputs
  this->clock_input = NULL;
  this->kick_pattern_input = NULL;  
  this->snare_pattern_input = NULL;  
  this->hihat_pattern_input = NULL;  

  // Instantiate all outputs
  kick_output = new ModuleOutput(this);
  snare_output = new ModuleOutput(this);
  hihat_output  = new ModuleOutput(this);
}

Step #3: Set the output values in the compute() method

		
uint16_t ModuleDrumSequencer::compute()
{
  uint32_t clock = this->readInput(clock_input);

  if((clock < MID_CV) && clocked)
  {
    clocked = false;
    kick_output->value = 0;
    snare_output->value = 0;
    hihat_output->value = 0;
  }

  if((clock >= MID_CV) && !clocked) 
  {
    clocked = true;
    
    uint32_t kick_pattern = this->readInput(kick_pattern_input, CONVERT_TO_3_BIT);
    uint32_t snare_pattern = this->readInput(snare_pattern_input, CONVERT_TO_3_BIT);
    uint32_t hihat_pattern = this->readInput(hihat_pattern_input, CONVERT_TO_3_BIT);
    
    kick_output->value = bitRead(patterns[0][kick_pattern], step) * MAX_CV;
    snare_output->value = bitRead(patterns[0][snare_pattern], step) * MAX_CV;
    hihat_output->value = bitRead(patterns[0][hihat_pattern], step) * MAX_CV;
    
    step++;
    if(step == 16) step = 0;
  }

  return(kick_output->value);
}

Step #4: Using the module in a synth

#include "defines.h"
#include "SynthDrumPlayer.h"

SynthDrumPlayer::SynthDrumPlayer(Inputs* inputs)
{
  // Uses 3 different drum modules

  ModuleDrumSequencer *drum_sequencer = new ModuleDrumSequencer();

  ModuleEqDrum *kick = new ModuleEqDrum();
  ModuleEqDrum *snare = new ModuleEqDrum();
  ModuleEqDrum *hihat = new ModuleEqDrum();
  ModuleMixer3 *mixer = new ModuleMixer3();

  drum_sequencer->clock_input = inputs->gate;

  drum_sequencer->kick_pattern_input = inputs->param1;
  drum_sequencer->snare_pattern_input = inputs->param2;
  drum_sequencer->hihat_pattern_input = inputs->param3;

  kick->trigger_input = drum_sequencer->kick_output;
  kick->sample_rate_input = inputs->sr;
  kick->drum_selection_input = new ModuleConstant(0);

  snare->trigger_input = drum_sequencer->snare_output;
  snare->sample_rate_input = inputs->sr;
  snare->drum_selection_input = new ModuleConstant(3);

  hihat->trigger_input = drum_sequencer->hihat_output;;
  hihat->sample_rate_input = inputs->sr;
  hihat->drum_selection_input = new ModuleConstant(7);

  mixer->input_1 = kick;
  mixer->input_2 = snare;
  mixer->input_3 = hihat;


  this->last_module = mixer;
}

Notice that ModuleDrumSequencer still returns a value at the end:

		

  return(kick_output->value);
}

All modules must return a value, and for convention sake, it's good if a module returns something useful.