//=========================================================
//  MusE
//  Linux Music Editor
//  $Id: organ.cpp,v 1.1.1.1 2003/10/29 10:06:07 wschweer Exp $
//
//  Parts of this file taken from:
//      Organ - Additive Organ Synthesizer Voice
//      Copyright (c) 1999, 2000 David A. Bartold
//
//  (C) Copyright 2001 Werner Schweer (ws@seh.de)
//=========================================================

#include <cmath>
#include <stdio.h>
#include <sys/time.h>
#include <pthread.h>
#include "mess.h"
#include "ladspa.h"
#include "midictrl.h"

#include "organ.h"

#define RESOLUTION   (16384)
#define VOICES          128    // max polyphonie

enum EnvelopeState {
      ATTACK,
      DECAY,
      SUSTAIN,
      RELEASE           // after not off
      };

//---------------------------------------------------------
//   EnvelopeGenerator
//---------------------------------------------------------

struct EnvelopeGenerator {
      static const int onStates  = 2;
      static const int offStates = 1;

      struct Segment {
            int ticks;        // len of segment
            double incr;
            };
      Segment segment[onStates + offStates];

      int state;
      double env;
      int tick;

      EnvelopeGenerator() {
            segment[0].ticks = 441;
            segment[0].incr  = 1.0/441.0;
            segment[1].ticks = 0;
            segment[1].incr  = 0.0;
            segment[2].ticks = 441;
            segment[2].incr  = -(1.0/441.0);
            }

      void setSegment(int seg, int ticks, double incr) {
            segment[seg].ticks = ticks;
            segment[seg].incr = incr;
            }

      void keyOn() {
            env   = 0.0;
            state = 0;
            tick  = segment[state].ticks;
            }
      void keyOff() {
            state = onStates;
            tick  = segment[state].ticks;
            }
      bool isOff() {
            return state == (onStates+offStates);
            }
      bool step() {
            if (state >= onStates+offStates)
                  return true;
            if (tick == 0)
                  return false;
            env += segment[state].incr;
            if (env < 0.0)
                  env = 0.0;
            --tick;
            while (tick == 0) {
                  ++state;
                  if (state >= onStates+offStates)
                        return true;
                  if (state == onStates)
                        return false;
                  tick = segment[state].ticks;
                  }
            return false;
            }
      };

//---------------------------------------------------------
//   Voice
//---------------------------------------------------------

struct Voice {
      bool isOn;
      int pitch;
      int channel;
      float velocity;
      float freq;

      EnvelopeGenerator env0;
      EnvelopeGenerator env1;

      unsigned harm0_accum;
      unsigned harm1_accum;
      unsigned harm2_accum;
      unsigned harm3_accum;
      unsigned harm4_accum;
      unsigned harm5_accum;
      };

struct Preset {
      char* name;
      bool brass, flute, reed;
      int attack0, attack1;
      int release0, release1;
      int decay0, decay1;
      double harm0, harm1, harm2, harm3, harm4, harm5;
      };

//---------------------------------------------------------
//   Organ
//---------------------------------------------------------

class Organ : public Mess {
      static SynthCtrl synthCtrl[NUM_CONTROLLER];
      static int useCount;
      static const int CB_AMP_SIZE = 961;

      static double cb2amp_tab[CB_AMP_SIZE];
      static double cb2amp(double cb);

      int ctrlHi;
      int ctrlLo;
      int dataHi;
      int dataLo;

      bool brass;
      bool flute;
      bool reed;

      int attack0;
      int attack1;
      int release0;
      int release1;
      int decay0;       // ticks
      int decay1;
      double sustain0;  // level
      double sustain1;

      double harm0, harm1, harm2, harm3, harm4, harm5;

      char curControllerVal[32];
      int curControllerIdx;
      int controller[NUM_CONTROLLER];
      Voice voices[VOICES];

      static float* sine_table;
      static float* g_triangle_table;
      static float* g_pulse_table;

      virtual void processEvent(MEvent* ev);
      void write(int, float**, int);
      void noteon(int channel, int pitch, int velo);
      void noteoff(int channel, int pitch);
      void setController(int channel, int ctrl, int val);
      void sysex(const unsigned char* data, int len);

   public:
      Organ(const char* classname);
      ~Organ();
      bool init();
      float* port;          // ladspa output ports
      virtual int getMidiInitEvent(int id, RawMidiEvent*);
      };

float* Organ::sine_table;
float* Organ::g_triangle_table;
float* Organ::g_pulse_table;
int Organ::useCount = 0;
double Organ::cb2amp_tab[Organ::CB_AMP_SIZE];

SynthCtrl Organ::synthCtrl[NUM_CONTROLLER] = {
      { "harm0",     0, 0.0, 1.0 },
      { "harm1",     0, 0.0, 1.0 },
      { "harm2",     0, 0.0, 1.0 },
      { "harm3",     0, 0.0, 1.0 },
      { "harm4",     0, 0.0, 1.0 },
      { "harm5",     0, 0.0, 1.0 },
      { "attackLo",  0, 0.0, 1.0 },
      { "decayLo",   0, 0.0, 1.0 },
      { "sustainLo", 0, 0.0, 1.0 },
      { "releaseLo", 0, 0.0, 1.0 },
      { "attackHi",  0, 0.0, 1.0 },
      { "decayHi",   0, 0.0, 1.0 },
      { "sustainHi", 0, 0.0, 1.0 },
      { "releaseHi", 0, 0.0, 1.0 },
      { "brass",     1, 0.0, 1.0 },
      { "flute",     1, 0.0, 1.0 },
      { "reed",      1, 0.0, 1.0 },
      };

//---------------------------------------------------------
//   cb2amp
//    convert centibel to amplification (0 - 96dB)
//---------------------------------------------------------

double Organ::cb2amp(double cb)
      {
      if (cb < 0.0)
            return 1.0;
      if (cb > 960.0)
            return 0.0;
      return cb2amp_tab[int(cb)];
      }

//---------------------------------------------------------
//   Organ
//---------------------------------------------------------

Organ::Organ(const char* cn)
   : Mess(cn, 1)
      {
      if (useCount == 0) {
            // centibels to amplitude conversion
            for (int i = 0; i < CB_AMP_SIZE; i++)
                  cb2amp_tab[i] = pow(10.0, double(i) / -200.0);

            int size = RESOLUTION;
            int half = size / 2;
            int slope = size / 10;
            int i;

            // Initialize sine table.
            sine_table = new float[size];
            for (i = 0; i < size; i++)
                  sine_table[i] = sin ((i * 2.0 * M_PI) / size) / 6.0;

            // Initialize triangle table.
            g_triangle_table = new float[size];
            for (i = 0; i < half; i++)
                  g_triangle_table[i] = (4.0 / size * i - 1.0) / 6.0;
            for (; i < size; i++)
                  g_triangle_table[i] = (4.0 / size * (size - i) - 1.0) / 6.0;

            // Initialize pulse table.
            g_pulse_table = new float[size];
            for (i = 0; i < slope; i++)
                  g_pulse_table[i] = ((double) -i) / slope / 6.0;
            for (; i < half - slope; i++)
                  g_pulse_table[i] = -1.0 / 6.0;
            for (; i < half + slope; i++)
                  g_pulse_table[i] = ((double) i - half) / slope / 6.0;
            for (; i < size - slope; i++)
                  g_pulse_table[i] = 1.0 / 6.0;
            for (; i < size; i++)
                  g_pulse_table[i] = ((double) size - i) / slope / 6.0;
            }
      ++useCount;
      ctrlHi = 0;
      ctrlLo = 0;
      dataHi = 0;
      dataLo = 0;
      }

//---------------------------------------------------------
//   ~Organ
//---------------------------------------------------------

Organ::~Organ()
      {
      --useCount;
      if (useCount == 0) {
            delete[] g_pulse_table;
            delete[] g_triangle_table;
            delete[] sine_table;
            }
      }

//---------------------------------------------------------
//   table_pos
//---------------------------------------------------------

static inline float table_pos (float* table, unsigned long freq_256, unsigned *accum)
      {
      *accum += freq_256;
      while (*accum >= RESOLUTION * 256)
            *accum -= RESOLUTION * 256;
      return table[*accum >> 8];
      }

//---------------------------------------------------------
//   multiplier
//---------------------------------------------------------

static inline double multiplier(double value, int sampleRate)
      {
      return 1.0 - pow (0.05, 1.0 / (sampleRate * value));
      }

//---------------------------------------------------------
//   init
//---------------------------------------------------------

bool Organ::init()
      {
      memset(controller, 0, sizeof(controller));

      int maxdata = 128*128-1;

      setController(0, HARM0, maxdata);
      setController(0, HARM1, maxdata);
      setController(0, HARM2, maxdata);
      setController(0, HARM3, maxdata);
      setController(0, HARM4, maxdata);
      setController(0, HARM5, maxdata);
      setController(0, ATTACK_LO,  maxdata/100);
      setController(0, DECAY_LO, 0);
      setController(0, SUSTAIN_LO, maxdata);
      setController(0, RELEASE_LO, maxdata/2);
      setController(0, ATTACK_HI,  maxdata/100);
      setController(0, DECAY_HI, 0);
      setController(0, SUSTAIN_HI, maxdata);
      setController(0, RELEASE_HI, maxdata/2);

      setController(0, BRASS, 1);
      setController(0, FLUTE, 1);
      setController(0, REED,  1);

      for (int i = 0; i < VOICES; ++i)
            voices[i].isOn = false;
      return false;
      }

//---------------------------------------------------------
//   processEvents
//---------------------------------------------------------

void Organ::processEvent(MEvent* ev)
      {
      switch(ev->type()) {
            case SND_SEQ_EVENT_NOTEON:
            case SND_SEQ_EVENT_KEYPRESS:
                  noteon(ev->chan(), ev->dataA(), ev->dataB());
                  break;

            case SND_SEQ_EVENT_NOTEOFF:
                  noteoff(ev->chan(), ev->dataA());
                  break;

            case SND_SEQ_EVENT_PGMCHANGE:
//                  program_change(ev->chan(), ev->dataB());
                  break;

            case SND_SEQ_EVENT_PITCHBEND:
//                  pitch_bend(ev->chan(), (ev->dataA()<<7) | ev->dataB());
                  break;

            case SND_SEQ_EVENT_CONTROLLER:
                  switch(ev->dataA()) {
                        case CTRL_LNRPN:  ctrlLo = ev->dataB(); break;
                        case CTRL_HNRPN:  ctrlHi = ev->dataB(); break;
                        case CTRL_LDATA:  dataLo = ev->dataB(); break;
                        case CTRL_HDATA:  dataHi = ev->dataB(); break;
                        }
                  if (ev->dataA() == CTRL_HDATA) {
                        setController(ev->chan(), ctrlLo+ctrlHi*128, dataLo+dataHi*128);
                        }
                  break;

            case SND_SEQ_EVENT_SYSEX:
                  sysex(ev->data(), ev->dataLen());
                  break;
            case SND_SEQ_EVENT_CHANPRESS:
                  break;
            default:
                  printf("processEvent: unknown event type %d\n", ev->type());
            }
      }

//---------------------------------------------------------
//   write
//---------------------------------------------------------

void Organ::write(int sampleCount, float** ports, int offset)
      {
      float* buffer = *ports + offset;
      for (int i = 0; i < VOICES; ++i) {
            Voice* v = &voices[i];
            if (!v->isOn)
                  continue;
            float velocity = v->velocity;
            float freq     = v->freq;
            unsigned* harm0_accum = &(v->harm0_accum);
            unsigned* harm1_accum = &(v->harm1_accum);
            unsigned* harm2_accum = &(v->harm2_accum);
            unsigned* harm3_accum = &(v->harm3_accum);
            unsigned* harm4_accum = &(v->harm4_accum);
            unsigned* harm5_accum = &(v->harm5_accum);

            unsigned long freq_256_harm2, freq_256_harm3;
            unsigned long freq_256_harm4, freq_256_harm5;

            float* reed_table  = reed ? g_pulse_table : sine_table;
            float* flute_table = flute ? g_triangle_table : sine_table;

            unsigned freq_256  = (int) (freq * ((double) RESOLUTION) / sampleRate() * 256.0);

            unsigned freq_256_harm0 = freq_256 / 2;
            unsigned freq_256_harm1 = freq_256;

            if (brass) {
                  freq_256_harm2 = freq_256       * 2;
                  freq_256_harm3 = freq_256_harm2 * 2;
                  freq_256_harm4 = freq_256_harm3 * 2;
                  freq_256_harm5 = freq_256_harm4 * 2;
                  for (int i = 0; i < sampleCount; i++) {
                        if (v->env0.step() | v->env1.step()) {
                              v->isOn = false;
                              break;
                              }
                        buffer[i] +=
                           ((table_pos (sine_table, freq_256_harm0, harm0_accum) * harm0
                           + table_pos (sine_table, freq_256_harm1, harm1_accum) * harm1
                           + table_pos (reed_table, freq_256_harm2, harm2_accum) * harm2)
                              * cb2amp(960.0 * (1.0 - v->env0.env))
                           + (table_pos (sine_table, freq_256_harm3, harm3_accum) * harm3
                           + table_pos (flute_table, freq_256_harm4, harm4_accum) * harm4
                           + table_pos (flute_table, freq_256_harm5, harm5_accum) * harm5)
                             * cb2amp(960.0 * (1.0 - v->env1.env)))
                           * velocity;
                        }
                  }
            else {
                  freq_256_harm2 = freq_256 * 3 / 2;
                  freq_256_harm3 = freq_256 * 2;
                  freq_256_harm4 = freq_256 * 3;
                  freq_256_harm5 = freq_256_harm3 * 2;
                  for (int i = 0; i < sampleCount; i++) {
                        if (v->env0.step() | v->env1.step()) {
                              v->isOn = false;
                              break;
                              }
                        buffer[i] +=
                           ((table_pos (sine_table, freq_256_harm0, harm0_accum) * harm0
                           + table_pos (sine_table, freq_256_harm1, harm1_accum) * harm1
                           + table_pos (sine_table, freq_256_harm2, harm2_accum) * harm2)
                              * cb2amp(960.0 * (1.0 - v->env0.env))
                           + (table_pos (reed_table, freq_256_harm3, harm3_accum) * harm3
                           + table_pos (sine_table, freq_256_harm4,  harm4_accum) * harm4
                           + table_pos (flute_table, freq_256_harm5, harm5_accum) * harm5)
                             * cb2amp(960.0 * (1.0 - v->env1.env)))
                           * velocity;
                        }
                  }
            }
      }

//---------------------------------------------------------
//   noteon
//---------------------------------------------------------

void Organ::noteon(int channel, int pitch, int velo)
      {
      if (velo == 0) {
            noteoff(channel, pitch);
            return;
            }
      for (int i = 0; i < VOICES; ++i) {
            if (voices[i].isOn)
                  continue;
// printf("Organ: noteon  %d:%d\n", channel, pitch);
            voices[i].isOn     = true;
            voices[i].pitch    = pitch;
            voices[i].channel  = channel;
            voices[i].velocity = velo / 127.0;
            voices[i].freq     = 8.176 * exp(float(pitch)*log(2.0)/12.0);
            voices[i].env0.setSegment(0, attack0,    1.0/attack0);
            voices[i].env0.setSegment(1, decay0, -((1.0-sustain0)/decay0));
            voices[i].env0.setSegment(2, release0, -(1.0/release0));
            voices[i].env1.setSegment(0, attack1,    1.0/attack1);
            voices[i].env1.setSegment(1, decay1, -((1.0-sustain1)/decay1));
            voices[i].env1.setSegment(2, release1, -(1.0/release1));
            voices[i].env0.keyOn();
            voices[i].env1.keyOn();
            voices[i].harm0_accum = 0;
            voices[i].harm1_accum = 0;
            voices[i].harm2_accum = 0;
            voices[i].harm3_accum = 0;
            voices[i].harm4_accum = 0;
            voices[i].harm5_accum = 0;
            return;
            }
      printf("organ: voices overflow!\n");
      }

//---------------------------------------------------------
//   noteoff
//---------------------------------------------------------

void Organ::noteoff(int channel, int pitch)
      {
      bool found = false;
      for (int i = 0; i < VOICES; ++i) {
            if (voices[i].isOn && (voices[i].pitch == pitch)
               && (voices[i].channel == channel)) {
// printf("Organ: noteoff %d:%d\n", channel, pitch);
                  found = true;
                  voices[i].env0.keyOff();
                  voices[i].env1.keyOff();
                  }
            }
      if (!found)
            printf("Organ: noteoff %d:%d not found\n", channel, pitch);
      }

//---------------------------------------------------------
//   setController
//---------------------------------------------------------

void Organ::setController(int /*channel*/, int ctrl, int data)
      {
// fprintf(stderr, "organ: setController %d %d\n", ctrl, data);
      int maxval = 128*128-1;
      double normval = double(data) / double(maxval);
      switch (ctrl) {
            case HARM0:
                  harm0 = normval;
                  break;
            case HARM1:
                  harm1 = normval;
                  break;
            case HARM2:
                  harm2 = normval;
                  break;
            case HARM3:
                  harm3 = normval;
                  break;
            case HARM4:
                  harm4 = normval;
                  break;
            case HARM5:
                  harm5 = normval;
                  break;
            case ATTACK_LO:   // maxval -> 500msec
                  attack0 = (data * sampleRate()) / (maxval * 2) + 1;
                  break;
            case DECAY_LO:    // maxval -> 5000msec
                  decay0 = (data * sampleRate()*5) / maxval + 1;
                  break;
            case SUSTAIN_LO:
                  sustain0 = normval;
                  break;
            case RELEASE_LO:
                  release0 = (data * sampleRate()) / (maxval * 2) + 1;
                  break;
            case ATTACK_HI:
                  attack1 = (data * sampleRate()) / (maxval * 2) + 1;
                  break;
            case DECAY_HI:
                  decay1 = (data * sampleRate()*5) / maxval + 1;
                  break;
            case SUSTAIN_HI:
                  sustain1 = normval;
                  break;
            case RELEASE_HI:
                  release1 = (data * sampleRate()) / (maxval * 2) + 1;
                  break;
            case BRASS:
                  brass = data;
                  break;
            case FLUTE:
                  flute = data;
                  break;
            case REED:
                  reed  = data;
                  break;
            default:
                  fprintf(stderr, "Organ:set unknown Ctrl %d to %d\n", ctrl, data);
                  return;
            }
      controller[ctrl] = data;
      }

//---------------------------------------------------------
//   sysex
//---------------------------------------------------------

void Organ::sysex(const unsigned char* data, int len)
      {
      if (len >= 6 && data[0] == 0xf0 && data[len-1] == 0xf7) {
            //---------------------------------------------
            //  MusE Soft Synth
            //---------------------------------------------

            if (data[1] == 0x7c) {
                  int n = len - 5;
                  if (n < 1) {
                        printf("organ: bad sysEx:\n");
                        return;
                        }
                  if (data[2] == 1) {     // organ
                        if (data[3] == 1) {  // get parameter
                              // parameterRequest(data[4]);
                              return;
                              }
                        }
                  }
            }
      printf("organ: unknown sysex received, len %d:\n", len);
      for (int i = 0; i < len; ++i)
            printf("%02x ", data[i]);
      printf("\n");
      }

//---------------------------------------------------------
//   getMidiInitEvent
//---------------------------------------------------------

int Organ::getMidiInitEvent(int id, RawMidiEvent* ev)
      {
      if (id >= NUM_CONTROLLER)
            return 0;
      ev->setType(SND_SEQ_EVENT_NONREGPARAM);
      ev->setChan(0);
      ev->setDataA(id);
      ev->setDataB(controller[id]);
      return ++id;
      }

//---------------------------------------------------------
//   inst
//---------------------------------------------------------

static Mess* inst(const char* name)
      {
      return new Organ(name);
      }

//---------------------------------------------------------
//   initMess
//---------------------------------------------------------

void initMess()
      {
      Mess::initMess(
          1222,
         "organ",
         "David A. Bartold, Werner Schweer",
         "Organ",
         "None",
         1,
         inst);
      }
