diff options
Diffstat (limited to 'attic/muse/synti/organ/organ.cpp')
-rw-r--r-- | attic/muse/synti/organ/organ.cpp | 710 |
1 files changed, 710 insertions, 0 deletions
diff --git a/attic/muse/synti/organ/organ.cpp b/attic/muse/synti/organ/organ.cpp new file mode 100644 index 00000000..a1788a6c --- /dev/null +++ b/attic/muse/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; } + } + |