| @@ -67,7 +67,10 @@ extern "C" { | |||
| #include "zynaddsubfx/rtosc/cpp/undo-history.cpp" | |||
| // zynaddsubfx includes | |||
| #include "zynaddsubfx/version.cpp" | |||
| #include "zynaddsubfx/Containers/MultiPseudoStack.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -75,6 +78,15 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Containers/NotePool.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| #undef rChangeCb | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Containers/ScratchString.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -82,6 +94,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/DSP/AnalogFilter.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -89,6 +102,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/DSP/FFTwrapper.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -96,6 +110,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/DSP/Filter.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -103,6 +118,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/DSP/FormantFilter.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -110,6 +126,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/DSP/SVFilter.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -117,6 +134,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/DSP/Unison.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -124,6 +142,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Effects/Alienwah.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -131,6 +150,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Effects/Chorus.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -138,6 +158,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Effects/Distorsion.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -145,6 +166,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Effects/DynamicFilter.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -153,6 +175,7 @@ extern "C" { | |||
| #include "zynaddsubfx/Effects/Echo.cpp" | |||
| #undef MAX_DELAY | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -160,6 +183,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Effects/Effect.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -167,6 +191,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Effects/EffectLFO.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -174,6 +199,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Effects/EffectMgr.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -181,6 +207,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Effects/EQ.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -188,16 +215,18 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Effects/Phaser.cpp" | |||
| #undef PHASER_LFO_SHAPE | |||
| #undef ONE_ | |||
| #undef ZERO_ | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| #undef rChangeCb | |||
| #define rChangeCb | |||
| #undef PHASER_LFO_SHAPE | |||
| #undef ONE_ | |||
| #undef ZERO_ | |||
| #include "zynaddsubfx/Effects/Reverb.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -205,6 +234,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Misc/Allocator.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -214,6 +244,15 @@ extern "C" { | |||
| #include "zynaddsubfx/Misc/Bank.cpp" | |||
| #undef INSTRUMENT_EXTENSION | |||
| #undef FORCE_BANK_DIR_FILE | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| #undef rChangeCb | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Misc/BankDb.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -221,6 +260,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Misc/CallbackRepeater.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -228,6 +268,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Misc/Config.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -243,6 +284,7 @@ extern "C" { | |||
| #include "zynaddsubfx/Misc/Microtonal.cpp" | |||
| #undef MAX_LINE_SIZE | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -250,6 +292,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Misc/MiddleWare.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -258,6 +301,7 @@ extern "C" { | |||
| #include "zynaddsubfx/Misc/Part.cpp" | |||
| #undef CLONE | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -265,6 +309,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Misc/PresetExtractor.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -272,6 +317,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Misc/Recorder.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -279,6 +325,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Misc/Util.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -286,6 +333,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Misc/WavFile.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -293,6 +341,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Misc/WaveShapeSmps.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -300,6 +349,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Misc/XMLwrapper.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -308,6 +358,7 @@ extern "C" { | |||
| #include "zynaddsubfx/Params/ADnoteParameters.cpp" | |||
| #undef EXPAND | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -315,6 +366,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Params/Controller.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -322,6 +374,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Params/EnvelopeParams.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -329,6 +382,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Params/FilterParams.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -336,6 +390,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Params/LFOParams.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -345,6 +400,7 @@ extern "C" { | |||
| #include "zynaddsubfx/Params/PADnoteParameters.cpp" | |||
| #undef PC | |||
| #undef P_C | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -352,6 +408,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Params/Presets.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -359,6 +416,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Params/PresetsArray.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -366,6 +424,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Params/PresetsStore.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -375,6 +434,7 @@ extern "C" { | |||
| #include "zynaddsubfx/Params/SUBnoteParameters.cpp" | |||
| #undef doPaste | |||
| #undef doPPaste | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -382,6 +442,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Synth/ADnote.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -389,6 +450,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Synth/Envelope.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -396,6 +458,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Synth/LFO.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -403,6 +466,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Synth/ModFilter.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -416,6 +480,7 @@ extern "C" { | |||
| #undef RESTORE | |||
| #undef FUNC | |||
| #undef FILTER | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -423,6 +488,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Synth/PADnote.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -430,6 +496,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Synth/Resonance.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -437,6 +504,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Synth/SUBnote.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -444,6 +512,15 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Synth/SynthNote.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| #undef rChangeCb | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/Synth/WatchPoint.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -451,6 +528,7 @@ extern "C" { | |||
| #define rChangeCb | |||
| #include "zynaddsubfx/UI/ConnectionDummy.cpp" | |||
| #undef rBegin | |||
| #undef rObject | |||
| #undef rStdString | |||
| #undef rStdStringCb | |||
| @@ -28,6 +28,11 @@ LockFreeQueue::LockFreeQueue(qli_t *data_, int n) | |||
| tag[i] = INVALID; | |||
| } | |||
| LockFreeQueue::~LockFreeQueue(void) | |||
| { | |||
| delete [] tag; | |||
| } | |||
| qli_t *LockFreeQueue::read(void) { | |||
| retry: | |||
| @@ -34,6 +34,7 @@ class LockFreeQueue | |||
| std::atomic<int32_t> avail; | |||
| public: | |||
| LockFreeQueue(qli_t *data_, int n); | |||
| ~LockFreeQueue(void); | |||
| qli_t *read(void); | |||
| void write(qli_t *Q); | |||
| }; | |||
| @@ -0,0 +1,39 @@ | |||
| #include "ScratchString.h" | |||
| #include <cstring> | |||
| #include <cstdio> | |||
| ScratchString::ScratchString(void) | |||
| { | |||
| memset(c_str, 0, sizeof(c_str)); | |||
| } | |||
| ScratchString::ScratchString(int num) | |||
| { | |||
| snprintf(c_str, SCRATCH_SIZE, "%d", num); | |||
| } | |||
| ScratchString::ScratchString(unsigned char num) | |||
| { | |||
| snprintf(c_str, SCRATCH_SIZE, "%d", num); | |||
| } | |||
| ScratchString::ScratchString(const char *str) | |||
| { | |||
| if(str) | |||
| strncpy(c_str, str, SCRATCH_SIZE); | |||
| else | |||
| memset(c_str, 0, sizeof(c_str)); | |||
| } | |||
| ScratchString ScratchString::operator+(const ScratchString s) | |||
| { | |||
| ScratchString ss; | |||
| strncpy(ss.c_str, c_str, SCRATCH_SIZE); | |||
| strncat(ss.c_str, s.c_str, SCRATCH_SIZE-strlen(c_str)); | |||
| return ss; | |||
| } | |||
| //ScratchString::operator const char*() const | |||
| //{ | |||
| // return c_str; | |||
| //} | |||
| @@ -0,0 +1,17 @@ | |||
| #pragma once | |||
| #define SCRATCH_SIZE 128 | |||
| //Fixed Size String Substitute | |||
| struct ScratchString | |||
| { | |||
| ScratchString(void); | |||
| ScratchString(int num); | |||
| ScratchString(unsigned char num); | |||
| ScratchString(const char *str); | |||
| ScratchString operator+(const ScratchString s); | |||
| //operator const char*() const; | |||
| char c_str[SCRATCH_SIZE]; | |||
| }; | |||
| @@ -61,28 +61,35 @@ void AnalogFilter::cleanup() | |||
| needsinterpolation = false; | |||
| } | |||
| void AnalogFilter::computefiltercoefs(void) | |||
| AnalogFilter::Coeff AnalogFilter::computeCoeff(int type, float cutoff, float q, | |||
| int stages, float gain, float fs, int &order) | |||
| { | |||
| float tmp; | |||
| AnalogFilter::Coeff coeff; | |||
| bool zerocoefs = false; //this is used if the freq is too high | |||
| const float samplerate_f = fs; | |||
| const float halfsamplerate_f = fs/2; | |||
| //do not allow frequencies bigger than samplerate/2 | |||
| float freq = this->freq; | |||
| float freq = cutoff; | |||
| if(freq > (halfsamplerate_f - 500.0f)) { | |||
| freq = halfsamplerate_f - 500.0f; | |||
| zerocoefs = true; | |||
| } | |||
| if(freq < 0.1f) | |||
| freq = 0.1f; | |||
| //do not allow bogus Q | |||
| if(q < 0.0f) | |||
| q = 0.0f; | |||
| float tmpq, tmpgain; | |||
| if(stages == 0) { | |||
| tmpq = q; | |||
| tmpgain = gain; | |||
| } | |||
| else { | |||
| } else { | |||
| tmpq = (q > 1.0f) ? powf(q, 1.0f / (stages + 1)) : q; | |||
| tmpgain = powf(gain, 1.0f / (stages + 1)); | |||
| } | |||
| @@ -100,6 +107,9 @@ void AnalogFilter::computefiltercoefs(void) | |||
| //the "Cookbook formulae for audio EQ" by Robert Bristow-Johnson | |||
| //The original location of the Cookbook is: | |||
| //http://www.harmony-central.com/Computer/Programming/Audio-EQ-Cookbook.txt | |||
| float tmp; | |||
| float tgp1; | |||
| float tgm1; | |||
| switch(type) { | |||
| case 0: //LPF 1 pole | |||
| if(!zerocoefs) | |||
| @@ -205,20 +215,15 @@ void AnalogFilter::computefiltercoefs(void) | |||
| if(!zerocoefs) { | |||
| tmpq = sqrtf(tmpq); | |||
| beta = sqrtf(tmpgain) / tmpq; | |||
| tmp = (tmpgain + 1.0f) + (tmpgain - 1.0f) * cs + beta * sn; | |||
| c[0] = tmpgain | |||
| * ((tmpgain | |||
| + 1.0f) - (tmpgain - 1.0f) * cs + beta * sn) / tmp; | |||
| c[1] = 2.0f * tmpgain | |||
| * ((tmpgain - 1.0f) - (tmpgain + 1.0f) * cs) / tmp; | |||
| c[2] = tmpgain | |||
| * ((tmpgain | |||
| + 1.0f) - (tmpgain - 1.0f) * cs - beta * sn) / tmp; | |||
| d[1] = -2.0f * ((tmpgain - 1.0f) + (tmpgain + 1.0f) * cs) | |||
| / tmp * -1.0f; | |||
| d[2] = ((tmpgain + 1.0f) + (tmpgain - 1.0f) * cs - beta * sn) | |||
| / tmp * -1.0f; | |||
| tgp1 = tmpgain + 1.0f; | |||
| tgm1 = tmpgain - 1.0f; | |||
| tmp = tgp1 + tgm1 * cs + beta * sn; | |||
| c[0] = tmpgain * (tgp1 - tgm1 * cs + beta * sn) / tmp; | |||
| c[1] = 2.0f * tmpgain * (tgm1 - tgp1 * cs) / tmp; | |||
| c[2] = tmpgain * (tgp1 - tgm1 * cs - beta * sn) / tmp; | |||
| d[1] = -2.0f * (tgm1 + tgp1 * cs) / tmp * -1.0f; | |||
| d[2] = (tgp1 + tgm1 * cs - beta * sn) / tmp * -1.0f; | |||
| } | |||
| else { | |||
| c[0] = tmpgain; | |||
| @@ -230,20 +235,15 @@ void AnalogFilter::computefiltercoefs(void) | |||
| if(!zerocoefs) { | |||
| tmpq = sqrtf(tmpq); | |||
| beta = sqrtf(tmpgain) / tmpq; | |||
| tmp = (tmpgain + 1.0f) - (tmpgain - 1.0f) * cs + beta * sn; | |||
| c[0] = tmpgain | |||
| * ((tmpgain | |||
| + 1.0f) + (tmpgain - 1.0f) * cs + beta * sn) / tmp; | |||
| c[1] = -2.0f * tmpgain | |||
| * ((tmpgain - 1.0f) + (tmpgain + 1.0f) * cs) / tmp; | |||
| c[2] = tmpgain | |||
| * ((tmpgain | |||
| + 1.0f) + (tmpgain - 1.0f) * cs - beta * sn) / tmp; | |||
| d[1] = 2.0f * ((tmpgain - 1.0f) - (tmpgain + 1.0f) * cs) | |||
| / tmp * -1.0f; | |||
| d[2] = ((tmpgain + 1.0f) - (tmpgain - 1.0f) * cs - beta * sn) | |||
| / tmp * -1.0f; | |||
| tgp1 = tmpgain + 1.0f; | |||
| tgm1 = tmpgain - 1.0f; | |||
| tmp = tgp1 - tgm1 * cs + beta * sn; | |||
| c[0] = tmpgain * (tgp1 + tgm1 * cs + beta * sn) / tmp; | |||
| c[1] = -2.0f * tmpgain * (tgm1 + tgp1 * cs) / tmp; | |||
| c[2] = tmpgain * (tgp1 + tgm1 * cs - beta * sn) / tmp; | |||
| d[1] = 2.0f * (tgm1 - tgp1 * cs) / tmp * -1.0f; | |||
| d[2] = (tgp1 - tgm1 * cs - beta * sn) / tmp * -1.0f; | |||
| } | |||
| else { | |||
| c[0] = 1.0f; | |||
| @@ -252,10 +252,16 @@ void AnalogFilter::computefiltercoefs(void) | |||
| order = 2; | |||
| break; | |||
| default: //wrong type | |||
| type = 0; | |||
| computefiltercoefs(); | |||
| assert(false && "wrong type for a filter"); | |||
| break; | |||
| } | |||
| return coeff; | |||
| } | |||
| void AnalogFilter::computefiltercoefs(void) | |||
| { | |||
| coeff = AnalogFilter::computeCoeff(type, freq, q, stages, gain, | |||
| samplerate_f, order); | |||
| } | |||
| @@ -46,6 +46,9 @@ class AnalogFilter:public Filter | |||
| d[3]; //Feed Back | |||
| } coeff, oldCoeff; | |||
| static Coeff computeCoeff(int type, float cutoff, float q, int stages, | |||
| float gain, float fs, int &order); | |||
| private: | |||
| struct fstage { | |||
| float x1, x2; //Input History | |||
| @@ -12,11 +12,37 @@ | |||
| */ | |||
| #include <cmath> | |||
| #include <rtosc/port-sugar.h> | |||
| #include <rtosc/ports.h> | |||
| #include "../Misc/Allocator.h" | |||
| #include "Alienwah.h" | |||
| using std::complex; | |||
| #define rObject Alienwah | |||
| #define rBegin [](const char *, rtosc::RtData &) { | |||
| #define rEnd } | |||
| rtosc::Ports Alienwah::ports = { | |||
| {"preset::i", rOptions(Alienwah 1, Alienwah 2, Alienwah 3, Alienwah 4) | |||
| rDoc("Instrument Presets"), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| //Pvolume/Ppanning are common | |||
| rEffPar(Pfreq, 2, rShort("freq"), "Effect Frequency"), | |||
| rEffPar(Pfreqrnd, 3, rShort("rand"), "Frequency Randomness"), | |||
| rEffPar(PLFOtype, 4, rShort("shape"), "LFO Shape"), | |||
| rEffParTF(PStereo, 5, rShort("stereo"), "Stereo/Mono Mode"), | |||
| rEffPar(Pdepth, 6, rShort("depth"), "LFO Depth"), | |||
| rEffPar(Pfeedback, 7, rShort("fb"), "Feedback"), | |||
| rEffPar(Pdelay, 8, rShort("delay"), "Delay"), | |||
| rEffPar(Plrcross, 9, rShort("l/r"), "Left/Right Crossover"), | |||
| rEffPar(Pphase, 10, rShort("phase"), "Phase"), | |||
| }; | |||
| #undef rBegin | |||
| #undef rEnd | |||
| #undef rObject | |||
| Alienwah::Alienwah(EffectParams pars) | |||
| :Effect(pars), | |||
| lfo(pars.srate, pars.bufsize), | |||
| @@ -33,6 +33,7 @@ class Alienwah:public Effect | |||
| unsigned char getpar(int npar) const; | |||
| void cleanup(void); | |||
| static rtosc::Ports ports; | |||
| private: | |||
| //Alienwah Parameters | |||
| EffectLFO lfo; //lfo-ul Alienwah | |||
| @@ -12,12 +12,39 @@ | |||
| */ | |||
| #include <cmath> | |||
| #include <rtosc/ports.h> | |||
| #include <rtosc/port-sugar.h> | |||
| #include "../Misc/Allocator.h" | |||
| #include "Chorus.h" | |||
| #include <iostream> | |||
| using namespace std; | |||
| #define rObject Chorus | |||
| #define rBegin [](const char *, rtosc::RtData &) { | |||
| #define rEnd } | |||
| rtosc::Ports Chorus::ports = { | |||
| {"preset::i", rOptions(Alienwah 1, Alienwah 2, Alienwah 3, Alienwah 4) | |||
| rDoc("Instrument Presets"), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| //Pvolume/Ppanning are common | |||
| rEffPar(Pfreq, 2, rShort("freq"), "Effect Frequency"), | |||
| rEffPar(Pfreqrnd, 3, rShort("rand"), "Frequency Randomness"), | |||
| rEffPar(PLFOtype, 4, rShort("shape"), "LFO Shape"), | |||
| rEffParTF(PStereo,5, rShort("stereo"), "Stereo/Mono Mode"), | |||
| rEffPar(Pdepth, 6, rShort("depth"), "LFO Depth"), | |||
| rEffPar(Pdelay, 7, rShort("delay"), "Delay"), | |||
| rEffPar(Pfeedback,8, rShort("fb"), "Feedback"), | |||
| rEffPar(Plrcross, 9, rShort("l/r"), "Left/Right Crossover"), | |||
| rEffParTF(Pflangemode, 10, rShort("flange"), "Flange Mode"), | |||
| rEffParTF(Poutsub, 11, rShort("sub"), "Output Subtraction"), | |||
| }; | |||
| #undef rBegin | |||
| #undef rEnd | |||
| #undef rObject | |||
| Chorus::Chorus(EffectParams pars) | |||
| :Effect(pars), | |||
| lfo(pars.srate, pars.bufsize), | |||
| @@ -68,6 +68,7 @@ class Chorus:public Effect | |||
| unsigned char getpar(int npar) const; | |||
| void cleanup(void); | |||
| static rtosc::Ports ports; | |||
| private: | |||
| //Chorus Parameters | |||
| unsigned char Pvolume; | |||
| @@ -16,6 +16,33 @@ | |||
| #include "../Misc/WaveShapeSmps.h" | |||
| #include "../Misc/Allocator.h" | |||
| #include <cmath> | |||
| #include <rtosc/ports.h> | |||
| #include <rtosc/port-sugar.h> | |||
| #define rObject Distorsion | |||
| #define rBegin [](const char *, rtosc::RtData &) { | |||
| #define rEnd } | |||
| rtosc::Ports Distorsion::ports = { | |||
| {"preset::i", rOptions(Alienwah 1, Alienwah 2, Alienwah 3, Alienwah 4) | |||
| rDoc("Instrument Presets"), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| //Pvolume/Ppanning are common | |||
| rEffPar(Plrcross, 2, rShort("l/r") "Left/Right Crossover"), | |||
| rEffPar(Pdrive, 3, rShort("drive"), "Input amplification"), | |||
| rEffPar(Plevel, 4, rShort("output"), "Output amplification"), | |||
| rEffPar(Ptype, 5, rShort("type"), "Distortion Shape"), | |||
| rEffParTF(Pnegate, 6, rShort("neg"), "Negate Signal"), | |||
| rEffPar(Plpf, 7, rShort("lpf"), "Low Pass Cutoff"), | |||
| rEffPar(Phpf, 8, rShort("hpf"), "High Pass Cutoff"), | |||
| rEffParTF(Pstereo, 9, rShort("stereo"), "Stereo"), | |||
| rEffParTF(Pprefiltering, 10, rShort("p.filt"), | |||
| "Filtering before/after non-linearity"), | |||
| }; | |||
| #undef rBegin | |||
| #undef rEnd | |||
| #undef rObject | |||
| Distorsion::Distorsion(EffectParams pars) | |||
| :Effect(pars), | |||
| @@ -29,6 +29,7 @@ class Distorsion:public Effect | |||
| void cleanup(void); | |||
| void applyfilters(float *efxoutl, float *efxoutr); | |||
| static rtosc::Ports ports; | |||
| private: | |||
| //Parameters | |||
| unsigned char Pvolume; //Volume or E/R | |||
| @@ -16,6 +16,55 @@ | |||
| #include "DynamicFilter.h" | |||
| #include "../DSP/Filter.h" | |||
| #include "../Misc/Allocator.h" | |||
| #include <rtosc/ports.h> | |||
| #include <rtosc/port-sugar.h> | |||
| #define rObject DynamicFilter | |||
| #define rBegin [](const char *, rtosc::RtData &) { | |||
| #define rEnd } | |||
| rtosc::Ports DynamicFilter::ports = { | |||
| {"preset::i", rOptions(WahWah, AutoWah, Sweep, VocalMorph1, VocalMorph1) | |||
| rDoc("Instrument Presets"), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| //Pvolume/Ppanning are common | |||
| {"Pfreq::i", rShort("freq") | |||
| rDoc("Effect Frequency"), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| {"Pfreqrnd::i", rShort("rand") | |||
| rDoc("Frequency Randomness"), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| {"PLFOtype::i", rShort("shape") | |||
| rDoc("LFO Shape"), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| {"PStereo::T:F", rShort("stereo") | |||
| rDoc("Stereo/Mono Mode"), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| {"Pdepth::i", rShort("depth") | |||
| rDoc("LFO Depth"), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| {"Pampsns::i", rShort("sense") | |||
| rDoc("how the filter varies according to the input amplitude"), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| {"Pampsnsinv::T:F", rShort("sns.inv") | |||
| rDoc("Sense Inversion"), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| {"Pampsmooth::i", rShort("smooth") | |||
| rDoc("how smooth the input amplitude changes the filter"), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| }; | |||
| #undef rBegin | |||
| #undef rEnd | |||
| #undef rObject | |||
| DynamicFilter::DynamicFilter(EffectParams pars, const AbsTime *time) | |||
| :Effect(pars), | |||
| @@ -30,6 +30,7 @@ class DynamicFilter:public Effect | |||
| unsigned char getpar(int npar) const; | |||
| void cleanup(void); | |||
| static rtosc::Ports ports; | |||
| private: | |||
| //Parametrii DynamicFilter | |||
| EffectLFO lfo; //lfo-ul DynamicFilter | |||
| @@ -14,11 +14,34 @@ | |||
| */ | |||
| #include <cmath> | |||
| #include <rtosc/ports.h> | |||
| #include <rtosc/port-sugar.h> | |||
| #include "../Misc/Allocator.h" | |||
| #include "Echo.h" | |||
| #define MAX_DELAY 2 | |||
| #define rObject Echo | |||
| #define rBegin [](const char *, rtosc::RtData &) { | |||
| #define rEnd } | |||
| rtosc::Ports Echo::ports = { | |||
| {"preset::i", rOptions(Echo 1, Echo 2, Echo 3, Simple Echo, Canyon, Panning Echo 1, Panning Echo 2, Panning Echo 3, Feedback Echo) | |||
| rProp(parameter) | |||
| rDoc("Instrument Presets"), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| //Pvolume/Ppanning are common | |||
| rEffPar(Pdelay, 2, rShort("delay"), "Length of Echo"), | |||
| rEffPar(Plrdelay, 3, rShort("lr delay"), "Difference In Left/Right Delay"), | |||
| rEffPar(Plrcross, 4, rShort("cross"), "Left/Right Crossover"), | |||
| rEffPar(Pfb, 5, rShort("feedback"), "Echo Feedback"), | |||
| rEffPar(Phidamp, 6, rShort("damp"), "Dampen High Frequencies"), | |||
| }; | |||
| #undef rBegin | |||
| #undef rEnd | |||
| #undef rObject | |||
| Echo::Echo(EffectParams pars) | |||
| :Effect(pars), | |||
| Pvolume(50), | |||
| @@ -59,6 +59,8 @@ class Echo:public Effect | |||
| unsigned char getpar(int npar) const; | |||
| int getnumparams(void); | |||
| void cleanup(void); | |||
| static rtosc::Ports ports; | |||
| private: | |||
| //Parameters | |||
| unsigned char Pvolume; /**<#1 Volume or Dry/Wetness*/ | |||
| @@ -22,6 +22,27 @@ | |||
| class FilterParams; | |||
| class Allocator; | |||
| #ifndef rEffPar | |||
| #define rEffPar(name, idx, ...) \ | |||
| {STRINGIFY(name) "::i", rProp(parameter) DOC(__VA_ARGS__), NULL, rEffParCb(idx)} | |||
| #define rEffParTF(name, idx, ...) \ | |||
| {STRINGIFY(name) "::T:F", rProp(parameter) DOC(__VA_ARGS__), NULL, rEffParTFCb(idx)} | |||
| #define rEffParCb(idx) \ | |||
| [](const char *msg, rtosc::RtData &d) {\ | |||
| rObject &obj = *(rObject*)d.obj; \ | |||
| if(rtosc_narguments(msg)) \ | |||
| obj.changepar(idx, rtosc_argument(msg, 0).i); \ | |||
| else \ | |||
| d.reply(d.loc, "i", obj.getpar(idx));} | |||
| #define rEffParTFCb(idx) \ | |||
| [](const char *msg, rtosc::RtData &d) {\ | |||
| rObject &obj = *(rObject*)d.obj; \ | |||
| if(rtosc_narguments(msg)) \ | |||
| obj.changepar(idx, rtosc_argument(msg, 0).T*127); \ | |||
| else \ | |||
| d.reply(d.loc, obj.getpar(idx)?"T":"F");} | |||
| #endif | |||
| struct EffectParams | |||
| { | |||
| /** | |||
| @@ -33,6 +33,14 @@ | |||
| #define rObject EffectMgr | |||
| #define rSubtype(name) \ | |||
| {STRINGIFY(name)"/", NULL, &name::ports,\ | |||
| [](const char *msg, rtosc::RtData &data){\ | |||
| rObject &o = *(rObject*)data.obj; \ | |||
| data.obj = o.efx; \ | |||
| SNIP \ | |||
| name::ports.dispatch(msg, data); \ | |||
| }} | |||
| static const rtosc::Ports local_ports = { | |||
| rSelf(EffectMgr), | |||
| rPaste, | |||
| @@ -71,7 +79,7 @@ static const rtosc::Ports local_ports = { | |||
| //update parameters as well | |||
| strncpy(loc, d.loc, 1024); | |||
| char *tail = rindex(loc, '/'); | |||
| char *tail = strrchr(loc, '/'); | |||
| if(!tail) | |||
| return; | |||
| for(int i=0;i<128;++i) { | |||
| @@ -94,7 +102,9 @@ static const rtosc::Ports local_ports = { | |||
| eq->getFilter(a,b); | |||
| d.reply(d.loc, "bb", sizeof(a), a, sizeof(b), b); | |||
| }}, | |||
| {"efftype::i", rProp(parameter) rDoc("Get Effect Type"), NULL, | |||
| {"efftype::i", rOptions(Disabled, Reverb, Echo, Chorus, | |||
| Phaser, Alienwah, Distorsion, EQ, DynamicFilter) | |||
| rProp(parameter) rDoc("Get Effect Type"), NULL, | |||
| [](const char *m, rtosc::RtData &d) | |||
| { | |||
| EffectMgr *eff = (EffectMgr*)d.obj; | |||
| @@ -121,7 +131,9 @@ static const rtosc::Ports local_ports = { | |||
| //Return the old data for distruction | |||
| d.reply("/free", "sb", "EffectMgr", sizeof(EffectMgr*), &eff_); | |||
| }}, | |||
| rSubtype(Echo), | |||
| rSubtype(Alienwah), | |||
| rSubtype(Distorsion), | |||
| }; | |||
| const rtosc::Ports &EffectMgr::ports = local_ports; | |||
| @@ -17,11 +17,43 @@ | |||
| #include <cmath> | |||
| #include <algorithm> | |||
| #include <rtosc/ports.h> | |||
| #include <rtosc/port-sugar.h> | |||
| #include "../Misc/Allocator.h" | |||
| #include "Phaser.h" | |||
| using namespace std; | |||
| #define rObject Phaser | |||
| #define rBegin [](const char *, rtosc::RtData &) { | |||
| #define rEnd } | |||
| rtosc::Ports Phaser::ports = { | |||
| {"preset::i", rOptions(Alienwah 1, Alienwah 2, Alienwah 3, Alienwah 4) | |||
| rDoc("Instrument Presets"), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| //Pvolume/Ppanning are common | |||
| rEffPar(lfo.Pfreq, 2, ""), | |||
| rEffPar(lfo.Prandomness, 3, ""), | |||
| rEffPar(lfo.PLFOtype, 4, ""), | |||
| rEffPar(lfo.Pstereo, 5, ""), | |||
| rEffPar(Pdepth, 6, ""), | |||
| rEffPar(Pfb, 7, ""), | |||
| rEffPar(Pstages, 8, ""), | |||
| rEffPar(Plrcross, 9, ""), | |||
| rEffPar(Poffset, 9, ""), | |||
| rEffParTF(Poutsub, 10, ""), | |||
| rEffPar(Pphase, 11, ""), | |||
| rEffPar(Pwidth, 11, ""), | |||
| rEffPar(Phyper, 12, ""), | |||
| rEffPar(Pdistortion, 13, ""), | |||
| rEffPar(Panalog, 14, ""), | |||
| }; | |||
| #undef rBegin | |||
| #undef rEnd | |||
| #undef rObject | |||
| #define PHASER_LFO_SHAPE 2 | |||
| #define ONE_ 0.99999f // To prevent LFO ever reaching 1.0f for filter stability purposes | |||
| #define ZERO_ 0.00001f // Same idea as above. | |||
| @@ -34,6 +34,7 @@ class Phaser:public Effect | |||
| unsigned char getpar(int npar) const; | |||
| void cleanup(); | |||
| static rtosc::Ports ports; | |||
| private: | |||
| //Phaser parameters | |||
| EffectLFO lfo; //Phaser modulator | |||
| @@ -29,6 +29,7 @@ | |||
| #include "Config.h" | |||
| #include "Util.h" | |||
| #include "Part.h" | |||
| #include "BankDb.h" | |||
| #define INSTRUMENT_EXTENSION ".xiz" | |||
| @@ -39,7 +40,7 @@ using namespace std; | |||
| Bank::Bank(Config *config) | |||
| :bankpos(0), defaultinsname(" "), config(config), | |||
| bank_msb(0), bank_lsb(0) | |||
| db(new BankDb), bank_msb(0), bank_lsb(0) | |||
| { | |||
| clearbank(); | |||
| bankfiletitle = dirname; | |||
| @@ -57,6 +58,7 @@ Bank::Bank(Config *config) | |||
| Bank::~Bank() | |||
| { | |||
| clearbank(); | |||
| delete db; | |||
| } | |||
| /* | |||
| @@ -179,7 +181,8 @@ int Bank::savetoslot(unsigned int ninstrument, Part *part) | |||
| err = part->saveXML(filename.c_str()); | |||
| if(err) | |||
| return err; | |||
| addtobank(ninstrument, legalizeFilename(tmpfilename) + ".xiz", (char *) part->Pname); | |||
| addtobank(ninstrument, legalizeFilename(tmpfilename) + ".xiz", | |||
| (char *) part->Pname); | |||
| return 0; | |||
| } | |||
| @@ -350,6 +353,7 @@ bool Bank::bankstruct::operator<(const bankstruct &b) const | |||
| void Bank::rescanforbanks() | |||
| { | |||
| db->clear(); | |||
| //remove old banks | |||
| banks.clear(); | |||
| @@ -362,6 +366,7 @@ void Bank::rescanforbanks() | |||
| //remove duplicate bank names | |||
| for(int j = 0; j < (int) banks.size() - 1; ++j) { | |||
| db->addBankDir(banks[j].dir); | |||
| int dupl = 0; | |||
| for(int i = j + 1; i < (int) banks.size(); ++i) { | |||
| if(banks[i].name == banks[j].name) { | |||
| @@ -376,6 +381,7 @@ void Bank::rescanforbanks() | |||
| if(dupl) | |||
| j += dupl; | |||
| } | |||
| db->scanBanks(); | |||
| } | |||
| void Bank::setMsb(uint8_t msb) | |||
| @@ -453,6 +459,17 @@ void Bank::clearbank() | |||
| dirname.clear(); | |||
| } | |||
| std::vector<std::string> Bank::search(std::string s) const | |||
| { | |||
| std::vector<std::string> out; | |||
| auto vec = db->search(s); | |||
| for(auto e:vec) { | |||
| out.push_back(e.name); | |||
| out.push_back(e.bank+e.file); | |||
| } | |||
| return out; | |||
| } | |||
| int Bank::addtobank(int pos, string filename, string name) | |||
| { | |||
| if((pos >= 0) && (pos < BANK_SIZE)) { | |||
| @@ -68,7 +68,7 @@ class Bank | |||
| std::vector<bankstruct> banks; | |||
| int bankpos; | |||
| struct ins_t { | |||
| ins_t(void); | |||
| std::string name; | |||
| @@ -76,6 +76,8 @@ class Bank | |||
| std::string filename; | |||
| } ins[BANK_SIZE]; | |||
| std::vector<std::string> search(std::string) const; | |||
| private: | |||
| //it adds a filename to the bank | |||
| @@ -100,6 +102,7 @@ class Bank | |||
| void normalizedirsuffix(std::string &dirname) const; | |||
| Config* const config; | |||
| class BankDb *db; | |||
| public: | |||
| uint8_t bank_msb; | |||
| @@ -0,0 +1,219 @@ | |||
| #include "BankDb.h" | |||
| #include "XMLwrapper.h" | |||
| #include "../globals.h" | |||
| #include <cstring> | |||
| #include <dirent.h> | |||
| #define INSTRUMENT_EXTENSION ".xiz" | |||
| using std::string; | |||
| typedef BankDb::svec svec; | |||
| typedef BankDb::bvec bvec; | |||
| BankEntry::BankEntry(void) | |||
| :id(0), add(false), pad(false), sub(false) | |||
| {} | |||
| bool sfind(std::string hay, std::string needle) | |||
| { | |||
| return strcasestr(hay.c_str(), needle.c_str()); | |||
| } | |||
| bool BankEntry::match(string s) const | |||
| { | |||
| if(s == "#pad") | |||
| return pad; | |||
| else if(s == "#sub") | |||
| return sub; | |||
| else if(s == "#add") | |||
| return add; | |||
| return sfind(file,s) || sfind(name,s) || sfind(bank, s) || | |||
| sfind(type, s) || sfind(comments,s) || sfind(author,s); | |||
| } | |||
| static svec split(string s) | |||
| { | |||
| svec vec; | |||
| string ss; | |||
| for(char c:s) { | |||
| if(isspace(c) && !ss.empty()) { | |||
| vec.push_back(ss); | |||
| ss.clear(); | |||
| } else if(!isspace(c)) { | |||
| ss.push_back(c); | |||
| } | |||
| } | |||
| if(!ss.empty()) | |||
| vec.push_back(ss); | |||
| return vec; | |||
| } | |||
| static string line(string s) | |||
| { | |||
| string ss; | |||
| for(char c:s) { | |||
| if(c != '\n') | |||
| ss.push_back(c); | |||
| else | |||
| return ss; | |||
| } | |||
| return ss; | |||
| } | |||
| bvec BankDb::search(std::string ss) const | |||
| { | |||
| bvec vec; | |||
| const svec sterm = split(ss); | |||
| for(auto field:fields) { | |||
| bool match = true; | |||
| for(auto s:sterm) | |||
| match &= field.match(s); | |||
| if(match) | |||
| vec.push_back(field); | |||
| } | |||
| return vec; | |||
| } | |||
| void BankDb::addBankDir(std::string bnk) | |||
| { | |||
| bool repeat = false; | |||
| for(auto b:banks) | |||
| repeat |= b == bnk; | |||
| if(!repeat) | |||
| banks.push_back(bnk); | |||
| } | |||
| void BankDb::clear(void) | |||
| { | |||
| banks.clear(); | |||
| fields.clear(); | |||
| } | |||
| void BankDb::scanBanks(void) | |||
| { | |||
| fields.clear(); | |||
| for(auto bank:banks) | |||
| { | |||
| DIR *dir = opendir(bank.c_str()); | |||
| if(!dir) | |||
| continue; | |||
| struct dirent *fn; | |||
| while((fn = readdir(dir))) { | |||
| const char *filename = fn->d_name; | |||
| //check for extension | |||
| if(!strstr(filename, INSTRUMENT_EXTENSION)) | |||
| continue; | |||
| auto xiz = processXiz(filename, bank); | |||
| fields.push_back(xiz); | |||
| } | |||
| closedir(dir); | |||
| } | |||
| } | |||
| BankEntry BankDb::processXiz(std::string filename, std::string bank) const | |||
| { | |||
| //verify if the name is like this NNNN-name (where N is a digit) | |||
| int no = 0; | |||
| unsigned int startname = 0; | |||
| for(unsigned int i = 0; i < 4; ++i) { | |||
| if(filename.length() <= i) | |||
| break; | |||
| if(isdigit(filename[i])) { | |||
| no = no * 10 + (filename[i] - '0'); | |||
| startname++; | |||
| } | |||
| } | |||
| if(startname + 1 < filename.length()) | |||
| startname++; //to take out the "-" | |||
| std::string name = filename; | |||
| //remove the file extension | |||
| for(int i = name.size() - 1; i >= 2; i--) { | |||
| if(name[i] == '.') { | |||
| name = name.substr(0, i); | |||
| break; | |||
| } | |||
| } | |||
| BankEntry entry; | |||
| entry.file = filename; | |||
| entry.bank = bank; | |||
| entry.id = no; | |||
| if(no != 0) //the instrument position in the bank is found | |||
| entry.name = name.substr(startname); | |||
| else | |||
| entry.name = name; | |||
| const char *types[] = { | |||
| "None", | |||
| "Piano", | |||
| "Chromatic Percussion", | |||
| "Organ", | |||
| "Guitar", | |||
| "Bass", | |||
| "Solo Strings", | |||
| "Ensemble", | |||
| "Brass", | |||
| "Reed", | |||
| "Pipe", | |||
| "Synth Lead", | |||
| "Synth Pad", | |||
| "Synth Effects", | |||
| "Ethnic", | |||
| "Percussive", | |||
| "Sound Effects", | |||
| }; | |||
| //Try to obtain other metadata (expensive) | |||
| XMLwrapper xml; | |||
| string fname = bank+filename; | |||
| int ret = xml.loadXMLfile(fname); | |||
| if(xml.enterbranch("INSTRUMENT")) { | |||
| if(xml.enterbranch("INFO")) { | |||
| char author[1024]; | |||
| char comments[1024]; | |||
| int type = 0; | |||
| xml.getparstr("author", author, 1024); | |||
| xml.getparstr("comments", comments, 1024); | |||
| type = xml.getpar("type", 0, 0, 16); | |||
| entry.author = author; | |||
| entry.comments = comments; | |||
| entry.type = types[type]; | |||
| xml.exitbranch(); | |||
| } | |||
| if(xml.enterbranch("INSTRUMENT_KIT")) { | |||
| for(int i = 0; i < NUM_KIT_ITEMS; ++i) { | |||
| if(xml.enterbranch("INSTRUMENT_KIT_ITEM", i) == 0) { | |||
| entry.add |= xml.getparbool("add_enabled", false); | |||
| entry.sub |= xml.getparbool("sub_enabled", false); | |||
| entry.pad |= xml.getparbool("pad_enabled", false); | |||
| xml.exitbranch(); | |||
| } | |||
| } | |||
| xml.exitbranch(); | |||
| } | |||
| xml.exitbranch(); | |||
| } | |||
| //printf("Bank Entry:\n"); | |||
| //printf("\tname - %s\n", entry.name.c_str()); | |||
| //printf("\tauthor - %s\n", line(entry.author).c_str()); | |||
| //printf("\tadd/pad/sub - %d/%d/%d\n", entry.add, entry.pad, entry.sub); | |||
| return entry; | |||
| } | |||
| @@ -0,0 +1,50 @@ | |||
| #pragma once | |||
| #include <string> | |||
| #include <vector> | |||
| struct BankEntry | |||
| { | |||
| BankEntry(void); | |||
| std::string file; | |||
| std::string bank; | |||
| std::string name; | |||
| std::string comments; | |||
| std::string author; | |||
| std::string type; | |||
| int id; | |||
| bool add; | |||
| bool pad; | |||
| bool sub; | |||
| typedef std::vector<std::string> svec; | |||
| svec tags(void) const; | |||
| bool match(std::string) const; | |||
| }; | |||
| class BankDb | |||
| { | |||
| public: | |||
| typedef std::vector<std::string> svec; | |||
| typedef std::vector<BankEntry> bvec; | |||
| //search for banks | |||
| //uses a space separated list of keywords and | |||
| //finds something that matches ALL keywords | |||
| bvec search(std::string) const; | |||
| //fully qualified paths only | |||
| void addBankDir(std::string); | |||
| //clear all known entries and banks | |||
| void clear(void); | |||
| //List of all tags | |||
| svec tags(void) const; | |||
| //scan banks | |||
| void scanBanks(void); | |||
| private: | |||
| BankEntry processXiz(std::string, std::string) const; | |||
| bvec fields; | |||
| svec banks; | |||
| }; | |||
| @@ -22,6 +22,7 @@ | |||
| #include "../Effects/EffectMgr.h" | |||
| #include "../DSP/FFTwrapper.h" | |||
| #include "../Misc/Allocator.h" | |||
| #include "../Containers/ScratchString.h" | |||
| #include "../Nio/Nio.h" | |||
| #include "PresetExtractor.h" | |||
| @@ -74,7 +75,7 @@ static const Ports sysefxPort = | |||
| static const Ports sysefsendto = | |||
| { | |||
| {"to#" STRINGIFY(NUM_SYS_EFX) "::i", | |||
| {"to#" STRINGIFY(NUM_SYS_EFX) "::i", | |||
| rProp(parameter) rDoc("sysefx to sysefx routing gain"), 0, [](const char *m, RtData&d) | |||
| { | |||
| //same ugly workaround as before | |||
| @@ -97,6 +98,17 @@ static const Ports sysefsendto = | |||
| }} | |||
| }; | |||
| #define rBegin [](const char *msg, RtData &d) { Master *m = (Master*)d.obj | |||
| #define rEnd } | |||
| static const Ports watchPorts = { | |||
| {"add:s", rDoc("Add synthesis state to watch"), 0, | |||
| rBegin; | |||
| m->watcher.add_watch(rtosc_argument(msg,0).s); | |||
| rEnd}, | |||
| }; | |||
| extern const Ports bankPorts; | |||
| static const Ports master_ports = { | |||
| rString(last_xmz, XMZ_PATH_MAX, "File name for last name loaded if any."), | |||
| rRecursp(part, 16, "Part"),//NUM_MIDI_PARTS | |||
| @@ -104,8 +116,12 @@ static const Ports master_ports = { | |||
| rRecursp(insefx, 8, "Insertion Effect"),//NUM_INS_EFX | |||
| rRecur(microtonal, "Micrtonal Mapping Functionality"), | |||
| rRecur(ctl, "Controller"), | |||
| rArrayI(Pinsparts, NUM_INS_EFX, "Part to insert part onto"), | |||
| {"Pkeyshift::i", rProp(parameter) rLinear(0,127) rDoc("Global Key Shift"), 0, [](const char *m, RtData&d) { | |||
| rArrayI(Pinsparts, NUM_INS_EFX, rOpt(-1, Master), | |||
| rOptions(Part1, Part2, Part3, Part4, Part5, Part6, | |||
| Part7, Part8, Part9, Part10, Part11, Part12, | |||
| Part13, Part14, Part15, Part16), | |||
| "Part to insert part onto"), | |||
| {"Pkeyshift::i", rShort("key shift") rProp(parameter) rLinear(0,127) rDoc("Global Key Shift"), 0, [](const char *m, RtData&d) { | |||
| if(rtosc_narguments(m)==0) { | |||
| d.reply(d.loc, "i", ((Master*)d.obj)->Pkeyshift); | |||
| } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') { | |||
| @@ -116,6 +132,22 @@ static const Ports master_ports = { | |||
| {"get-vu:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) { | |||
| Master *m = (Master*)d.obj; | |||
| d.reply("/vu-meter", "bb", sizeof(m->vu), &m->vu, sizeof(float)*NUM_MIDI_PARTS, m->vuoutpeakpart);}}, | |||
| {"vu-meter:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) { | |||
| Master *m = (Master*)d.obj; | |||
| char types[6+NUM_MIDI_PARTS+1] = {0}; | |||
| rtosc_arg_t args[6+NUM_MIDI_PARTS+1]; | |||
| for(int i=0; i<6+NUM_MIDI_PARTS; ++i) | |||
| types[i] = 'f'; | |||
| args[0].f = m->vu.outpeakl; | |||
| args[1].f = m->vu.outpeakr; | |||
| args[2].f = m->vu.maxoutpeakl; | |||
| args[3].f = m->vu.maxoutpeakr; | |||
| args[4].f = m->vu.rmspeakl; | |||
| args[5].f = m->vu.rmspeakr; | |||
| for(int i=0; i<NUM_MIDI_PARTS; ++i) | |||
| args[6+i].f = m->vuoutpeakpart[i]; | |||
| d.replyArray("/vu-meter", types, args);}}, | |||
| {"reset-vu:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) { | |||
| Master *m = (Master*)d.obj; | |||
| m->vuresetpeaks();}}, | |||
| @@ -129,14 +161,21 @@ static const Ports master_ports = { | |||
| m->part[i] = p; | |||
| p->initialize_rt(); | |||
| }}, | |||
| {"Pvolume::i", rProp(parameter) rLinear(0,127) rDoc("Master Volume"), 0, | |||
| {"active_keys:", rProp("Obtain a list of active notes"), 0, | |||
| rBegin; | |||
| char keys[129] = {0}; | |||
| for(int i=0; i<128; ++i) | |||
| keys[i] = m->activeNotes[i] ? 'T' : 'F'; | |||
| d.broadcast(d.loc, keys); | |||
| rEnd}, | |||
| {"Pvolume::i", rShort("volume") rProp(parameter) rLinear(0,127) rDoc("Master Volume"), 0, | |||
| [](const char *m, rtosc::RtData &d) { | |||
| if(rtosc_narguments(m)==0) { | |||
| d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume); | |||
| } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') { | |||
| ((Master*)d.obj)->setPvolume(limit<char>(rtosc_argument(m,0).i,0,127)); | |||
| d.broadcast(d.loc, "i", ((Master*)d.obj)->Pvolume);}}}, | |||
| {"volume::i", rProp(parameter) rLinear(0,127) rDoc("Master Volume"), 0, | |||
| {"volume::i", rShort("volume") rProp(parameter) rLinear(0,127) rDoc("Master Volume"), 0, | |||
| [](const char *m, rtosc::RtData &d) { | |||
| if(rtosc_narguments(m)==0) { | |||
| d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume); | |||
| @@ -240,8 +279,18 @@ static const Ports master_ports = { | |||
| {"HDDRecorder/pause:", rDoc("Pause recording"), 0, [](const char *, RtData &d) { | |||
| Master *m = (Master*)d.obj; | |||
| m->HDDRecorder.pause();}}, | |||
| {"watch/", rDoc("Interface to grab out live synthesis state"), &watchPorts, | |||
| rBOIL_BEGIN; | |||
| SNIP; | |||
| watchPorts.dispatch(msg, data); | |||
| rBOIL_END}, | |||
| {"bank/", rDoc("Controls for instrument banks"), &bankPorts, | |||
| [](const char*,RtData&) {}}, | |||
| }; | |||
| #undef rBegin | |||
| #undef rEnd | |||
| const Ports &Master::ports = master_ports; | |||
| class DataObj:public rtosc::RtData | |||
| @@ -257,6 +306,12 @@ class DataObj:public rtosc::RtData | |||
| forwarded = false; | |||
| } | |||
| virtual void replyArray(const char *path, const char *args, rtosc_arg_t *vals) override | |||
| { | |||
| char *buffer = bToU->buffer(); | |||
| rtosc_amessage(buffer,bToU->buffer_size(),path,args,vals); | |||
| reply(buffer); | |||
| } | |||
| virtual void reply(const char *path, const char *args, ...) override | |||
| { | |||
| va_list va; | |||
| @@ -333,9 +388,11 @@ Master::Master(const SYNTH_T &synth_, Config* config) | |||
| fakepeakpart[npart] = 0; | |||
| } | |||
| ScratchString ss; | |||
| for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) | |||
| part[npart] = new Part(*memory, synth, time, config->cfg.GzipCompression, | |||
| config->cfg.Interpolation, µtonal, fft); | |||
| config->cfg.Interpolation, µtonal, fft, &watcher, | |||
| (ss+"/part"+npart+"/").c_str); | |||
| //Insertion Effects init | |||
| for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx) | |||
| @@ -345,6 +402,9 @@ Master::Master(const SYNTH_T &synth_, Config* config) | |||
| for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) | |||
| sysefx[nefx] = new EffectMgr(*memory, synth, 0, &time); | |||
| //Note Visualization | |||
| for(int i=0; i<128; ++i) | |||
| activeNotes[i] = 0; | |||
| defaults(); | |||
| @@ -358,7 +418,7 @@ void Master::applyOscEvent(const char *msg) | |||
| DataObj d{loc_buf, 1024, this, bToU}; | |||
| memset(loc_buf, 0, sizeof(loc_buf)); | |||
| d.matches = 0; | |||
| if(strcmp(msg, "/get-vu") && false) { | |||
| fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 5 + 30, 0 + 40); | |||
| fprintf(stdout, "backend[*]: '%s'<%s>\n", msg, | |||
| @@ -411,12 +471,14 @@ void Master::defaults() | |||
| void Master::noteOn(char chan, char note, char velocity) | |||
| { | |||
| if(velocity) { | |||
| for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) | |||
| for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) { | |||
| if(chan == part[npart]->Prcvchn) { | |||
| fakepeakpart[npart] = velocity * 2; | |||
| if(part[npart]->Penabled) | |||
| part[npart]->NoteOn(note, velocity, keyshift); | |||
| } | |||
| } | |||
| activeNotes[(int)note] = 1; | |||
| } | |||
| else | |||
| this->noteOff(chan, note); | |||
| @@ -431,6 +493,7 @@ void Master::noteOff(char chan, char note) | |||
| for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) | |||
| if((chan == part[npart]->Prcvchn) && part[npart]->Penabled) | |||
| part[npart]->NoteOff(note); | |||
| activeNotes[(int)note] = 0; | |||
| } | |||
| /* | |||
| @@ -633,6 +696,13 @@ bool Master::AudioOut(float *outr, float *outl) | |||
| bToU->write("/request-memory", ""); | |||
| pendingMemory = true; | |||
| } | |||
| //Handle watch points | |||
| if(bToU) | |||
| watcher.write_back = bToU; | |||
| watcher.tick(); | |||
| //Handle user events TODO move me to a proper location | |||
| char loc_buf[1024]; | |||
| DataObj d{loc_buf, 1024, this, bToU}; | |||
| @@ -677,7 +747,7 @@ bool Master::AudioOut(float *outr, float *outl) | |||
| } | |||
| if(events>1 && false) | |||
| fprintf(stderr, "backend: %d events per cycle\n",events); | |||
| //Swaps the Left channel with Right Channel | |||
| if(swaplr) | |||
| @@ -24,6 +24,7 @@ | |||
| #include "Recorder.h" | |||
| #include "../Params/Controller.h" | |||
| #include "../Synth/WatchPoint.h" | |||
| class Allocator; | |||
| @@ -158,6 +159,13 @@ class Master | |||
| //Statistics on output levels | |||
| vuData vu; | |||
| //Display info on midi notes | |||
| bool activeNotes[128]; | |||
| //Other watchers | |||
| WatchManager watcher; | |||
| //Midi Learn | |||
| rtosc::MidiMapperRT midi; | |||
| bool frozenState;//read-only parameters for threadsafe actions | |||
| @@ -73,8 +73,8 @@ void path_search(const char *m, const char *url) | |||
| using rtosc::Port; | |||
| //assumed upper bound of 32 ports (may need to be resized) | |||
| char types[129]; | |||
| rtosc_arg_t args[128]; | |||
| char types[256+1]; | |||
| rtosc_arg_t args[256]; | |||
| size_t pos = 0; | |||
| const Ports *ports = NULL; | |||
| const char *str = rtosc_argument(m,0).s; | |||
| @@ -95,7 +95,7 @@ void path_search(const char *m, const char *url) | |||
| if(ports) { | |||
| //RTness not confirmed here | |||
| for(const Port &p:*ports) { | |||
| if(strstr(p.name, needle)!=p.name) | |||
| if(strstr(p.name, needle) != p.name || !p.name) | |||
| continue; | |||
| types[pos] = 's'; | |||
| args[pos++].s = p.name; | |||
| @@ -120,6 +120,8 @@ void path_search(const char *m, const char *url) | |||
| lo_address addr = lo_address_new_from_url(url); | |||
| if(addr) | |||
| lo_send_message(addr, buffer, msg); | |||
| lo_address_free(addr); | |||
| lo_message_free(msg); | |||
| } | |||
| } | |||
| @@ -137,7 +139,7 @@ static int handler_function(const char *path, const char *types, lo_arg **argv, | |||
| mw->transmitMsg("/echo", "ss", "OSC_URL", tmp); | |||
| mw->activeUrl(tmp); | |||
| } | |||
| free((void*)tmp); | |||
| } | |||
| char buffer[2048]; | |||
| @@ -146,7 +148,7 @@ static int handler_function(const char *path, const char *types, lo_arg **argv, | |||
| lo_message_serialise(msg, path, buffer, &size); | |||
| if(!strcmp(buffer, "/path-search") && !strcmp("ss", rtosc_argument_string(buffer))) { | |||
| path_search(buffer, mw->activeUrl().c_str()); | |||
| } else if(buffer[0]=='/' && rindex(buffer, '/')[1]) { | |||
| } else if(buffer[0]=='/' && strrchr(buffer, '/')[1]) { | |||
| mw->transmitMsg(rtosc::Ports::collapsePath(buffer)); | |||
| } | |||
| @@ -155,6 +157,16 @@ static int handler_function(const char *path, const char *types, lo_arg **argv, | |||
| typedef void(*cb_t)(void*,const char*); | |||
| //utility method (should be moved to a better location) | |||
| template <class T, class V> | |||
| std::vector<T> keys(const std::map<T,V> &m) | |||
| { | |||
| std::vector<T> vec; | |||
| for(auto &kv: m) | |||
| vec.push_back(kv.first); | |||
| return vec; | |||
| } | |||
| /***************************************************************************** | |||
| * Memory Deallocation * | |||
| @@ -484,7 +496,8 @@ public: | |||
| master->time, | |||
| config->cfg.GzipCompression, | |||
| config->cfg.Interpolation, | |||
| &master->microtonal, master->fft); | |||
| &master->microtonal, master->fft, &master->watcher, | |||
| ("/part"+to_s(npart)+"/").c_str()); | |||
| if(p->loadXMLinstrument(filename)) | |||
| fprintf(stderr, "Warning: failed to load part<%s>!\n", filename); | |||
| @@ -662,6 +675,11 @@ public: | |||
| void write(const char *path, const char *args, ...); | |||
| void write(const char *path, const char *args, va_list va); | |||
| void currentUrl(string addr) | |||
| { | |||
| curr_url = addr; | |||
| known_remotes.insert(addr); | |||
| } | |||
| // Send a message to a remote client | |||
| void sendToRemote(const char *msg, std::string dest); | |||
| @@ -720,6 +738,7 @@ public: | |||
| //LIBLO | |||
| lo_server server; | |||
| string last_url, curr_url; | |||
| std::set<string> known_remotes; | |||
| //Synthesis Rate Parameters | |||
| const SYNTH_T synth; | |||
| @@ -750,6 +769,7 @@ class MwDataObj:public rtosc::RtData | |||
| ~MwDataObj(void) | |||
| { | |||
| delete[] loc; | |||
| delete[] buffer; | |||
| } | |||
| @@ -773,6 +793,17 @@ class MwDataObj:public rtosc::RtData | |||
| } | |||
| va_end(va); | |||
| } | |||
| virtual void replyArray(const char *path, const char *args, rtosc_arg_t *argd) override | |||
| { | |||
| //printf("reply building '%s'\n", path); | |||
| if(!strcmp(path, "/forward")) { //forward the information to the backend | |||
| args++; | |||
| rtosc_amessage(buffer,4*4096,path,args,argd); | |||
| } else { | |||
| rtosc_amessage(buffer,4*4096,path,args,argd); | |||
| reply(buffer); | |||
| } | |||
| } | |||
| virtual void reply(const char *msg){ | |||
| mwi->sendToCurrentRemote(msg); | |||
| }; | |||
| @@ -841,7 +872,8 @@ using rtosc::RtData; | |||
| * - Load Bank * | |||
| * - Refresh List of Banks * | |||
| *****************************************************************************/ | |||
| rtosc::Ports bankPorts = { | |||
| extern const rtosc::Ports bankPorts; | |||
| const rtosc::Ports bankPorts = { | |||
| {"rescan:", 0, 0, | |||
| rBegin; | |||
| impl.rescanforbanks(); | |||
| @@ -851,6 +883,48 @@ rtosc::Ports bankPorts = { | |||
| d.reply("/bank/bank_select", "iss", i++, elm.name.c_str(), elm.dir.c_str()); | |||
| d.reply("/bank/bank_select", "i", impl.bankpos); | |||
| rEnd}, | |||
| {"bank_list:", 0, 0, | |||
| rBegin; | |||
| #define MAX_BANKS 256 | |||
| char types[MAX_BANKS*2+1]={0}; | |||
| rtosc_arg_t args[MAX_BANKS*2]; | |||
| int i = 0; | |||
| for(auto &elm : impl.banks) { | |||
| types[i] = types [i + 1] = 's'; | |||
| args[i++].s = elm.name.c_str(); | |||
| args[i++].s = elm.dir.c_str(); | |||
| } | |||
| d.replyArray("/bank/bank_list", types, args); | |||
| #undef MAX_BANKS | |||
| rEnd}, | |||
| {"types:", 0, 0, | |||
| rBegin; | |||
| const char *types[17]; | |||
| types[ 0] = "None"; | |||
| types[ 1] = "Piano"; | |||
| types[ 2] = "Chromatic Percussion"; | |||
| types[ 3] = "Organ"; | |||
| types[ 4] = "Guitar"; | |||
| types[ 5] = "Bass"; | |||
| types[ 6] = "Solo Strings"; | |||
| types[ 7] = "Ensemble"; | |||
| types[ 8] = "Brass"; | |||
| types[ 9] = "Reed"; | |||
| types[10] = "Pipe"; | |||
| types[11] = "Synth Lead"; | |||
| types[12] = "Synth Pad"; | |||
| types[13] = "Synth Effects"; | |||
| types[14] = "Ethnic"; | |||
| types[15] = "Percussive"; | |||
| types[16] = "Sound Effects"; | |||
| char t[17+1]={0}; | |||
| rtosc_arg_t args[17]; | |||
| for(int i=0; i<17; ++i) { | |||
| t[i] = 's'; | |||
| args[i].s = types[i]; | |||
| } | |||
| d.replyArray("/bank/types", t, args); | |||
| rEnd}, | |||
| {"slot#1024:", 0, 0, | |||
| rBegin; | |||
| @@ -913,13 +987,19 @@ rtosc::Ports bankPorts = { | |||
| d.reply("/alert", "s", | |||
| "Failed To Clear Bank Slot, please check file permissions"); | |||
| rEnd}, | |||
| {"msb:i", 0, 0, | |||
| {"msb::i", 0, 0, | |||
| rBegin; | |||
| impl.setMsb(rtosc_argument(msg, 0).i); | |||
| if(rtosc_narguments(msg)) | |||
| impl.setMsb(rtosc_argument(msg, 0).i); | |||
| else | |||
| d.reply(d.loc, "i", impl.bank_msb); | |||
| rEnd}, | |||
| {"lsb:i", 0, 0, | |||
| {"lsb::i", 0, 0, | |||
| rBegin; | |||
| impl.setLsb(rtosc_argument(msg, 0).i); | |||
| if(rtosc_narguments(msg)) | |||
| impl.setLsb(rtosc_argument(msg, 0).i); | |||
| else | |||
| d.reply(d.loc, "i", impl.bank_lsb); | |||
| rEnd}, | |||
| {"newbank:s", 0, 0, | |||
| rBegin; | |||
| @@ -927,6 +1007,19 @@ rtosc::Ports bankPorts = { | |||
| if(err) | |||
| d.reply("/alert", "s", "Error: Could not make a new bank (directory).."); | |||
| rEnd}, | |||
| {"search:s", 0, 0, | |||
| rBegin; | |||
| auto res = impl.search(rtosc_argument(msg, 0).s); | |||
| #define MAX_SEARCH 128 | |||
| char res_type[MAX_SEARCH+1] = {0}; | |||
| rtosc_arg_t res_dat[MAX_SEARCH] = {0}; | |||
| for(unsigned i=0; i<res.size() && i<MAX_SEARCH; ++i) { | |||
| res_type[i] = 's'; | |||
| res_dat[i].s = res[i].c_str(); | |||
| } | |||
| d.replyArray("/bank/search_results", res_type, res_dat); | |||
| #undef MAX_SEARCH | |||
| rEnd}, | |||
| }; | |||
| /****************************************************************************** | |||
| @@ -1025,6 +1118,10 @@ static rtosc::Ports middwareSnoopPorts = { | |||
| xml.loadXMLfile(file); | |||
| loadMidiLearn(xml, impl.midi_mapper); | |||
| rEnd}, | |||
| {"clear_xlz:", 0, 0, | |||
| rBegin; | |||
| impl.midi_mapper.clear(); | |||
| rEnd}, | |||
| //scale file stuff | |||
| {"load_xsz:s", 0, 0, | |||
| rBegin; | |||
| @@ -1134,6 +1231,30 @@ static rtosc::Ports middwareSnoopPorts = { | |||
| rBegin; | |||
| impl.undo.seekHistory(+1); | |||
| rEnd}, | |||
| //port to observe the midi mappings | |||
| {"midi-learn-values:", 0, 0, | |||
| rBegin; | |||
| auto &midi = impl.midi_mapper; | |||
| auto key = keys(midi.inv_map); | |||
| //cc-id, path, min, max | |||
| #define MAX_MIDI 32 | |||
| rtosc_arg_t args[MAX_MIDI*4]; | |||
| char argt[MAX_MIDI*4+1] = {0}; | |||
| for(unsigned i=0; i<key.size() && i<MAX_MIDI; ++i) { | |||
| auto val = midi.inv_map[key[i]]; | |||
| argt[4*i+0] = 'i'; | |||
| args[4*i+0].i = std::get<1>(val); | |||
| argt[4*i+1] = 's'; | |||
| args[4*i+1].s = key[i].c_str(); | |||
| argt[4*i+2] = 'i'; | |||
| args[4*i+2].i = 0; | |||
| argt[4*i+3] = 'i'; | |||
| args[4*i+3].i = 127; | |||
| } | |||
| d.replyArray(d.loc, argt, args); | |||
| #undef MAX_MIDI | |||
| rEnd}, | |||
| {"learn:s", 0, 0, | |||
| rBegin; | |||
| string addr = rtosc_argument(msg, 0).s; | |||
| @@ -1162,7 +1283,7 @@ static rtosc::Ports middlewareReplyPorts = { | |||
| const char *type = rtosc_argument(msg, 0).s; | |||
| const char *url = rtosc_argument(msg, 1).s; | |||
| if(!strcmp(type, "OSC_URL")) | |||
| impl.curr_url = url; | |||
| impl.currentUrl(url); | |||
| rEnd}, | |||
| {"free:sb", 0, 0, | |||
| rBegin; | |||
| @@ -1349,8 +1470,9 @@ void MiddleWareImpl::broadcastToRemote(const char *rtmsg) | |||
| sendToRemote(rtmsg, "GUI"); | |||
| //Send to remote UI if there's one listening | |||
| if(curr_url != "GUI") | |||
| sendToRemote(rtmsg, curr_url); | |||
| for(auto rem:known_remotes) | |||
| if(rem != "GUI") | |||
| sendToRemote(rtmsg, rem); | |||
| broadcast = false; | |||
| } | |||
| @@ -1369,6 +1491,8 @@ void MiddleWareImpl::sendToRemote(const char *rtmsg, std::string dest) | |||
| lo_address addr = lo_address_new_from_url(dest.c_str()); | |||
| if(addr) | |||
| lo_send_message(addr, rtmsg, msg); | |||
| lo_address_free(addr); | |||
| lo_message_free(msg); | |||
| } | |||
| } | |||
| @@ -1480,7 +1604,7 @@ void MiddleWareImpl::kitEnable(int part, int kit, int type) | |||
| void MiddleWareImpl::handleMsg(const char *msg) | |||
| { | |||
| //Check for known bugs | |||
| assert(msg && *msg && rindex(msg, '/')[1]); | |||
| assert(msg && *msg && strrchr(msg, '/')[1]); | |||
| assert(strstr(msg,"free") == NULL || strstr(rtosc_argument_string(msg), "b") == NULL); | |||
| assert(strcmp(msg, "/part0/Psysefxvol")); | |||
| assert(strcmp(msg, "/Penabled")); | |||
| @@ -1495,7 +1619,7 @@ void MiddleWareImpl::handleMsg(const char *msg) | |||
| fprintf(stdout, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40); | |||
| } | |||
| const char *last_path = rindex(msg, '/'); | |||
| const char *last_path = strrchr(msg, '/'); | |||
| if(!last_path) { | |||
| printf("Bad message in handleMsg() <%s>\n", msg); | |||
| assert(false); | |||
| @@ -25,6 +25,7 @@ | |||
| #include "../Synth/ADnote.h" | |||
| #include "../Synth/SUBnote.h" | |||
| #include "../Synth/PADnote.h" | |||
| #include "../Containers/ScratchString.h" | |||
| #include "../DSP/FFTwrapper.h" | |||
| #include "../Misc/Util.h" | |||
| #include <cstdlib> | |||
| @@ -59,7 +60,8 @@ static const Ports partPorts = { | |||
| rParamZyn(Pminkey, rShort("min"), "Min Used Key"), | |||
| rParamZyn(Pmaxkey, rShort("max"), "Max Used Key"), | |||
| rParamZyn(Pkeyshift, rShort("shift"), "Part keyshift"), | |||
| rParamZyn(Prcvchn, "Active MIDI channel"), | |||
| rParamZyn(Prcvchn, rOptions(ch1, ch2, ch3, ch4, ch5, ch6, ch7, ch8, ch9, ch10, ch11, ch12, ch13, ch14, ch15, ch16), | |||
| "Active MIDI channel"), | |||
| rParamZyn(Pvelsns, "Velocity sensing"), | |||
| rParamZyn(Pveloffs, "Velocity offset"), | |||
| rToggle(Pnoteon, "If the channel accepts note on events"), | |||
| @@ -163,7 +165,9 @@ static const Ports kitPorts = { | |||
| rToggle(Padenabled, "ADsynth enable"), | |||
| rToggle(Psubenabled, "SUBsynth enable"), | |||
| rToggle(Ppadenabled, "PADsynth enable"), | |||
| rParamZyn(Psendtoparteffect, "Effect Levels"), | |||
| rParamZyn(Psendtoparteffect, | |||
| rOptions(FX1, FX2, FX3, Off), | |||
| "Effect Levels"), | |||
| rString(Pname, PART_MAX_NAME_LEN, "Kit User Specified Label"), | |||
| {"captureMin:", rDoc("Capture minimum valid note"), NULL, | |||
| [](const char *, RtData &r) | |||
| @@ -195,7 +199,7 @@ const Ports &Part::ports = partPorts; | |||
| Part::Part(Allocator &alloc, const SYNTH_T &synth_, const AbsTime &time_, | |||
| const int &gzip_compression, const int &interpolation, | |||
| Microtonal *microtonal_, FFTwrapper *fft_) | |||
| Microtonal *microtonal_, FFTwrapper *fft_, WatchManager *wm_, const char *prefix_) | |||
| :Pdrummode(false), | |||
| Ppolymode(true), | |||
| Plegatomode(false), | |||
| @@ -204,12 +208,18 @@ Part::Part(Allocator &alloc, const SYNTH_T &synth_, const AbsTime &time_, | |||
| ctl(synth_, &time_), | |||
| microtonal(microtonal_), | |||
| fft(fft_), | |||
| wm(wm_), | |||
| memory(alloc), | |||
| synth(synth_), | |||
| time(time_), | |||
| gzip_compression(gzip_compression), | |||
| interpolation(interpolation) | |||
| { | |||
| if(prefix_) | |||
| strncpy(prefix, prefix_, sizeof(prefix)); | |||
| else | |||
| memset(prefix, 0, sizeof(prefix)); | |||
| monomemClear(); | |||
| for(int n = 0; n < NUM_KIT_ITEMS; ++n) { | |||
| @@ -482,6 +492,7 @@ bool Part::NoteOn(unsigned char note, | |||
| //Create New Notes | |||
| for(uint8_t i = 0; i < NUM_KIT_ITEMS; ++i) { | |||
| ScratchString pre = prefix; | |||
| auto &item = kit[i]; | |||
| if(Pkitmode != 0 && !item.validNote(note)) | |||
| continue; | |||
| @@ -493,13 +504,15 @@ bool Part::NoteOn(unsigned char note, | |||
| try { | |||
| if(item.Padenabled) | |||
| notePool.insertNote(note, sendto, | |||
| {memory.alloc<ADnote>(kit[i].adpars, pars), 0, i}); | |||
| {memory.alloc<ADnote>(kit[i].adpars, pars, | |||
| wm, (pre+"kit"+i+"/adpars/").c_str), 0, i}); | |||
| if(item.Psubenabled) | |||
| notePool.insertNote(note, sendto, | |||
| {memory.alloc<SUBnote>(kit[i].subpars, pars), 1, i}); | |||
| if(item.Ppadenabled) | |||
| notePool.insertNote(note, sendto, | |||
| {memory.alloc<PADnote>(kit[i].padpars, pars, interpolation), 2, i}); | |||
| {memory.alloc<PADnote>(kit[i].padpars, pars, interpolation, wm, | |||
| (pre+"kit"+i+"/padpars/").c_str), 2, i}); | |||
| } catch (std::bad_alloc & ba) { | |||
| std::cerr << "dropped new note: " << ba.what() << std::endl; | |||
| } | |||
| @@ -31,7 +31,7 @@ class Part | |||
| * @param fft_ Pointer to the FFTwrapper*/ | |||
| Part(Allocator &alloc, const SYNTH_T &synth, const AbsTime &time, | |||
| const int& gzip_compression, const int& interpolation, | |||
| Microtonal *microtonal_, FFTwrapper *fft_); | |||
| Microtonal *microtonal_, FFTwrapper *fft_, WatchManager *wm=0, const char *prefix=0); | |||
| /**Destructor*/ | |||
| ~Part(); | |||
| @@ -192,6 +192,8 @@ class Part | |||
| float oldfreq; //this is used for portamento | |||
| Microtonal *microtonal; | |||
| FFTwrapper *fft; | |||
| WatchManager *wm; | |||
| char prefix[64]; | |||
| Allocator &memory; | |||
| const SYNTH_T &synth; | |||
| const AbsTime &time; | |||
| @@ -12,6 +12,7 @@ using namespace rtosc; | |||
| * - 'tooltip' : string [OPTIONAL] | |||
| * - 'type' : type | |||
| * - 'domain' : range [OPTIONAL] | |||
| * - 'options' : [option...] [OPTIONAL] | |||
| * type : {'int', 'float', 'boolean'} | |||
| * action : | |||
| * - 'path' : path-id | |||
| @@ -19,6 +20,9 @@ using namespace rtosc; | |||
| * arg : | |||
| * - 'type' : type | |||
| * - 'domain' : range [OPTIONAL] | |||
| * option : | |||
| * - 'id' : id-number | |||
| * - 'value' : string-rep | |||
| */ | |||
| void walk_ports2(const rtosc::Ports *base, | |||
| @@ -98,15 +102,24 @@ static ostream &add_options(ostream &o, Port::MetaContainer meta) | |||
| * - 'domain' : range [OPTIONAL] | |||
| */ | |||
| static bool first = true; | |||
| void dump_param_cb(const rtosc::Port *p, const char *name, void *v) | |||
| void dump_param_cb(const rtosc::Port *p, const char *full_name, void *v) | |||
| { | |||
| typedef std::vector<std::pair<int,string>> opts; | |||
| std::ostream &o = *(std::ostream*)v; | |||
| auto meta = p->meta(); | |||
| const char *args = strchr(p->name, ':'); | |||
| auto mparameter = meta.find("parameter"); | |||
| auto mdoc = meta.find("documentation"); | |||
| auto msname = meta.find("shortname"); | |||
| opts options; | |||
| string doc; | |||
| string name = p->name;; | |||
| { | |||
| size_t pos = 0; | |||
| if((pos = name.find_first_of(":")) != string::npos) | |||
| name = name.substr(0, pos); | |||
| } | |||
| //Escape Characters | |||
| if(mdoc != p->meta().end()) { | |||
| @@ -139,7 +152,7 @@ void dump_param_cb(const rtosc::Port *p, const char *name, void *v) | |||
| } | |||
| if(!type) { | |||
| fprintf(stderr, "rtosc port dumper: Cannot handle '%s'\n", name); | |||
| fprintf(stderr, "rtosc port dumper: Cannot handle '%s'\n", full_name); | |||
| fprintf(stderr, " args = <%s>\n", args); | |||
| return; | |||
| } | |||
| @@ -154,23 +167,43 @@ void dump_param_cb(const rtosc::Port *p, const char *name, void *v) | |||
| const char *min = meta["min"]; | |||
| const char *max = meta["max"]; | |||
| for(auto m:meta) { | |||
| if(strlen(m.title) >= 5 && !bcmp(m.title, "map ", 4)) { | |||
| int id = atoi(m.title+4); | |||
| std::string val = m.value; | |||
| options.push_back(std::make_pair(id, val)); | |||
| } | |||
| } | |||
| if(!first) | |||
| o << ",\n"; | |||
| else | |||
| first = false; | |||
| o << " {\n"; | |||
| o << " \"path\" : \"" << name << "\",\n"; | |||
| o << " \"path\" : \"" << full_name << "\",\n"; | |||
| if(msname != meta.end()) | |||
| o << " \"shortname\": \"" << msname.value << "\",\n"; | |||
| o << " \"name\" : \"" << p->name << "\",\n"; | |||
| o << " \"name\" : \"" << name << "\",\n"; | |||
| o << " \"tooltip\" : \"" << doc << "\",\n"; | |||
| o << " \"type\" : \"" << type << "\""; | |||
| if(min && max) | |||
| o << ",\n \"range\" : [" << min << "," << max << "]\n"; | |||
| else | |||
| o << "\n"; | |||
| o << " }"; | |||
| o << ",\n \"range\" : [" << min << "," << max << "]"; | |||
| if(!options.empty()) { | |||
| o << ",\n \"options\" : [\n"; | |||
| int N = options.size(); | |||
| for(int i=0; i<N; ++i) { | |||
| o << " {\n"; | |||
| o << " \"id\" : " << options[i].first << ",\n"; | |||
| o << " \"value\" : \"" << options[i].second << "\"\n"; | |||
| o << " }"; | |||
| if(i != N-1) | |||
| o << ","; | |||
| o << "\n"; | |||
| } | |||
| o << " ]"; | |||
| } | |||
| o << "\n }"; | |||
| } | |||
| void dump_json(std::ostream &o, const rtosc::Ports &p) | |||
| @@ -87,10 +87,6 @@ const char *mxmlElementGetAttr(const mxml_node_t *node, const char *name) | |||
| XMLwrapper::XMLwrapper() | |||
| { | |||
| version.Major = 2; | |||
| version.Minor = 5; | |||
| version.Revision = 3; | |||
| minimal = true; | |||
| node = tree = mxmlNewElement(MXML_NO_PARENT, | |||
| @@ -106,11 +102,11 @@ XMLwrapper::XMLwrapper() | |||
| node = root = addparams("ZynAddSubFX-data", 4, | |||
| "version-major", stringFrom<int>( | |||
| version.Major).c_str(), | |||
| version.major()).c_str(), | |||
| "version-minor", stringFrom<int>( | |||
| version.Minor).c_str(), | |||
| version.minor()).c_str(), | |||
| "version-revision", | |||
| stringFrom<int>(version.Revision).c_str(), | |||
| stringFrom<int>(version.revision()).c_str(), | |||
| "ZynAddSubFX-author", "Nasca Octavian Paul"); | |||
| //make the empty branch that will contain the information parameters | |||
| @@ -327,14 +323,13 @@ int XMLwrapper::loadXMLfile(const string &filename) | |||
| return -3; //the XML doesnt embbed zynaddsubfx data | |||
| //fetch version information | |||
| version.Major = stringTo<int>(mxmlElementGetAttr(root, "version-major")); | |||
| version.Minor = stringTo<int>(mxmlElementGetAttr(root, "version-minor")); | |||
| version.Revision = | |||
| stringTo<int>(mxmlElementGetAttr(root, "version-revision")); | |||
| fileversion.set_major(stringTo<int>(mxmlElementGetAttr(root, "version-major"))); | |||
| fileversion.set_minor(stringTo<int>(mxmlElementGetAttr(root, "version-minor"))); | |||
| fileversion.set_revision( | |||
| stringTo<int>(mxmlElementGetAttr(root, "version-revision"))); | |||
| if(verbose) | |||
| cout << "loadXMLfile() version: " << version.Major << '.' | |||
| << version.Minor << '.' << version.Revision << endl; | |||
| cout << "loadXMLfile() version: " << fileversion << endl; | |||
| return 0; | |||
| } | |||
| @@ -16,6 +16,7 @@ | |||
| #include <mxml.h> | |||
| #include <string> | |||
| #include <vector> | |||
| #include "../version.h" | |||
| #ifndef XML_WRAPPER_H | |||
| #define XML_WRAPPER_H | |||
| @@ -272,12 +273,7 @@ class XMLwrapper | |||
| mxml_node_t *addparams(const char *name, unsigned int params, | |||
| ...) const; | |||
| /**@todo keep these numbers up to date*/ | |||
| struct { | |||
| int Major; /**<major version number.*/ | |||
| int Minor; /**<minor version number.*/ | |||
| int Revision; /**<version revision number.*/ | |||
| } version; | |||
| version_type fileversion; | |||
| }; | |||
| #endif | |||
| @@ -11,6 +11,7 @@ | |||
| of the License, or (at your option) any later version. | |||
| */ | |||
| #include <stdlib.h> | |||
| #include <iostream> | |||
| #include <cmath> | |||
| @@ -20,6 +21,7 @@ using namespace std; | |||
| #include "../Misc/Config.h" | |||
| #include "InMgr.h" | |||
| #include "AlsaEngine.h" | |||
| #include "Compressor.h" | |||
| #include "Nio.h" | |||
| AlsaEngine::AlsaEngine(const SYNTH_T &synth) | |||
| @@ -28,6 +30,7 @@ AlsaEngine::AlsaEngine(const SYNTH_T &synth) | |||
| audio.buffer = new short[synth.buffersize * 2]; | |||
| name = "ALSA"; | |||
| audio.handle = NULL; | |||
| audio.peaks[0] = 0; | |||
| midi.handle = NULL; | |||
| midi.alsaId = -1; | |||
| @@ -251,9 +254,13 @@ short *AlsaEngine::interleave(const Stereo<float *> &smps) | |||
| int idx = 0; //possible off by one error here | |||
| double scaled; | |||
| for(int frame = 0; frame < bufferSize; ++frame) { // with a nod to libsamplerate ... | |||
| scaled = smps.l[frame] * (8.0f * 0x10000000); | |||
| float l = smps.l[frame]; | |||
| float r = smps.r[frame]; | |||
| stereoCompressor(synth.samplerate, audio.peaks[0], l, r); | |||
| scaled = l * (8.0f * 0x10000000); | |||
| shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16); | |||
| scaled = smps.r[frame] * (8.0f * 0x10000000); | |||
| scaled = r * (8.0f * 0x10000000); | |||
| shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16); | |||
| } | |||
| return shortInterleaved; | |||
| @@ -267,7 +274,12 @@ bool AlsaEngine::openAudio() | |||
| int rc = 0; | |||
| /* Open PCM device for playback. */ | |||
| audio.handle = NULL; | |||
| rc = snd_pcm_open(&audio.handle, "hw:0", | |||
| const char *device = getenv("ALSA_DEVICE"); | |||
| if(device == 0) | |||
| device = "hw:0"; | |||
| rc = snd_pcm_open(&audio.handle, device, | |||
| SND_PCM_STREAM_PLAYBACK, 0); | |||
| if(rc < 0) { | |||
| fprintf(stderr, | |||
| @@ -67,6 +67,7 @@ class AlsaEngine:public AudioOut, MidiIn | |||
| unsigned int periods; | |||
| short *buffer; | |||
| pthread_t pThread; | |||
| float peaks[1]; | |||
| } audio; | |||
| void *processAudio(); | |||
| @@ -0,0 +1,59 @@ | |||
| /* | |||
| ZynAddSubFX - a software synthesizer | |||
| Compressor.h - simple audio compressor macros | |||
| Copyright (C) 2016 Hans Petter Selasky | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| as published by the Free Software Foundation; either version 2 | |||
| of the License, or (at your option) any later version. | |||
| */ | |||
| #ifndef _COMPRESSOR_H_ | |||
| #define _COMPRESSOR_H_ | |||
| #define floatIsValid(x) ({ \ | |||
| float __r = (x) * 0.0; \ | |||
| __r == 0.0 || __r == -0.0; \ | |||
| }) | |||
| #define stereoCompressor(div,pv,l,r) do { \ | |||
| /* \ | |||
| * Don't max the output range to avoid \ | |||
| * overflowing sample rate conversion and \ | |||
| * equalizer filters in the DSP's output \ | |||
| * path. Keep one 10th, 1dB, reserved. \ | |||
| */ \ | |||
| const float __limit = 1.0 - (1.0 / 10.0); \ | |||
| float __peak; \ | |||
| \ | |||
| /* sanity checks */ \ | |||
| __peak = (pv); \ | |||
| if (!floatIsValid(__peak)) \ | |||
| __peak = 0.0; \ | |||
| if (!floatIsValid(l)) \ | |||
| (l) = 0.0; \ | |||
| if (!floatIsValid(r)) \ | |||
| (r) = 0.0; \ | |||
| /* compute maximum */ \ | |||
| if ((l) < -__peak) \ | |||
| __peak = -(l); \ | |||
| else if ((l) > __peak) \ | |||
| __peak = (l); \ | |||
| if ((r) < -__peak) \ | |||
| __peak = -(r); \ | |||
| else if ((r) > __peak) \ | |||
| __peak = (r); \ | |||
| /* compressor */ \ | |||
| if (__peak > __limit) { \ | |||
| (l) /= __peak; \ | |||
| (r) /= __peak; \ | |||
| (l) *= __limit; \ | |||
| (r) *= __limit; \ | |||
| __peak -= __peak / (div); \ | |||
| } \ | |||
| (pv) = __peak; \ | |||
| } while (0) | |||
| #endif /* _COMPRESSOR_H_ */ | |||
| @@ -131,6 +131,10 @@ int JackMultiEngine::processAudio(jack_nframes_t nframes) | |||
| float *buffers[NUM_MIDI_PARTS * 2 + 2]; | |||
| for(int i = 0; i < NUM_MIDI_PARTS * 2 + 2; ++i) { | |||
| //Abort if ports are only partially initialized | |||
| if(!impl->ports[i]) | |||
| return false; | |||
| buffers[i] = | |||
| (float *)jack_port_get_buffer(impl->ports[i], nframes); | |||
| assert(buffers[i]); | |||
| @@ -12,6 +12,8 @@ | |||
| */ | |||
| #include "OssEngine.h" | |||
| #include "Compressor.h" | |||
| #include "../Misc/Util.h" | |||
| #include "../Misc/Config.h" | |||
| #include "../globals.h" | |||
| @@ -186,6 +188,8 @@ OssEngine::OssEngine(const SYNTH_T &synth, | |||
| audio.smps.ps32 = new int[synth.buffersize * 2]; | |||
| memset(audio.smps.ps32, 0, sizeof(int) * synth.buffersize * 2); | |||
| memset(&midi.state, 0, sizeof(midi.state)); | |||
| audio.peaks[0] = 0; | |||
| } | |||
| OssEngine::~OssEngine() | |||
| @@ -401,21 +405,10 @@ void *OssEngine::audioThreadCb() | |||
| while(getAudioEn()) { | |||
| const Stereo<float *> smps = getNext(); | |||
| float l, r; | |||
| for(int i = 0; i < synth.buffersize; ++i) { | |||
| l = smps.l[i]; | |||
| r = smps.r[i]; | |||
| if(l < -1.0f) | |||
| l = -1.0f; | |||
| else | |||
| if(l > 1.0f) | |||
| l = 1.0f; | |||
| if(r < -1.0f) | |||
| r = -1.0f; | |||
| else | |||
| if(r > 1.0f) | |||
| r = 1.0f; | |||
| float l = smps.l[i]; | |||
| float r = smps.r[i]; | |||
| stereoCompressor(synth.samplerate, audio.peaks[0], l, r); | |||
| if (audio.is32bit) { | |||
| audio.smps.ps32[i * 2] = (int) (l * 2147483647.0f); | |||
| @@ -71,6 +71,10 @@ class OssEngine:public AudioOut, MidiIn | |||
| short int *ps16; | |||
| int *ps32; | |||
| } smps; | |||
| /* peak values used for compressor */ | |||
| float peaks[1]; | |||
| bool en; | |||
| bool is32bit; | |||
| } audio; | |||
| @@ -29,6 +29,7 @@ | |||
| #include "../Misc/Util.h" | |||
| #include "OssMultiEngine.h" | |||
| #include "Compressor.h" | |||
| extern MiddleWare *middleware; | |||
| @@ -53,12 +54,18 @@ OssMultiEngine :: OssMultiEngine(const SYNTH_T &synth, | |||
| /* allocate buffer */ | |||
| smps.ps32 = new int[maxbuffersize / sizeof(int)]; | |||
| memset(smps.ps32, 0, maxbuffersize); | |||
| /* setup compressor */ | |||
| unsigned peaksize = NUM_MIDI_PARTS * sizeof(float); | |||
| peaks = new float[peaksize / sizeof(float)]; | |||
| memset(peaks, 0, peaksize); | |||
| } | |||
| OssMultiEngine :: ~OssMultiEngine() | |||
| { | |||
| Stop(); | |||
| delete [] smps.ps32; | |||
| delete [] peaks; | |||
| } | |||
| bool | |||
| @@ -213,8 +220,6 @@ OssMultiEngine :: audioThreadCb() | |||
| while(getAudioEn()) { | |||
| int error; | |||
| float l; | |||
| float r; | |||
| int x; | |||
| int y; | |||
| @@ -227,32 +232,18 @@ OssMultiEngine :: audioThreadCb() | |||
| if (is32bit) { | |||
| for (y = 0; y != synth.buffersize; y++) { | |||
| l = part->partoutl[y]; | |||
| if (l < -1.0f) | |||
| l = -1.0f; | |||
| else if (l > 1.0f) | |||
| l = 1.0f; | |||
| float l = part->partoutl[y]; | |||
| float r = part->partoutr[y]; | |||
| stereoCompressor(synth.samplerate, peaks[x/2], l, r); | |||
| smps.ps32[y * channels + x] = (int)(l * 2147483647.0f); | |||
| r = part->partoutr[y]; | |||
| if (r < -1.0f) | |||
| r = -1.0f; | |||
| else if (r > 1.0f) | |||
| r = 1.0f; | |||
| smps.ps32[y * channels + x + 1] = (int)(r * 2147483647.0f); | |||
| } | |||
| } else { | |||
| for (y = 0; y != synth.buffersize; y++) { | |||
| l = part->partoutl[y]; | |||
| if (l < -1.0f) | |||
| l = -1.0f; | |||
| else if (l > 1.0f) | |||
| l = 1.0f; | |||
| float l = part->partoutl[y]; | |||
| float r = part->partoutr[y]; | |||
| stereoCompressor(synth.samplerate, peaks[x/2], l, r); | |||
| smps.ps16[y * channels + x] = (short int)(l * 32767.0f); | |||
| r = part->partoutr[y]; | |||
| if (r < -1.0f) | |||
| r = -1.0f; | |||
| else if (r > 1.0f) | |||
| r = 1.0f; | |||
| smps.ps16[y * channels + x + 1] = (short int)(r * 32767.0f); | |||
| } | |||
| } | |||
| @@ -52,6 +52,9 @@ class OssMultiEngine : public AudioOut | |||
| int *ps32; | |||
| } smps; | |||
| /* peak values used for compressor */ | |||
| float *peaks; | |||
| bool en; | |||
| bool is32bit; | |||
| @@ -18,6 +18,7 @@ | |||
| #include "ADnoteParameters.h" | |||
| #include "EnvelopeParams.h" | |||
| #include "LFOParams.h" | |||
| #include "../Misc/Time.h" | |||
| #include "../Misc/XMLwrapper.h" | |||
| #include "../DSP/FFTwrapper.h" | |||
| #include "../Synth/OscilGen.h" | |||
| @@ -66,63 +67,68 @@ static const Ports voicePorts = { | |||
| rRecurp(FMAmpEnvelope, "Modulator Amplitude Envelope"), | |||
| rRecurp(VoiceFilter, "Optional Voice Filter"), | |||
| rToggle(Enabled, "Voice Enable"), | |||
| rParamZyn(Unison_size, "Number of subvoices"), | |||
| rParamZyn(Unison_phase_randomness, "Phase Randomness"), | |||
| rParamZyn(Unison_frequency_spread, "Subvoice detune"), | |||
| rParamZyn(Unison_stereo_spread, "Subvoice L/R Separation"), | |||
| rParamZyn(Unison_vibratto, "Subvoice vibratto"), | |||
| rParamZyn(Unison_vibratto_speed, "Subvoice vibratto speed"), | |||
| rOption(Unison_invert_phase, rOptions(none, random, 50%, 33%, 25%), "Subvoice Phases"), | |||
| rOption(Type, rOptions(Sound,White,Pink), "Type of Sound"), | |||
| rParamZyn(PDelay, "Voice Startup Delay"), | |||
| rToggle(Presonance, "Resonance Enable"), | |||
| rParamI(Pextoscil, rMap(min, -1), rMap(max, 16), "External Oscilator Selection"), | |||
| rParamI(PextFMoscil, rMap(min, -1), rMap(max, 16), "External FM Oscilator Selection"), | |||
| rParamZyn(Poscilphase, "Oscillator Phase"), | |||
| rParamZyn(PFMoscilphase, "FM Oscillator Phase"), | |||
| rToggle(Pfilterbypass, "Filter Bypass"), | |||
| rToggle(Enabled, rShort("enable"), "Voice Enable"), | |||
| rParamZyn(Unison_size, rShort("size"), "Number of subvoices"), | |||
| rParamZyn(Unison_phase_randomness, rShort("ph.rnd."), "Phase Randomness"), | |||
| rParamZyn(Unison_frequency_spread, rShort("detune"), "Subvoice detune"), | |||
| rParamZyn(Unison_stereo_spread, rShort("spread"), | |||
| "Subvoice L/R Separation"), | |||
| rParamZyn(Unison_vibratto, rShort("vib."), "Subvoice vibratto"), | |||
| rParamZyn(Unison_vibratto_speed, rShort("speed"), | |||
| "Subvoice vibratto speed"), | |||
| rOption(Unison_invert_phase, rShort("inv."), | |||
| rOptions(none, random, 50%, 33%, 25%), "Subvoice Phases"), | |||
| rOption(Type, rShort("type"), rOptions(Sound,White,Pink), "Type of Sound"), | |||
| rParamZyn(PDelay, rShort("delay"), "Voice Startup Delay"), | |||
| rToggle(Presonance, rShort("enable"), "Resonance Enable"), | |||
| rParamI(Pextoscil, rShort("ext."), | |||
| rMap(min, -1), rMap(max, 16), "External Oscilator Selection"), | |||
| rParamI(PextFMoscil, rShort("ext."), | |||
| rMap(min, -1), rMap(max, 16), "External FM Oscilator Selection"), | |||
| rParamZyn(Poscilphase, rShort("phase"), "Oscillator Phase"), | |||
| rParamZyn(PFMoscilphase, rShort("phase"), "FM Oscillator Phase"), | |||
| rToggle(Pfilterbypass, rShort("bypass"), "Filter Bypass"), | |||
| //Freq Stuff | |||
| rToggle(Pfixedfreq, "If frequency is fixed"), | |||
| rParamZyn(PfixedfreqET, "Equal Tempermant Parameter"), | |||
| rParamZyn(PBendAdjust, "Pitch bend adjustment"), | |||
| rParamZyn(POffsetHz, "Voice constant offset"), | |||
| rParamI(PDetune, "Fine Detune"), | |||
| rParamI(PCoarseDetune, "Coarse Detune"), | |||
| rParamZyn(PDetuneType, "Magnitude of Detune"), | |||
| rToggle(PFreqEnvelopeEnabled, "Frequency Envelope Enable"), | |||
| rToggle(PFreqLfoEnabled, "Frequency LFO Enable"), | |||
| rToggle(Pfixedfreq, rShort("fixed"), "If frequency is fixed"), | |||
| rParamZyn(PfixedfreqET, rShort("e.t."), "Equal Tempermant Parameter"), | |||
| rParamZyn(PBendAdjust, rShort("bend"), "Pitch bend adjustment"), | |||
| rParamZyn(POffsetHz, rShort("offset"), "Voice constant offset"), | |||
| rParamI(PDetune, rShort("fine"), "Fine Detune"), | |||
| rParamI(PCoarseDetune, rShort("coarse"), "Coarse Detune"), | |||
| rParamZyn(PDetuneType, rShort("type"), "Magnitude of Detune"), | |||
| rToggle(PFreqEnvelopeEnabled, rShort("enable"), "Frequency Envelope Enable"), | |||
| rToggle(PFreqLfoEnabled, rShort("enable"), "Frequency LFO Enable"), | |||
| //Amplitude Stuff | |||
| rParamZyn(PPanning, "Panning"), | |||
| rParamZyn(PVolume, "Volume"), | |||
| rToggle(PVolumeminus, "Signal Inverter"), //do we really need this?? | |||
| rParamZyn(PAmpVelocityScaleFunction, "Velocity Sensing"), | |||
| rToggle(PAmpEnvelopeEnabled, "Amplitude Envelope Enable"), | |||
| rToggle(PAmpLfoEnabled, "Amplitude LFO Enable"), | |||
| rParamZyn(PPanning, rShort("pan."), "Panning"), | |||
| rParamZyn(PVolume, rShort("vol."), "Volume"), | |||
| rToggle(PVolumeminus, rShort("inv."), "Signal Inverter"), //do we really need this?? | |||
| rParamZyn(PAmpVelocityScaleFunction, rShort("sense"), "Velocity Sensing"), | |||
| rToggle(PAmpEnvelopeEnabled, rShort("enable"), "Amplitude Envelope Enable"), | |||
| rToggle(PAmpLfoEnabled, rShort("enable"), "Amplitude LFO Enable"), | |||
| //Filter Stuff | |||
| rToggle(PFilterEnabled, "Filter Enable"), | |||
| rToggle(PFilterEnvelopeEnabled, "Filter Envelope Enable"), | |||
| rToggle(PFilterLfoEnabled, "Filter LFO Enable"), | |||
| rParamZyn(PFilterVelocityScale, "Filter Velocity Magnitude"), | |||
| rParamZyn(PFilterVelocityScaleFunction, "Filter Velocity Function Shape"), | |||
| rToggle(PFilterEnabled, rShort("enable"), "Filter Enable"), | |||
| rToggle(PFilterEnvelopeEnabled, rShort("enable"), "Filter Envelope Enable"), | |||
| rToggle(PFilterLfoEnabled, rShort("enable"), "Filter LFO Enable"), | |||
| rParamZyn(PFilterVelocityScale, rShort("v.scale"), "Filter Velocity Magnitude"), | |||
| rParamZyn(PFilterVelocityScaleFunction, rShort("v.sense"), "Filter Velocity Function Shape"), | |||
| //Modulator Stuff | |||
| rOption(PFMEnabled, rOptions(none, morph, ring modulation, phase modulation, | |||
| frequency modulation, pitch modulation), "Modulator mode"), | |||
| rParamI(PFMVoice, "Modulator Oscillator Selection"), | |||
| rParamZyn(PFMVolume, "Modulator Magnitude"), | |||
| rParamZyn(PFMVolumeDamp, "Modulator HF dampening"), | |||
| rParamZyn(PFMVelocityScaleFunction, "Modulator Velocity Function"), | |||
| rParamI(PFMDetune, "Modulator Fine Detune"), | |||
| rParamI(PFMCoarseDetune, "Modulator Coarse Detune"), | |||
| rParamZyn(PFMDetuneType, "Modulator Detune Magnitude"), | |||
| rToggle(PFMFixedFreq, "Modulator Frequency Fixed"), | |||
| rToggle(PFMFreqEnvelopeEnabled, "Modulator Frequency Envelope"), | |||
| rToggle(PFMAmpEnvelopeEnabled, "Modulator Amplitude Envelope"), | |||
| rOption(PFMEnabled, rShort("mode"), rOptions(none, morph, ring, phase, | |||
| frequency, pitch), "Modulator mode"), | |||
| rParamI(PFMVoice, rShort("voice"), "Modulator Oscillator Selection"), | |||
| rParamZyn(PFMVolume, rShort("vol."), "Modulator Magnitude"), | |||
| rParamZyn(PFMVolumeDamp, rShort("damp."), "Modulator HF dampening"), | |||
| rParamZyn(PFMVelocityScaleFunction, rShort("sense"), "Modulator Velocity Function"), | |||
| rParamI(PFMDetune, rShort("fine"), "Modulator Fine Detune"), | |||
| rParamI(PFMCoarseDetune, rShort("coarse"), "Modulator Coarse Detune"), | |||
| rParamZyn(PFMDetuneType, rShort("type"), "Modulator Detune Magnitude"), | |||
| rToggle(PFMFixedFreq, rShort("fixed"), "Modulator Frequency Fixed"), | |||
| rToggle(PFMFreqEnvelopeEnabled, rShort("enable"), "Modulator Frequency Envelope"), | |||
| rToggle(PFMAmpEnvelopeEnabled, rShort("enable"), "Modulator Amplitude Envelope"), | |||
| //weird stuff for PCoarseDetune | |||
| @@ -229,28 +235,31 @@ static const Ports globalPorts = { | |||
| rRecurp(AmpEnvelope, "Frequency Envelope"), | |||
| rRecurp(FilterEnvelope, "Frequency Envelope"), | |||
| rRecurp(GlobalFilter, "Filter"), | |||
| rToggle(PStereo, "Mono/Stereo Enable"), | |||
| rToggle(PStereo, rShort("stereo"), "Mono/Stereo Enable"), | |||
| //Frequency | |||
| rParamI(PDetune, "Fine Detune"), | |||
| rParamI(PCoarseDetune, "Coarse Detune"), | |||
| rParamZyn(PDetuneType, "Detune Scaling Type"), | |||
| rParamZyn(PBandwidth, "Relative Fine Detune Gain"), | |||
| rParamI(PDetune, rShort("fine"), "Fine Detune"), | |||
| rParamI(PCoarseDetune, rShort("coarse"), "Coarse Detune"), | |||
| rParamZyn(PDetuneType, rShort("type"), | |||
| rOptions(L35cents, L10cents, E100cents, E1200cents), | |||
| "Detune Scaling Type"), | |||
| rParamZyn(PBandwidth, rShort("bw."), "Relative Fine Detune Gain"), | |||
| //Amplitude | |||
| rParamZyn(PPanning, "Panning of ADsynth (0 random, 1 left, 127 right)"), | |||
| rParamZyn(PVolume, "volume control"), | |||
| rParamZyn(PAmpVelocityScaleFunction, "Volume Velocity Control"), | |||
| rParamZyn(PPanning, rShort("pan"), "Panning of ADsynth (0 random, 1 left, 127 right)"), | |||
| rParamZyn(PVolume, rShort("vol"), "volume control"), | |||
| rParamZyn(PAmpVelocityScaleFunction, rShort("scale"), "Volume Velocity Control"), | |||
| rParamZyn(Fadein_adjustment, "Adjustment for anti-pop strategy."), | |||
| rParamZyn(PPunchStrength, "Punch Strength"), | |||
| rParamZyn(PPunchTime, "UNKNOWN"), | |||
| rParamZyn(PPunchStretch, "How Punch changes with note frequency"), | |||
| rParamZyn(PPunchVelocitySensing, "Punch Velocity control"), | |||
| rParamZyn(PPunchStrength, rShort("strength"), "Punch Strength"), | |||
| rParamZyn(PPunchTime, rShort("time"), "Length of Punch"), | |||
| rParamZyn(PPunchStretch, rShort("stretch"), "How Punch changes with note frequency"), | |||
| rParamZyn(PPunchVelocitySensing, rShort("v.sns"), "Punch Velocity control"), | |||
| //Filter | |||
| rParamZyn(PFilterVelocityScale, "Filter Velocity Magnitude"), | |||
| rParamZyn(PFilterVelocityScaleFunction, "Filter Velocity Function Shape"), | |||
| rParamZyn(PFilterVelocityScale, rShort("scale"), "Filter Velocity Magnitude"), | |||
| rParamZyn(PFilterVelocityScaleFunction, rShort("sense"), "Filter Velocity Function Shape"), | |||
| //Resonance | |||
| @@ -27,31 +27,31 @@ using namespace rtosc; | |||
| #undef rChangeCb | |||
| #define rChangeCb if (obj->time) { obj->last_update_timestamp = obj->time->time(); } | |||
| const rtosc::Ports Controller::ports = { | |||
| rParamZyn(panning.depth, "Depth of Panning MIDI Control"), | |||
| rParamZyn(filtercutoff.depth, "Depth of Filter Cutoff MIDI Control"), | |||
| rParamZyn(filterq.depth, "Depth of Filter Q MIDI Control"), | |||
| rParamZyn(bandwidth.depth, "Depth of Bandwidth MIDI Control"), | |||
| rToggle(bandwidth.exponential, "Bandwidth Exponential Mode"), | |||
| rParamZyn(modwheel.depth, "Depth of Modwheel MIDI Control"), | |||
| rToggle(modwheel.exponential, "Modwheel Exponential Mode"), | |||
| rToggle(pitchwheel.is_split, "If PitchWheel Has unified bendrange or not"), | |||
| rParamZyn(panning.depth, rShort("pan.d"), "Depth of Panning MIDI Control"), | |||
| rParamZyn(filtercutoff.depth, rShort("fc.d"), "Depth of Filter Cutoff MIDI Control"), | |||
| rParamZyn(filterq.depth, rShort("fq.d"), "Depth of Filter Q MIDI Control"), | |||
| rParamZyn(bandwidth.depth, rShort("bw.d"), "Depth of Bandwidth MIDI Control"), | |||
| rToggle(bandwidth.exponential, rShort("bw.exp"), "Bandwidth Exponential Mode"), | |||
| rParamZyn(modwheel.depth, rShort("mdw.d"), "Depth of Modwheel MIDI Control"), | |||
| rToggle(modwheel.exponential, rShort("mdw.exp"), "Modwheel Exponential Mode"), | |||
| rToggle(pitchwheel.is_split, "If PitchWheel Has unified bendrange or not"), | |||
| rParamI(pitchwheel.bendrange, "Range of MIDI Pitch Wheel"), | |||
| rParamI(pitchwheel.bendrange_down, "Lower Range of MIDI Pitch Wheel"), | |||
| rToggle(expression.receive, "Expression MIDI Receive"), | |||
| rToggle(fmamp.receive, "FM amplitude MIDI Receive"), | |||
| rToggle(volume.receive, "Volume MIDI Receive"), | |||
| rToggle(sustain.receive, "Sustain MIDI Receive"), | |||
| rToggle(portamento.receive, "Portamento MIDI Receive"), | |||
| rToggle(expression.receive, rShort("exp.rcv"), "Expression MIDI Receive"), | |||
| rToggle(fmamp.receive, rShort("fma.rcv"), "FM amplitude MIDI Receive"), | |||
| rToggle(volume.receive, rShort("vol.rcv"), "Volume MIDI Receive"), | |||
| rToggle(sustain.receive, rShort("sus.rcv"), "Sustain MIDI Receive"), | |||
| rToggle(portamento.receive, rShort("prt.rcv"), "Portamento MIDI Receive"), | |||
| rToggle(portamento.portamento, "UNDOCUMENTED"), | |||
| rParamZyn(portamento.time, "Portamento Length"), | |||
| rToggle(portamento.proportional, "If all portamentos are proportional to the distance they span"), | |||
| rParamZyn(portamento.propRate, "Portamento proportional rate"), | |||
| rParamZyn(portamento.propDepth, "Portamento proportional depth"), | |||
| rParamZyn(portamento.pitchthresh, "Threshold for portamento"), | |||
| rToggle(portamento.pitchthreshtype, "Type of threshold"), | |||
| rParamZyn(portamento.time, rShort("time"), "Portamento Length"), | |||
| rToggle(portamento.proportional, rShort("propt."), "If all portamentos are proportional to the distance they span"), | |||
| rParamZyn(portamento.propRate, rShort("rate"), "Portamento proportional rate"), | |||
| rParamZyn(portamento.propDepth, rShort("depth"), "Portamento proportional depth"), | |||
| rParamZyn(portamento.pitchthresh, rShort("thresh"), "Threshold for portamento"), | |||
| rToggle(portamento.pitchthreshtype, rShort("tr.type"), "Type of threshold"), | |||
| rParamZyn(portamento.updowntimestretch, "UNDOCUMENTED"), | |||
| rParamZyn(resonancecenter.depth, "Resonance Center MIDI Depth"), | |||
| rParamZyn(resonancebandwidth.depth, "Resonance Bandwidth MIDI Depth"), | |||
| rParamZyn(resonancecenter.depth, rShort("rfc.d"), "Resonance Center MIDI Depth"), | |||
| rParamZyn(resonancebandwidth.depth, rShort("rbw.d"), "Resonance Bandwidth MIDI Depth"), | |||
| rToggle(NRPN.receive, "NRPN MIDI Enable"), | |||
| rAction(defaults), | |||
| }; | |||
| @@ -23,6 +23,9 @@ | |||
| #define rObject EnvelopeParams | |||
| using namespace rtosc; | |||
| #define rBegin [](const char *msg, RtData &d) { \ | |||
| EnvelopeParams *env = (rObject*) d.obj | |||
| #define rEnd } | |||
| static const rtosc::Ports localPorts = { | |||
| rSelf(EnvelopeParams), | |||
| @@ -32,60 +35,85 @@ static const rtosc::Ports localPorts = { | |||
| obj->last_update_timestamp = obj->time->time(); } | |||
| rToggle(Pfreemode, "Complex Envelope Definitions"), | |||
| #undef rChangeCb | |||
| #define rChangeCb if (obj->time) { obj->last_update_timestamp = obj->time->time(); } | |||
| #define rChangeCb if(!obj->Pfreemode) obj->converttofree(); \ | |||
| if(obj->time) { obj->last_update_timestamp = obj->time->time(); } | |||
| rParamZyn(Penvpoints, rProp(internal), "Number of points in complex definition"), | |||
| rParamZyn(Penvsustain, rProp(internal), "Location of the sustain point"), | |||
| rParams(Penvdt, MAX_ENVELOPE_POINTS, "Envelope Delay Times"), | |||
| rParams(Penvval, MAX_ENVELOPE_POINTS, "Envelope Values"), | |||
| rParamZyn(Penvstretch, "Stretch with respect to frequency"), | |||
| rToggle(Pforcedrelease, "Force Envelope to fully evaluate"), | |||
| rToggle(Plinearenvelope, "Linear or Logarithmic Envelopes"), | |||
| rParamZyn(PA_dt, "Attack Time"), | |||
| rParamZyn(PA_val, "Attack Value"), | |||
| rParamZyn(PD_dt, "Decay Time"), | |||
| rParamZyn(PD_val, "Decay Value"), | |||
| rParamZyn(PS_val, "Sustain Value"), | |||
| rParamZyn(PR_dt, "Release Time"), | |||
| rParamZyn(PR_val, "Release Value"), | |||
| {"addPoint:i", rProp(internal) rDoc("Add point to envelope"), NULL, [](const char *msg, RtData &d) | |||
| { | |||
| EnvelopeParams *env = (rObject*) d.obj; | |||
| const int curpoint = rtosc_argument(msg, 0).i; | |||
| //int curpoint=freeedit->lastpoint; | |||
| if (curpoint<0 || curpoint>env->Penvpoints || env->Penvpoints>=MAX_ENVELOPE_POINTS) | |||
| return; | |||
| for (int i=env->Penvpoints; i>=curpoint+1; i--) { | |||
| env->Penvdt[i]=env->Penvdt[i-1]; | |||
| env->Penvval[i]=env->Penvval[i-1]; | |||
| } | |||
| if (curpoint==0) { | |||
| env->Penvdt[1]=64; | |||
| } | |||
| env->Penvpoints++; | |||
| if (curpoint<=env->Penvsustain) env->Penvsustain++; | |||
| }}, | |||
| {"delPoint:i", rProp(internal) rDoc("Delete Envelope Point"), NULL, [](const char *msg, RtData &d) | |||
| { | |||
| EnvelopeParams *env = (rObject*) d.obj; | |||
| const int curpoint=rtosc_argument(msg, 0).i; | |||
| if(curpoint<1 || curpoint>=env->Penvpoints-1 || env->Penvpoints<=3) | |||
| return; | |||
| for (int i=curpoint+1;i<env->Penvpoints;i++){ | |||
| env->Penvdt[i-1]=env->Penvdt[i]; | |||
| env->Penvval[i-1]=env->Penvval[i]; | |||
| }; | |||
| env->Penvpoints--; | |||
| if (curpoint<=env->Penvsustain) | |||
| env->Penvsustain--; | |||
| }}, | |||
| rParamZyn(Penvstretch, rShort("stretch"), | |||
| "Stretch with respect to frequency"), | |||
| rToggle(Pforcedrelease, rShort("frcr"), | |||
| "Force Envelope to fully evaluate"), | |||
| rToggle(Plinearenvelope, rShort("lin/log"), | |||
| "Linear or Logarithmic Envelopes"), | |||
| rParamZyn(PA_dt, rShort("a.dt"), "Attack Time"), | |||
| rParamZyn(PA_val, rShort("a.val"), "Attack Value"), | |||
| rParamZyn(PD_dt, rShort("d.dt"), "Decay Time"), | |||
| rParamZyn(PD_val, rShort("d.val"), "Decay Value"), | |||
| rParamZyn(PS_val, rShort("s.val"), "Sustain Value"), | |||
| rParamZyn(PR_dt, rShort("r.dt"), "Release Time"), | |||
| rParamZyn(PR_val, rShort("r.val"), "Release Value"), | |||
| {"envdt:", rDoc("Envelope Delay Times"), NULL, | |||
| rBegin; | |||
| const int N = MAX_ENVELOPE_POINTS; | |||
| rtosc_arg_t args[N]; | |||
| char arg_types[N+1] = {0}; | |||
| for(int i=0; i<N; ++i) { | |||
| args[i].f = env->getdt(i); | |||
| arg_types[i] = 'f'; | |||
| } | |||
| d.replyArray(d.loc, arg_types, args); | |||
| rEnd}, | |||
| {"envval:", rDoc("Envelope Delay Times"), NULL, | |||
| rBegin; | |||
| const int N = MAX_ENVELOPE_POINTS; | |||
| rtosc_arg_t args[N]; | |||
| char arg_types[N+1] = {0}; | |||
| for(int i=0; i<N; ++i) { | |||
| args[i].f = env->Penvval[i]/127.0f; | |||
| arg_types[i] = 'f'; | |||
| } | |||
| d.replyArray(d.loc, arg_types, args); | |||
| rEnd}, | |||
| {"addPoint:i", rProp(internal) rDoc("Add point to envelope"), NULL, | |||
| rBegin; | |||
| const int curpoint = rtosc_argument(msg, 0).i; | |||
| //int curpoint=freeedit->lastpoint; | |||
| if (curpoint<0 || curpoint>env->Penvpoints || env->Penvpoints>=MAX_ENVELOPE_POINTS) | |||
| return; | |||
| for (int i=env->Penvpoints; i>=curpoint+1; i--) { | |||
| env->Penvdt[i]=env->Penvdt[i-1]; | |||
| env->Penvval[i]=env->Penvval[i-1]; | |||
| } | |||
| if (curpoint==0) | |||
| env->Penvdt[1]=64; | |||
| env->Penvpoints++; | |||
| if (curpoint<=env->Penvsustain) | |||
| env->Penvsustain++; | |||
| rEnd}, | |||
| {"delPoint:i", rProp(internal) rDoc("Delete Envelope Point"), NULL, | |||
| rBegin; | |||
| const int curpoint=rtosc_argument(msg, 0).i; | |||
| if(curpoint<1 || curpoint>=env->Penvpoints-1 || env->Penvpoints<=3) | |||
| return; | |||
| for (int i=curpoint+1;i<env->Penvpoints;i++){ | |||
| env->Penvdt[i-1]=env->Penvdt[i]; | |||
| env->Penvval[i-1]=env->Penvval[i]; | |||
| }; | |||
| env->Penvpoints--; | |||
| if (curpoint<=env->Penvsustain) | |||
| env->Penvsustain--; | |||
| rEnd}, | |||
| }; | |||
| #undef rChangeCb | |||
| @@ -14,6 +14,7 @@ | |||
| #include "FilterParams.h" | |||
| #include "../Misc/Util.h" | |||
| #include "../Misc/Time.h" | |||
| #include "../DSP/AnalogFilter.h" | |||
| #include <cmath> | |||
| #include <cstdio> | |||
| #include <cstdlib> | |||
| @@ -58,18 +59,29 @@ const rtosc::Ports FilterParams::ports = { | |||
| rSelf(FilterParams), | |||
| rPaste, | |||
| rArrayPaste, | |||
| rParamZyn(Pcategory, "Class of filter"), | |||
| rParamZyn(Ptype, "Filter Type"), | |||
| rParamZyn(Pfreq, "Center Freq"), | |||
| rParamZyn(Pq, "Quality Factor (resonance/bandwidth)"), | |||
| rParamZyn(Pstages, "Filter Stages + 1"), | |||
| rParamZyn(Pfreqtrack, "Frequency Tracking amount"), | |||
| rParamZyn(Pgain, "Output Gain"), | |||
| rParamZyn(Pnumformants, "Number of formants to be used"), | |||
| rParamZyn(Pformantslowness, "Rate that formants change"), | |||
| rParamZyn(Pvowelclearness, "Cost for mixing vowels"), | |||
| rParamZyn(Pcenterfreq, "Center Freq (formant)"), | |||
| rParamZyn(Poctavesfreq, "Number of octaves for formant"), | |||
| rOption(Pcategory, rShort("class"), | |||
| rOptions(analog, formant, st.var.), "Class of filter"), | |||
| rOption(Ptype, rShort("type"), | |||
| rOptions(LP1, HP1, LP2, HP2, BP, notch, peak, | |||
| l.shelf, h.shelf), "Filter Type"), | |||
| rParamZyn(Pfreq, rShort("cutoff"), "Center Freq"), | |||
| rParamZyn(Pq, rShort("q"), | |||
| "Quality Factor (resonance/bandwidth)"), | |||
| rOption(Pstages, rShort("stages"), | |||
| rOptions(1, 2, 3, 4, 5), "Filter Stages"), | |||
| rParamZyn(Pfreqtrack, rShort("f.track"), | |||
| "Frequency Tracking amount"), | |||
| rParamZyn(Pgain, rShort("gain"), "Output Gain"), | |||
| rParamZyn(Pnumformants, rShort("formants"), | |||
| "Number of formants to be used"), | |||
| rParamZyn(Pformantslowness, rShort("slew"), | |||
| "Rate that formants change"), | |||
| rParamZyn(Pvowelclearness, rShort("clarity"), | |||
| "Cost for mixing vowels"), | |||
| rParamZyn(Pcenterfreq, rShort("cutoff"), | |||
| "Center Freq (formant)"), | |||
| rParamZyn(Poctavesfreq, rShort("octaves"), | |||
| "Number of octaves for formant"), | |||
| //TODO check if FF_MAX_SEQUENCE is acutally expanded or not | |||
| rParamZyn(Psequencesize, rMap(max, FF_MAX_SEQUENCE), "Length of vowel sequence"), | |||
| @@ -110,11 +122,79 @@ const rtosc::Ports FilterParams::ports = { | |||
| FilterParams *obj = (FilterParams *) d.obj; | |||
| d.reply(d.loc, "f", obj->getoctavesfreq()); | |||
| }}, | |||
| {"q_value:", | |||
| rDoc("Q value for UI Response Graphs"), | |||
| NULL, [](const char *, RtData &d) { | |||
| FilterParams *obj = (FilterParams *) d.obj; | |||
| d.reply(d.loc, "f", obj->getq()); | |||
| }}, | |||
| {"response:", | |||
| rDoc("Get a frequency response"), | |||
| NULL, [](const char *, RtData &d) { | |||
| FilterParams *obj = (FilterParams *) d.obj; | |||
| int order = 0; | |||
| float gain = dB2rap(obj->getgain()); | |||
| if(obj->Ptype != 6 && obj->Ptype != 7 && obj->Ptype != 8) | |||
| gain = 1.0; | |||
| auto cf = AnalogFilter::computeCoeff(obj->Ptype, | |||
| Filter::getrealfreq(obj->getfreq()), | |||
| obj->getq(), obj->Pstages, | |||
| gain, 48000, order); | |||
| if(order == 2) { | |||
| d.reply(d.loc, "fffffff", | |||
| (float)obj->Pstages, | |||
| cf.c[0], cf.c[1], cf.c[2], | |||
| 0.0, cf.d[1], cf.d[2]); | |||
| } else if(order == 1) { | |||
| d.reply(d.loc, "fffff", | |||
| (float)obj->Pstages, | |||
| cf.c[0], cf.c[1], | |||
| 0.0, cf.d[1]); | |||
| } | |||
| }}, | |||
| // "", NULL, [](){}},"/freq" | |||
| //{"Pvowels#" FF_MAX_VOWELS "/formants#" FF_MAX_FORMANTS "/amp", | |||
| // "", NULL, [](){}}, | |||
| //{"Pvowels#" FF_MAX_VOWELS "/formants#" FF_MAX_FORMANTS "/q", | |||
| // "", NULL, [](){}}, | |||
| // | |||
| //struct Pvowels_t { | |||
| // struct formants_t { | |||
| // unsigned char freq, amp, q; //frequency,amplitude,Q | |||
| // } formants[FF_MAX_FORMANTS]; | |||
| //} Pvowels[FF_MAX_VOWELS]; | |||
| {"vowels:", | |||
| rDoc("Get info for formant graph"), | |||
| NULL, [](const char *, RtData &d) { | |||
| FilterParams *obj = (FilterParams *) d.obj; | |||
| rtosc_arg_t args[2+3*FF_MAX_FORMANTS*FF_MAX_VOWELS]; | |||
| char type[2+3*FF_MAX_FORMANTS*FF_MAX_VOWELS + 1] = {0}; | |||
| type[0] = 'i'; | |||
| type[1] = 'i'; | |||
| args[0].i = FF_MAX_VOWELS; | |||
| args[1].i = FF_MAX_FORMANTS; | |||
| for(int i=0; i<FF_MAX_VOWELS; ++i) { | |||
| auto &val = obj->Pvowels[i]; | |||
| for(int j=0; j<FF_MAX_FORMANTS; ++j) { | |||
| auto &f = val.formants[j]; | |||
| //each formant is 3 arguments | |||
| //each vowel is FF_MAX_FORMANTS * length of formants long | |||
| auto *a = args + i*FF_MAX_FORMANTS*3 + j*3 + 2; | |||
| auto *t = type + i*FF_MAX_FORMANTS*3 + j*3 + 2; | |||
| a[0].f = obj->getformantfreq(f.freq); | |||
| a[1].f = obj->getformantamp(f.amp); | |||
| a[2].f = obj->getformantq(f.q); | |||
| //printf("<%d,%d,%d,%d,%d,%f,%f,%f>\n", i, j, f.freq, f.amp, f.q, a[0].f, a[1].f, a[2].f); | |||
| t[0] = t[1] = t[2] = 'f'; | |||
| } | |||
| } | |||
| d.replyArray(d.loc, type, args); | |||
| }}, | |||
| }; | |||
| #undef rChangeCb | |||
| #define rChangeCb | |||
| @@ -287,21 +367,18 @@ float FilterParams::getfreqpos(float freq) const | |||
| */ | |||
| float FilterParams::getformantfreq(unsigned char freq) const | |||
| { | |||
| float result = getfreqx(freq / 127.0f); | |||
| return result; | |||
| return getfreqx(freq / 127.0f); | |||
| } | |||
| float FilterParams::getformantamp(unsigned char amp) const | |||
| { | |||
| float result = powf(0.1f, (1.0f - amp / 127.0f) * 4.0f); | |||
| return result; | |||
| return powf(0.1f, (1.0f - amp / 127.0f) * 4.0f); | |||
| } | |||
| float FilterParams::getformantq(unsigned char q) const | |||
| { | |||
| //temp | |||
| float result = powf(25.0f, (q - 32.0f) / 64.0f); | |||
| return result; | |||
| return powf(25.0f, (q - 32.0f) / 64.0f); | |||
| } | |||
| @@ -27,6 +27,8 @@ using namespace rtosc; | |||
| #define rObject LFOParams | |||
| #undef rChangeCb | |||
| #define rChangeCb if (obj->time) { obj->last_update_timestamp = obj->time->time(); } | |||
| #define rBegin [](const char *msg, rtosc::RtData &d) { | |||
| #define rEnd } | |||
| static const rtosc::Ports _ports = { | |||
| rSelf(LFOParams), | |||
| rPaste, | |||
| @@ -35,8 +37,8 @@ static const rtosc::Ports _ports = { | |||
| "true frequency is [0,85.33] Hz"), | |||
| rParamZyn(Pintensity, rShort("depth"), "Intensity of LFO"), | |||
| rParamZyn(Pstartphase, rShort("start"), rSpecial(random), "Starting Phase"), | |||
| rOption(PLFOtype, rShort("type"), rOptions(sine, triangle, square, ramp-up, ramp-down, | |||
| exponential-down1, exponential-down2), "Shape of LFO"), | |||
| rOption(PLFOtype, rShort("type"), rOptions(sine, triangle, square, up, down, | |||
| exp1, exp2), "Shape of LFO"), | |||
| rParamZyn(Prandomness, rShort("a.r."), rSpecial(disable), | |||
| "Amplitude Randomness (calculated uniformly at each cycle)"), | |||
| rParamZyn(Pfreqrand, rShort("f.r."), rSpecial(disable), | |||
| @@ -45,7 +47,20 @@ static const rtosc::Ports _ports = { | |||
| "0..4 second delay"), | |||
| rToggle(Pcontinous, rShort("c"), "Enable for global operation"), | |||
| rParamZyn(Pstretch, rShort("str"), rCentered, "Note frequency stretch"), | |||
| //Float valued aliases | |||
| {"delay::f", rProp(parameter) rMap(units, ms) rLog(0,4000), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| #define rPseudoLog(a,b) rLog(a,b) | |||
| {"period::f", rProp(parameter) rMap(units, ms) rPseudoLog(0.10, 1500.0), 0, | |||
| rBegin; | |||
| rEnd}, | |||
| }; | |||
| #undef rPseudoLog | |||
| #undef rBegin | |||
| #undef rEnd | |||
| #undef rChangeCb | |||
| const rtosc::Ports &LFOParams::ports = _ports; | |||
| @@ -18,6 +18,7 @@ | |||
| #include "../Synth/Resonance.h" | |||
| #include "../Synth/OscilGen.h" | |||
| #include "../Misc/WavFile.h" | |||
| #include "../Misc/Time.h" | |||
| #include <cstdio> | |||
| #include <rtosc/ports.h> | |||
| @@ -136,30 +137,30 @@ static const rtosc::Ports non_realtime_ports = | |||
| rRecurp(resonance, "Resonance"), | |||
| //Harmonic Shape | |||
| rOption(Pmode, rMap(min, 0), rMap(max, 2), rOptions(bandwidth,discrete,continious), | |||
| rOption(Pmode, rMap(min, 0), rMap(max, 2), rShort("distribution"), rOptions(bandwidth,discrete,continious), | |||
| "Harmonic Distribution Model"), | |||
| rOption(Php.base.type, rOptions(Gaussian, Rectanglar, Double Exponential), | |||
| rOption(Php.base.type, rOptions(Gaussian, Rectanglar, Double Exponential), rShort("shape"), | |||
| "Harmonic profile shape"), | |||
| rParamZyn(Php.base.par1, "Harmonic shape distribution parameter"), | |||
| rParamZyn(Php.freqmult, "Frequency multiplier on distribution"), | |||
| rParamZyn(Php.modulator.par1, "Distribution modulator parameter"), | |||
| rParamZyn(Php.modulator.freq, "Frequency of modulator parameter"), | |||
| rParamZyn(Php.width, "Width of base harmonic"), | |||
| rOption(Php.amp.mode, rOptions(Sum, Mult, Div1, Div2), | |||
| rParamZyn(Php.base.par1, rShort("warp"), "Harmonic shape distribution parameter"), | |||
| rParamZyn(Php.freqmult, rShort("clone"), "Frequency multiplier on distribution"), | |||
| rParamZyn(Php.modulator.par1, rShort("p1"), "Distribution modulator parameter"), | |||
| rParamZyn(Php.modulator.freq, rShort("freq"), "Frequency of modulator parameter"), | |||
| rParamZyn(Php.width, rShort("bandwidth"), "Width of base harmonic"), | |||
| rOption(Php.amp.mode, rShort("mode"), rOptions(Sum, Mult, Div1, Div2), | |||
| "Amplitude harmonic multiplier type"), | |||
| //Harmonic Modulation | |||
| rOption(Php.amp.type, rOptions(Off, Gauss, Sine, Flat), | |||
| rOption(Php.amp.type, rShort("mult"), rOptions(Off, Gauss, Sine, Flat), | |||
| "Type of amplitude multipler"), | |||
| rParamZyn(Php.amp.par1, "Amplitude multiplier parameter"), | |||
| rParamZyn(Php.amp.par2, "Amplitude multiplier parameter"), | |||
| rToggle(Php.autoscale, "Autoscaling Harmonics"), | |||
| rOption(Php.onehalf, | |||
| rParamZyn(Php.amp.par1, rShort("p1"), "Amplitude multiplier parameter"), | |||
| rParamZyn(Php.amp.par2, rShort("p2"), "Amplitude multiplier parameter"), | |||
| rToggle(Php.autoscale, rShort("auto"), "Autoscaling Harmonics"), | |||
| rOption(Php.onehalf, rShort("side"), | |||
| rOptions(Full, Upper Half, Lower Half), | |||
| "Harmonic cutoff model"), | |||
| //Harmonic Bandwidth | |||
| rOption(Pbwscale, | |||
| rOption(Pbwscale, rShort("bw scale"), | |||
| rOptions(Normal, | |||
| EqualHz, Quater, | |||
| Half, 75%, 150%, | |||
| @@ -171,26 +172,26 @@ static const rtosc::Ports non_realtime_ports = | |||
| rOptions(Harmonic, ShiftU, ShiftL, PowerU, PowerL, Sine, | |||
| Power, Shift), | |||
| "Harmonic Overtone shifting mode"), | |||
| rParamI(Phrpos.par1, rLinear(0,255), "Harmonic position parameter"), | |||
| rParamI(Phrpos.par2, rLinear(0,255), "Harmonic position parameter"), | |||
| rParamI(Phrpos.par3, rLinear(0,255), "Harmonic position parameter"), | |||
| rParamI(Phrpos.par1, rShort("p1"), rLinear(0,255), "Harmonic position parameter"), | |||
| rParamI(Phrpos.par2, rShort("p2"), rLinear(0,255), "Harmonic position parameter"), | |||
| rParamI(Phrpos.par3, rShort("force h."), rLinear(0,255), "Harmonic position parameter"), | |||
| //Quality | |||
| rOption(Pquality.samplesize, | |||
| rOption(Pquality.samplesize, rShort("quality"), | |||
| rOptions(16k (Tiny), 32k, 64k (Small), 128k, | |||
| 256k (Normal), 512k, 1M (Big)), | |||
| "Size of each wavetable element"), | |||
| rOption(Pquality.basenote, | |||
| rOptions( C-2, G-2, C-3, G-3, C-4, | |||
| rOption(Pquality.basenote, rShort("basenote"), | |||
| rOptions(C-2, G-2, C-3, G-3, C-4, | |||
| G-4, C-5, G-5, G-6,), | |||
| "Base note for wavetable"), | |||
| rOption(Pquality.smpoct, | |||
| rOption(Pquality.smpoct, rShort("smp/oct"), | |||
| rOptions(0.5, 1, 2, 3, 4, 6, 12), | |||
| "Samples per octave"), | |||
| rParamI(Pquality.oct, rLinear(0,7), | |||
| rParamI(Pquality.oct, rShort("octaves"), rLinear(0,7), | |||
| "Number of octaves to sample (above the first sample"), | |||
| {"Pbandwidth::i", rProp(parameter) rLinear(0,1000) rDoc("Bandwith Of Harmonics"), NULL, | |||
| {"Pbandwidth::i", rShort("bandwidth") rProp(parameter) rLinear(0,1000) rDoc("Bandwith Of Harmonics"), NULL, | |||
| [](const char *msg, rtosc::RtData &d) { | |||
| PADnoteParameters *p = ((PADnoteParameters*)d.obj); | |||
| if(rtosc_narguments(msg)) { | |||
| @@ -225,8 +226,24 @@ static const rtosc::Ports non_realtime_ports = | |||
| float *tmp = new float[n]; | |||
| float realbw = p->getprofile(tmp, n); | |||
| d.reply(d.loc, "b", n*sizeof(float), tmp); | |||
| d.reply(d.loc, "i", realbw); | |||
| d.reply(d.loc, "i", (int)realbw); | |||
| delete[] tmp;}}, | |||
| {"harmonic_profile:", rProp(non-realtime) rDoc("UI display of the harmonic profile"), | |||
| NULL, [](const char *m, rtosc::RtData &d) { | |||
| PADnoteParameters *p = ((PADnoteParameters*)d.obj); | |||
| #define RES 512 | |||
| char types[RES+2] = {0}; | |||
| rtosc_arg_t args[RES+1]; | |||
| float tmp[RES]; | |||
| types[0] = 'f'; | |||
| args[0].f = p->getprofile(tmp, RES); | |||
| for(int i=0; i<RES; ++i) { | |||
| types[i+1] = 'f'; | |||
| args[i+1].f = tmp[i]; | |||
| } | |||
| d.replyArray(d.loc, types, args); | |||
| #undef RES | |||
| }}, | |||
| {"needPrepare:", rDoc("Unimplemented Stub"), | |||
| NULL, [](const char *, rtosc::RtData&) {}}, | |||
| }; | |||
| @@ -26,102 +26,147 @@ | |||
| #define rObject SUBnoteParameters | |||
| using namespace rtosc; | |||
| #define rBegin [](const char *msg, RtData &d) { \ | |||
| SUBnoteParameters *obj = (SUBnoteParameters*) d.obj | |||
| #define rEnd } | |||
| #undef rChangeCb | |||
| #define rChangeCb if (obj->time) { obj->last_update_timestamp = obj->time->time(); } | |||
| static const rtosc::Ports SUBnotePorts = { | |||
| rSelf(SUBnoteParameters), | |||
| rPaste, | |||
| rToggle(Pstereo, "Stereo Enable"), | |||
| rParamZyn(PVolume, "Volume"), | |||
| rParamZyn(PPanning, "Left Right Panning"), | |||
| rParamZyn(PAmpVelocityScaleFunction, "Amplitude Velocity Sensing function"), | |||
| rParamI(PDetune, "Detune in detune type units"), | |||
| rParamI(PCoarseDetune, "Coarse Detune"), | |||
| rToggle(Pstereo, rShort("stereo"), "Stereo Enable"), | |||
| rParamZyn(PVolume, rShort("volume"), "Volume"), | |||
| rParamZyn(PPanning, rShort("panning"), "Left Right Panning"), | |||
| rParamZyn(PAmpVelocityScaleFunction, rShort("sense"), "Amplitude Velocity Sensing function"), | |||
| rParamI(PDetune, rShort("detune"), "Detune in detune type units"), | |||
| rParamI(PCoarseDetune, rShort("cdetune"), "Coarse Detune"), | |||
| //Real values needed | |||
| rOption(PDetuneType, rOptions("100 cents", "200 cents", "500 cents"), "Detune Scale"), | |||
| rToggle(PFreqEnvelopeEnabled, "Enable for Frequency Envelope"), | |||
| rToggle(PBandWidthEnvelopeEnabled, "Enable for Bandwidth Envelope"), | |||
| rToggle(PGlobalFilterEnabled, "Enable for Global Filter"), | |||
| rParamZyn(PGlobalFilterVelocityScale, "Filter Velocity Magnitude"), | |||
| rParamZyn(PGlobalFilterVelocityScaleFunction, "Filter Velocity Function Shape"), | |||
| rOption(PDetuneType, rShort("det. scl."), rOptions(100 cents, 200 cents, 500 cents), "Detune Scale"), | |||
| rToggle(PFreqEnvelopeEnabled, rShort("enable"), "Enable for Frequency Envelope"), | |||
| rToggle(PBandWidthEnvelopeEnabled, rShort("enable"), "Enable for Bandwidth Envelope"), | |||
| rToggle(PGlobalFilterEnabled, rShort("enable"), "Enable for Global Filter"), | |||
| rParamZyn(PGlobalFilterVelocityScale, rShort("scale"), "Filter Velocity Magnitude"), | |||
| rParamZyn(PGlobalFilterVelocityScaleFunction, rShort("sense"), "Filter Velocity Function Shape"), | |||
| //rRecur(FreqEnvelope, EnvelopeParams), | |||
| //rToggle(),//continue | |||
| rToggle(Pfixedfreq, "Base frequency fixed frequency enable"), | |||
| rParamZyn(PfixedfreqET, "Equal temeperate control for fixed frequency operation"), | |||
| rParamZyn(PBendAdjust, "Pitch bend adjustment"), | |||
| rParamZyn(POffsetHz, "Voice constant offset"), | |||
| rToggle(Pfixedfreq, rShort("fixed freq"), "Base frequency fixed frequency enable"), | |||
| rParamZyn(PfixedfreqET, rShort("fixed ET"), "Equal temeperate control for fixed frequency operation"), | |||
| rParamZyn(PBendAdjust, rShort("bend"), "Pitch bend adjustment"), | |||
| rParamZyn(POffsetHz, rShort("+ Hz"), "Voice constant offset"), | |||
| #undef rChangeCb | |||
| #define rChangeCb obj->updateFrequencyMultipliers(); if (obj->time) { \ | |||
| obj->last_update_timestamp = obj->time->time(); } | |||
| rParamI(POvertoneSpread.type, rMap(min, 0), rMap(max, 7), | |||
| rParamI(POvertoneSpread.type, rMap(min, 0), rMap(max, 7), rShort("spread type") | |||
| rOptions(Harmonic, ShiftU, ShiftL, PowerU, PowerL, Sine, Power, Shift), | |||
| "Spread of harmonic frequencies"), | |||
| rParamI(POvertoneSpread.par1, rMap(min, 0), rMap(max, 255), | |||
| rParamI(POvertoneSpread.par1, rMap(min, 0), rMap(max, 255), rShort("p1"), | |||
| "Overtone Parameter"), | |||
| rParamI(POvertoneSpread.par2, rMap(min, 0), rMap(max, 255), | |||
| rParamI(POvertoneSpread.par2, rMap(min, 0), rMap(max, 255), rShort("p2"), | |||
| "Overtone Parameter"), | |||
| rParamI(POvertoneSpread.par3, rMap(min, 0), rMap(max, 255), | |||
| rParamI(POvertoneSpread.par3, rMap(min, 0), rMap(max, 255), rShort("p3"), | |||
| "Overtone Parameter"), | |||
| #undef rChangeCb | |||
| #define rChangeCb if (obj->time) { obj->last_update_timestamp = obj->time->time(); } | |||
| rParamZyn(Pnumstages, rMap(min, 1), rMap(max, 5), "Number of filter stages"), | |||
| rParamZyn(Pbandwidth, "Bandwidth of filters"), | |||
| rParamZyn(Phmagtype, "How the magnitudes are computed (0=linear,1=-60dB,2=-60dB)"), | |||
| rParamZyn(Pnumstages, rShort("stages"), rMap(min, 1), rMap(max, 5), "Number of filter stages"), | |||
| rParamZyn(Pbandwidth, rShort("bandwidth"), "Bandwidth of filters"), | |||
| rParamZyn(Phmagtype, rShort("mag. type"),"How the magnitudes are computed (0=linear,1=-60dB,2=-60dB)"), | |||
| rArray(Phmag, MAX_SUB_HARMONICS, "Harmonic magnitudes"), | |||
| rArray(Phrelbw, MAX_SUB_HARMONICS, "Relative bandwidth"), | |||
| rParamZyn(Pbwscale, "Bandwidth scaling with frequency"), | |||
| rParamZyn(Pbwscale, rShort("stretch"), "Bandwidth scaling with frequency"), | |||
| rRecurp(AmpEnvelope, "Amplitude envelope"), | |||
| rRecurp(FreqEnvelope, "Frequency Envelope"), | |||
| rRecurp(BandWidthEnvelope, "Bandwidth Envelope"), | |||
| rRecurp(GlobalFilterEnvelope, "Post Filter Envelope"), | |||
| rRecurp(GlobalFilter, "Post Filter"), | |||
| rOption(Pstart, rOptions("zero", "random", "ones"), "How harmonics are initialized"), | |||
| {"clear:", rDoc("Reset all harmonics to equal bandwidth/zero amplitude"), NULL, [](const char *, RtData &d) | |||
| { | |||
| SUBnoteParameters *obj = (SUBnoteParameters *)d.obj; | |||
| for(int i=0; i<MAX_SUB_HARMONICS; ++i) { | |||
| obj->Phmag[i] = 0; | |||
| obj->Phrelbw[i] = 64; | |||
| } | |||
| obj->Phmag[0] = 127; | |||
| }}, | |||
| {"detunevalue:", rDoc("Get note detune value"), NULL, [](const char *, RtData &d) | |||
| { | |||
| SUBnoteParameters *obj = (SUBnoteParameters *)d.obj; | |||
| d.reply(d.loc, "f", getdetune(obj->PDetuneType, 0, obj->PDetune)); | |||
| }}, | |||
| rOption(Pstart, rShort("initial"), rOptions(zero, random, ones), "How harmonics are initialized"), | |||
| {"clear:", rDoc("Reset all harmonics to equal bandwidth/zero amplitude"), NULL, | |||
| rBegin; | |||
| for(int i=0; i<MAX_SUB_HARMONICS; ++i) { | |||
| obj->Phmag[i] = 0; | |||
| obj->Phrelbw[i] = 64; | |||
| } | |||
| obj->Phmag[0] = 127; | |||
| rEnd}, | |||
| {"detunevalue:", rDoc("Get note detune value"), NULL, | |||
| rBegin; | |||
| d.reply(d.loc, "f", getdetune(obj->PDetuneType, 0, obj->PDetune)); | |||
| rEnd}, | |||
| //weird stuff for PCoarseDetune | |||
| {"octave::c:i", rProp(parameter) rDoc("Note octave shift"), NULL, | |||
| [](const char *msg, RtData &d) | |||
| { | |||
| SUBnoteParameters *obj = (SUBnoteParameters *)d.obj; | |||
| if(!rtosc_narguments(msg)) { | |||
| int k=obj->PCoarseDetune/1024; | |||
| if (k>=8) k-=16; | |||
| d.reply(d.loc, "i", k); | |||
| } else { | |||
| int k=(int) rtosc_argument(msg, 0).i; | |||
| if (k<0) k+=16; | |||
| obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; | |||
| } | |||
| }}, | |||
| rBegin; | |||
| if(!rtosc_narguments(msg)) { | |||
| int k=obj->PCoarseDetune/1024; | |||
| if (k>=8) k-=16; | |||
| d.reply(d.loc, "i", k); | |||
| } else { | |||
| int k=(int) rtosc_argument(msg, 0).i; | |||
| if (k<0) k+=16; | |||
| obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; | |||
| } | |||
| rEnd}, | |||
| {"coarsedetune::c:i", rProp(parameter) rDoc("Note coarse detune"), NULL, | |||
| [](const char *msg, RtData &d) | |||
| { | |||
| SUBnoteParameters *obj = (SUBnoteParameters *)d.obj; | |||
| if(!rtosc_narguments(msg)) { | |||
| int k=obj->PCoarseDetune%1024; | |||
| if (k>=512) k-=1024; | |||
| d.reply(d.loc, "i", k); | |||
| } else { | |||
| int k=(int) rtosc_argument(msg, 0).i; | |||
| if (k<0) k+=1024; | |||
| obj->PCoarseDetune = k + (obj->PCoarseDetune/1024)*1024; | |||
| } | |||
| }}, | |||
| rBegin; | |||
| if(!rtosc_narguments(msg)) { | |||
| int k=obj->PCoarseDetune%1024; | |||
| if (k>=512) k-=1024; | |||
| d.reply(d.loc, "i", k); | |||
| } else { | |||
| int k=(int) rtosc_argument(msg, 0).i; | |||
| if (k<0) k+=1024; | |||
| obj->PCoarseDetune = k + (obj->PCoarseDetune/1024)*1024; | |||
| } | |||
| rEnd}, | |||
| {"response:", rDoc("Filter response at 440Hz. with 48kHz sample rate\n\n" | |||
| "Format: stages, filter*active_filters\n" | |||
| " filter = [frequency, bandwidth, amplitude]"), | |||
| NULL, | |||
| rBegin; | |||
| //Identify the active harmonics | |||
| int pos[MAX_SUB_HARMONICS]; | |||
| int harmonics; | |||
| obj->activeHarmonics(pos, harmonics); | |||
| float base_freq = 440.0f; | |||
| char types[3*MAX_SUB_HARMONICS+2]; | |||
| rtosc_arg_t args[3*MAX_SUB_HARMONICS+1]; | |||
| args[0].i = obj->Pnumstages; | |||
| types[0] = 'i'; | |||
| for(int n=0; n<harmonics; ++n) { | |||
| const float freq = base_freq * obj->POvertoneFreqMult[pos[n]]; | |||
| //the bandwidth is not absolute(Hz); it is relative to frequency | |||
| const float bw = obj->convertBandwidth(obj->Pbandwidth, | |||
| obj->Pnumstages, freq, obj->Pbwscale, obj->Phrelbw[pos[n]]); | |||
| //try to keep same amplitude on all freqs and bw. (empirically) | |||
| const float hgain = obj->convertHarmonicMag(obj->Phmag[pos[n]], | |||
| obj->Phmagtype); | |||
| const float gain = hgain * sqrt(1500.0f / (bw * freq)); | |||
| int base = 1+3*n; | |||
| args[base + 0].f = freq; | |||
| args[base + 1].f = bw; | |||
| args[base + 2].f = gain; | |||
| types[base + 0] = 'f'; | |||
| types[base + 1] = 'f'; | |||
| types[base + 2] = 'f'; | |||
| } | |||
| types[3*harmonics+1] = 0; | |||
| d.replyArray(d.loc, types, args); | |||
| rEnd}, | |||
| }; | |||
| #undef rChangeCb | |||
| #undef rBegin | |||
| #undef rEnd | |||
| const rtosc::Ports &SUBnoteParameters::ports = SUBnotePorts; | |||
| @@ -143,6 +188,56 @@ SUBnoteParameters::SUBnoteParameters(const AbsTime *time_) | |||
| defaults(); | |||
| } | |||
| void SUBnoteParameters::activeHarmonics(int *pos, int &harmonics) const | |||
| { | |||
| harmonics = 0; | |||
| for(int n = 0; n < MAX_SUB_HARMONICS; ++n) { | |||
| if(Phmag[n] == 0) | |||
| continue; | |||
| pos[harmonics++] = n; | |||
| } | |||
| } | |||
| float SUBnoteParameters::convertBandwidth(int bw_, int stages, float freq, | |||
| int scale, int relbw) | |||
| { | |||
| //the bandwidth is not absolute(Hz); it is relative to frequency | |||
| float bw = powf(10, (bw_ - 127.0f) / 127.0f * 4) * stages; | |||
| //Bandwidth Scale | |||
| bw *= powf(1000 / freq, (scale - 64.0f) / 64.0f * 3.0f); | |||
| //Relative BandWidth | |||
| bw *= powf(100, (relbw - 64.0f) / 64.0f); | |||
| if(bw > 25.0f) | |||
| bw = 25.0f; | |||
| return bw; | |||
| } | |||
| float SUBnoteParameters::convertHarmonicMag(int mag, int type) | |||
| { | |||
| const float hmagnew = 1.0f - mag / 127.0f; | |||
| switch(type) { | |||
| case 1: | |||
| return expf(hmagnew * logf(0.01f)); | |||
| break; | |||
| case 2: | |||
| return expf(hmagnew * logf(0.001f)); | |||
| break; | |||
| case 3: | |||
| return expf(hmagnew * logf(0.0001f)); | |||
| break; | |||
| case 4: | |||
| return expf(hmagnew * logf(0.00001f)); | |||
| break; | |||
| default: | |||
| return 1.0f - hmagnew; | |||
| } | |||
| } | |||
| void SUBnoteParameters::defaults() | |||
| { | |||
| @@ -24,6 +24,15 @@ class SUBnoteParameters:public Presets | |||
| SUBnoteParameters(const AbsTime *time_ = nullptr); | |||
| ~SUBnoteParameters(); | |||
| //Identify active harmonic positions | |||
| // - pos : int[MAX_SUB_HARMONICS] offsets of active harmonics | |||
| // - harmonics : number of active harmonics | |||
| void activeHarmonics(int *pos, int &harmonics) const; | |||
| static float convertBandwidth(int bw, int stages, float freq, | |||
| int scale, int relbw); | |||
| static float convertHarmonicMag(int mag, int type); | |||
| void add2XML(XMLwrapper& xml); | |||
| void defaults(); | |||
| void getfromXML(XMLwrapper& xml); | |||
| @@ -31,7 +40,7 @@ class SUBnoteParameters:public Presets | |||
| void paste(SUBnoteParameters &sub); | |||
| //Parameters | |||
| //AMPLITUDE PARAMETRERS | |||
| //AMPLITUDE PARAMETERS | |||
| unsigned char Pstereo; //0 for mono,1 for stereo | |||
| unsigned char PVolume; | |||
| unsigned char PPanning; | |||
| @@ -22,11 +22,13 @@ | |||
| #include "../Misc/Util.h" | |||
| #include "../Misc/Allocator.h" | |||
| #include "../Params/ADnoteParameters.h" | |||
| #include "../Containers/ScratchString.h" | |||
| #include "ModFilter.h" | |||
| #include "OscilGen.h" | |||
| #include "ADnote.h" | |||
| ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) | |||
| ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars, | |||
| WatchManager *wm, const char *prefix) | |||
| :SynthNote(spars), pars(*pars_) | |||
| { | |||
| memory.beginTransaction(); | |||
| @@ -450,7 +452,7 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) | |||
| memset(tmpwave_unison[k], 0, synth.bufferbytes); | |||
| } | |||
| initparameters(); | |||
| initparameters(wm, prefix); | |||
| memory.endTransaction(); | |||
| } | |||
| @@ -772,16 +774,17 @@ ADnote::~ADnote() | |||
| /* | |||
| * Init the parameters | |||
| */ | |||
| void ADnote::initparameters() | |||
| void ADnote::initparameters(WatchManager *wm, const char *prefix) | |||
| { | |||
| int tmp[NUM_VOICES]; | |||
| ScratchString pre = prefix; | |||
| //ADnoteParameters &pars = *partparams; | |||
| // Global Parameters | |||
| NoteGlobalPar.initparameters(pars.GlobalPar, synth, | |||
| time, | |||
| memory, basefreq, velocity, | |||
| stereo); | |||
| stereo, wm, prefix); | |||
| NoteGlobalPar.AmpEnvelope->envout_dB(); //discard the first envelope output | |||
| globalnewamplitude = NoteGlobalPar.Volume | |||
| @@ -816,22 +819,28 @@ void ADnote::initparameters() | |||
| newamplitude[nvoice] = 1.0f; | |||
| if(param.PAmpEnvelopeEnabled) { | |||
| vce.AmpEnvelope = memory.alloc<Envelope>(*param.AmpEnvelope, basefreq, synth.dt()); | |||
| vce.AmpEnvelope = memory.alloc<Envelope>(*param.AmpEnvelope, | |||
| basefreq, synth.dt(), wm, | |||
| (pre+"VoicePar"+nvoice+"/AmpEnvelope/").c_str); | |||
| vce.AmpEnvelope->envout_dB(); //discard the first envelope sample | |||
| newamplitude[nvoice] *= vce.AmpEnvelope->envout_dB(); | |||
| } | |||
| if(param.PAmpLfoEnabled) { | |||
| vce.AmpLfo = memory.alloc<LFO>(*param.AmpLfo, basefreq, time); | |||
| vce.AmpLfo = memory.alloc<LFO>(*param.AmpLfo, basefreq, time, wm, | |||
| (pre+"VoicePar"+nvoice+"/AmpLfo/").c_str); | |||
| newamplitude[nvoice] *= vce.AmpLfo->amplfoout(); | |||
| } | |||
| /* Voice Frequency Parameters Init */ | |||
| if(param.PFreqEnvelopeEnabled) | |||
| vce.FreqEnvelope = memory.alloc<Envelope>(*param.FreqEnvelope, basefreq, synth.dt()); | |||
| vce.FreqEnvelope = memory.alloc<Envelope>(*param.FreqEnvelope, | |||
| basefreq, synth.dt(), wm, | |||
| (pre+"VoicePar"+nvoice+"/FreqEnvelope/").c_str); | |||
| if(param.PFreqLfoEnabled) | |||
| vce.FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, time); | |||
| vce.FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, time, wm, | |||
| (pre+"VoicePar"+nvoice+"/FreqLfo/").c_str); | |||
| /* Voice Filter Parameters Init */ | |||
| if(param.PFilterEnabled) { | |||
| @@ -843,12 +852,15 @@ void ADnote::initparameters() | |||
| if(param.PFilterEnvelopeEnabled) { | |||
| vce.FilterEnvelope = | |||
| memory.alloc<Envelope>(*param.FilterEnvelope, basefreq, synth.dt()); | |||
| memory.alloc<Envelope>(*param.FilterEnvelope, | |||
| basefreq, synth.dt(), wm, | |||
| (pre+"VoicePar"+nvoice+"/FilterEnvelope/").c_str); | |||
| vce.Filter->addMod(*vce.FilterEnvelope); | |||
| } | |||
| if(param.PFilterLfoEnabled) { | |||
| vce.FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time); | |||
| vce.FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time, wm, | |||
| (pre+"VoicePar"+nvoice+"/FilterLfo/").c_str); | |||
| vce.Filter->addMod(*vce.FilterLfo); | |||
| } | |||
| } | |||
| @@ -892,13 +904,17 @@ void ADnote::initparameters() | |||
| } | |||
| if(param.PFMFreqEnvelopeEnabled) | |||
| vce.FMFreqEnvelope = memory.alloc<Envelope>(*param.FMFreqEnvelope, basefreq, synth.dt()); | |||
| vce.FMFreqEnvelope = memory.alloc<Envelope>(*param.FMFreqEnvelope, | |||
| basefreq, synth.dt(), wm, | |||
| (pre+"VoicePar"+nvoice+"/FMFreqEnvelope/").c_str); | |||
| FMnewamplitude[nvoice] = vce.FMVolume * ctl.fmamp.relamp; | |||
| if(param.PFMAmpEnvelopeEnabled ) { | |||
| vce.FMAmpEnvelope = | |||
| memory.alloc<Envelope>(*param.FMAmpEnvelope, basefreq, synth.dt()); | |||
| memory.alloc<Envelope>(*param.FMAmpEnvelope, | |||
| basefreq, synth.dt(), wm, | |||
| (pre+"VoicePar"+nvoice+"/FMAmpEnvelope/").c_str); | |||
| FMnewamplitude[nvoice] *= vce.FMAmpEnvelope->envout_dB(); | |||
| } | |||
| } | |||
| @@ -1873,13 +1889,20 @@ void ADnote::Global::initparameters(const ADnoteGlobalParam ¶m, | |||
| const AbsTime &time, | |||
| class Allocator &memory, | |||
| float basefreq, float velocity, | |||
| bool stereo) | |||
| bool stereo, | |||
| WatchManager *wm, | |||
| const char *prefix) | |||
| { | |||
| FreqEnvelope = memory.alloc<Envelope>(*param.FreqEnvelope, basefreq, synth.dt()); | |||
| FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, time); | |||
| ScratchString pre = prefix; | |||
| FreqEnvelope = memory.alloc<Envelope>(*param.FreqEnvelope, basefreq, | |||
| synth.dt(), wm, (pre+"GlobalPar/FreqEnvelope/").c_str); | |||
| FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, time, wm, | |||
| (pre+"GlobalPar/FreqLfo/").c_str); | |||
| AmpEnvelope = memory.alloc<Envelope>(*param.AmpEnvelope, basefreq, synth.dt()); | |||
| AmpLfo = memory.alloc<LFO>(*param.AmpLfo, basefreq, time); | |||
| AmpEnvelope = memory.alloc<Envelope>(*param.AmpEnvelope, basefreq, | |||
| synth.dt(), wm, (pre+"GlobalPar/AmpEnvelope/").c_str); | |||
| AmpLfo = memory.alloc<LFO>(*param.AmpLfo, basefreq, time, wm, | |||
| (pre+"GlobalPar/AmpLfo/").c_str); | |||
| Volume = 4.0f * powf(0.1f, 3.0f * (1.0f - param.PVolume / 96.0f)) //-60 dB .. 0 dB | |||
| * VelF(velocity, param.PAmpVelocityScaleFunction); //sensing | |||
| @@ -1887,8 +1910,10 @@ void ADnote::Global::initparameters(const ADnoteGlobalParam ¶m, | |||
| Filter = memory.alloc<ModFilter>(*param.GlobalFilter, synth, time, memory, | |||
| stereo, basefreq); | |||
| FilterEnvelope = memory.alloc<Envelope>(*param.FilterEnvelope, basefreq, synth.dt()); | |||
| FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time); | |||
| FilterEnvelope = memory.alloc<Envelope>(*param.FilterEnvelope, basefreq, | |||
| synth.dt(), wm, (pre+"GlobalPar/FilterEnvelope/").c_str); | |||
| FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time, wm, | |||
| (pre+"GlobalPar/FilterLfo/").c_str); | |||
| Filter->addMod(*FilterEnvelope); | |||
| Filter->addMod(*FilterLfo); | |||
| @@ -34,7 +34,8 @@ class ADnote:public SynthNote | |||
| /**Constructor. | |||
| * @param pars Note Parameters | |||
| * @param spars Synth Engine Agnostic Parameters*/ | |||
| ADnote(ADnoteParameters *pars, SynthParams &spars); | |||
| ADnote(ADnoteParameters *pars, SynthParams &spars, | |||
| WatchManager *wm=0, const char *prefix=0); | |||
| /**Destructor*/ | |||
| ~ADnote(); | |||
| @@ -62,7 +63,7 @@ class ADnote:public SynthNote | |||
| /**Compute parameters for next tick*/ | |||
| void computecurrentparameters(); | |||
| /**Initializes All Parameters*/ | |||
| void initparameters(); | |||
| void initparameters(WatchManager *wm, const char *prefix); | |||
| /**Deallocate/Cleanup given voice*/ | |||
| void KillVoice(int nvoice); | |||
| /**Deallocate Note resources and voice resources*/ | |||
| @@ -117,7 +118,9 @@ class ADnote:public SynthNote | |||
| const AbsTime &time, | |||
| class Allocator &memory, | |||
| float basefreq, float velocity, | |||
| bool stereo); | |||
| bool stereo, | |||
| WatchManager *wm, | |||
| const char *prefix); | |||
| /****************************************** | |||
| * FREQUENCY GLOBAL PARAMETERS * | |||
| ******************************************/ | |||
| @@ -15,7 +15,9 @@ | |||
| #include "Envelope.h" | |||
| #include "../Params/EnvelopeParams.h" | |||
| Envelope::Envelope(EnvelopeParams &pars, float basefreq, float bufferdt) | |||
| Envelope::Envelope(EnvelopeParams &pars, float basefreq, float bufferdt, | |||
| WatchManager *m, const char *watch_prefix) | |||
| :watchOut(m, watch_prefix, "out") | |||
| { | |||
| envpoints = pars.Penvpoints; | |||
| if(envpoints > MAX_ENVELOPE_POINTS) | |||
| @@ -88,7 +90,7 @@ void Envelope::releasekey() | |||
| if(keyreleased) | |||
| return; | |||
| keyreleased = true; | |||
| if(forcedrelease != 0) | |||
| if(forcedrelease) | |||
| t = 0.0f; | |||
| } | |||
| @@ -100,20 +102,28 @@ void Envelope::forceFinish(void) | |||
| /* | |||
| * Envelope Output | |||
| */ | |||
| float Envelope::envout() | |||
| float Envelope::envout(bool doWatch) | |||
| { | |||
| float out; | |||
| if(envfinish) { //if the envelope is finished | |||
| envoutval = envval[envpoints - 1]; | |||
| if(doWatch) { | |||
| float pos[2] = {(float)envpoints - 1, envoutval}; | |||
| watchOut(pos, 2); | |||
| } | |||
| return envoutval; | |||
| } | |||
| if((currentpoint == envsustain + 1) && !keyreleased) { //if it is sustaining now | |||
| envoutval = envval[envsustain]; | |||
| if(doWatch) { | |||
| float pos[2] = {(float)envsustain, envoutval}; | |||
| watchOut(pos, 2); | |||
| } | |||
| return envoutval; | |||
| } | |||
| if(keyreleased && (forcedrelease != 0)) { //do the forced release | |||
| if(keyreleased && forcedrelease) { //do the forced release | |||
| int tmp = (envsustain < 0) ? (envpoints - 1) : (envsustain + 1); //if there is no sustain point, use the last point for release | |||
| if(envdt[tmp] < 0.00000001f) | |||
| @@ -130,6 +140,12 @@ float Envelope::envout() | |||
| if((currentpoint >= envpoints) || (envsustain < 0)) | |||
| envfinish = true; | |||
| } | |||
| if(doWatch) { | |||
| float pos[2] = {(float)tmp + t, envoutval}; | |||
| watchOut(pos, 2); | |||
| } | |||
| return out; | |||
| } | |||
| if(inct >= 1.0f) | |||
| @@ -149,6 +165,11 @@ float Envelope::envout() | |||
| } | |||
| envoutval = out; | |||
| if(doWatch) { | |||
| float pos[2] = {(float)currentpoint + t, envoutval}; | |||
| watchOut(pos, 2); | |||
| } | |||
| return out; | |||
| } | |||
| @@ -166,10 +187,10 @@ inline float Envelope::env_rap2dB(float rap) { | |||
| float Envelope::envout_dB() | |||
| { | |||
| float out; | |||
| if(linearenvelope != 0) | |||
| return envout(); | |||
| if(linearenvelope) | |||
| return envout(true); | |||
| if((currentpoint == 1) && (!keyreleased || (forcedrelease == 0))) { //first point is always lineary interpolated | |||
| if((currentpoint == 1) && (!keyreleased || !forcedrelease)) { //first point is always lineary interpolated | |||
| float v1 = env_dB2rap(envval[0]); | |||
| float v2 = env_dB2rap(envval[1]); | |||
| out = v1 + (v2 - v1) * t; | |||
| @@ -186,9 +207,11 @@ float Envelope::envout_dB() | |||
| envoutval = env_rap2dB(out); | |||
| else | |||
| envoutval = MIN_ENVELOPE_DB; | |||
| } | |||
| else | |||
| out = env_dB2rap(envout()); | |||
| } else | |||
| out = env_dB2rap(envout(false)); | |||
| float pos[2] = {(float)currentpoint + t, out}; | |||
| watchOut(pos, 2); | |||
| return out; | |||
| } | |||
| @@ -15,6 +15,7 @@ | |||
| #define ENVELOPE_H | |||
| #include "../globals.h" | |||
| #include "WatchPoint.h" | |||
| /**Implementation of a general Envelope*/ | |||
| class Envelope | |||
| @@ -22,17 +23,18 @@ class Envelope | |||
| public: | |||
| /**Constructor*/ | |||
| Envelope(class EnvelopeParams &pars, float basefreq, float dt); | |||
| Envelope(class EnvelopeParams &pars, float basefreq, float dt, WatchManager *m=0, | |||
| const char *watch_prefix=0); | |||
| /**Destructor*/ | |||
| ~Envelope(); | |||
| void releasekey(); | |||
| ~Envelope(void); | |||
| void releasekey(void); | |||
| /**Push Envelope to finishing state*/ | |||
| void forceFinish(void); | |||
| float envout(); | |||
| float envout_dB(); | |||
| float envout(bool doWatch=true); | |||
| float envout_dB(void); | |||
| /**Determines the status of the Envelope | |||
| * @return returns 1 if the envelope is finished*/ | |||
| bool finished() const; | |||
| bool finished(void) const; | |||
| private: | |||
| float env_rap2dB(float rap); | |||
| float env_dB2rap(float db); | |||
| @@ -44,12 +46,14 @@ class Envelope | |||
| int linearenvelope; | |||
| int currentpoint; //current envelope point (starts from 1) | |||
| int forcedrelease; | |||
| bool forcedrelease; | |||
| bool keyreleased; //if the key was released | |||
| bool envfinish; | |||
| float t; // the time from the last point | |||
| float inct; // the time increment | |||
| float envoutval; //used to do the forced release | |||
| VecWatchPoint watchOut; | |||
| }; | |||
| @@ -19,13 +19,15 @@ | |||
| #include <cstdio> | |||
| #include <cmath> | |||
| LFO::LFO(const LFOParams &lfopars, float basefreq, const AbsTime &t) | |||
| LFO::LFO(const LFOParams &lfopars, float basefreq, const AbsTime &t, WatchManager *m, | |||
| const char *watch_prefix) | |||
| :first_half(-1), | |||
| delayTime(t, lfopars.Pdelay / 127.0f * 4.0f), //0..4 sec | |||
| waveShape(lfopars.PLFOtype), | |||
| deterministic(!lfopars.Pfreqrand), | |||
| dt_(t.dt()), | |||
| lfopars_(lfopars), basefreq_(basefreq) | |||
| lfopars_(lfopars), basefreq_(basefreq), | |||
| watchOut(m, watch_prefix, "out") | |||
| { | |||
| int stretch = lfopars.Pstretch; | |||
| if(stretch == 0) | |||
| @@ -166,6 +168,10 @@ float LFO::lfoout() | |||
| computeNextFreqRnd(); | |||
| } | |||
| float watch_data[2] = {phase, out}; | |||
| watchOut(watch_data, 2); | |||
| return out; | |||
| } | |||
| @@ -16,6 +16,7 @@ | |||
| #include "../globals.h" | |||
| #include "../Misc/Time.h" | |||
| #include "WatchPoint.h" | |||
| /**Class for creating Low Frequency Oscillators*/ | |||
| class LFO | |||
| @@ -26,7 +27,8 @@ class LFO | |||
| * @param lfopars pointer to a LFOParams object | |||
| * @param basefreq base frequency of LFO | |||
| */ | |||
| LFO(const LFOParams &lfopars, float basefreq, const AbsTime &t); | |||
| LFO(const LFOParams &lfopars, float basefreq, const AbsTime &t, WatchManager *m=0, | |||
| const char *watch_prefix=0); | |||
| ~LFO(); | |||
| float lfoout(); | |||
| @@ -63,6 +65,8 @@ class LFO | |||
| const LFOParams &lfopars_; | |||
| const float basefreq_; | |||
| VecWatchPoint watchOut; | |||
| void computeNextFreqRnd(void); | |||
| }; | |||
| @@ -34,54 +34,54 @@ const rtosc::Ports OscilGen::non_realtime_ports = { | |||
| rSelf(OscilGen), | |||
| rPaste, | |||
| //TODO ensure min/max | |||
| rOption(Phmagtype, | |||
| rOption(Phmagtype, rShort("scale"), | |||
| rOptions(linear,dB scale (-40), | |||
| dB scale (-60), dB scale (-80), | |||
| dB scale (-100)), | |||
| "Type of magnitude for harmonics"), | |||
| rOption(Pcurrentbasefunc, | |||
| rOption(Pcurrentbasefunc, rShort("base"), | |||
| rOptions(sine, triangle, pulse, saw, power, gauss, | |||
| diode, abssine, pulsesine, stretchsine, | |||
| chirp, absstretchsine, chebyshev, sqr, | |||
| spike, circle), rOpt(127,use-as-base waveform), | |||
| "Base Waveform for harmonics"), | |||
| rParamZyn(Pbasefuncpar, | |||
| rParamZyn(Pbasefuncpar, rShort("shape"), | |||
| "Morph between possible base function shapes " | |||
| "(e.g. rising sawtooth vs a falling sawtooth)"), | |||
| rOption(Pbasefuncmodulation, | |||
| rOption(Pbasefuncmodulation, rShort("mod"), | |||
| rOptions(None, Rev, Sine, Power, Chop), | |||
| "Modulation applied to Base function spectra"), | |||
| rParamZyn(Pbasefuncmodulationpar1, | |||
| rParamZyn(Pbasefuncmodulationpar1, rShort("p1"), | |||
| "Base function modulation parameter"), | |||
| rParamZyn(Pbasefuncmodulationpar2, | |||
| rParamZyn(Pbasefuncmodulationpar2, rShort("p2"), | |||
| "Base function modulation parameter"), | |||
| rParamZyn(Pbasefuncmodulationpar3, | |||
| rParamZyn(Pbasefuncmodulationpar3, rShort("p3"), | |||
| "Base function modulation parameter"), | |||
| rParamZyn(Pwaveshaping, "Degree Of Waveshaping"), | |||
| rOption(Pwaveshapingfunction, | |||
| rOption(Pwaveshapingfunction, rShort("distort"), | |||
| rOptions(Undistorted, | |||
| Arctangent, Asymmetric, Pow, Sine, Quantisize, | |||
| Zigzag, Limiter, Upper Limiter, Lower Limiter, | |||
| Inverse Limiter, Clip, Asym2, Pow2, sigmoid), | |||
| "Shape of distortion to be applied"), | |||
| rOption(Pfiltertype, rOptions(No Filter, | |||
| rOption(Pfiltertype, rShort("filter"), rOptions(No Filter, | |||
| lp, hp1, hp1b, bp1, bs1, lp2, hp2, bp2, bs2, | |||
| cos, sin, low_shelf, s), "Harmonic Filter"), | |||
| rParamZyn(Pfilterpar1, "Filter parameter"), | |||
| rParamZyn(Pfilterpar2, "Filter parameter"), | |||
| rToggle(Pfilterbeforews, "Filter before waveshaping spectra;" | |||
| rParamZyn(Pfilterpar1, rShort("p1"), "Filter parameter"), | |||
| rParamZyn(Pfilterpar2, rShort("p2"), "Filter parameter"), | |||
| rToggle(Pfilterbeforews, rShort("pre/post"), "Filter before waveshaping spectra;" | |||
| "When enabled oscilfilter(freqs); then waveshape(freqs);, " | |||
| "otherwise waveshape(freqs); then oscilfilter(freqs);"), | |||
| rOption(Psatype, rOptions(None, Pow, ThrsD, ThrsU), | |||
| rOption(Psatype, rShort("spec. adj."), rOptions(None, Pow, ThrsD, ThrsU), | |||
| "Spectral Adjustment Type"), | |||
| rParamZyn(Psapar, "Spectral Adjustment Parameter"), | |||
| rParamI(Pharmonicshift, "Amount of shift on harmonics"), | |||
| rToggle(Pharmonicshiftfirst, "If harmonics are shifted before waveshaping/filtering"), | |||
| rOption(Pmodulation, rOptions(None, Rev, Sine, Power), | |||
| rParamZyn(Psapar, rShort("p1"), "Spectral Adjustment Parameter"), | |||
| rParamI(Pharmonicshift, rShort("shift"), "Amount of shift on harmonics"), | |||
| rToggle(Pharmonicshiftfirst, rShort("pre/post"), "If harmonics are shifted before waveshaping/filtering"), | |||
| rOption(Pmodulation, rShort("FM"), rOptions(None, Rev, Sine, Power), | |||
| "Frequency Modulation To Combined Spectra"), | |||
| rParamZyn(Pmodulationpar1, "modulation parameter"), | |||
| rParamZyn(Pmodulationpar2, "modulation parameter"), | |||
| rParamZyn(Pmodulationpar3, "modulation parameter"), | |||
| rParamZyn(Pmodulationpar1, rShort("p1"), "modulation parameter"), | |||
| rParamZyn(Pmodulationpar2, rShort("p2"), "modulation parameter"), | |||
| rParamZyn(Pmodulationpar3, rShort("p3"), "modulation parameter"), | |||
| //TODO update to rArray and test | |||
| @@ -91,9 +91,20 @@ const rtosc::Ports OscilGen::non_realtime_ports = { | |||
| while(*mm && !isdigit(*mm)) ++mm; | |||
| unsigned char &phase = ((OscilGen*)d.obj)->Phphase[atoi(mm)]; | |||
| if(!rtosc_narguments(m)) | |||
| d.reply(d.loc, "c", phase); | |||
| else | |||
| d.reply(d.loc, "i", phase); | |||
| else { | |||
| phase = rtosc_argument(m,0).i; | |||
| //XXX hack hack | |||
| char *repath = strdup(d.loc); | |||
| char *edit = strrchr(repath, '/')+1; | |||
| strcpy(edit, "prepare"); | |||
| OscilGen &o = *((OscilGen*)d.obj); | |||
| fft_t *data = new fft_t[o.synth.oscilsize / 2]; | |||
| o.prepare(data); | |||
| // fprintf(stderr, "sending '%p' of fft data\n", data); | |||
| d.chain(repath, "b", sizeof(fft_t*), &data); | |||
| o.pendingfreqs = data; | |||
| } | |||
| }}, | |||
| //TODO update to rArray and test | |||
| {"magnitude#128::c:i", rProp(parameter) rLinear(0,127) rDoc("Sets harmonic magnitude"), | |||
| @@ -103,13 +114,13 @@ const rtosc::Ports OscilGen::non_realtime_ports = { | |||
| while(*mm && !isdigit(*mm)) ++mm; | |||
| unsigned char &mag = ((OscilGen*)d.obj)->Phmag[atoi(mm)]; | |||
| if(!rtosc_narguments(m)) | |||
| d.reply(d.loc, "c", mag); | |||
| d.reply(d.loc, "i", mag); | |||
| else { | |||
| mag = rtosc_argument(m,0).i; | |||
| //printf("setting magnitude\n\n"); | |||
| //XXX hack hack | |||
| char *repath = strdup(d.loc); | |||
| char *edit = rindex(repath, '/')+1; | |||
| char *edit = strrchr(repath, '/')+1; | |||
| strcpy(edit, "prepare"); | |||
| OscilGen &o = *((OscilGen*)d.obj); | |||
| fft_t *data = new fft_t[o.synth.oscilsize / 2]; | |||
| @@ -163,20 +174,20 @@ const rtosc::Ports OscilGen::non_realtime_ports = { | |||
| const rtosc::Ports OscilGen::realtime_ports{ | |||
| rSelf(OscilGen), | |||
| rPresetType, | |||
| rParamZyn(Prand, "Oscilator Phase Randomness: smaller than 0 is \"" | |||
| rParamZyn(Prand, rShort("phase rnd"), "Oscilator Phase Randomness: smaller than 0 is \"" | |||
| "group\", larger than 0 is for each harmonic"), | |||
| rParamZyn(Pamprandpower, | |||
| rParamZyn(Pamprandpower, rShort("variance"), | |||
| "Variance of harmonic randomness"), | |||
| rOption(Pamprandtype, rOptions(None, Pow, Sin), | |||
| rOption(Pamprandtype, rShort("distribution"), rOptions(None, Pow, Sin), | |||
| "Harmonic random distribution to select from"), | |||
| rOption(Padaptiveharmonics, | |||
| rOption(Padaptiveharmonics, rShort("adapt") | |||
| rOptions(OFF, ON, Square, 2xSub, 2xAdd, 3xSub, 3xAdd, 4xSub, 4xAdd), | |||
| "Adaptive Harmonics Mode"), | |||
| rParamI(Padaptiveharmonicsbasefreq, rLinear(0,255), | |||
| rParamI(Padaptiveharmonicsbasefreq, rShort("c. freq"), rLinear(0,255), | |||
| "Base frequency of adaptive harmonic (30..3000Hz)"), | |||
| rParamI(Padaptiveharmonicspower,rLinear(0,200), | |||
| rParamI(Padaptiveharmonicspower, rShort("amount"), rLinear(0,200), | |||
| "Adaptive Harmonic Strength"), | |||
| rParamZyn(Padaptiveharmonicspar, | |||
| rParamZyn(Padaptiveharmonicspar, rShort("par"), | |||
| "Adaptive Harmonics Postprocessing Power"), | |||
| {"waveform:", rDoc("Returns waveform points"), | |||
| NULL, [](const char *, rtosc::RtData &d) { | |||
| @@ -19,10 +19,12 @@ | |||
| #include "../Params/PADnoteParameters.h" | |||
| #include "../Params/Controller.h" | |||
| #include "../Params/FilterParams.h" | |||
| #include "../Containers/ScratchString.h" | |||
| #include "../Misc/Util.h" | |||
| PADnote::PADnote(const PADnoteParameters *parameters, | |||
| SynthParams pars, const int& interpolation) | |||
| SynthParams pars, const int& interpolation, WatchManager *wm, | |||
| const char *prefix) | |||
| :SynthNote(pars), pars(*parameters), interpolation(interpolation) | |||
| { | |||
| NoteGlobalPar.GlobalFilter = nullptr; | |||
| @@ -30,14 +32,16 @@ PADnote::PADnote(const PADnoteParameters *parameters, | |||
| NoteGlobalPar.FilterLfo = nullptr; | |||
| firsttime = true; | |||
| setup(pars.frequency, pars.velocity, pars.portamento, pars.note); | |||
| setup(pars.frequency, pars.velocity, pars.portamento, pars.note, false, wm, prefix); | |||
| } | |||
| void PADnote::setup(float freq, | |||
| float velocity_, | |||
| int portamento_, | |||
| int midinote, | |||
| bool legato) | |||
| bool legato, | |||
| WatchManager *wm, | |||
| const char *prefix) | |||
| { | |||
| portamento = portamento_; | |||
| velocity = velocity_; | |||
| @@ -129,11 +133,21 @@ void PADnote::setup(float freq, | |||
| else | |||
| NoteGlobalPar.Punch.Enabled = 0; | |||
| NoteGlobalPar.FreqEnvelope = memory.alloc<Envelope>(*pars.FreqEnvelope, basefreq, synth.dt()); | |||
| NoteGlobalPar.FreqLfo = memory.alloc<LFO>(*pars.FreqLfo, basefreq, time); | |||
| NoteGlobalPar.AmpEnvelope = memory.alloc<Envelope>(*pars.AmpEnvelope, basefreq, synth.dt()); | |||
| NoteGlobalPar.AmpLfo = memory.alloc<LFO>(*pars.AmpLfo, basefreq, time); | |||
| ScratchString pre = prefix; | |||
| NoteGlobalPar.FreqEnvelope = | |||
| memory.alloc<Envelope>(*pars.FreqEnvelope, basefreq, synth.dt(), | |||
| wm, (pre+"FreqEnvelope/").c_str); | |||
| NoteGlobalPar.FreqLfo = | |||
| memory.alloc<LFO>(*pars.FreqLfo, basefreq, time, | |||
| wm, (pre+"FreqLfo/").c_str); | |||
| NoteGlobalPar.AmpEnvelope = | |||
| memory.alloc<Envelope>(*pars.AmpEnvelope, basefreq, synth.dt(), | |||
| wm, (pre+"AmpEnvelope/").c_str); | |||
| NoteGlobalPar.AmpLfo = | |||
| memory.alloc<LFO>(*pars.AmpLfo, basefreq, time, | |||
| wm, (pre+"AmpLfo/").c_str); | |||
| } | |||
| NoteGlobalPar.Volume = 4.0f | |||
| @@ -147,6 +161,7 @@ void PADnote::setup(float freq, | |||
| * NoteGlobalPar.AmpLfo->amplfoout(); | |||
| if(!legato) { | |||
| ScratchString pre = prefix; | |||
| auto &flt = NoteGlobalPar.GlobalFilter; | |||
| auto &env = NoteGlobalPar.FilterEnvelope; | |||
| auto &lfo = NoteGlobalPar.FilterLfo; | |||
| @@ -154,8 +169,10 @@ void PADnote::setup(float freq, | |||
| flt = memory.alloc<ModFilter>(*pars.GlobalFilter, synth, time, memory, true, basefreq); | |||
| //setup mod | |||
| env = memory.alloc<Envelope>(*pars.FilterEnvelope, basefreq, synth.dt()); | |||
| lfo = memory.alloc<LFO>(*pars.FilterLfo, basefreq, time); | |||
| env = memory.alloc<Envelope>(*pars.FilterEnvelope, basefreq, | |||
| synth.dt(), wm, (pre+"FilterEnvelope/").c_str); | |||
| lfo = memory.alloc<LFO>(*pars.FilterLfo, basefreq, time, | |||
| wm, (pre+"FilterLfo/").c_str); | |||
| flt->addMod(*env); | |||
| flt->addMod(*lfo); | |||
| } | |||
| @@ -23,7 +23,7 @@ class PADnote:public SynthNote | |||
| { | |||
| public: | |||
| PADnote(const PADnoteParameters *parameters, SynthParams pars, | |||
| const int &interpolation); | |||
| const int &interpolation, WatchManager *wm=0, const char *prefix=0); | |||
| ~PADnote(); | |||
| SynthNote *cloneLegato(void); | |||
| @@ -36,7 +36,7 @@ class PADnote:public SynthNote | |||
| void releasekey(); | |||
| private: | |||
| void setup(float freq, float velocity, int portamento_, | |||
| int midinote, bool legato = false); | |||
| int midinote, bool legato = false, WatchManager *wm=0, const char *prefix=0); | |||
| void fadein(float *smps); | |||
| void computecurrentparameters(); | |||
| bool finished_; | |||
| @@ -20,17 +20,19 @@ | |||
| #include <rtosc/port-sugar.h> | |||
| #define rObject Resonance | |||
| #define rBegin [](const char *msg, RtData &d) { rObject &o = *(rObject*)d.obj | |||
| #define rEnd } | |||
| using namespace rtosc; | |||
| const rtosc::Ports Resonance::ports = { | |||
| rSelf(Resonance), | |||
| rPaste, | |||
| rToggle(Penabled, "resonance enable"), | |||
| rToggle(Pprotectthefundamental, "Disable resonance filter on first harmonic"), | |||
| rToggle(Penabled, rShort("enable"), "resonance enable"), | |||
| rToggle(Pprotectthefundamental, rShort("p.fund."), "Disable resonance filter on first harmonic"), | |||
| rParams(Prespoints, N_RES_POINTS, "Resonance data points"), | |||
| rParamZyn(PmaxdB, "how many dB the signal may be amplified"), | |||
| rParamZyn(Pcenterfreq, "Center frequency"), | |||
| rParamZyn(Poctavesfreq, "The number of octaves..."), | |||
| rParamZyn(PmaxdB, rShort("max"), "how many dB the signal may be amplified"), | |||
| rParamZyn(Pcenterfreq, rShort("c.freq"), "Center frequency"), | |||
| rParamZyn(Poctavesfreq, rShort("oct"), "The number of octaves..."), | |||
| rActioni(randomize, rMap(min,0), rMap(max, 2), "Randomize frequency response"), | |||
| rActioni(interpolatepeaks, rMap(min,0), rMap(max, 2), "Generate response from peak values"), | |||
| rAction(smooth, "Smooth out frequency response"), | |||
| @@ -42,7 +44,29 @@ const rtosc::Ports Resonance::ports = { | |||
| {"octavesfreq:", rDoc("Get center freq of graph"), NULL, | |||
| [](const char *, RtData &d) | |||
| {d.reply(d.loc, "f", ((rObject*)d.obj)->getoctavesfreq());}}, | |||
| {"respoints", 0, 0, | |||
| rBegin; | |||
| if(rtosc_narguments(msg)) { | |||
| int i=0; | |||
| auto itr = rtosc_itr_begin(msg); | |||
| while(!rtosc_itr_end(itr) && i < N_RES_POINTS) { | |||
| auto ival = rtosc_itr_next(&itr); | |||
| if(ival.type == 'f') | |||
| o.Prespoints[i++] = ival.val.f*127; | |||
| } | |||
| } else { | |||
| rtosc_arg_t args[N_RES_POINTS]; | |||
| char types[N_RES_POINTS+1] = {0}; | |||
| for(int i=0; i<N_RES_POINTS; ++i) { | |||
| args[i].f = o.Prespoints[i]/127.0; | |||
| types[i] = 'f'; | |||
| } | |||
| d.replyArray(d.loc, types, args); | |||
| } | |||
| rEnd}, | |||
| }; | |||
| #undef rBegin | |||
| #undef rEnd | |||
| Resonance::Resonance():Presets() | |||
| { | |||
| @@ -20,6 +20,7 @@ | |||
| #include "SUBnote.h" | |||
| #include "Envelope.h" | |||
| #include "ModFilter.h" | |||
| #include "../Containers/ScratchString.h" | |||
| #include "../Params/Controller.h" | |||
| #include "../Params/SUBnoteParameters.h" | |||
| #include "../Params/FilterParams.h" | |||
| @@ -35,7 +36,8 @@ SUBnote::SUBnote(const SUBnoteParameters *parameters, SynthParams &spars) | |||
| GlobalFilter(nullptr), | |||
| GlobalFilterEnvelope(nullptr), | |||
| NoteEnabled(true), | |||
| lfilter(nullptr), rfilter(nullptr) | |||
| lfilter(nullptr), rfilter(nullptr), | |||
| wm(nullptr) | |||
| { | |||
| setup(spars.frequency, spars.velocity, spars.portamento, spars.note); | |||
| } | |||
| @@ -46,46 +48,19 @@ float SUBnote::setupFilters(int *pos, bool automation) | |||
| float reduceamp = 0.0f; | |||
| for(int n = 0; n < numharmonics; ++n) { | |||
| float freq = basefreq * pars.POvertoneFreqMult[pos[n]]; | |||
| const float freq = basefreq * pars.POvertoneFreqMult[pos[n]]; | |||
| overtone_freq[n] = freq; | |||
| overtone_rolloff[n] = computerolloff(freq); | |||
| //the bandwidth is not absolute(Hz); it is relative to frequency | |||
| float bw = | |||
| powf(10, (pars.Pbandwidth - 127.0f) / 127.0f * 4) * numstages; | |||
| //Bandwidth Scale | |||
| bw *= powf(1000 / freq, (pars.Pbwscale - 64.0f) / 64.0f * 3.0f); | |||
| //Relative BandWidth | |||
| bw *= powf(100, (pars.Phrelbw[pos[n]] - 64.0f) / 64.0f); | |||
| if(bw > 25.0f) | |||
| bw = 25.0f; | |||
| const float bw = SUBnoteParameters::convertBandwidth(pars.Pbandwidth, | |||
| numstages, freq, pars.Pbwscale, pars.Phrelbw[pos[n]]); | |||
| //try to keep same amplitude on all freqs and bw. (empirically) | |||
| float gain = sqrt(1500.0f / (bw * freq)); | |||
| float hmagnew = 1.0f - pars.Phmag[pos[n]] / 127.0f; | |||
| float hgain; | |||
| switch(pars.Phmagtype) { | |||
| case 1: | |||
| hgain = expf(hmagnew * logf(0.01f)); | |||
| break; | |||
| case 2: | |||
| hgain = expf(hmagnew * logf(0.001f)); | |||
| break; | |||
| case 3: | |||
| hgain = expf(hmagnew * logf(0.0001f)); | |||
| break; | |||
| case 4: | |||
| hgain = expf(hmagnew * logf(0.00001f)); | |||
| break; | |||
| default: | |||
| hgain = 1.0f - hmagnew; | |||
| } | |||
| gain *= hgain; | |||
| const float hgain = SUBnoteParameters::convertHarmonicMag(pars.Phmag[pos[n]], | |||
| pars.Phmagtype); | |||
| const float gain = hgain * sqrt(1500.0f / (bw * freq)); | |||
| reduceamp += hgain; | |||
| for(int nph = 0; nph < numstages; ++nph) { | |||
| @@ -158,14 +133,10 @@ void SUBnote::setup(float freq, | |||
| // basefreq*=ctl.pitchwheel.relfreq;//pitch wheel | |||
| int pos[MAX_SUB_HARMONICS]; | |||
| int harmonics = 0; | |||
| int harmonics; | |||
| pars.activeHarmonics(pos, harmonics); | |||
| //select only harmonics that desire to compute | |||
| for(int n = 0; n < MAX_SUB_HARMONICS; ++n) { | |||
| if(pars.Phmag[n] == 0) | |||
| continue; | |||
| pos[harmonics++] = n; | |||
| } | |||
| if(!legato) //normal note | |||
| firstnumharmonics = numharmonics = harmonics; | |||
| else { | |||
| @@ -198,9 +169,9 @@ void SUBnote::setup(float freq, | |||
| oldbandwidth = 64; | |||
| if(!legato) { //normal note | |||
| if(pars.Pfixedfreq == 0) | |||
| initparameters(basefreq); | |||
| initparameters(basefreq, wm); | |||
| else | |||
| initparameters(basefreq / 440.0f * freq); | |||
| initparameters(basefreq / 440.0f * freq, wm); | |||
| } | |||
| else { | |||
| if(pars.Pfixedfreq == 0) | |||
| @@ -376,18 +347,25 @@ void SUBnote::filter(bpfilter &filter, float *smps) | |||
| /* | |||
| * Init Parameters | |||
| */ | |||
| void SUBnote::initparameters(float freq) | |||
| void SUBnote::initparameters(float freq, WatchManager *wm) | |||
| { | |||
| AmpEnvelope = memory.alloc<Envelope>(*pars.AmpEnvelope, freq, synth.dt()); | |||
| //TODO populate this base string | |||
| ScratchString pre; | |||
| AmpEnvelope = memory.alloc<Envelope>(*pars.AmpEnvelope, freq, | |||
| synth.dt(), wm, (pre+"AmpEnvelope/").c_str); | |||
| if(pars.PFreqEnvelopeEnabled) | |||
| FreqEnvelope = memory.alloc<Envelope>(*pars.FreqEnvelope, freq, synth.dt()); | |||
| FreqEnvelope = memory.alloc<Envelope>(*pars.FreqEnvelope, freq, | |||
| synth.dt(), wm, (pre+"FreqEnvelope/").c_str); | |||
| if(pars.PBandWidthEnvelopeEnabled) | |||
| BandWidthEnvelope = memory.alloc<Envelope>(*pars.BandWidthEnvelope, freq, synth.dt()); | |||
| BandWidthEnvelope = memory.alloc<Envelope>(*pars.BandWidthEnvelope, | |||
| freq, synth.dt(), wm, (pre+"BandWidthEnvelope/").c_str); | |||
| if(pars.PGlobalFilterEnabled) { | |||
| GlobalFilterEnvelope = memory.alloc<Envelope>(*pars.GlobalFilterEnvelope, freq, synth.dt()); | |||
| GlobalFilterEnvelope = | |||
| memory.alloc<Envelope>(*pars.GlobalFilterEnvelope, freq, | |||
| synth.dt(), wm, (pre+"GlobalFilterEnvelope/").c_str); | |||
| GlobalFilter = memory.alloc<ModFilter>(*pars.GlobalFilter, synth, time, memory, stereo, freq); | |||
| @@ -429,14 +407,9 @@ void SUBnote::computecurrentparameters() | |||
| //A little bit of copy/paste for now | |||
| int pos[MAX_SUB_HARMONICS]; | |||
| int harmonics = 0; | |||
| int harmonics; | |||
| //select only harmonics that desire to compute | |||
| for(int n = 0; n < MAX_SUB_HARMONICS; ++n) { | |||
| if(pars.Phmag[n] == 0) | |||
| continue; | |||
| pos[harmonics++] = n; | |||
| } | |||
| pars.activeHarmonics(pos, harmonics); | |||
| bool delta_harmonics = (harmonics != numharmonics); | |||
| if(delta_harmonics) { | |||
| @@ -43,7 +43,7 @@ class SUBnote:public SynthNote | |||
| * Initialize envelopes and global filter | |||
| * calls computercurrentparameters() | |||
| */ | |||
| void initparameters(float freq); | |||
| void initparameters(float freq, WatchManager *wm); | |||
| void KillNote(); | |||
| const SUBnoteParameters &pars; | |||
| @@ -101,6 +101,7 @@ class SUBnote:public SynthNote | |||
| int oldpitchwheel, oldbandwidth; | |||
| float globalfiltercenterq; | |||
| float velocity; | |||
| WatchManager *wm; | |||
| }; | |||
| #endif | |||
| @@ -108,6 +108,7 @@ class SynthNote | |||
| const Controller &ctl; | |||
| const SYNTH_T &synth; | |||
| const AbsTime &time; | |||
| WatchManager *wm; | |||
| }; | |||
| #endif | |||
| @@ -0,0 +1,163 @@ | |||
| /* | |||
| ZynAddSubFX - a software synthesizer | |||
| WatchPoint.cpp - Synthesis State Watcher | |||
| Copyright (C) 2015-2015 Mark McCurry | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of version 2 of the GNU General Public License | |||
| as published by the Free Software Foundation. | |||
| This program 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 (version 2 or later) for more details. | |||
| You should have received a copy of the GNU General Public License (version 2) | |||
| along with this program; if not, write to the Free Software Foundation, | |||
| Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||
| */ | |||
| #include "WatchPoint.h" | |||
| #include <cstring> | |||
| #include <rtosc/thread-link.h> | |||
| WatchPoint::WatchPoint(WatchManager *ref, const char *prefix, const char *id) | |||
| :active(false), samples_left(0), reference(ref) | |||
| { | |||
| identity[0] = 0; | |||
| if(prefix) | |||
| strncpy(identity, prefix, 128); | |||
| if(id) | |||
| strncat(identity, id, 128); | |||
| } | |||
| bool WatchPoint::is_active(void) | |||
| { | |||
| //Either the watchpoint is already active or the watchpoint manager has | |||
| //received another activation this frame | |||
| if(active) | |||
| return true; | |||
| if(reference && reference->active(identity)) { | |||
| active = true; | |||
| samples_left = 1; | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| FloatWatchPoint::FloatWatchPoint(WatchManager *ref, const char *prefix, const char *id) | |||
| :WatchPoint(ref, prefix, id) | |||
| {} | |||
| VecWatchPoint::VecWatchPoint(WatchManager *ref, const char *prefix, const char *id) | |||
| :WatchPoint(ref, prefix, id) | |||
| {} | |||
| WatchManager::WatchManager(thrlnk *link) | |||
| :write_back(link), new_active(false) | |||
| { | |||
| memset(active_list, 0, sizeof(active_list)); | |||
| memset(sample_list, 0, sizeof(sample_list)); | |||
| memset(data_list, 0, sizeof(data_list)); | |||
| memset(deactivate, 0, sizeof(deactivate)); | |||
| } | |||
| void WatchManager::add_watch(const char *id) | |||
| { | |||
| //Apply to a free slot | |||
| for(int i=0; i<MAX_WATCH; ++i) { | |||
| if(!active_list[i][0]) { | |||
| strncpy(active_list[i], id, 128); | |||
| new_active = true; | |||
| sample_list[i] = 0; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| void WatchManager::del_watch(const char *id) | |||
| { | |||
| //Queue up the delete | |||
| for(int i=0; i<MAX_WATCH; ++i) | |||
| if(!strcmp(active_list[i], id)) | |||
| return (void) (deactivate[i] = true); | |||
| } | |||
| void WatchManager::tick(void) | |||
| { | |||
| //Try to send out any vector stuff | |||
| for(int i=0; i<MAX_WATCH; ++i) { | |||
| if(sample_list[i]) { | |||
| char arg_types[MAX_SAMPLE+1] = {0}; | |||
| rtosc_arg_t arg_val[MAX_SAMPLE]; | |||
| for(int j=0; j<sample_list[i]; ++j) { | |||
| arg_types[j] = 'f'; | |||
| arg_val[j].f = data_list[i][j]; | |||
| } | |||
| write_back->writeArray(active_list[i], arg_types, arg_val); | |||
| deactivate[i] = true; | |||
| } | |||
| } | |||
| //Cleanup internal data | |||
| new_active = false; | |||
| //Clear deleted slots | |||
| for(int i=0; i<MAX_WATCH; ++i) { | |||
| if(deactivate[i]) { | |||
| memset(active_list[i], 0, 128); | |||
| sample_list[i] = 0; | |||
| deactivate[i] = false; | |||
| } | |||
| } | |||
| } | |||
| bool WatchManager::active(const char *id) const | |||
| { | |||
| assert(this); | |||
| assert(id); | |||
| if(new_active || true) | |||
| for(int i=0; i<MAX_WATCH; ++i) | |||
| if(!strcmp(active_list[i], id)) | |||
| return true; | |||
| return false; | |||
| } | |||
| int WatchManager::samples(const char *id) const | |||
| { | |||
| for(int i=0; i<MAX_WATCH; ++i) | |||
| if(!strcmp(active_list[i], id)) | |||
| return sample_list[i]; | |||
| return 0; | |||
| } | |||
| void WatchManager::satisfy(const char *id, float f) | |||
| { | |||
| //printf("trying to satisfy '%s'\n", id); | |||
| if(write_back) | |||
| write_back->write(id, "f", f); | |||
| del_watch(id); | |||
| } | |||
| void WatchManager::satisfy(const char *id, float *f, int n) | |||
| { | |||
| int selected = -1; | |||
| for(int i=0; i<MAX_WATCH; ++i) | |||
| if(!strcmp(active_list[i], id)) | |||
| selected = i; | |||
| if(selected == -1) | |||
| return; | |||
| //FIXME buffer overflow | |||
| for(int i=0; i<n; ++i) | |||
| data_list[selected][sample_list[selected]++] = f[i]; | |||
| } | |||
| @@ -0,0 +1,81 @@ | |||
| /* | |||
| ZynAddSubFX - a software synthesizer | |||
| WatchPoint.h - Synthesis State Watcher | |||
| Copyright (C) 2015-2015 Mark McCurry | |||
| Author: Mark McCurry | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| as published by the Free Software Foundation; either version 2 | |||
| of the License, or (at your option) any later version. | |||
| */ | |||
| #pragma once | |||
| struct WatchManager; | |||
| namespace rtosc {class ThreadLink;} | |||
| struct WatchPoint | |||
| { | |||
| bool active; | |||
| int samples_left; | |||
| WatchManager *reference; | |||
| char identity[128]; | |||
| WatchPoint(WatchManager *ref, const char *prefix, const char *id); | |||
| bool is_active(void); | |||
| }; | |||
| #define MAX_WATCH 16 | |||
| #define MAX_WATCH_PATH 128 | |||
| #define MAX_SAMPLE 128 | |||
| struct WatchManager | |||
| { | |||
| typedef rtosc::ThreadLink thrlnk; | |||
| thrlnk *write_back; | |||
| bool new_active; | |||
| char active_list[MAX_WATCH][MAX_WATCH_PATH]; | |||
| float data_list[MAX_SAMPLE][MAX_WATCH]; | |||
| int sample_list[MAX_WATCH]; | |||
| bool deactivate[MAX_WATCH]; | |||
| //External API | |||
| WatchManager(thrlnk *link=0); | |||
| void add_watch(const char *); | |||
| void del_watch(const char *); | |||
| void tick(void); | |||
| //Watch Point Query API | |||
| bool active(const char *) const; | |||
| int samples(const char *) const; | |||
| //Watch Point Response API | |||
| void satisfy(const char *, float); | |||
| void satisfy(const char *, float*, int); | |||
| }; | |||
| struct FloatWatchPoint:public WatchPoint | |||
| { | |||
| FloatWatchPoint(WatchManager *ref, const char *prefix, const char *id); | |||
| inline void operator()(float f) | |||
| { | |||
| if(is_active() && reference) { | |||
| reference->satisfy(identity, f); | |||
| active = false; | |||
| } | |||
| } | |||
| }; | |||
| //basically the same as the float watch point, only it consumes tuples | |||
| struct VecWatchPoint : public WatchPoint | |||
| { | |||
| VecWatchPoint(WatchManager *ref, const char *prefix, const char *id); | |||
| inline void operator()(float *f, int n) | |||
| { | |||
| if(is_active() && reference) { | |||
| reference->satisfy(identity, f, n); | |||
| active = false; | |||
| } | |||
| } | |||
| }; | |||
| @@ -407,7 +407,7 @@ class UI_Interface:public Fl_Osc_Interface | |||
| virtual void damage(const char *path) override | |||
| { | |||
| #ifndef NO_UI | |||
| printf("\n\nDamage(\"%s\")\n", path); | |||
| //printf("\n\nDamage(\"%s\")\n", path); | |||
| std::set<Fl_Osc_Widget*> to_update; | |||
| for(auto pair:map) { | |||
| if(strstr(pair.first.c_str(), path)) { | |||
| @@ -428,7 +428,7 @@ class UI_Interface:public Fl_Osc_Interface | |||
| //DEBUG | |||
| //if(strcmp(msg, "/vu-meter"))//Ignore repeated message | |||
| // printf("trying the link for a '%s'<%s>\n", msg, rtosc_argument_string(msg)); | |||
| const char *handle = rindex(msg,'/'); | |||
| const char *handle = strrchr(msg,'/'); | |||
| if(handle) | |||
| ++handle; | |||
| @@ -312,6 +312,11 @@ if (filename==NULL) return; | |||
| osc->write("/load_xlz", "s", filename);} | |||
| xywh {40 40 100 20} | |||
| } | |||
| MenuItem {} { | |||
| label {Clear Midi Learn...} | |||
| callback {osc->write("/clear_xlz", "");} | |||
| xywh {40 40 100 20} | |||
| } | |||
| MenuItem {} { | |||
| label {Save Midi Learn...} | |||
| callback {char *filename; | |||
| @@ -1485,7 +1490,7 @@ selectuiwindow->hide();} | |||
| xywh {10 165 100 35} color 229 labelfont 1 labelsize 16 | |||
| } | |||
| Fl_Box {} { | |||
| label {.. if you have used ZynAddSubFX before, or you like to have full controll to all parameters.} | |||
| label {.. if you have used ZynAddSubFX before, or you like to have full control to all parameters.} | |||
| xywh {110 165 310 35} labelfont 1 labelsize 11 align 144 | |||
| } | |||
| Fl_Button {} { | |||
| @@ -470,7 +470,7 @@ class UI_Interface:public Fl_Osc_Interface | |||
| //DEBUG | |||
| //if(strcmp(msg, "/vu-meter"))//Ignore repeated message | |||
| // printf("trying the link for a '%s'<%s>\n", msg, rtosc_argument_string(msg)); | |||
| const char *handle = rindex(msg,'/'); | |||
| const char *handle = strrchr(msg,'/'); | |||
| if(handle) | |||
| ++handle; | |||
| @@ -572,9 +572,11 @@ const char *help_message = | |||
| "zynaddsubfx-ext-gui [options] uri - Connect to remote ZynAddSubFX\n" | |||
| " --help print this help message\n" | |||
| " --no-uri run without a remote ZynAddSubFX\n" | |||
| " --embed window ID [Internal Flag For Embedding Windows]\n" | |||
| "\n" | |||
| " example: zynaddsubfx-ext-gui osc.udp://localhost:1234/\n" | |||
| " use the -P option for zynaddsubfx to specify the port of the backend\n"; | |||
| " This will connect to a running zynaddsubfx instance on the same\n" | |||
| " machine on port 1234.\n"; | |||
| #ifndef CARLA_VERSION_STRING | |||
| int main(int argc, char *argv[]) | |||
| @@ -45,6 +45,7 @@ class EnvelopeParams; | |||
| class LFOParams; | |||
| class FilterParams; | |||
| struct WatchManager; | |||
| class LFO; | |||
| class Envelope; | |||
| class OscilGen; | |||
| @@ -3,7 +3,7 @@ | |||
| main.cpp - Main file of the synthesizer | |||
| Copyright (C) 2002-2005 Nasca Octavian Paul | |||
| Copyright (C) 2012-2014 Mark McCurry | |||
| Copyright (C) 2012-2016 Mark McCurry | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| @@ -35,6 +35,7 @@ | |||
| #include "Misc/Master.h" | |||
| #include "Misc/Part.h" | |||
| #include "Misc/Util.h" | |||
| #include "version.h" | |||
| //Nio System | |||
| #include "Nio/Nio.h" | |||
| @@ -364,7 +365,7 @@ int main(int argc, char *argv[]) | |||
| synth.alias(); | |||
| if(exitwithversion) { | |||
| cout << "Version: " << VERSION << endl; | |||
| cout << "Version: " << version << endl; | |||
| return 0; | |||
| } | |||
| if(exitwithhelp != 0) { | |||
| @@ -382,7 +383,7 @@ int main(int argc, char *argv[]) | |||
| " -U , --no-gui\t\t\t\t Run ZynAddSubFX without user interface\n" | |||
| << " -N , --named\t\t\t\t Postfix IO Name when possible\n" | |||
| << " -a , --auto-connect\t\t\t AutoConnect when using JACK\n" | |||
| << " -A , --auto-save=INTERVAL\t\t Automatically save at interval (disabled for negative intervals)\n" | |||
| << " -A , --auto-save=INTERVAL\t\t Automatically save at interval (disabled with 0 interval)\n" | |||
| << " -p , --pid-in-client-name\t\t Append PID to (JACK) " | |||
| "client name\n" | |||
| << " -P , --preferred-port\t\t\t Preferred OSC Port\n" | |||
| @@ -489,7 +490,7 @@ int main(int argc, char *argv[]) | |||
| "Default IO did not initialize.\nDefaulting to NULL backend."); | |||
| } | |||
| if(auto_save_interval >= 0) { | |||
| if(auto_save_interval > 0) { | |||
| int old_save = middleware->checkAutoSave(); | |||
| if(old_save > 0) | |||
| GUI::raiseUi(gui, "/alert-reload", "i", old_save); | |||
| @@ -342,6 +342,16 @@ void MidiMappernRT::delMapping(int ID, bool coarse, const char *addr){ | |||
| }; | |||
| void MidiMappernRT::replaceMapping(int, bool, const char *){}; | |||
| void MidiMappernRT::clear(void) | |||
| { | |||
| storage = new MidiMapperStorage(); | |||
| learnQueue.clear(); | |||
| inv_map.clear(); | |||
| char buf[1024]; | |||
| rtosc_message(buf, 1024, "/midi-learn/midi-bind", "b", sizeof(storage), &storage); | |||
| rt_cb(buf); | |||
| } | |||
| std::map<std::string, std::string> MidiMappernRT::getMidiMappingStrings(void) | |||
| @@ -16,6 +16,10 @@ class UndoHistoryImpl | |||
| UndoHistoryImpl(void) | |||
| :max_history_size(20) | |||
| {} | |||
| ~UndoHistoryImpl(void) | |||
| { | |||
| clear(); | |||
| } | |||
| std::deque<pair<time_t, const char *>> history; | |||
| long history_pos; | |||
| unsigned max_history_size;//XXX Expose this via a public API | |||
| @@ -24,6 +28,7 @@ class UndoHistoryImpl | |||
| void rewind(const char *msg); | |||
| void replay(const char *msg); | |||
| bool mergeEvent(time_t t, const char *msg, char *buf, size_t N); | |||
| void clear(void); | |||
| }; | |||
| UndoHistory::UndoHistory(void) | |||
| @@ -32,6 +37,11 @@ UndoHistory::UndoHistory(void) | |||
| impl->history_pos = 0; | |||
| } | |||
| UndoHistory::~UndoHistory(void) | |||
| { | |||
| delete impl; | |||
| } | |||
| void UndoHistory::recordEvent(const char *msg) | |||
| { | |||
| //TODO Properly account for when you have traveled back in time. | |||
| @@ -121,6 +131,14 @@ bool UndoHistoryImpl::mergeEvent(time_t now, const char *msg, char *buf, size_t | |||
| return false; | |||
| } | |||
| void UndoHistoryImpl::clear(void) | |||
| { | |||
| for(auto elm : history) | |||
| delete [] elm.second; | |||
| history.clear(); | |||
| history_pos = 0; | |||
| } | |||
| void UndoHistory::seekHistory(int distance) | |||
| @@ -200,8 +200,8 @@ static bool is_charwise(uint8_t c) | |||
| int rtosc_subpath_pat_type(const char *pattern) | |||
| { | |||
| int charwise_only = 1; | |||
| const char *last_star = rindex(pattern, '*'); | |||
| const char *pound = index(pattern, '#'); | |||
| const char *last_star = strrchr(pattern, '*'); | |||
| const char *pound = strchr(pattern, '#'); | |||
| if(!strcmp("*", pattern)) | |||
| return RTOSC_MATCH_ALL; | |||
| @@ -115,6 +115,8 @@ class MidiMappernRT | |||
| void delMapping(int ID, bool coarse, const char *addr); | |||
| void replaceMapping(int, bool, const char *); | |||
| void clear(void); | |||
| std::map<std::string, std::string> getMidiMappingStrings(void); | |||
| //unclear if this should be be here as a helper or not | |||
| @@ -144,7 +144,7 @@ struct rtosc_hack_decltype_t | |||
| #define rArrayF(name, length, ...) \ | |||
| {STRINGIFY(name) "#" STRINGIFY(length) "::f", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayFCb(name)} | |||
| #define rArray(name, length, ...) \ | |||
| {STRINGIFY(name) "#" STRINGIFY(length) "::c", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayCb(name)} | |||
| {STRINGIFY(name) "#" STRINGIFY(length) "::c:i", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayCb(name)} | |||
| #define rArrayT(name, length, ...) \ | |||
| {STRINGIFY(name) "#" STRINGIFY(length) "::T:F", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayTCb(name)} | |||
| #define rArrayI(name, length, ...) \ | |||
| @@ -54,12 +54,18 @@ struct RtData | |||
| const Port *port; | |||
| const char *message; | |||
| virtual void replyArray(const char *path, const char *args, | |||
| rtosc_arg_t *vals){}; | |||
| virtual void reply(const char *path, const char *args, ...); | |||
| virtual void reply(const char *msg); | |||
| virtual void chain(const char *path, const char *args, ...){}; | |||
| virtual void chain(const char *msg){}; | |||
| virtual void chainArray(const char *path, const char *args, | |||
| rtosc_arg_t *vals){}; | |||
| virtual void broadcast(const char *path, const char *args, ...); | |||
| virtual void broadcast(const char *msg); | |||
| virtual void broadcastArray(const char *path, const char *args, | |||
| rtosc_arg_t *vals){}; | |||
| virtual void forward(const char *rational=NULL); | |||
| }; | |||
| @@ -13,6 +13,7 @@ class UndoHistory | |||
| //TODO think about the consequences of largish loads | |||
| public: | |||
| UndoHistory(void); | |||
| ~UndoHistory(void); | |||
| //Records any undoable event | |||
| void recordEvent(const char *msg); | |||
| @@ -0,0 +1,46 @@ | |||
| /* | |||
| ZynAddSubFX - a software synthesizer | |||
| version.cpp - implementation of version_type class | |||
| Copyright (C) 2016 Johannes Lorenz | |||
| Author: Johannes Lorenz | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| as published by the Free Software Foundation; either version 2 | |||
| of the License, or (at your option) any later version. | |||
| */ | |||
| #include <iostream> | |||
| #include "version.h" | |||
| constexpr int version_type::v_strcmp(const version_type& v2, int i) const | |||
| { | |||
| return (i == sizeof(version)) | |||
| ? 0 | |||
| : ((version[i] == v2.version[i]) | |||
| ? v_strcmp(v2, i+1) | |||
| : (version[i] - v2.version[i])); | |||
| } | |||
| constexpr bool version_type::operator<(const version_type& other) const | |||
| { | |||
| return v_strcmp(other, 0) < 0; | |||
| } | |||
| std::ostream& operator<< (std::ostream& os, | |||
| const version_type& v) | |||
| { | |||
| return os << v.major() << '.' | |||
| << v.minor() << '.' | |||
| << v.revision(); | |||
| } | |||
| static_assert(!(version_type(3,1,1) < version_type(1,3,3)), | |||
| "version operator failed"); | |||
| static_assert(version_type(2,9,9) < version_type(3,4,3), | |||
| "version operator failed"); | |||
| static_assert(!(version_type(2,4,3) < version_type(2,4,3)), | |||
| "version operator failed"); | |||
| @@ -0,0 +1,59 @@ | |||
| /* | |||
| ZynAddSubFX - a software synthesizer | |||
| version.h - declaration of version_type class | |||
| contains the current zynaddsubfx version | |||
| Copyright (C) 2016 Johannes Lorenz | |||
| Author: Johannes Lorenz | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License | |||
| as published by the Free Software Foundation; either version 2 | |||
| of the License, or (at your option) any later version. | |||
| */ | |||
| #ifndef VERSION_H | |||
| #define VERSION_H | |||
| #include <iosfwd> | |||
| //! class containing a zynaddsubfx version | |||
| class version_type | |||
| { | |||
| char version[3]; | |||
| // strcmp-like comparison against another version_type | |||
| constexpr int v_strcmp(const version_type& v2, int i) const; | |||
| public: | |||
| constexpr version_type(char maj, char min, char rev) : | |||
| version{maj, min, rev} | |||
| { | |||
| } | |||
| //! constructs the current zynaddsubfx version | |||
| constexpr version_type() : | |||
| version_type(2, 5, 4) | |||
| { | |||
| } | |||
| void set_major(int maj) { version[0] = maj; } | |||
| void set_minor(int min) { version[1] = min; } | |||
| void set_revision(int rev) { version[2] = rev; } | |||
| int major() const { return version[0]; } | |||
| int minor() const { return version[1]; } | |||
| int revision() const { return version[2]; } | |||
| constexpr bool operator<(const version_type& other) const; | |||
| //! prints version as <major>.<minor>.<revision> | |||
| friend std::ostream& operator<< (std::ostream& os, | |||
| const version_type& v); | |||
| }; | |||
| //! the current zynaddsubfx version | |||
| constexpr version_type version; | |||
| #endif | |||