Program Listing for File dreammakerfx.h

Return to documentation for file (src/dreammakerfx.h)

// Copyright (c) 2020 Run Jump Labs LLC.  All right reserved.
// This code is licensed under MIT license (see license.txt for details)
#ifndef DM_FX_H
#define DM_FX_H

#include "Arduino.h"
#include <SPI.h>
#include <Wire.h>
#include <math.h>

#include "dm_fx_dsp.h"
#include "dm_fx_spi_proto.h"
#include "dm_fx_codec.h"
#include "dm_fx_ui.h"
#include "dm_fx_debug.h"
#include "dm_fx_platform_constants.h"
#include "dm_fx_semitones.h"

#include "effects/dm_fx_effects_defines.h"


#define API_VERSION         10602


class fx_effect;
class fx_pedal;

#ifndef DOXYGEN_SHOULD_SKIP_THIS

typedef enum {
  FOOTSWITCH_NONE,
  FOOTSWITCH_RIGHT,
  FOOTSWITCH_LEFT,
  FOOTSWITCH_BOTH,
} FOOTSWITCH;

typedef enum {
  SPI_RX_WAITING,
  SPI_RX_HEADER_1_RX,
  SPI_RX_RECEIVING,
  SPI_RX_FRAME_READY
} SPI_RX_STATE;

typedef enum {
  NODE_IN,
  NODE_OUT
} NODE_DIRECTION;

typedef struct {
  uint8_t id;
  EFFECT_TYPE type;
  void * address;
} FX_INSTANCE;

typedef struct {
  uint8_t src_id;
  uint8_t src_node_indx;
  uint8_t dest_id;
  uint8_t dest_node_indx;
} AUDIO_ROUTE;

typedef struct {
  uint8_t src_id;
  uint8_t src_node_indx;
  uint8_t src_param_id;
  uint8_t dest_id;
  uint8_t dest_node_indx;
  uint8_t dest_param_id;
  float scale;
  float offset;
  CTRL_NODE_TYPE type;
} CTRL_ROUTE;

#endif




/************************************************************************
 * NODES
 ***********************************************************************/
#ifndef DOXYGEN_SHOULD_SKIP_THIS

class fx_audio_node {

  friend class fx_pedal;

  protected:
    fx_effect      * parent_effect;
    fx_pedal      * parent_canvas;

  public:

    NODE_DIRECTION node_direction;
    bool           connected;
    char           node_name[MAX_NODE_NAME];

    // Audio nodes that are part of the effect
    fx_audio_node(NODE_DIRECTION dir, const char * name, fx_effect * p) {
      node_direction = dir;
      strcpy(node_name, name);
      parent_effect = p;
      parent_canvas = NULL;
      connected = false;
    }

    // Audio nodes that are part of the canvas (i.e. ADCs, DACs)
    fx_audio_node(NODE_DIRECTION dir, const char * name, fx_pedal * p) {
      node_direction = dir;
      strcpy(node_name, name);
      parent_canvas = p;
      parent_effect = NULL;
      connected = false;
    }

};

class fx_control_node {

  friend class fx_pedal;

  protected:
    fx_effect      * parent_effect;
    fx_pedal      * parent_canvas;

  public:
    uint8_t        param_id;
    NODE_DIRECTION node_direction;
    CTRL_NODE_TYPE node_type;
    char           node_name[MAX_NODE_NAME];

    bool           connected;

    // Control nodes that are part of effects
    fx_control_node(NODE_DIRECTION dir, CTRL_NODE_TYPE type, const char * name, fx_effect * p, uint8_t ctrl_param_id) {
      param_id = ctrl_param_id;
      node_direction = dir;
      node_type = type;
      strcpy(node_name, name);
      parent_effect = p;
      parent_canvas = NULL;
      connected = false;
    }

    // Control nodes that are part of the canvas (i.e. POTs, MIDI, etc.)
    fx_control_node(NODE_DIRECTION dir, CTRL_NODE_TYPE type, const char * name, fx_pedal * p, uint8_t ctrl_param_id) {
      param_id = ctrl_param_id;
      node_direction = dir;
      node_type = type;
      strcpy(node_name, name);
      parent_canvas = p;
      parent_effect = NULL;
      connected = false;
    }
};

#endif  // DOXYGEN_SHOULD_SKIP_THIS

#ifndef DOXYGEN_SHOULD_SKIP_THIS
  typedef enum {
    LED_RIGHT = 0,
    LED_CENTER = 1,
    LED_LEFT = 2,
  } LED_POS;
  #define LED_UPDATE_RATE_MS   25
#endif



typedef enum {
  BLACK   = 0x0,
  RED     = 0x800000,
  GREEN   = 0x008000,
  BLUE    = 0x000080,
  YELLOW  = 0x808000,
  PURPLE  = 0x800080
} LED_COLOR;


class fx_led {

  private:
    LED_POS led_pos;
    float  cur_r, cur_g, cur_b;
    float  target_r, target_g, target_b;
    float  inc_r, inc_g, inc_b;
    uint32_t steps;
    uint32_t last_scan;

    void  update_rgb_led(void);

  public:

    #ifndef DOXYGEN_SHOULD_SKIP_THIS
      fx_led(LED_POS pos);
    #endif
    void  turn_on();
    void  turn_on(uint8_t red, uint8_t green, uint8_t blue);
    void  turn_on(LED_COLOR rgb);
    void  turn_off();

    void  set_rgb(uint8_t red, uint8_t green, uint8_t blue);
    void  set_rgb(LED_COLOR rgb);
    void  fade_to_rgb(uint8_t red, uint8_t green, uint8_t blue, uint32_t milliseconds);
    void  fade_to_rgb(LED_COLOR rgb, uint32_t milliseconds);
    void  service();

};




typedef enum {
  SWITCH_POS_UP,
  SWITCH_POS_MIDDLE,
  SWITCH_POS_DOWN,
} SWITCH_POS;

class fx_switch {

  private:
    int up_pin, down_pin;
    SWITCH_POS switch_pos_last;

  public:
    SWITCH_POS  position;
    bool has_changed(void) {
      bool res = (position==switch_pos_last?false:true);
      switch_pos_last = position;
      return res;
    }


  #ifndef DOXYGEN_SHOULD_SKIP_THIS
    void read_switch(void) {

      switch_pos_last = position;

      bool down_pos = digitalRead(down_pin);
      bool up_pos = digitalRead(up_pin);

      if (!up_pos) {
        position = SWITCH_POS_UP;
      } else if (!down_pos) {
        position = SWITCH_POS_DOWN;
      } else {
        position = SWITCH_POS_MIDDLE;
      }

    }

    fx_switch(int up_pin_id, int down_pin_id) {
      up_pin = up_pin_id;
      down_pin = down_pin_id;

      // Set reset pin to output
      pinMode(up_pin, INPUT);
      pinMode(down_pin, INPUT);

    }
  #endif

};

class fx_pot {

  #define POT_LONG_HIST_LEN  (10)
  #define POT_SHORT_HIST_LEN (3)

  private:
    bool  first_read;
    bool  changed;
    float avg_val;
    float val_last;
    int   avg_ptr;
    int   pin_number;
    float coeff;
    float pot_history_long[POT_LONG_HIST_LEN];
    float pot_history_short[POT_SHORT_HIST_LEN];
    int   pot_long_hist_indx, pot_short_hist_indx;
    int   last_poll;

    #pragma GCC optimize ("-O3")
    #pragma GCC push_options

    float pot_mean(float * a, float pts) {
      float mean = 0;
      float r = 1.0 / pts;
      for (int i=0;i<pts;i++) {
        mean += a[i];
      }
      mean *= r;
      return mean;
    }

    float pot_variance(float * a, float pts) {
      float mean = 0;

      float r = 1.0 / pts;
      for (int i=0;i<pts;i++) {
        mean += a[i];
      }
      mean *= r;

      float v = 0.0;
      for (int i=0;i<pts;i++) {
        float x = a[i] - mean;
        v += x*x;
      }
      v *= r;

      return v;
    }

    #pragma GCC pop_options

  public:

    float val;
    float val_inv;
    float val_log;
    float val_log_inv;
    bool has_changed(void) {
      bool return_val = changed;
      changed = false;
      return return_val;
    }

    #ifndef DOXYGEN_SHOULD_SKIP_THIS

    #pragma GCC optimize ("-O3")
    #pragma GCC push_options
    void read_pot() {

      changed = false;

      if (millis() < last_poll + 50) {
        return;
      }
      last_poll = millis();

      int val_int;
      if (pin_number == 0) val_int = analogRead(A0);
      else if (pin_number == 1) val_int = analogRead(A1);
      else if (pin_number == 2) val_int = analogRead(A2);
      else if (pin_number == 3) val_int = analogRead(A3);
      else if (pin_number == 4) val_int = analogRead(A4);
      else if (pin_number == 5) val_int = analogRead(A5);
      else {
        val_int = 0;
      }
      float valf = (1.0/1023.0) * (float) val_int;

      if (first_read) {
        for (int i=0;i<POT_LONG_HIST_LEN;i++) {
          pot_history_long[i] = valf;
        }
        for (int i=0;i<POT_SHORT_HIST_LEN;i++) {
          pot_history_short[i] = valf;
        }
        changed = true;
        first_read = false;
      } else {

        pot_history_long[pot_long_hist_indx++] = valf;
        pot_history_short[pot_short_hist_indx++] = valf;

        if (pot_long_hist_indx >= POT_LONG_HIST_LEN) {
          pot_long_hist_indx = 0;
        }
        if (pot_short_hist_indx >= POT_SHORT_HIST_LEN) {
          pot_short_hist_indx = 0;
        }

        float v_long = pot_variance(pot_history_long, POT_LONG_HIST_LEN);

        if (v_long > 0.0001) {
          changed = true;
          float v_short= pot_variance(pot_history_short, POT_SHORT_HIST_LEN);
          if (v_short < 0.00005) {
            changed = false;
          }
        }
      }
      val = pot_mean(pot_history_short, POT_SHORT_HIST_LEN);

      #if defined (DM_FX_TWO)
        val = 1.0 - val;
      #endif
      val_inv = 1.0-val;
      val_log = log10(1.0 + (val*9.0));
      val_log_inv = 1.0 - log10(1.0 + ((1.0-val)*9.0));
    }
    #pragma GCC pop_options


    fx_pot(int pin) {

      pin_number = pin;
      first_read = true;
      val = 0;
      val_inv = 0;

      for (int i=0;i<POT_LONG_HIST_LEN;i++) {
        pot_history_long[i] = 0;
      }
      for (int i=0;i<POT_SHORT_HIST_LEN;i++) {
        pot_history_short[i] = 0;
      }
      pot_short_hist_indx = pot_long_hist_indx = 0;

      last_poll = millis();

    }

    #endif // DOXYGEN_SHOULD_SKIP_THIS



};


/**********************************************************************
 * CANVAS
 *********************************************************************/

class fx_pedal {

  private:

    // Has the system been initialized
    bool        initialized;
    bool        valid_audio_routes;
    bool        valid_control_routes;
    bool        debug_mode, debug_dsp_telemetry, debug_no_reset;

    uint32_t    last_service_ts;

    uint32_t    tap_history[16];
    uint16_t    tap_indx = 0;
    float       tap_interval_ms;
    bool        tap_locked;
    bool        tap_led_flash;
    uint32_t    tap_led_flash_cntr;
    uint32_t    tap_last_tap;
    bool        tap_new_val;

    bool        footswitch_left_pressed, footswitch_right_pressed;
    bool        footswitch_left_released, footswitch_right_released;
    bool        footswitch_left_last_state, footswitch_right_last_state;

    DSP_STATUS * status;

    char * get_effect_type(EFFECT_TYPE t);

    // All effect instances in canvas
    FX_INSTANCE instance_stack[MAX_INSTANCES];
    int         total_instances;

    // All audio routes between effects in cancas
    AUDIO_ROUTE audio_routing_stack[MAX_ROUTES];
    int         total_audio_routes;

    // All control routes between effects in cancas
    CTRL_ROUTE  control_routing_stack[MAX_ROUTES];
    int         total_control_routes;

    // Does this canvas have a valid topology
    bool        valid_canvas;

    // Canvas audio nodes
    fx_audio_node sys_input_instr_l;
    fx_audio_node sys_input_instr_r;
    fx_audio_node sys_output_amp_l;
    fx_audio_node sys_output_amp_r;
    fx_audio_node sys_input_mic_l;
    fx_audio_node sys_input_mic_r;

    // Canvas control nodes
    fx_control_node sys_current_frequency;
    fx_control_node sys_note_duration_ms;
    fx_control_node sys_new_note;

    // Adds a new route
    bool    add_audio_route_to_stack(uint8_t src_id, uint8_t src_node_indx, uint8_t dest_id, uint8_t dest_node_indx);
    bool    add_control_route_to_stack(uint8_t src_id,
                                            uint8_t src_node_indx,
                                            uint8_t src_param_id,
                                            uint8_t dest_id,
                                            uint8_t dest_node_indx,
                                            uint8_t dest_param_id,
                                            float scale,
                                            float offset,
                                            CTRL_NODE_TYPE type);

    // SPI protocoll support functions
    void    spi_get_status(void);
    void    spi_service(void);
    void    spi_transmit_bypass(uint16_t bypass_state);
    void    spi_transmit_all_params(void);
    void    spi_transmit_params(uint16_t node_index);
    void    spi_transmit_audio_routing_stack(void);
    void    spi_transmit_control_routing_stack(void);
    void    spi_transmit_instance_stack(void);

    // Returns the index in the node index for this effect
    bool    get_audio_node_index(fx_audio_node * node, uint8_t * node_index);
    bool    get_control_node_index(fx_control_node * node, uint8_t * node_index);

    const char * get_name() {
        return "canvas";
    }


  protected:

    fx_audio_node   * audio_node_stack[4];
    fx_control_node * control_node_stack[4];

    // Services any SPI transactions that need to happen




  public:

    #ifndef DOXYGEN_SHOULD_SKIP_THIS
    friend class fx_effect;

    bool        bypass_control_enabled;
    bool        bypassed;
    FOOTSWITCH  bypass_footswitch;

    bool        tap_control_enabled;
    bool        tap_blink_only_enabled;
    FOOTSWITCH  tap_footswitch;
    #endif // DOXYGEN_SHOULD_SKIP_THIS


    #if defined (DM_FX)
      fx_pot  pot_right;
      fx_pot  pot_center;
      fx_pot  pot_left;

      fx_led  led_left;
      fx_led  led_right;

    #elif defined (DM_FX_TWO)
      fx_pot  pot_top_left;
      fx_pot  pot_top_right;
      fx_pot  pot_bot_left;
      fx_pot  pot_bot_center;
      fx_pot  pot_bot_right;
      fx_pot  exp_pedal;

      // Create aliases for backwards compatibility
      #define pot_left    pot_bot_left
      #define pot_center  pot_bot_center
      #define pot_right   pot_bot_right

      fx_switch toggle_left;
      fx_switch toggle_right;

      fx_led  led_left;
      fx_led  led_center;
      fx_led  led_right;

    #endif

    fx_audio_node * instr_in;
    fx_audio_node * instr_in_l;
    fx_audio_node * instr_in_r;
    fx_audio_node * amp_out;
    fx_audio_node * amp_out_l;
    fx_audio_node * amp_out_r;
    fx_audio_node * mic_in_l;
    fx_audio_node * mic_in_r;

    fx_control_node * note_frequency;
    fx_control_node * note_duration;
    fx_control_node * new_note;
    #ifndef DOXYGEN_SHOULD_SKIP_THIS
    fx_pedal():

      #if defined (DM_FX)
        pot_right(0),
        pot_center(1),
        pot_left(2),
        led_left(LED_LEFT),
        led_right(LED_RIGHT),

      #elif defined (DM_FX_TWO)
        pot_bot_left(2),
        pot_bot_center(3),
        pot_bot_right(4),
        pot_top_left(0),
        pot_top_right(1),
        exp_pedal(5),
        toggle_left(8, 9),
        toggle_right(10, 11),
        led_left(LED_LEFT),
        led_center(LED_CENTER),
        led_right(LED_RIGHT),
      #endif

      sys_input_instr_l(NODE_OUT, "instr_in_l", this),
      sys_input_instr_r(NODE_OUT, "instr_in_r", this),
      sys_output_amp_l(NODE_IN, "amp_out_l", this),
      sys_output_amp_r(NODE_IN, "amp_out_r", this),
      sys_input_mic_l(NODE_IN, "mic_in_l", this),
      sys_input_mic_r(NODE_IN, "mic_in_r", this),
      sys_current_frequency(NODE_OUT, NODE_FLOAT, "current note frequency", this, FX_CANVAS_PARAM_ID_NOTE_FREQ),
      sys_note_duration_ms(NODE_OUT, NODE_FLOAT, "current note duration (ms)", this, FX_CANVAS_PARAM_ID_NOTE_DURATION),
      sys_new_note(NODE_OUT, NODE_FLOAT, "New note playing event", this, FX_CANVAS_PARAM_ID_NOTE_NEW_NOTE) {

        // Audio routing nodes
        instr_in = &sys_input_instr_l;    // Alias
        instr_in_l = &sys_input_instr_l;
        instr_in_r = &sys_input_instr_r;
        amp_out = &sys_output_amp_l;      // Alias
        amp_out_l = &sys_output_amp_l;
        amp_out_r = &sys_output_amp_r;
        mic_in_l = &sys_input_mic_l;
        mic_in_r = &sys_input_mic_r;

        // Control node routing
        note_frequency = &sys_current_frequency;
        note_duration = &sys_note_duration_ms;
        new_note = &sys_new_note;

        // Add to node stack
        audio_node_stack[0] = instr_in_l;
        audio_node_stack[1] = amp_out_l;
        audio_node_stack[2] = instr_in_r;
        audio_node_stack[3] = amp_out_r;

        // Todo - populate 0 with canvas enable function
        control_node_stack[1] = &sys_current_frequency;
        control_node_stack[2] = &sys_note_duration_ms;
        control_node_stack[3] = &sys_new_note;
        /*
        audio_node_stack[3] = amp_out_r;
        audio_node_stack[4] = mic_in_l;
        audio_node_stack[5] = mic_in_r;
        */

        // Add canvas as instance 0
        total_instances = 1;
        instance_stack[0].id = 0;
        instance_stack[0].type = FX_CANVAS;

        // Init the remaining slots in the instance stack
        for (int i=1;i<MAX_INSTANCES;i++) {
          instance_stack[i].id = UNDEFINED;
          instance_stack[i].address = NULL;
          instance_stack[i].type = FX_UNDEFINED;
        }

        // Init the slots in the audio and control routing stacks
        total_audio_routes = 0;
        total_control_routes = 0;
        for (int i=0;i<MAX_ROUTES;i++) {
          audio_routing_stack[i].src_id = UNDEFINED;
          audio_routing_stack[i].src_node_indx = UNDEFINED;
          audio_routing_stack[i].dest_id = UNDEFINED;
          audio_routing_stack[i].src_node_indx = UNDEFINED;

          control_routing_stack[i].src_id = UNDEFINED;
          control_routing_stack[i].src_node_indx = UNDEFINED;
          control_routing_stack[i].dest_id = UNDEFINED;
          control_routing_stack[i].src_node_indx = UNDEFINED;
        }

        // Reset DSP by default
        debug_no_reset = false;

        // Set valid canvas to false
        valid_canvas = false;

        // Set initialized to false
        initialized = false;

        // Set bypass control set to false
        bypass_control_enabled = false;
        bypassed = false;

        // Tap control
        tap_control_enabled = false;
        tap_blink_only_enabled = false;

        // Button press detection
        footswitch_left_last_state = true;
        footswitch_right_last_state = true;

        footswitch_left_pressed = false;
        footswitch_right_pressed = false;
        footswitch_left_released = false;
        footswitch_right_released = false;

        // Set routes valid to false
        valid_audio_routes = false;
        valid_control_routes = false;

        status = &dsp_status;

        // Set last service timetamp
        last_service_ts = millis();

    }
    #endif    // DOXYGEN_SHOULD_SKIP_THIS

    // Pedal init functions
    void    init(void);
    void    init(DEBUG_MSG_LEVEL debug_level);
    void    init(DEBUG_MSG_LEVEL debug_level, bool dsp_no_reset);

    // Depricated
    void    init(bool debug_enable);
    void    init(bool debug_enable, bool dsp_telem);

    // Canvas main control functions
    bool    run(void);
    void    service(void);

    // Canvas configuration

    // Route audio and control links
    bool    route_audio(fx_audio_node * out, fx_audio_node * in);
    bool    route_control(fx_control_node * src, fx_control_node * dest);
    bool    route_control(fx_control_node * src, fx_control_node * dest, float scale, float offset);

    // Attach a bypass button / LED to the effect
    void    add_bypass_button(FOOTSWITCH footswitch);
    void    add_tap_interval_button(FOOTSWITCH footswitch, bool enable_led_flash);

    // Canvas control
    void    bypass_fx(void);
    void    enable_fx(void);


    // Canvas control

    // Tap LED controls
    bool    new_tap_interval(void);
    float   get_tap_interval_ms(void);
    float   get_tap_freq_hz(void);
    void    set_tap_blink_rate_hz(float rate_hz);
    void    set_tap_blink_rate_hz(float rate_hz, FOOTSWITCH led);
    void    set_tap_blink_rate_ms(float ms);
    void    set_tap_blink_rate_ms(float ms, FOOTSWITCH led);

    // Events for when a button is pressed and released
    bool    button_pressed(FOOTSWITCH footswitch, bool enable_led);
    bool    button_released(FOOTSWITCH footswitch, bool enable_led);

    // Register a tap for tap reading
#ifndef DOXYGEN_SHOULD_SKIP_THIS
      void    register_tap(void);
      void    button_press_check(void);
      void    service_button_events(void);
#endif  // DOXYGEN_SHOULD_SKIP_THIS

    // Utility functions to print the instance and routing stack
    void    print_instance_stack(void);
    void    print_routing_table(void);
    void    print_param_tables(void);
    void    print_processor_load(int seconds);

        // Supporting functions control
    #ifndef DOXYGEN_SHOULD_SKIP_THIS
      void    spi_transmit_param(EFFECT_TYPE instance_type, uint32_t instance_id, PARAM_TYPES param_type, uint8_t param_id, void * value);


      // Parameter serice function
      void    parameter_service(void);
    #endif  // DOXYGEN_SHOULD_SKIP_THIS

};


extern fx_pedal pedal;



/**********************************************************************
 * EFFECTS
 *********************************************************************/
class fx_effect {

  protected:

    friend fx_pedal;


    EFFECT_TYPE     type;

    // input / output node stack (audio and control)
    fx_audio_node * audio_node_stack[MAX_NODES_PER_FX];
    int             total_audio_nodes;
    fx_control_node * control_node_stack[MAX_NODES_PER_FX];
    int             total_control_nodes;

    // Parameter stack
    void            * param_stack[MAX_PARMS_PER_FX];
    PARAM_TYPES     param_stack_types[MAX_PARMS_PER_FX];

    // total number of parameters that this effect has
    int             total_params;

    // Universal effect parameters
    bool            param_enabled;

    fx_audio_node   node_input;
    fx_audio_node   node_output;
    fx_control_node node_enabled;

    int             node_index;
    fx_pedal       * parent_canvas;
    uint8_t         instance_id;

    // Set when there are new parameters to send down to DSP
    bool            updated_parameters;


    bool get_audio_node_index(fx_audio_node * node, uint8_t * local_node_index);
    bool get_control_node_index(fx_control_node * node, uint8_t * local_node_index);

    uint16_t * serialize_params(uint16_t * serialized_params, uint16_t * size);
    bool  float_param_updated( float * param, float * param_last, float threshold );
    bool  bool_param_updated( bool * param, bool * param_last );


  public:

    friend class fx_pedal;

     #ifndef DOXYGEN_SHOULD_SKIP_THIS
      // String name of current effect
      char  effect_name[32];

      // Constructor
      fx_effect() :
        node_input(NODE_IN, "input", this),
        node_output(NODE_OUT, "output", this),
        node_enabled(NODE_IN, NODE_BOOL, "enabled", this, FX_PARAM_ID_ENABLED) {

          // Set up default audio nodes
          total_audio_nodes = 0;
          audio_node_stack[total_audio_nodes++] = &node_input;
          audio_node_stack[total_audio_nodes++] = &node_output;

          // Set up default control nodes
          total_control_nodes = 0;
          control_node_stack[total_control_nodes++] = &node_enabled;

          // Set up initial parameter stack
          param_enabled = true;
          param_stack[0] = &param_enabled;
          param_stack_types[0] = T_BOOL;
          total_params = 1;

          // Node index has not been assigned
          node_index = 0;

          // No parameters to update an init
          updated_parameters = false;

          // Set instance ID to 0xFF (meaning it hasn't been routed/placed yet)
          instance_id = 0xFF;

      }

      bool  service(void);
      void  print_params(void);

      const char * get_name() {
          return effect_name;
      }

      EFFECT_TYPE get_type() {
        return type;
      }

      #endif // DOXYGEN_SHOULD_SKIP_THIS

    void  enable(void) { param_enabled = true;  }

    void  bypass(void) { param_enabled = false; }

    #ifndef DOXYGEN_SHOULD_SKIP_THIS
    void print_ctrl_node_status(fx_control_node * t) {
      char buf[64];

      sprintf(buf," + [%s] %s: ", (t->node_direction==NODE_IN?"ctrl-in":"ctrl-out"), t->node_name);  Serial.print(buf);
      if (t->connected) {
        Serial.println("routed");
      } else {
        Serial.println("not routed");
      }
    }

    void print_audio_node_status(fx_audio_node * t) {
      char buf[64];
      sprintf(buf," * [%s] %s: ", (t->node_direction==NODE_IN?"audio-in":"audio-out"), t->node_name);  Serial.print(buf);
      if (t->connected) {
        Serial.println("routed");
      } else {
        Serial.println("not routed");
      }
    }

    void print_parameter( void * val, char * name, PARAM_TYPES type) {
      char buf[64];
      if (type == T_FLOAT) {
        sprintf(buf," %s: %.2f", name, *(float*) val);  Serial.println(buf);
      } else if (type == T_BOOL) {
        sprintf(buf," %s: %s", name, *(bool*) val?"true":"false");  Serial.println(buf);
      } else if (type == T_INT16) {
        sprintf(buf," %s: %u", name, *(uint16_t*) val);  Serial.println(buf);
      }
    }
    #endif
};

#include "effects/dm_fx_effect_macros.h"

#include "effects/dm_fx_adsr_envelope.h"
#include "effects/dm_fx_allpass_filter.h"
#include "effects/dm_fx_amplitude_modulator.h"
#include "effects/dm_fx_arpeggiator.h"
#include "effects/dm_fx_biquad_filter.h"
#include "effects/dm_fx_destructor.h"
#include "effects/dm_fx_compressor.h"
#include "effects/dm_fx_delay.h"
#include "effects/dm_fx_delay_multitap.h"
#include "effects/dm_fx_envelope_tracker.h"
#include "effects/dm_fx_gain.h"
#include "effects/dm_fx_harmonizer.h"
#include "effects/dm_fx_impulse_response.h"
#include "effects/dm_fx_instrument_synth.h"
#include "effects/dm_fx_looper.h"
#include "effects/dm_fx_mixers.h"
#include "effects/dm_fx_oscillators.h"
#include "effects/dm_fx_phase_shifter.h"
#include "effects/dm_fx_pitch_shift.h"
#include "effects/dm_fx_ring_modulator.h"
#include "effects/dm_fx_slicer.h"
#include "effects/dm_fx_spectralizer.h"
#include "effects/dm_fx_variable_delay.h"


#endif // DM_FX_H