/* syndrum.cpp

   SynDrum - Drum Synthesizer
   Copyright (c) 1999, 2000 David A. Bartold

   Computer Music Toolkit - a library of LADSPA plugins. Copyright (C)
   2000 Richard W.E. Furse. The author may be contacted at
   richard@muse.demon.co.uk.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public Licence as
   published by the Free Software Foundation; either version 2 of the
   Licence, or (at your option) any later version.

   This library is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307, USA. */

/*****************************************************************************/

#include <malloc.h>
#include <math.h>
#include "cmt.h"

#ifdef RISCOS

LADSPA_Handle
CMT_Instantiate_SynDrum(const LADSPA_Descriptor * Descriptor,
		unsigned long             SampleRate);

#endif

#define PORT_OUT       0
#define PORT_TRIGGER   1
#define PORT_VELOCITY  2
#define PORT_FREQ      3
#define PORT_RESONANCE 4
#define PORT_RATIO     5

#define NUM_PORTS      6

#ifndef PI
#define PI 3.14159265358979
#endif

class SynDrum : public CMT_PluginInstance {
  LADSPA_Data sample_rate;

  LADSPA_Data spring_vel;
  LADSPA_Data spring_pos;
  LADSPA_Data env;

  int         last_trigger;

public:
  SynDrum(const LADSPA_Descriptor *,
          unsigned long s_rate)
    : CMT_PluginInstance(NUM_PORTS),
      sample_rate(s_rate),
      spring_vel(0.0F),
      spring_pos(0.0F),
      env(0.0F) {
  }

  ~SynDrum() {
  }

  static void
  activate(LADSPA_Handle Instance) {
    SynDrum *syndrum = (SynDrum*) Instance;
    syndrum->spring_vel = 0.0F;
    syndrum->spring_pos = 0.0F;
    syndrum->env = 0.0F;
    syndrum->last_trigger = 0;
  }

  static void
  run(LADSPA_Handle Instance,
      unsigned long SampleCount) {
    SynDrum *syndrum = (SynDrum*) Instance;
    unsigned long i;
    int trigger;
    LADSPA_Data freq_shift;
    LADSPA_Data factor;
    LADSPA_Data res;

    trigger = *syndrum->m_ppfPorts[PORT_TRIGGER] > 0.0;
    if (trigger == 1 && syndrum->last_trigger == 0)
      {
        syndrum->spring_vel = *syndrum->m_ppfPorts[PORT_VELOCITY];
        syndrum->env = *syndrum->m_ppfPorts[PORT_VELOCITY];
      }
    syndrum->last_trigger = trigger;

    factor = 2.0 * PI / syndrum->sample_rate;
    freq_shift = *syndrum->m_ppfPorts[PORT_FREQ] *
                 *syndrum->m_ppfPorts[PORT_RATIO];
    res = pow (0.05, 1.0 / (syndrum->sample_rate * *syndrum->m_ppfPorts[PORT_RESONANCE]));

    for (i = 0; i < SampleCount; i++)
      {
        LADSPA_Data cur_freq;

        cur_freq = *syndrum->m_ppfPorts[PORT_FREQ] +
                   (syndrum->env * freq_shift);
        cur_freq *= factor;
        syndrum->spring_vel -= syndrum->spring_pos * cur_freq;
        syndrum->spring_pos += syndrum->spring_vel * cur_freq;
        syndrum->spring_vel *= res;
        syndrum->env *= res;

        syndrum->m_ppfPorts[PORT_OUT][i] = syndrum->spring_pos;
      }
  }
};


static LADSPA_PortDescriptor g_psPortDescriptors[] =
{
  LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT,
  LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT,
  LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT,
  LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT,
  LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT,
  LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT
};

static const char * const g_psPortNames[] =
{
  "Out",
  "Trigger",
  "Velocity",
  "Frequency (Hz)",
  "Resonance",
  "Frequency Ratio"
};

static LADSPA_PortRangeHint g_psPortRangeHints[] =
{
  /* Hints, Lower bound, Upper bound */
  { 0, 0.0, 0.0 },
  { LADSPA_HINT_TOGGLED, 0.0, 0.0 },
  { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 10.0 },
  { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 20000.0 },
  { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.001, 1.0 },
  { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 10.0 }
};

void
initialise_syndrum() {
  CMT_Descriptor * psDescriptor;

  psDescriptor = new CMT_Descriptor
      (1223,
       "syndrum",
       LADSPA_PROPERTY_HARD_RT_CAPABLE,
       "Syn Drum",
       CMT_MAKER("David A. Bartold"),
       CMT_COPYRIGHT("1999, 2000", "David A. Bartold"),
       NULL,
#ifdef RISCOS
       CMT_Instantiate_SynDrum,
#else
       CMT_Instantiate<SynDrum>,
#endif
       SynDrum::activate,
       SynDrum::run,
       NULL,
       NULL,
       NULL);

  for (int i = 0; i < NUM_PORTS; i++)
    psDescriptor->addPort(
      g_psPortDescriptors[i],
      g_psPortNames[i],
      g_psPortRangeHints[i].HintDescriptor,
      g_psPortRangeHints[i].LowerBound,
      g_psPortRangeHints[i].UpperBound);

  registerNewPluginDescriptor(psDescriptor);
}

#ifdef RISCOS

LADSPA_Handle
CMT_Instantiate_SynDrum(const LADSPA_Descriptor * Descriptor,
		unsigned long             SampleRate) {
  return new SynDrum(Descriptor, SampleRate);
}

#endif
