summaryrefslogtreecommitdiff
path: root/muse2/synti/organ/organ.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'muse2/synti/organ/organ.cpp')
-rw-r--r--muse2/synti/organ/organ.cpp710
1 files changed, 710 insertions, 0 deletions
diff --git a/muse2/synti/organ/organ.cpp b/muse2/synti/organ/organ.cpp
new file mode 100644
index 00000000..a1788a6c
--- /dev/null
+++ b/muse2/synti/organ/organ.cpp
@@ -0,0 +1,710 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+// $Id: organ.cpp,v 1.15.2.8 2009/12/06 10:05:00 terminator356 Exp $
+//
+// Parts of this file taken from:
+// Organ - Additive Organ Synthesizer Voice
+// Copyright (c) 1999, 2000 David A. Bartold
+//
+// (C) Copyright 2001-2004 Werner Schweer (ws@seh.de)
+//=========================================================
+
+#include <cmath>
+#include <stdio.h>
+
+#include "muse/midi.h"
+#include "libsynti/mpevent.h"
+
+#include "organ.h"
+#include "organgui.h"
+
+//#define ORGAN_DEBUG
+
+SynthCtrl Organ::synthCtrl[] = {
+ { "harm0", HARM0, 0 },
+ { "harm1", HARM1, 0 },
+ { "harm2", HARM2, 0 },
+ { "harm3", HARM3, 0 },
+ { "harm4", HARM4, 0 },
+ { "harm5", HARM5, 0 },
+ { "attackLo", ATTACK_LO, 20 },
+ { "decayLo", DECAY_LO, 20 },
+ { "sustainLo", SUSTAIN_LO, 0 },
+ { "releaseLo", RELEASE_LO, 20 },
+ { "attackHi", ATTACK_HI, 10 },
+ { "decayHi", DECAY_HI, 10 },
+ { "sustainHi", SUSTAIN_HI, 0 },
+ { "releaseHi", RELEASE_HI, 10 },
+ { "brass", BRASS, 1 },
+ { "flute", FLUTE, 1 },
+ { "reed", REED, 1 },
+ { "velocity", VELO, 0 },
+ // next controller not send as init data
+ { "volume", CTRL_VOLUME, 100 },
+ };
+
+static int NUM_CONTROLLER = sizeof(Organ::synthCtrl)/sizeof(*(Organ::synthCtrl));
+static int NUM_INIT_CONTROLLER = NUM_CONTROLLER - 1;
+
+float* Organ::sine_table;
+float* Organ::g_triangle_table;
+float* Organ::g_pulse_table;
+int Organ::useCount = 0;
+double Organ::cb2amp_tab[MAX_ATTENUATION];
+unsigned Organ::freq256[128];
+
+//---------------------------------------------------------
+// cb2amp
+// convert centibel to amplification (0 - 96dB)
+//---------------------------------------------------------
+
+double Organ::cb2amp(int cb)
+ {
+ if (cb < 0)
+ return 1.0;
+ if (cb >= MAX_ATTENUATION)
+ return 0.0;
+ return cb2amp_tab[cb];
+ }
+
+//---------------------------------------------------------
+// Organ
+//---------------------------------------------------------
+
+Organ::Organ(int sr)
+ : Mess(1)
+ {
+ idata = new int[NUM_CONTROLLER];
+ setSampleRate(sr);
+ gui = 0;
+
+ ++useCount;
+ if (useCount > 1)
+ return;
+
+ // centibels to amplitude conversion
+ for (int i = 0; i < MAX_ATTENUATION; i++)
+ cb2amp_tab[i] = pow(10.0, double(i) / -200.0);
+
+ for (int i = 0; i < 128; ++i) {
+ double freq = 8.176 * exp(double(i)*log(2.0)/12.0);
+ freq256[i] = (int) (freq * ((double) RESOLUTION) / sr * 256.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;
+ }
+
+//---------------------------------------------------------
+// ~Organ
+//---------------------------------------------------------
+
+Organ::~Organ()
+ {
+ if (gui)
+ delete gui;
+ delete idata;
+ --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];
+ }
+
+//---------------------------------------------------------
+// init
+//---------------------------------------------------------
+
+bool Organ::init(const char* name)
+ {
+ gui = new OrganGui;
+ gui->setCaption(QString(name));
+ gui->show();
+
+ for (int i = 0; i < NUM_CONTROLLER; ++i)
+ setController(0, synthCtrl[i].num, synthCtrl[i].val);
+
+ for (int i = 0; i < VOICES; ++i)
+ voices[i].isOn = false;
+ return false;
+ }
+
+//---------------------------------------------------------
+// processMessages
+// Called from host always, even if output path is unconnected.
+//---------------------------------------------------------
+
+void Organ::processMessages()
+{
+ //Process messages from the gui
+ //
+ // get and process all pending events from the
+ // synthesizer GUI
+ //
+ while (gui->fifoSize())
+ {
+ MidiPlayEvent ev = gui->readEvent();
+ if (ev.type() == ME_CONTROLLER)
+ {
+ // process local?
+ setController(ev.dataA(), ev.dataB());
+ sendEvent(ev);
+ }
+ else
+ printf("Organ::process(): unknown event\n");
+ }
+}
+
+//---------------------------------------------------------
+// process
+// Called from host, ONLY if output path is connected.
+//---------------------------------------------------------
+
+void Organ::process(float** ports, int offset, int sampleCount)
+ {
+ /*
+ //
+ // get and process all pending events from the
+ // synthesizer GUI
+ //
+ while (gui->fifoSize()) {
+ MidiPlayEvent ev = gui->readEvent();
+ if (ev.type() == ME_CONTROLLER) {
+ // process local?
+ setController(ev.dataA(), ev.dataB());
+ sendEvent(ev);
+ }
+ else
+ printf("Organ::process(): unknown event\n");
+ }
+ */
+
+ float* buffer = *ports + offset;
+ for (int i = 0; i < VOICES; ++i) {
+ Voice* v = &voices[i];
+ if (!v->isOn)
+ continue;
+ double vol = velo ? v->velocity : 1.0;
+ vol *= volume;
+
+ unsigned freq_256 = freq256[v->pitch];
+ 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_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++) {
+ int a1=0, a2=0; //prevent compiler warning: unitialized usage of vars a1 & a2
+ switch(v->state1) {
+ case ATTACK:
+ if (v->envL1.step(&a1))
+ break;
+ v->state1 = DECAY;
+ case DECAY:
+ if (v->envL2.step(&a1))
+ break;
+ v->state1 = SUSTAIN;
+ case SUSTAIN:
+ a1 = sustain0;
+ break;
+ case RELEASE:
+ if (v->envL3.step(&a1))
+ break;
+ v->state1 = OFF;
+ a1 = MAX_ATTENUATION;
+ break;
+ }
+ switch(v->state2) {
+ case ATTACK:
+ if (v->envH1.step(&a2))
+ break;
+ v->state2 = DECAY;
+ case DECAY:
+ if (v->envH2.step(&a2))
+ break;
+ v->state2 = SUSTAIN;
+ case SUSTAIN:
+ a2 = sustain1;
+ break;
+ case RELEASE:
+ if (v->envH3.step(&a2))
+ break;
+ v->state2 = OFF;
+ a1 = MAX_ATTENUATION;
+ break;
+ }
+ if (v->state1 == OFF && v->state2 == OFF) {
+ 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(a1) * vol
+ + (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(a2) * vol;
+ }
+ }
+ 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++) {
+ int a1=0, a2=0;//prevent compiler warning: unitialized usage of vars a1 & a2
+ switch(v->state1) {
+ case ATTACK:
+ if (v->envL1.step(&a1))
+ break;
+ v->state1 = DECAY;
+ case DECAY:
+ if (v->envL2.step(&a1))
+ break;
+ v->state1 = SUSTAIN;
+ case SUSTAIN:
+ a1 = sustain0;
+ break;
+ case RELEASE:
+ if (v->envL3.step(&a1))
+ break;
+ v->state1 = OFF;
+ a1 = MAX_ATTENUATION;
+ break;
+ }
+ switch(v->state2) {
+ case ATTACK:
+ if (v->envH1.step(&a2))
+ break;
+ v->state2 = DECAY;
+ case DECAY:
+ if (v->envH2.step(&a2))
+ break;
+ v->state2 = SUSTAIN;
+ case SUSTAIN:
+ a2 = sustain1;
+ break;
+ case RELEASE:
+ if (v->envH3.step(&a2))
+ break;
+ v->state2 = OFF;
+ a1 = MAX_ATTENUATION;
+ break;
+ }
+ if (v->state1 == OFF && v->state2 == OFF) {
+ 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(a1) * vol
+ + (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(a2) * vol;
+ }
+ }
+ }
+ }
+
+//---------------------------------------------------------
+// playNote
+//---------------------------------------------------------
+
+bool Organ::playNote(int channel, int pitch, int velo)
+ {
+ if (velo == 0) {
+ noteoff(channel, pitch);
+ return false;
+ }
+ for (int i = 0; i < VOICES; ++i) {
+ if (voices[i].isOn)
+ continue;
+ voices[i].isOn = true;
+ voices[i].pitch = pitch;
+ voices[i].channel = channel;
+ // velo is never 0
+ voices[i].velocity = cb2amp(int(200 * log10((127.0 * 127)/(velo*velo))));
+ voices[i].state1 = ATTACK;
+ voices[i].state2 = ATTACK;
+ voices[i].envL1.set(attack0, MAX_ATTENUATION, 0);
+ voices[i].envL2.set(decay0, MAX_ATTENUATION, sustain0);
+ voices[i].envL3.set(release0, sustain0, MAX_ATTENUATION);
+
+ voices[i].envH1.set(attack1, MAX_ATTENUATION, 0);
+ voices[i].envH2.set(decay1, MAX_ATTENUATION, sustain1);
+ voices[i].envH3.set(release1, sustain1, MAX_ATTENUATION);
+
+ 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 false;
+ }
+ printf("organ: voices overflow!\n");
+ return false;
+ }
+
+//---------------------------------------------------------
+// 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)) {
+ found = true;
+ voices[i].state1 = RELEASE;
+ voices[i].state2 = RELEASE;
+ }
+ }
+ if (!found)
+ printf("Organ: noteoff %d:%d not found\n", channel, pitch);
+ }
+
+//---------------------------------------------------------
+// setController
+//---------------------------------------------------------
+
+void Organ::setController(int ctrl, int data)
+ {
+ int sr = sampleRate();
+
+ // Changed By T356.
+ // Because of muse's auto-bias controllers, some of these negative-range
+ // controls need to apply the auto-bias correction.
+
+ switch (ctrl) {
+ case HARM0:
+ //harm0 = cb2amp(-data);
+ harm0 = cb2amp(-data + 8192);
+ break;
+ case HARM1:
+ //harm1 = cb2amp(-data);
+ harm1 = cb2amp(-data + 8192);
+ break;
+ case HARM2:
+ //harm2 = cb2amp(-data);
+ harm2 = cb2amp(-data + 8192);
+ break;
+ case HARM3:
+ //harm3 = cb2amp(-data);
+ harm3 = cb2amp(-data + 8192);
+ break;
+ case HARM4:
+ //harm4 = cb2amp(-data);
+ harm4 = cb2amp(-data + 8192);
+ break;
+ case HARM5:
+ //harm5 = cb2amp(-data);
+ harm5 = cb2amp(-data + 8192);
+ break;
+ case ATTACK_LO: // maxval -> 500msec
+ attack0 = (data * sr) / 1000;
+ break;
+ case DECAY_LO: // maxval -> 5000msec
+ decay0 = (data * sr) / 1000;
+ break;
+ case SUSTAIN_LO:
+ //sustain0 = -data;
+ sustain0 = -data + 8192;
+ break;
+ case RELEASE_LO:
+ release0 = (data * sr) / 1000;
+ break;
+ case ATTACK_HI:
+ attack1 = (data * sr) / 1000;
+ break;
+ case DECAY_HI:
+ decay1 = (data * sr) / 1000;
+ break;
+ case SUSTAIN_HI:
+ //sustain1 = -data;
+ sustain1 = -data + 8192;
+ break;
+ case RELEASE_HI:
+ release1 = (data * sr) / 1000;
+ break;
+ case BRASS:
+ brass = data;
+ break;
+ case FLUTE:
+ flute = data;
+ break;
+ case REED:
+ reed = data;
+ break;
+ case VELO:
+ velo = data;
+ break;
+ case CTRL_VOLUME:
+ data &= 0x7f;
+ volume = data == 0 ? 0.0 : cb2amp(int(200 * log10((127.0 * 127)/(data*data))));
+ break;
+ case CTRL_ALL_SOUNDS_OFF:
+ for (int i = 0; i < VOICES; ++i)
+ voices[i].isOn = false;
+ break;
+ case CTRL_RESET_ALL_CTRL:
+ for (int i = 0; i < NUM_CONTROLLER; ++i)
+ setController(0, synthCtrl[i].num, synthCtrl[i].val);
+ break;
+ default:
+ fprintf(stderr, "Organ:set unknown Ctrl 0x%x to 0x%x\n", ctrl, data);
+ return;
+ }
+ for (int i = 0; i < NUM_CONTROLLER; ++i) {
+ if (synthCtrl[i].num == ctrl) {
+ synthCtrl[i].val = data;
+ break;
+ }
+ }
+ }
+
+//---------------------------------------------------------
+// setController
+//---------------------------------------------------------
+
+bool Organ::setController(int channel, int ctrl, int data)
+ {
+ setController(ctrl, data);
+
+ switch (ctrl) {
+ case HARM0:
+ case HARM1:
+ case HARM2:
+ case HARM3:
+ case HARM4:
+ case HARM5:
+ case ATTACK_LO:
+ case DECAY_LO:
+ case SUSTAIN_LO:
+ case RELEASE_LO:
+ case ATTACK_HI:
+ case DECAY_HI:
+ case SUSTAIN_HI:
+ case RELEASE_HI:
+ case BRASS:
+ case FLUTE:
+ case REED:
+ case VELO:
+ {
+ MidiPlayEvent ev(0, 0, channel, ME_CONTROLLER, ctrl, data);
+ #ifdef ORGAN_DEBUG
+ fprintf(stderr, "OrganGui:setController before gui->writeEvent ctrl:%d data:%d\n", ctrl, data);
+ #endif
+
+ gui->writeEvent(ev);
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+
+//---------------------------------------------------------
+// sysex
+//---------------------------------------------------------
+
+bool Organ::sysex(int n, const unsigned char* data)
+ {
+ #ifdef ORGAN_DEBUG
+ printf("Organ: sysex\n");
+ #endif
+ if (unsigned(n) != (NUM_INIT_CONTROLLER * sizeof(int))) {
+ printf("Organ: unknown sysex\n");
+ return false;
+ }
+ int* s = (int*) data;
+ for (int i = 0; i < NUM_INIT_CONTROLLER; ++i) {
+ int val = *s++;
+ #ifdef ORGAN_DEBUG
+ printf("Organ: sysex before setController num:%d val:%d\n", synthCtrl[i].num, val);
+ #endif
+ setController(0, synthCtrl[i].num, val);
+ }
+ return false;
+ }
+
+//---------------------------------------------------------
+// getInitData
+//---------------------------------------------------------
+
+void Organ::getInitData(int* n, const unsigned char**p) const
+ {
+ int* d = idata;
+ for (int i = 0; i < NUM_INIT_CONTROLLER; ++i)
+ *d++ = synthCtrl[i].val;
+ *n = NUM_INIT_CONTROLLER * sizeof(int); // sizeof(idata);
+ *p = (unsigned char*)idata;
+ }
+
+//---------------------------------------------------------
+// MESS
+//---------------------------------------------------------
+
+//---------------------------------------------------------
+// getControllerInfo
+//---------------------------------------------------------
+
+int Organ::getControllerInfo(int id, const char** name, int* controller,
+ int* min, int* max, int* initval) const
+ {
+ if (id >= NUM_CONTROLLER)
+ return 0;
+ *controller = synthCtrl[id].num;
+ *name = synthCtrl[id].name;
+ *initval = synthCtrl[id].val;
+
+ if(synthCtrl[id].num == CTRL_VOLUME)
+ {
+ *min = 0;
+ *max = 127;
+ }
+ else
+ gui->getControllerMinMax(id,min,max);
+
+ //*min = 0;
+ //*max = 128*128-1;
+ return ++id;
+ }
+
+//---------------------------------------------------------
+// guiVisible
+//---------------------------------------------------------
+
+bool Organ::guiVisible() const
+ {
+ return gui->isVisible();
+ }
+
+//---------------------------------------------------------
+// showGui
+//---------------------------------------------------------
+
+void Organ::showGui(bool val)
+ {
+ gui->setShown(val);
+ }
+
+//---------------------------------------------------------
+// getGeometry
+//---------------------------------------------------------
+
+void Organ::getGeometry(int* x, int* y, int* w, int* h) const
+ {
+ QPoint pos(gui->pos());
+ QSize size(gui->size());
+ *x = pos.x();
+ *y = pos.y();
+ *w = size.width();
+ *h = size.height();
+ }
+
+//---------------------------------------------------------
+// setGeometry
+//---------------------------------------------------------
+
+void Organ::setGeometry(int x, int y, int w, int h)
+ {
+ gui->resize(QSize(w, h));
+ gui->move(QPoint(x, y));
+ }
+
+//---------------------------------------------------------
+// instantiate
+// construct a new synthesizer instance
+//---------------------------------------------------------
+
+static Mess* instantiate(int sr, QWidget*, QString* projectPathPtr, const char* name)
+ {
+ Organ* synth = new Organ(sr);
+ if (synth->init(name)) {
+ delete synth;
+ synth = 0;
+ }
+ return synth;
+ }
+
+//---------------------------------------------------------
+// msynth_descriptor
+// Return a descriptor of the requested plugin type.
+//---------------------------------------------------------
+
+extern "C" {
+ static MESS descriptor = {
+ "Organ",
+ "Organ based on David A. Bartold's LADSPA plugin",
+ "0.1", // version string
+ MESS_MAJOR_VERSION, MESS_MINOR_VERSION,
+ instantiate,
+ };
+
+ const MESS* mess_descriptor() { return &descriptor; }
+ }
+