diff --git a/source/native-plugins/zynaddsubfx-src.cpp b/source/native-plugins/zynaddsubfx-src.cpp index eff15a8dd..4c5de0b5d 100644 --- a/source/native-plugins/zynaddsubfx-src.cpp +++ b/source/native-plugins/zynaddsubfx-src.cpp @@ -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 diff --git a/source/native-plugins/zynaddsubfx/Containers/MultiPseudoStack.cpp b/source/native-plugins/zynaddsubfx/Containers/MultiPseudoStack.cpp index ae5f2b470..0065bb159 100644 --- a/source/native-plugins/zynaddsubfx/Containers/MultiPseudoStack.cpp +++ b/source/native-plugins/zynaddsubfx/Containers/MultiPseudoStack.cpp @@ -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: diff --git a/source/native-plugins/zynaddsubfx/Containers/MultiPseudoStack.h b/source/native-plugins/zynaddsubfx/Containers/MultiPseudoStack.h index ee495e913..713567883 100644 --- a/source/native-plugins/zynaddsubfx/Containers/MultiPseudoStack.h +++ b/source/native-plugins/zynaddsubfx/Containers/MultiPseudoStack.h @@ -34,6 +34,7 @@ class LockFreeQueue std::atomic avail; public: LockFreeQueue(qli_t *data_, int n); + ~LockFreeQueue(void); qli_t *read(void); void write(qli_t *Q); }; diff --git a/source/native-plugins/zynaddsubfx/Containers/ScratchString.cpp b/source/native-plugins/zynaddsubfx/Containers/ScratchString.cpp new file mode 100644 index 000000000..bcb9921bb --- /dev/null +++ b/source/native-plugins/zynaddsubfx/Containers/ScratchString.cpp @@ -0,0 +1,39 @@ +#include "ScratchString.h" +#include +#include + +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; +//} diff --git a/source/native-plugins/zynaddsubfx/Containers/ScratchString.h b/source/native-plugins/zynaddsubfx/Containers/ScratchString.h new file mode 100644 index 000000000..5e2a69f08 --- /dev/null +++ b/source/native-plugins/zynaddsubfx/Containers/ScratchString.h @@ -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]; +}; diff --git a/source/native-plugins/zynaddsubfx/DSP/AnalogFilter.cpp b/source/native-plugins/zynaddsubfx/DSP/AnalogFilter.cpp index 8ff064111..435256b80 100644 --- a/source/native-plugins/zynaddsubfx/DSP/AnalogFilter.cpp +++ b/source/native-plugins/zynaddsubfx/DSP/AnalogFilter.cpp @@ -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); } diff --git a/source/native-plugins/zynaddsubfx/DSP/AnalogFilter.h b/source/native-plugins/zynaddsubfx/DSP/AnalogFilter.h index 07deff03e..95d7e6714 100644 --- a/source/native-plugins/zynaddsubfx/DSP/AnalogFilter.h +++ b/source/native-plugins/zynaddsubfx/DSP/AnalogFilter.h @@ -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 diff --git a/source/native-plugins/zynaddsubfx/Effects/Alienwah.cpp b/source/native-plugins/zynaddsubfx/Effects/Alienwah.cpp index a20c95daf..18a36ba14 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Alienwah.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/Alienwah.cpp @@ -12,11 +12,37 @@ */ #include +#include +#include #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), diff --git a/source/native-plugins/zynaddsubfx/Effects/Alienwah.h b/source/native-plugins/zynaddsubfx/Effects/Alienwah.h index d7ad54401..a5fdf3096 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Alienwah.h +++ b/source/native-plugins/zynaddsubfx/Effects/Alienwah.h @@ -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 diff --git a/source/native-plugins/zynaddsubfx/Effects/Chorus.cpp b/source/native-plugins/zynaddsubfx/Effects/Chorus.cpp index 386303644..1d97e3b2c 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Chorus.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/Chorus.cpp @@ -12,12 +12,39 @@ */ #include +#include +#include #include "../Misc/Allocator.h" #include "Chorus.h" #include 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), diff --git a/source/native-plugins/zynaddsubfx/Effects/Chorus.h b/source/native-plugins/zynaddsubfx/Effects/Chorus.h index 8efa07aec..f4fb95341 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Chorus.h +++ b/source/native-plugins/zynaddsubfx/Effects/Chorus.h @@ -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; diff --git a/source/native-plugins/zynaddsubfx/Effects/Distorsion.cpp b/source/native-plugins/zynaddsubfx/Effects/Distorsion.cpp index a503c6a4b..fe5c127fc 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Distorsion.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/Distorsion.cpp @@ -16,6 +16,33 @@ #include "../Misc/WaveShapeSmps.h" #include "../Misc/Allocator.h" #include +#include +#include + +#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), diff --git a/source/native-plugins/zynaddsubfx/Effects/Distorsion.h b/source/native-plugins/zynaddsubfx/Effects/Distorsion.h index 5d983c89e..255fcd848 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Distorsion.h +++ b/source/native-plugins/zynaddsubfx/Effects/Distorsion.h @@ -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 diff --git a/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.cpp b/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.cpp index 2aef703f1..277380bf3 100644 --- a/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.cpp @@ -16,6 +16,55 @@ #include "DynamicFilter.h" #include "../DSP/Filter.h" #include "../Misc/Allocator.h" +#include +#include + +#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), diff --git a/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.h b/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.h index b4d252651..e15cd0296 100644 --- a/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.h +++ b/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.h @@ -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 diff --git a/source/native-plugins/zynaddsubfx/Effects/Echo.cpp b/source/native-plugins/zynaddsubfx/Effects/Echo.cpp index 9ff793482..9858f31e7 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Echo.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/Echo.cpp @@ -14,11 +14,34 @@ */ #include +#include +#include #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), diff --git a/source/native-plugins/zynaddsubfx/Effects/Echo.h b/source/native-plugins/zynaddsubfx/Effects/Echo.h index 5316369b7..be4420db8 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Echo.h +++ b/source/native-plugins/zynaddsubfx/Effects/Echo.h @@ -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*/ diff --git a/source/native-plugins/zynaddsubfx/Effects/Effect.h b/source/native-plugins/zynaddsubfx/Effects/Effect.h index 667f09390..36d8ea8be 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Effect.h +++ b/source/native-plugins/zynaddsubfx/Effects/Effect.h @@ -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 { /** diff --git a/source/native-plugins/zynaddsubfx/Effects/EffectMgr.cpp b/source/native-plugins/zynaddsubfx/Effects/EffectMgr.cpp index 56badb869..52a35649b 100644 --- a/source/native-plugins/zynaddsubfx/Effects/EffectMgr.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/EffectMgr.cpp @@ -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; diff --git a/source/native-plugins/zynaddsubfx/Effects/Phaser.cpp b/source/native-plugins/zynaddsubfx/Effects/Phaser.cpp index 363c77706..5a34a54ad 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Phaser.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/Phaser.cpp @@ -17,11 +17,43 @@ #include #include +#include +#include #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. diff --git a/source/native-plugins/zynaddsubfx/Effects/Phaser.h b/source/native-plugins/zynaddsubfx/Effects/Phaser.h index b572aafc7..b62078f27 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Phaser.h +++ b/source/native-plugins/zynaddsubfx/Effects/Phaser.h @@ -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 diff --git a/source/native-plugins/zynaddsubfx/Misc/Bank.cpp b/source/native-plugins/zynaddsubfx/Misc/Bank.cpp index aa46605f0..7e087fcf8 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Bank.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Bank.cpp @@ -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 Bank::search(std::string s) const +{ + std::vector 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)) { diff --git a/source/native-plugins/zynaddsubfx/Misc/Bank.h b/source/native-plugins/zynaddsubfx/Misc/Bank.h index 9caa9abb2..aa7684da9 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Bank.h +++ b/source/native-plugins/zynaddsubfx/Misc/Bank.h @@ -68,7 +68,7 @@ class Bank std::vector 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 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; diff --git a/source/native-plugins/zynaddsubfx/Misc/BankDb.cpp b/source/native-plugins/zynaddsubfx/Misc/BankDb.cpp new file mode 100644 index 000000000..ae136bfe3 --- /dev/null +++ b/source/native-plugins/zynaddsubfx/Misc/BankDb.cpp @@ -0,0 +1,219 @@ +#include "BankDb.h" +#include "XMLwrapper.h" +#include "../globals.h" +#include +#include + +#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; +} diff --git a/source/native-plugins/zynaddsubfx/Misc/BankDb.h b/source/native-plugins/zynaddsubfx/Misc/BankDb.h new file mode 100644 index 000000000..b0acfe942 --- /dev/null +++ b/source/native-plugins/zynaddsubfx/Misc/BankDb.h @@ -0,0 +1,50 @@ +#pragma once +#include +#include + +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 svec; + svec tags(void) const; + bool match(std::string) const; +}; + +class BankDb +{ + public: + typedef std::vector svec; + typedef std::vector 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; +}; diff --git a/source/native-plugins/zynaddsubfx/Misc/Master.cpp b/source/native-plugins/zynaddsubfx/Misc/Master.cpp index 5b00ad210..d36a43020 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Master.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Master.cpp @@ -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; ivuoutpeakpart[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(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) diff --git a/source/native-plugins/zynaddsubfx/Misc/Master.h b/source/native-plugins/zynaddsubfx/Misc/Master.h index c7f18cee2..1256bd8bb 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Master.h +++ b/source/native-plugins/zynaddsubfx/Misc/Master.h @@ -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 diff --git a/source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp b/source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp index a431def84..0324aa79f 100644 --- a/source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp @@ -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 +std::vector keys(const std::map &m) +{ + std::vector 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 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(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); diff --git a/source/native-plugins/zynaddsubfx/Misc/Part.cpp b/source/native-plugins/zynaddsubfx/Misc/Part.cpp index c40672964..fd7c2d511 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Part.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Part.cpp @@ -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 @@ -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(kit[i].adpars, pars), 0, i}); + {memory.alloc(kit[i].adpars, pars, + wm, (pre+"kit"+i+"/adpars/").c_str), 0, i}); if(item.Psubenabled) notePool.insertNote(note, sendto, {memory.alloc(kit[i].subpars, pars), 1, i}); if(item.Ppadenabled) notePool.insertNote(note, sendto, - {memory.alloc(kit[i].padpars, pars, interpolation), 2, i}); + {memory.alloc(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; } diff --git a/source/native-plugins/zynaddsubfx/Misc/Part.h b/source/native-plugins/zynaddsubfx/Misc/Part.h index 8795f4d2e..fd538303e 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Part.h +++ b/source/native-plugins/zynaddsubfx/Misc/Part.h @@ -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; diff --git a/source/native-plugins/zynaddsubfx/Misc/Schema.cpp b/source/native-plugins/zynaddsubfx/Misc/Schema.cpp index 2b23675a8..b68caf779 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Schema.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Schema.cpp @@ -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> 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( - version.Major).c_str(), + version.major()).c_str(), "version-minor", stringFrom( - version.Minor).c_str(), + version.minor()).c_str(), "version-revision", - stringFrom(version.Revision).c_str(), + stringFrom(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(mxmlElementGetAttr(root, "version-major")); - version.Minor = stringTo(mxmlElementGetAttr(root, "version-minor")); - version.Revision = - stringTo(mxmlElementGetAttr(root, "version-revision")); + fileversion.set_major(stringTo(mxmlElementGetAttr(root, "version-major"))); + fileversion.set_minor(stringTo(mxmlElementGetAttr(root, "version-minor"))); + fileversion.set_revision( + stringTo(mxmlElementGetAttr(root, "version-revision"))); if(verbose) - cout << "loadXMLfile() version: " << version.Major << '.' - << version.Minor << '.' << version.Revision << endl; + cout << "loadXMLfile() version: " << fileversion << endl; return 0; } diff --git a/source/native-plugins/zynaddsubfx/Misc/XMLwrapper.h b/source/native-plugins/zynaddsubfx/Misc/XMLwrapper.h index 536015c3d..3b67b5a3b 100644 --- a/source/native-plugins/zynaddsubfx/Misc/XMLwrapper.h +++ b/source/native-plugins/zynaddsubfx/Misc/XMLwrapper.h @@ -16,6 +16,7 @@ #include #include #include +#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; /** #include #include @@ -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 &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, diff --git a/source/native-plugins/zynaddsubfx/Nio/AlsaEngine.h b/source/native-plugins/zynaddsubfx/Nio/AlsaEngine.h index 7a6240a7d..c263f934e 100644 --- a/source/native-plugins/zynaddsubfx/Nio/AlsaEngine.h +++ b/source/native-plugins/zynaddsubfx/Nio/AlsaEngine.h @@ -67,6 +67,7 @@ class AlsaEngine:public AudioOut, MidiIn unsigned int periods; short *buffer; pthread_t pThread; + float peaks[1]; } audio; void *processAudio(); diff --git a/source/native-plugins/zynaddsubfx/Nio/Compressor.h b/source/native-plugins/zynaddsubfx/Nio/Compressor.h new file mode 100644 index 000000000..f473e8a9e --- /dev/null +++ b/source/native-plugins/zynaddsubfx/Nio/Compressor.h @@ -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_ */ diff --git a/source/native-plugins/zynaddsubfx/Nio/JackMultiEngine.cpp b/source/native-plugins/zynaddsubfx/Nio/JackMultiEngine.cpp index a4fa5dc62..85ca9b7f3 100644 --- a/source/native-plugins/zynaddsubfx/Nio/JackMultiEngine.cpp +++ b/source/native-plugins/zynaddsubfx/Nio/JackMultiEngine.cpp @@ -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]); diff --git a/source/native-plugins/zynaddsubfx/Nio/OssEngine.cpp b/source/native-plugins/zynaddsubfx/Nio/OssEngine.cpp index 7bdffb38b..3e113c24f 100644 --- a/source/native-plugins/zynaddsubfx/Nio/OssEngine.cpp +++ b/source/native-plugins/zynaddsubfx/Nio/OssEngine.cpp @@ -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 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); diff --git a/source/native-plugins/zynaddsubfx/Nio/OssEngine.h b/source/native-plugins/zynaddsubfx/Nio/OssEngine.h index dd0e027ee..b5be7cd65 100644 --- a/source/native-plugins/zynaddsubfx/Nio/OssEngine.h +++ b/source/native-plugins/zynaddsubfx/Nio/OssEngine.h @@ -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; diff --git a/source/native-plugins/zynaddsubfx/Nio/OssMultiEngine.cpp b/source/native-plugins/zynaddsubfx/Nio/OssMultiEngine.cpp index 837d3d300..d6229e662 100644 --- a/source/native-plugins/zynaddsubfx/Nio/OssMultiEngine.cpp +++ b/source/native-plugins/zynaddsubfx/Nio/OssMultiEngine.cpp @@ -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); } } diff --git a/source/native-plugins/zynaddsubfx/Nio/OssMultiEngine.h b/source/native-plugins/zynaddsubfx/Nio/OssMultiEngine.h index fba2cfa1f..b77c99346 100644 --- a/source/native-plugins/zynaddsubfx/Nio/OssMultiEngine.h +++ b/source/native-plugins/zynaddsubfx/Nio/OssMultiEngine.h @@ -52,6 +52,9 @@ class OssMultiEngine : public AudioOut int *ps32; } smps; + /* peak values used for compressor */ + float *peaks; + bool en; bool is32bit; diff --git a/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.cpp b/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.cpp index 366daa4ef..8f84820d4 100644 --- a/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.cpp +++ b/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.cpp @@ -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 diff --git a/source/native-plugins/zynaddsubfx/Params/Controller.cpp b/source/native-plugins/zynaddsubfx/Params/Controller.cpp index efbf7d17f..985ce2668 100644 --- a/source/native-plugins/zynaddsubfx/Params/Controller.cpp +++ b/source/native-plugins/zynaddsubfx/Params/Controller.cpp @@ -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), }; diff --git a/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.cpp b/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.cpp index c822f81ad..0318dce2b 100644 --- a/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.cpp +++ b/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.cpp @@ -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;iPenvpoints;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; igetdt(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; iPenvval[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;iPenvpoints;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 diff --git a/source/native-plugins/zynaddsubfx/Params/FilterParams.cpp b/source/native-plugins/zynaddsubfx/Params/FilterParams.cpp index b2ac7ceaf..0b9350878 100644 --- a/source/native-plugins/zynaddsubfx/Params/FilterParams.cpp +++ b/source/native-plugins/zynaddsubfx/Params/FilterParams.cpp @@ -14,6 +14,7 @@ #include "FilterParams.h" #include "../Misc/Util.h" #include "../Misc/Time.h" +#include "../DSP/AnalogFilter.h" #include #include #include @@ -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; iPvowels[i]; + for(int j=0; jgetformantfreq(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); } diff --git a/source/native-plugins/zynaddsubfx/Params/LFOParams.cpp b/source/native-plugins/zynaddsubfx/Params/LFOParams.cpp index be8a17169..d52fd0984 100644 --- a/source/native-plugins/zynaddsubfx/Params/LFOParams.cpp +++ b/source/native-plugins/zynaddsubfx/Params/LFOParams.cpp @@ -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; diff --git a/source/native-plugins/zynaddsubfx/Params/PADnoteParameters.cpp b/source/native-plugins/zynaddsubfx/Params/PADnoteParameters.cpp index 67b71fb42..8b5257bd0 100644 --- a/source/native-plugins/zynaddsubfx/Params/PADnoteParameters.cpp +++ b/source/native-plugins/zynaddsubfx/Params/PADnoteParameters.cpp @@ -18,6 +18,7 @@ #include "../Synth/Resonance.h" #include "../Synth/OscilGen.h" #include "../Misc/WavFile.h" +#include "../Misc/Time.h" #include #include @@ -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; itime) { 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; iPhmag[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; iPhmag[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; nPOvertoneFreqMult[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() { diff --git a/source/native-plugins/zynaddsubfx/Params/SUBnoteParameters.h b/source/native-plugins/zynaddsubfx/Params/SUBnoteParameters.h index f5f6c8ec2..b5385765c 100644 --- a/source/native-plugins/zynaddsubfx/Params/SUBnoteParameters.h +++ b/source/native-plugins/zynaddsubfx/Params/SUBnoteParameters.h @@ -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; diff --git a/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp b/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp index 6799a4b92..aff2887f3 100644 --- a/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp @@ -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(*param.AmpEnvelope, basefreq, synth.dt()); + vce.AmpEnvelope = memory.alloc(*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(*param.AmpLfo, basefreq, time); + vce.AmpLfo = memory.alloc(*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(*param.FreqEnvelope, basefreq, synth.dt()); + vce.FreqEnvelope = memory.alloc(*param.FreqEnvelope, + basefreq, synth.dt(), wm, + (pre+"VoicePar"+nvoice+"/FreqEnvelope/").c_str); if(param.PFreqLfoEnabled) - vce.FreqLfo = memory.alloc(*param.FreqLfo, basefreq, time); + vce.FreqLfo = memory.alloc(*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(*param.FilterEnvelope, basefreq, synth.dt()); + memory.alloc(*param.FilterEnvelope, + basefreq, synth.dt(), wm, + (pre+"VoicePar"+nvoice+"/FilterEnvelope/").c_str); vce.Filter->addMod(*vce.FilterEnvelope); } if(param.PFilterLfoEnabled) { - vce.FilterLfo = memory.alloc(*param.FilterLfo, basefreq, time); + vce.FilterLfo = memory.alloc(*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(*param.FMFreqEnvelope, basefreq, synth.dt()); + vce.FMFreqEnvelope = memory.alloc(*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(*param.FMAmpEnvelope, basefreq, synth.dt()); + memory.alloc(*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(*param.FreqEnvelope, basefreq, synth.dt()); - FreqLfo = memory.alloc(*param.FreqLfo, basefreq, time); + ScratchString pre = prefix; + FreqEnvelope = memory.alloc(*param.FreqEnvelope, basefreq, + synth.dt(), wm, (pre+"GlobalPar/FreqEnvelope/").c_str); + FreqLfo = memory.alloc(*param.FreqLfo, basefreq, time, wm, + (pre+"GlobalPar/FreqLfo/").c_str); - AmpEnvelope = memory.alloc(*param.AmpEnvelope, basefreq, synth.dt()); - AmpLfo = memory.alloc(*param.AmpLfo, basefreq, time); + AmpEnvelope = memory.alloc(*param.AmpEnvelope, basefreq, + synth.dt(), wm, (pre+"GlobalPar/AmpEnvelope/").c_str); + AmpLfo = memory.alloc(*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(*param.GlobalFilter, synth, time, memory, stereo, basefreq); - FilterEnvelope = memory.alloc(*param.FilterEnvelope, basefreq, synth.dt()); - FilterLfo = memory.alloc(*param.FilterLfo, basefreq, time); + FilterEnvelope = memory.alloc(*param.FilterEnvelope, basefreq, + synth.dt(), wm, (pre+"GlobalPar/FilterEnvelope/").c_str); + FilterLfo = memory.alloc(*param.FilterLfo, basefreq, time, wm, + (pre+"GlobalPar/FilterLfo/").c_str); Filter->addMod(*FilterEnvelope); Filter->addMod(*FilterLfo); diff --git a/source/native-plugins/zynaddsubfx/Synth/ADnote.h b/source/native-plugins/zynaddsubfx/Synth/ADnote.h index 7335870fb..71bb019f8 100644 --- a/source/native-plugins/zynaddsubfx/Synth/ADnote.h +++ b/source/native-plugins/zynaddsubfx/Synth/ADnote.h @@ -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 * ******************************************/ diff --git a/source/native-plugins/zynaddsubfx/Synth/Envelope.cpp b/source/native-plugins/zynaddsubfx/Synth/Envelope.cpp index bddbbe684..2a51707a4 100644 --- a/source/native-plugins/zynaddsubfx/Synth/Envelope.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/Envelope.cpp @@ -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; } diff --git a/source/native-plugins/zynaddsubfx/Synth/Envelope.h b/source/native-plugins/zynaddsubfx/Synth/Envelope.h index ee6018d19..9f5a1525c 100644 --- a/source/native-plugins/zynaddsubfx/Synth/Envelope.h +++ b/source/native-plugins/zynaddsubfx/Synth/Envelope.h @@ -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; }; diff --git a/source/native-plugins/zynaddsubfx/Synth/LFO.cpp b/source/native-plugins/zynaddsubfx/Synth/LFO.cpp index b79be9829..0f55fde1a 100644 --- a/source/native-plugins/zynaddsubfx/Synth/LFO.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/LFO.cpp @@ -19,13 +19,15 @@ #include #include -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; } diff --git a/source/native-plugins/zynaddsubfx/Synth/LFO.h b/source/native-plugins/zynaddsubfx/Synth/LFO.h index 05bf3c549..123015ee9 100644 --- a/source/native-plugins/zynaddsubfx/Synth/LFO.h +++ b/source/native-plugins/zynaddsubfx/Synth/LFO.h @@ -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); }; diff --git a/source/native-plugins/zynaddsubfx/Synth/OscilGen.cpp b/source/native-plugins/zynaddsubfx/Synth/OscilGen.cpp index 009c98150..2228ec615 100644 --- a/source/native-plugins/zynaddsubfx/Synth/OscilGen.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/OscilGen.cpp @@ -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) { diff --git a/source/native-plugins/zynaddsubfx/Synth/PADnote.cpp b/source/native-plugins/zynaddsubfx/Synth/PADnote.cpp index 9eeea4674..0407a036b 100644 --- a/source/native-plugins/zynaddsubfx/Synth/PADnote.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/PADnote.cpp @@ -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(*pars.FreqEnvelope, basefreq, synth.dt()); - NoteGlobalPar.FreqLfo = memory.alloc(*pars.FreqLfo, basefreq, time); - - NoteGlobalPar.AmpEnvelope = memory.alloc(*pars.AmpEnvelope, basefreq, synth.dt()); - NoteGlobalPar.AmpLfo = memory.alloc(*pars.AmpLfo, basefreq, time); + ScratchString pre = prefix; + + NoteGlobalPar.FreqEnvelope = + memory.alloc(*pars.FreqEnvelope, basefreq, synth.dt(), + wm, (pre+"FreqEnvelope/").c_str); + NoteGlobalPar.FreqLfo = + memory.alloc(*pars.FreqLfo, basefreq, time, + wm, (pre+"FreqLfo/").c_str); + + NoteGlobalPar.AmpEnvelope = + memory.alloc(*pars.AmpEnvelope, basefreq, synth.dt(), + wm, (pre+"AmpEnvelope/").c_str); + NoteGlobalPar.AmpLfo = + memory.alloc(*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(*pars.GlobalFilter, synth, time, memory, true, basefreq); //setup mod - env = memory.alloc(*pars.FilterEnvelope, basefreq, synth.dt()); - lfo = memory.alloc(*pars.FilterLfo, basefreq, time); + env = memory.alloc(*pars.FilterEnvelope, basefreq, + synth.dt(), wm, (pre+"FilterEnvelope/").c_str); + lfo = memory.alloc(*pars.FilterLfo, basefreq, time, + wm, (pre+"FilterLfo/").c_str); flt->addMod(*env); flt->addMod(*lfo); } diff --git a/source/native-plugins/zynaddsubfx/Synth/PADnote.h b/source/native-plugins/zynaddsubfx/Synth/PADnote.h index dd25ebbf9..b60dc61b3 100644 --- a/source/native-plugins/zynaddsubfx/Synth/PADnote.h +++ b/source/native-plugins/zynaddsubfx/Synth/PADnote.h @@ -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_; diff --git a/source/native-plugins/zynaddsubfx/Synth/Resonance.cpp b/source/native-plugins/zynaddsubfx/Synth/Resonance.cpp index 97e1a07c0..528eb8ccd 100644 --- a/source/native-plugins/zynaddsubfx/Synth/Resonance.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/Resonance.cpp @@ -20,17 +20,19 @@ #include #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 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(*pars.AmpEnvelope, freq, synth.dt()); + //TODO populate this base string + ScratchString pre; + AmpEnvelope = memory.alloc(*pars.AmpEnvelope, freq, + synth.dt(), wm, (pre+"AmpEnvelope/").c_str); if(pars.PFreqEnvelopeEnabled) - FreqEnvelope = memory.alloc(*pars.FreqEnvelope, freq, synth.dt()); + FreqEnvelope = memory.alloc(*pars.FreqEnvelope, freq, + synth.dt(), wm, (pre+"FreqEnvelope/").c_str); if(pars.PBandWidthEnvelopeEnabled) - BandWidthEnvelope = memory.alloc(*pars.BandWidthEnvelope, freq, synth.dt()); + BandWidthEnvelope = memory.alloc(*pars.BandWidthEnvelope, + freq, synth.dt(), wm, (pre+"BandWidthEnvelope/").c_str); if(pars.PGlobalFilterEnabled) { - GlobalFilterEnvelope = memory.alloc(*pars.GlobalFilterEnvelope, freq, synth.dt()); + GlobalFilterEnvelope = + memory.alloc(*pars.GlobalFilterEnvelope, freq, + synth.dt(), wm, (pre+"GlobalFilterEnvelope/").c_str); GlobalFilter = memory.alloc(*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) { diff --git a/source/native-plugins/zynaddsubfx/Synth/SUBnote.h b/source/native-plugins/zynaddsubfx/Synth/SUBnote.h index 69ec7e523..398578706 100644 --- a/source/native-plugins/zynaddsubfx/Synth/SUBnote.h +++ b/source/native-plugins/zynaddsubfx/Synth/SUBnote.h @@ -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 diff --git a/source/native-plugins/zynaddsubfx/Synth/SynthNote.h b/source/native-plugins/zynaddsubfx/Synth/SynthNote.h index 3ed1a4c8d..938cf9a18 100644 --- a/source/native-plugins/zynaddsubfx/Synth/SynthNote.h +++ b/source/native-plugins/zynaddsubfx/Synth/SynthNote.h @@ -108,6 +108,7 @@ class SynthNote const Controller &ctl; const SYNTH_T &synth; const AbsTime &time; + WatchManager *wm; }; #endif diff --git a/source/native-plugins/zynaddsubfx/Synth/WatchPoint.cpp b/source/native-plugins/zynaddsubfx/Synth/WatchPoint.cpp new file mode 100644 index 000000000..5064c4b17 --- /dev/null +++ b/source/native-plugins/zynaddsubfx/Synth/WatchPoint.cpp @@ -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 +#include + + +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; iwriteArray(active_list[i], arg_types, arg_val); + deactivate[i] = true; + } + } + + //Cleanup internal data + new_active = false; + + //Clear deleted slots + for(int i=0; iwrite(id, "f", f); + del_watch(id); +} + +void WatchManager::satisfy(const char *id, float *f, int n) +{ + int selected = -1; + for(int i=0; isatisfy(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; + } + } +}; diff --git a/source/native-plugins/zynaddsubfx/UI/Connection.cpp b/source/native-plugins/zynaddsubfx/UI/Connection.cpp index 9eeb6c0ec..281425d95 100644 --- a/source/native-plugins/zynaddsubfx/UI/Connection.cpp +++ b/source/native-plugins/zynaddsubfx/UI/Connection.cpp @@ -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 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; diff --git a/source/native-plugins/zynaddsubfx/UI/MasterUI.fl b/source/native-plugins/zynaddsubfx/UI/MasterUI.fl index 4c278766d..c942af511 100644 --- a/source/native-plugins/zynaddsubfx/UI/MasterUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/MasterUI.fl @@ -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 {} { diff --git a/source/native-plugins/zynaddsubfx/UI/guimain.cpp b/source/native-plugins/zynaddsubfx/UI/guimain.cpp index 63846c68a..77bbc62de 100644 --- a/source/native-plugins/zynaddsubfx/UI/guimain.cpp +++ b/source/native-plugins/zynaddsubfx/UI/guimain.cpp @@ -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[]) diff --git a/source/native-plugins/zynaddsubfx/globals.h b/source/native-plugins/zynaddsubfx/globals.h index f3fb1c7c0..e21a639e2 100644 --- a/source/native-plugins/zynaddsubfx/globals.h +++ b/source/native-plugins/zynaddsubfx/globals.h @@ -45,6 +45,7 @@ class EnvelopeParams; class LFOParams; class FilterParams; +struct WatchManager; class LFO; class Envelope; class OscilGen; diff --git a/source/native-plugins/zynaddsubfx/main.cpp b/source/native-plugins/zynaddsubfx/main.cpp index 9e8bb3a94..7b90854ee 100644 --- a/source/native-plugins/zynaddsubfx/main.cpp +++ b/source/native-plugins/zynaddsubfx/main.cpp @@ -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); diff --git a/source/native-plugins/zynaddsubfx/rtosc/cpp/midimapper.cpp b/source/native-plugins/zynaddsubfx/rtosc/cpp/midimapper.cpp index 7d510dcc0..75a5a3848 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/cpp/midimapper.cpp +++ b/source/native-plugins/zynaddsubfx/rtosc/cpp/midimapper.cpp @@ -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 MidiMappernRT::getMidiMappingStrings(void) diff --git a/source/native-plugins/zynaddsubfx/rtosc/cpp/undo-history.cpp b/source/native-plugins/zynaddsubfx/rtosc/cpp/undo-history.cpp index 7dfac0a53..9f5ac4e1f 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/cpp/undo-history.cpp +++ b/source/native-plugins/zynaddsubfx/rtosc/cpp/undo-history.cpp @@ -16,6 +16,10 @@ class UndoHistoryImpl UndoHistoryImpl(void) :max_history_size(20) {} + ~UndoHistoryImpl(void) + { + clear(); + } std::deque> 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) diff --git a/source/native-plugins/zynaddsubfx/rtosc/dispatch.c b/source/native-plugins/zynaddsubfx/rtosc/dispatch.c index c8ad975d7..990a641f6 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/dispatch.c +++ b/source/native-plugins/zynaddsubfx/rtosc/dispatch.c @@ -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; diff --git a/source/native-plugins/zynaddsubfx/rtosc/miditable.h b/source/native-plugins/zynaddsubfx/rtosc/miditable.h index d5ff8c129..776bd3da1 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/miditable.h +++ b/source/native-plugins/zynaddsubfx/rtosc/miditable.h @@ -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 getMidiMappingStrings(void); //unclear if this should be be here as a helper or not diff --git a/source/native-plugins/zynaddsubfx/rtosc/port-sugar.h b/source/native-plugins/zynaddsubfx/rtosc/port-sugar.h index 4d1d72aef..fba65eb14 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/port-sugar.h +++ b/source/native-plugins/zynaddsubfx/rtosc/port-sugar.h @@ -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, ...) \ diff --git a/source/native-plugins/zynaddsubfx/rtosc/ports.h b/source/native-plugins/zynaddsubfx/rtosc/ports.h index e42f825a7..22506315d 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/ports.h +++ b/source/native-plugins/zynaddsubfx/rtosc/ports.h @@ -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); }; diff --git a/source/native-plugins/zynaddsubfx/rtosc/undo-history.h b/source/native-plugins/zynaddsubfx/rtosc/undo-history.h index 072b15a88..6c86aa254 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/undo-history.h +++ b/source/native-plugins/zynaddsubfx/rtosc/undo-history.h @@ -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); diff --git a/source/native-plugins/zynaddsubfx/version.cpp b/source/native-plugins/zynaddsubfx/version.cpp new file mode 100644 index 000000000..48bfb84d3 --- /dev/null +++ b/source/native-plugins/zynaddsubfx/version.cpp @@ -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 + +#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"); + diff --git a/source/native-plugins/zynaddsubfx/version.h b/source/native-plugins/zynaddsubfx/version.h new file mode 100644 index 000000000..913bb6d0d --- /dev/null +++ b/source/native-plugins/zynaddsubfx/version.h @@ -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 + +//! 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 .. + friend std::ostream& operator<< (std::ostream& os, + const version_type& v); +}; + +//! the current zynaddsubfx version +constexpr version_type version; + +#endif +