diff --git a/data/copy-zynaddsubfx b/data/copy-zynaddsubfx index 4921c1cb3..4127c39df 100755 --- a/data/copy-zynaddsubfx +++ b/data/copy-zynaddsubfx @@ -2,8 +2,8 @@ set -e -ORIG_ZYN_DIR="/home/falktx/Projects/FOSS/GIT-mine/zynaddsubfx-code" -CARLA_ZYN_DIR="/home/falktx/Projects/FOSS/GIT-mine/Carla/source/native-plugins/zynaddsubfx" +ORIG_ZYN_DIR="/home/falktx/FOSS/GIT-mine/falkTX/zynaddsubfx" +CARLA_ZYN_DIR="/home/falktx/FOSS/GIT-mine/falkTX/Carla/source/native-plugins/zynaddsubfx" rm -f $CARLA_ZYN_DIR/*.cpp rm -f $CARLA_ZYN_DIR/*.h @@ -36,7 +36,12 @@ cp $ORIG_ZYN_DIR/tlsf/*.c $CARLA_ZYN_DIR/tlsf/ rm $CARLA_ZYN_DIR/*/CMakeLists.txt rm $CARLA_ZYN_DIR/UI/zynaddsubfx.xpm -sed -i "s|emplace_uint32(|emplace_uint32_cpp(|" $CARLA_ZYN_DIR/rtosc/cpp/subtree-serialize.cpp +cp $ORIG_ZYN_DIR/src/zyn-version.h.in $CARLA_ZYN_DIR/zyn-version.h +sed -i 's|${VERSION_MAJOR}|3|' $CARLA_ZYN_DIR/zyn-version.h +sed -i 's|${VERSION_MINOR}|0|' $CARLA_ZYN_DIR/zyn-version.h +sed -i 's|${VERSION_REVISION}|1|' $CARLA_ZYN_DIR/zyn-version.h + +sed -i "s|emplace_uint32(|emplace_uint32_cpp(|" $CARLA_ZYN_DIR/rtosc/cpp/subtree-serialize.cpp sed -i "s|../../include/rtosc/|../|" $CARLA_ZYN_DIR/rtosc/cpp/*.cpp sed -i "s|../../tlsf/tlsf.h|tlsf/tlsf.h|" $CARLA_ZYN_DIR/Misc/Allocator.cpp sed -i "s|../src/globals.h|globals.h|" $CARLA_ZYN_DIR/Misc/Config.cpp diff --git a/source/native-plugins/zynaddsubfx-fx.cpp b/source/native-plugins/zynaddsubfx-fx.cpp index 3a1747d7c..13f1068ee 100644 --- a/source/native-plugins/zynaddsubfx-fx.cpp +++ b/source/native-plugins/zynaddsubfx-fx.cpp @@ -45,6 +45,7 @@ protected: fProgramCount(programCount), fBufferSize(getBufferSize()), fSampleRate(getSampleRate()), + fFilterParams(nullptr), fEffect(nullptr), efxoutl(nullptr), efxoutr(nullptr) @@ -191,7 +192,9 @@ protected: delete fEffect; } - EffectParams pars(fAllocator.getObject(), false, efxoutl, efxoutr, 0, static_cast(fSampleRate), static_cast(fBufferSize)); + EffectParams pars(fAllocator.getObject(), false, efxoutl, efxoutr, 0, + static_cast(fSampleRate), static_cast(fBufferSize), &fFilterParams); + fEffect = new ZynFX(pars); if (firstInit) @@ -217,6 +220,7 @@ protected: uint32_t fBufferSize; double fSampleRate; + FilterParams fFilterParams; Effect* fEffect; float* efxoutl; float* efxoutr; diff --git a/source/native-plugins/zynaddsubfx-src.cpp b/source/native-plugins/zynaddsubfx-src.cpp index 4c5de0b5d..ea953e41d 100644 --- a/source/native-plugins/zynaddsubfx-src.cpp +++ b/source/native-plugins/zynaddsubfx-src.cpp @@ -17,16 +17,6 @@ #include "CarlaDefines.h" -#ifdef CARLA_OS_WIN -# include -# define errx(...) {} -# define warnx(...) {} -# define index strchr -# define rindex strrchr -#else -# include -#endif - #define PLUGINVERSION #define SOURCE_DIR "/usr/share/zynaddsubfx/examples" #undef override diff --git a/source/native-plugins/zynaddsubfx-ui.cpp b/source/native-plugins/zynaddsubfx-ui.cpp index 235f9529e..fce94124a 100644 --- a/source/native-plugins/zynaddsubfx-ui.cpp +++ b/source/native-plugins/zynaddsubfx-ui.cpp @@ -17,13 +17,6 @@ #include "CarlaPipeUtils.cpp" -#ifdef CARLA_OS_WIN -# define errx(...) -# define warnx(...) -#else -# include -#endif - #define PLUGINVERSION #define SOURCE_DIR "/usr/share/zynaddsubfx" #undef override diff --git a/source/native-plugins/zynaddsubfx/DSP/SVFilter.cpp b/source/native-plugins/zynaddsubfx/DSP/SVFilter.cpp index 991182e84..db1f32f08 100644 --- a/source/native-plugins/zynaddsubfx/DSP/SVFilter.cpp +++ b/source/native-plugins/zynaddsubfx/DSP/SVFilter.cpp @@ -18,6 +18,8 @@ #include "../Misc/Util.h" #include "SVFilter.h" +#define errx(...) {} +#define warnx(...) {} #ifndef errx #include #endif diff --git a/source/native-plugins/zynaddsubfx/DSP/Unison.cpp b/source/native-plugins/zynaddsubfx/DSP/Unison.cpp index 5febd7619..307f1c5e5 100644 --- a/source/native-plugins/zynaddsubfx/DSP/Unison.cpp +++ b/source/native-plugins/zynaddsubfx/DSP/Unison.cpp @@ -18,6 +18,8 @@ #include "Unison.h" #include "globals.h" +#define errx(...) {} +#define warnx(...) {} #ifndef errx #include #endif diff --git a/source/native-plugins/zynaddsubfx/Effects/Alienwah.cpp b/source/native-plugins/zynaddsubfx/Effects/Alienwah.cpp index 18a36ba14..ae4e1c859 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Alienwah.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/Alienwah.cpp @@ -20,22 +20,29 @@ using std::complex; #define rObject Alienwah -#define rBegin [](const char *, rtosc::RtData &) { +#define rBegin [](const char *msg, rtosc::RtData &d) { #define rEnd } rtosc::Ports Alienwah::ports = { - {"preset::i", rOptions(Alienwah 1, Alienwah 2, Alienwah 3, Alienwah 4) + {"preset::i", rProp(parameter) + rOptions(wah 1, wah 2, wah 3, wah 4) rDoc("Instrument Presets"), 0, rBegin; + rObject *o = (rObject*)d.obj; + if(rtosc_narguments(msg)) + o->setpreset(rtosc_argument(msg, 0).i); + else + d.reply(d.loc, "i", o->Ppreset); 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(PLFOtype, 4, rShort("shape"), + rOptions(sine, triangle), "LFO Shape"), + rEffPar(PStereo, 5, rShort("stereo"), "Stereo Mode"), rEffPar(Pdepth, 6, rShort("depth"), "LFO Depth"), rEffPar(Pfeedback, 7, rShort("fb"), "Feedback"), - rEffPar(Pdelay, 8, rShort("delay"), "Delay"), + rEffPar(Pdelay, 8, rLinear(1,100), rShort("delay"), "Delay"), rEffPar(Plrcross, 9, rShort("l/r"), "Left/Right Crossover"), rEffPar(Pphase, 10, rShort("phase"), "Phase"), }; @@ -159,7 +166,7 @@ void Alienwah::setdelay(unsigned char _Pdelay) { memory.devalloc(oldl); memory.devalloc(oldr); - Pdelay = (_Pdelay >= MAX_ALIENWAH_DELAY) ? MAX_ALIENWAH_DELAY : _Pdelay; + Pdelay = limit(_Pdelay, 1, MAX_ALIENWAH_DELAY); oldl = memory.valloc>(Pdelay); oldr = memory.valloc>(Pdelay); cleanup(); diff --git a/source/native-plugins/zynaddsubfx/Effects/Chorus.cpp b/source/native-plugins/zynaddsubfx/Effects/Chorus.cpp index 1d97e3b2c..87d2cc760 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Chorus.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/Chorus.cpp @@ -21,19 +21,28 @@ using namespace std; #define rObject Chorus -#define rBegin [](const char *, rtosc::RtData &) { +#define rBegin [](const char *msg, rtosc::RtData &d) { #define rEnd } rtosc::Ports Chorus::ports = { - {"preset::i", rOptions(Alienwah 1, Alienwah 2, Alienwah 3, Alienwah 4) + {"preset::i", rProp(parameter) + rOptions(Chorus1, Chorus2, Chorus3, Celeste1, Celeste2, + Flange1, Flange2, Flange3, Flange4, Flange5) rDoc("Instrument Presets"), 0, rBegin; + rObject *o = (rObject*)d.obj; + if(rtosc_narguments(msg)) + o->setpreset(rtosc_argument(msg, 0).i); + else + d.reply(d.loc, "i", o->Ppreset); + 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(PLFOtype, 4, rShort("shape"), + rOptions(sine, tri), "LFO Shape"), + rEffPar(PStereo, 5, rShort("stereo"), "Stereo Mode"), rEffPar(Pdepth, 6, rShort("depth"), "LFO Depth"), rEffPar(Pdelay, 7, rShort("delay"), "Delay"), rEffPar(Pfeedback,8, rShort("fb"), "Feedback"), diff --git a/source/native-plugins/zynaddsubfx/Effects/Distorsion.cpp b/source/native-plugins/zynaddsubfx/Effects/Distorsion.cpp index fe5c127fc..b71772c67 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Distorsion.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/Distorsion.cpp @@ -20,25 +20,55 @@ #include #define rObject Distorsion -#define rBegin [](const char *, rtosc::RtData &) { +#define rBegin [](const char *msg, rtosc::RtData &d) { #define rEnd } rtosc::Ports Distorsion::ports = { - {"preset::i", rOptions(Alienwah 1, Alienwah 2, Alienwah 3, Alienwah 4) + {"preset::i", rProp(parameter) + rOptions(Overdrive 1, Overdrive 2, A. Exciter 1, A. Exciter 2, Guitar Amp, + Quantisize) rDoc("Instrument Presets"), 0, rBegin; + rObject *o = (rObject*)d.obj; + if(rtosc_narguments(msg)) + o->setpreset(rtosc_argument(msg, 0).i); + else + d.reply(d.loc, "i", o->Ppreset); rEnd}, //Pvolume/Ppanning are common - rEffPar(Plrcross, 2, rShort("l/r") "Left/Right Crossover"), + 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"), + rEffPar(Ptype, 5, rShort("type"), + rOptions(Arctangent, Asymmetric, Pow, Sine, Quantisize, + Zigzag, Limiter, Upper Limiter, Lower Limiter, + Inverse Limiter, Clip, Asym2, Pow2, sigmoid), + "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"), + {"waveform:", 0, 0, [](const char *, rtosc::RtData &d) + { + Distorsion &dd = *(Distorsion*)d.obj; + float buffer[128]; + rtosc_arg_t args[128]; + char arg_str[128+1] = {0}; + + for(int i=0; i<128; ++i) + buffer[i] = 2*(i/128.0)-1; + + waveShapeSmps(sizeof(buffer), buffer, dd.Ptype + 1, dd.Pdrive); + + for(int i=0; i<128; ++i) { + arg_str[i] = 'f'; + args[i].f = buffer[i]; + } + + d.replyArray(d.loc, arg_str, args); + }}, }; #undef rBegin #undef rEnd diff --git a/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.cpp b/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.cpp index 277380bf3..a777c28a2 100644 --- a/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.cpp @@ -11,6 +11,7 @@ of the License, or (at your option) any later version. */ +#include #include #include #include "DynamicFilter.h" @@ -20,47 +21,29 @@ #include #define rObject DynamicFilter -#define rBegin [](const char *, rtosc::RtData &) { +#define rBegin [](const char *msg, rtosc::RtData &d) { #define rEnd } rtosc::Ports DynamicFilter::ports = { {"preset::i", rOptions(WahWah, AutoWah, Sweep, VocalMorph1, VocalMorph1) rDoc("Instrument Presets"), 0, rBegin; + rObject *o = (rObject*)d.obj; + if(rtosc_narguments(msg)) + o->setpreset(rtosc_argument(msg, 0).i); + else + d.reply(d.loc, "i", o->Ppreset); 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}, + rEffPar(Pfreq, 2, rShort("freq"), "Effect Frequency"), + rEffPar(Pfreqrnd, 3, rShort("rand"), "Frequency Randomness"), + rEffPar(PLFOtype, 4, rShort("shape"), + rOptions(sin, tri), "LFO Shape"), + rEffPar(PStereo, 5, rShort("stereo"), "Stereo Mode"), + rEffPar(Pdepth, 6, rShort("depth"), "LFO Depth"), + rEffPar(Pampsns, 7, rShort("sense"), "how the filter varies according to the input amplitude"), + rEffPar(Pampsnsinv, 8, rShort("sns.inv"), "Sense Inversion"), + rEffPar(Pampsmooth, 9, rShort("smooth"), "how smooth the input amplitude changes the filter"), }; #undef rBegin #undef rEnd @@ -77,14 +60,13 @@ DynamicFilter::DynamicFilter(EffectParams pars, const AbsTime *time) filterl(NULL), filterr(NULL) { - filterpars = memory.alloc(0,0,0,time); - setpreset(Ppreset); + filterpars = pars.filterpars; + setpreset(Ppreset, pars.filterprotect); cleanup(); } DynamicFilter::~DynamicFilter() { - memory.dealloc(filterpars); memory.dealloc(filterl); memory.dealloc(filterr); } @@ -188,28 +170,8 @@ void DynamicFilter::reinitfilter(void) } } -void DynamicFilter::setpreset(unsigned char npreset) +void DynamicFilter::setfilterpreset(unsigned char npreset) { - const int PRESET_SIZE = 10; - const int NUM_PRESETS = 5; - unsigned char presets[NUM_PRESETS][PRESET_SIZE] = { - //WahWah - {110, 64, 80, 0, 0, 64, 0, 90, 0, 60}, - //AutoWah - {110, 64, 70, 0, 0, 80, 70, 0, 0, 60}, - //Sweep - {100, 64, 30, 0, 0, 50, 80, 0, 0, 60}, - //VocalMorph1 - {110, 64, 80, 0, 0, 64, 0, 64, 0, 60}, - //VocalMorph1 - {127, 64, 50, 0, 0, 96, 64, 0, 0, 60} - }; - - if(npreset >= NUM_PRESETS) - npreset = NUM_PRESETS - 1; - for(int n = 0; n < PRESET_SIZE; ++n) - changepar(n, presets[npreset][n]); - filterpars->defaults(); switch(npreset) { @@ -298,10 +260,36 @@ void DynamicFilter::setpreset(unsigned char npreset) // for (int i=0;i<5;i++){ // printf("freq=%d amp=%d q=%d\n",filterpars->Pvowels[0].formants[i].freq,filterpars->Pvowels[0].formants[i].amp,filterpars->Pvowels[0].formants[i].q); // }; + reinitfilter(); +} + +void DynamicFilter::setpreset(unsigned char npreset, bool protect) +{ + const int PRESET_SIZE = 10; + const int NUM_PRESETS = 5; + unsigned char presets[NUM_PRESETS][PRESET_SIZE] = { + //WahWah + {110, 64, 80, 0, 0, 64, 0, 90, 0, 60}, + //AutoWah + {110, 64, 70, 0, 0, 80, 70, 0, 0, 60}, + //Sweep + {100, 64, 30, 0, 0, 50, 80, 0, 0, 60}, + //VocalMorph1 + {110, 64, 80, 0, 0, 64, 0, 64, 0, 60}, + //VocalMorph1 + {127, 64, 50, 0, 0, 96, 64, 0, 0, 60} + }; + + if(npreset >= NUM_PRESETS) + npreset = NUM_PRESETS - 1; + for(int n = 0; n < PRESET_SIZE; ++n) + changepar(n, presets[npreset][n]); + if(insertion == 0) //lower the volume if this is system effect changepar(0, presets[npreset][0] * 0.5f); Ppreset = npreset; - reinitfilter(); + if(!protect) + setfilterpreset(npreset); } diff --git a/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.h b/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.h index e15cd0296..501f30ad2 100644 --- a/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.h +++ b/source/native-plugins/zynaddsubfx/Effects/DynamicFilter.h @@ -25,7 +25,8 @@ class DynamicFilter:public Effect ~DynamicFilter(); void out(const Stereo &smp); - void setpreset(unsigned char npreset); + void setpreset(unsigned char npreset) { setpreset(npreset, false); }; + void setpreset(unsigned char npreset, bool protect); void changepar(int npar, unsigned char value); unsigned char getpar(int npar) const; void cleanup(void); @@ -45,6 +46,7 @@ class DynamicFilter:public Effect void setdepth(unsigned char _Pdepth); void setampsns(unsigned char _Pampsns); + void setfilterpreset(unsigned char npreset); void reinitfilter(void); //Internal Values diff --git a/source/native-plugins/zynaddsubfx/Effects/EQ.cpp b/source/native-plugins/zynaddsubfx/Effects/EQ.cpp index 850b054c5..96bf13aeb 100644 --- a/source/native-plugins/zynaddsubfx/Effects/EQ.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/EQ.cpp @@ -12,10 +12,88 @@ */ #include +#include +#include #include "EQ.h" #include "../DSP/AnalogFilter.h" #include "../Misc/Allocator.h" +using rtosc::RtData; +#define rObject EQ +#define rBegin [](const char *msg, RtData &d) {\ + rObject *obj = (rObject*)d.obj; +#define rEQ(offset) \ + int nfilt = atoi(msg-2); \ + int id = 10+nfilt*5+offset; \ + if(rtosc_narguments(msg)) \ + obj->changepar(id, rtosc_argument(msg,0).i);\ + else \ + d.reply(d.loc, "i", obj->getpar(id)) + +#define rEnd } + +static rtosc::Ports filterports { + {"Ptype::i", rProp(parameter) rOptions(Off, LP1, HP1, LP2, + HP2, BP, notch, peak, l.shelf, h.shelf) + rShort("type") rDoc("Filter Type"), 0, + rBegin; + rEQ(0); + rEnd}, + {"Pfreq::i", rProp(parameter) rMap(min, 0) rMap(max, 127) + rShort("freq"), 0, + rBegin; + rEQ(1); + rEnd}, + {"Pgain::i", rProp(parameter) rMap(min, 0) rMap(max, 127) + rShort("gain"), 0, + rBegin; + rEQ(2); + rEnd}, + {"Pq::i", rProp(parameter) rMap(min, 0) rMap(max, 127) + rShort("q") rDoc("Resonance/Bandwidth"), 0, + rBegin; + rEQ(3); + rEnd}, + {"Pstages::i", rProp(parameter) rMap(min, 0) rMap(max, 4) + rShort("stages") rDoc("Additional filter stages"), 0, + rBegin; + rEQ(4); + rEnd}, +}; + +rtosc::Ports EQ::ports = { + {"filter#8/", 0, &filterports, + rBegin; + (void)obj; + SNIP; + filterports.dispatch(msg, d); + rEnd}, + {"coeff:", rProp(internal) rDoc("Get equalizer Coefficients"), NULL, + [](const char *, rtosc::RtData &d) + { + EQ *eq = (EQ*)d.obj; + float a[MAX_EQ_BANDS*MAX_FILTER_STAGES*3]; + float b[MAX_EQ_BANDS*MAX_FILTER_STAGES*3]; + memset(a, 0, sizeof(a)); + memset(b, 0, sizeof(b)); + eq->getFilter(a,b); + + char type[MAX_EQ_BANDS*MAX_FILTER_STAGES*3*2+1] = {0}; + rtosc_arg_t val[MAX_EQ_BANDS*MAX_FILTER_STAGES*3*2] = {0}; + for(int i=0; isetpreset(rtosc_argument(msg, 0).i); + else + d.reply(d.loc, "i", o->Ppreset); rEnd}, //Pvolume/Ppanning are common rEffPar(Pdelay, 2, rShort("delay"), "Length of Echo"), diff --git a/source/native-plugins/zynaddsubfx/Effects/Effect.cpp b/source/native-plugins/zynaddsubfx/Effects/Effect.cpp index 2a3e26321..3fddf04c3 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Effect.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/Effect.cpp @@ -18,9 +18,11 @@ #include EffectParams::EffectParams(Allocator &alloc_, bool insertion_, float *efxoutl_, float *efxoutr_, - unsigned char Ppreset_, unsigned int srate_, int bufsize_, FilterParams *filterpars_) + unsigned char Ppreset_, unsigned int srate_, int bufsize_, FilterParams *filterpars_, + bool filterprotect_) :alloc(alloc_), insertion(insertion_), efxoutl(efxoutl_), efxoutr(efxoutr_), - Ppreset(Ppreset_), srate(srate_), bufsize(bufsize_), filterpars(filterpars_) + Ppreset(Ppreset_), srate(srate_), bufsize(bufsize_), filterpars(filterpars_), + filterprotect(filterprotect_) {} Effect::Effect(EffectParams pars) :Ppreset(pars.Ppreset), diff --git a/source/native-plugins/zynaddsubfx/Effects/Effect.h b/source/native-plugins/zynaddsubfx/Effects/Effect.h index 36d8ea8be..1e28048df 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Effect.h +++ b/source/native-plugins/zynaddsubfx/Effects/Effect.h @@ -55,7 +55,8 @@ struct EffectParams * @param Ppreset_ chosen preset * @return Initialized Effect Parameter object*/ EffectParams(Allocator &alloc_, bool insertion_, float *efxoutl_, float *efxoutr_, - unsigned char Ppreset_, unsigned int srate, int bufsize, FilterParams *filterpars_=0); + unsigned char Ppreset_, unsigned int srate, int bufsize, FilterParams *filterpars_, + bool filterprotect=false); Allocator &alloc; @@ -66,6 +67,7 @@ struct EffectParams unsigned int srate; int bufsize; FilterParams *filterpars; + bool filterprotect; }; /**this class is inherited by the all effects(Reverb, Echo, ..)*/ diff --git a/source/native-plugins/zynaddsubfx/Effects/EffectMgr.cpp b/source/native-plugins/zynaddsubfx/Effects/EffectMgr.cpp index 8d92a53c5..1e2148b2a 100644 --- a/source/native-plugins/zynaddsubfx/Effects/EffectMgr.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/EffectMgr.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "EffectMgr.h" @@ -38,6 +39,8 @@ [](const char *msg, rtosc::RtData &data){\ rObject &o = *(rObject*)data.obj; \ data.obj = o.efx; \ + if(!dynamic_cast(o.efx)) \ + return; \ SNIP \ name::ports.dispatch(msg, data); \ }} @@ -45,6 +48,30 @@ static const rtosc::Ports local_ports = { rSelf(EffectMgr), rPaste, rRecurp(filterpars, "Filter Parameter for Dynamic Filter"), + {"Pvolume::i", rProp(parameter) rLinear(0,127) rShort("amt") rDoc("amount of effect"), + 0, + [](const char *msg, rtosc::RtData &d) + { + EffectMgr *eff = (EffectMgr*)d.obj; + if(!rtosc_narguments(msg)) + d.reply(d.loc, "i", eff->geteffectparrt(0)); + else if(rtosc_type(msg, 0) == 'i'){ + eff->seteffectparrt(0, rtosc_argument(msg, 0).i); + d.broadcast(d.loc, "i", eff->geteffectparrt(0)); + } + }}, + {"Ppanning::i", rProp(parameter) rLinear(0,127) rShort("pan") rDoc("panning"), + 0, + [](const char *msg, rtosc::RtData &d) + { + EffectMgr *eff = (EffectMgr*)d.obj; + if(!rtosc_narguments(msg)) + d.reply(d.loc, "i", eff->geteffectparrt(1)); + else if(rtosc_type(msg, 0) == 'i'){ + eff->seteffectparrt(1, rtosc_argument(msg, 0).i); + d.broadcast(d.loc, "i", eff->geteffectparrt(1)); + } + }}, {"parameter#128::i:T:F", rProp(parameter) rProp(alias) rLinear(0,127) rDoc("Parameter Accessor"), NULL, [](const char *msg, rtosc::RtData &d) @@ -103,7 +130,7 @@ static const rtosc::Ports local_ports = { d.reply(d.loc, "bb", sizeof(a), a, sizeof(b), b); }}, {"efftype::i", rOptions(Disabled, Reverb, Echo, Chorus, - Phaser, Alienwah, Distorsion, EQ, DynamicFilter) + Phaser, Alienwah, Distorsion, EQ, DynFilter) rProp(parameter) rDoc("Get Effect Type"), NULL, [](const char *m, rtosc::RtData &d) { @@ -134,7 +161,9 @@ static const rtosc::Ports local_ports = { rSubtype(Alienwah), rSubtype(Chorus), rSubtype(Distorsion), + rSubtype(DynamicFilter), rSubtype(Echo), + rSubtype(EQ), rSubtype(Phaser), rSubtype(Reverb), }; @@ -146,7 +175,7 @@ EffectMgr::EffectMgr(Allocator &alloc, const SYNTH_T &synth_, :insertion(insertion_), efxoutl(new float[synth_.buffersize]), efxoutr(new float[synth_.buffersize]), - filterpars(NULL), + filterpars(new FilterParams(time_)), nefx(0), efx(NULL), time(time_), @@ -165,6 +194,7 @@ EffectMgr::EffectMgr(Allocator &alloc, const SYNTH_T &synth_, EffectMgr::~EffectMgr() { memory.dealloc(efx); + delete filterpars; delete [] efxoutl; delete [] efxoutr; } @@ -186,7 +216,7 @@ void EffectMgr::changeeffectrt(int _nefx, bool avoidSmash) memset(efxoutr, 0, synth.bufferbytes); memory.dealloc(efx); EffectParams pars(memory, insertion, efxoutl, efxoutr, 0, - synth.samplerate, synth.buffersize); + synth.samplerate, synth.buffersize, filterpars, avoidSmash); try { switch (nefx) { case 1: @@ -223,9 +253,6 @@ void EffectMgr::changeeffectrt(int _nefx, bool avoidSmash) return; } - if(efx) - filterpars = efx->filterpars; - if(!avoidSmash) for(int i=0; i<128; ++i) settings[i] = geteffectparrt(i); @@ -288,6 +315,10 @@ void EffectMgr::changepreset(unsigned char npreset) void EffectMgr::changepresetrt(unsigned char npreset, bool avoidSmash) { preset = npreset; + if(avoidSmash && dynamic_cast(efx)) { + efx->Ppreset = npreset; + return; + } if(efx) efx->setpreset(npreset); if(!avoidSmash) @@ -424,6 +455,11 @@ void EffectMgr::paste(EffectMgr &e) changepresetrt(e.preset, true); for(int i=0;i<128;++i) seteffectparrt(i, e.settings[i]); + if(dynamic_cast(efx)) { + std::swap(filterpars, e.filterpars); + efx->filterpars = filterpars; + } + cleanup(); // cleanup the effect and recompute its parameters } void EffectMgr::add2XML(XMLwrapper& xml) @@ -443,7 +479,8 @@ void EffectMgr::add2XML(XMLwrapper& xml) xml.addpar("par", par); xml.endbranch(); } - if(filterpars) { + assert(filterpars); + if(nefx == 8) { xml.beginbranch("FILTER"); filterpars->add2XML(xml); xml.endbranch(); @@ -469,11 +506,11 @@ void EffectMgr::getfromXML(XMLwrapper& xml) seteffectpar(n, xml.getpar127("par", par)); xml.exitbranch(); } - if(filterpars) - if(xml.enterbranch("FILTER")) { - filterpars->getfromXML(xml); - xml.exitbranch(); - } + assert(filterpars); + if(xml.enterbranch("FILTER")) { + filterpars->getfromXML(xml); + xml.exitbranch(); + } xml.exitbranch(); } cleanup(); diff --git a/source/native-plugins/zynaddsubfx/Effects/Phaser.cpp b/source/native-plugins/zynaddsubfx/Effects/Phaser.cpp index 901fe3328..d0ef170bd 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Phaser.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/Phaser.cpp @@ -25,30 +25,50 @@ using namespace std; #define rObject Phaser -#define rBegin [](const char *, rtosc::RtData &) { +#define rBegin [](const char *msg, rtosc::RtData &d) { #define rEnd } +#define ucharParamCb(pname) rBegin \ + rObject &p = *(rObject*)d.obj; \ + if(rtosc_narguments(msg)) \ + p.set##pname(rtosc_argument(msg, 0).i); \ + else \ + d.reply(d.loc, "i", p.P##pname); \ + rEnd +#define rParamPhaser(name, ...) \ + {STRINGIFY(P##name) "::i", rProp(parameter) rMap(min, 0) rMap(max, 127) DOC(__VA_ARGS__), NULL, ucharParamCb(name)} + rtosc::Ports Phaser::ports = { - {"preset::i", rOptions(Alienwah 1, Alienwah 2, Alienwah 3, Alienwah 4) + {"preset::i", rProp(parameter) + rOptions(Phaser 1, Phaser 2, Phaser 3, Phaser 4, + Phaser 5, Phaser 6, + APhaser 1, APhaser 2, APhaser 3, APhaser 4, + APhaser 5, APhaser 6) rDoc("Instrument Presets"), 0, rBegin; + rObject *o = (rObject*)d.obj; + if(rtosc_narguments(msg)) + o->setpreset(rtosc_argument(msg, 0).i); + else + d.reply(d.loc, "i", o->Ppreset); rEnd}, //Pvolume/Ppanning are common - rEffPar(lfo.Pfreq, 2, rShort("freq"), ""), - rEffPar(lfo.Prandomness, 3, rShort("rnd."), ""), - rEffPar(lfo.PLFOtype, 4, rShort("type"), ""), - rEffParTF(lfo.Pstereo, 5, rShort("stereo"), ""), - rEffPar(Pdepth, 6, rShort("depth"), ""), - rEffPar(Pfb, 7, rShort("fb"), ""), - rEffPar(Pstages, 8, rShort("stages"), ""), - rEffPar(Plrcross, 9, rShort("cross"), ""), - rEffPar(Poffset, 9, rShort("off"), ""), - rEffParTF(Poutsub, 10, rShort("sub") ""), - rEffPar(Pphase, 11, rShort("phase"), ""), - rEffPar(Pwidth, 11, rShort("width"), ""), - rEffParTF(Phyper, 12, rShort("hyp."), ""), + rEffPar(lfo.Pfreq, 2, rShort("freq"), "LFO frequency"), + rEffPar(lfo.Prandomness, 3, rShort("rnd."), "LFO randomness"), + rEffPar(lfo.PLFOtype, 4, rShort("type"), + rOptions(sine, tri), "lfo shape"), + rEffPar(lfo.Pstereo, 5, rShort("stereo"), "Left/right channel phase shift"), + rEffPar(Pdepth, 6, rShort("depth"), "LFP depth"), + rEffPar(Pfb, 7, rShort("fb"), "Feedback"), + rEffPar(Pstages, 8, rLinear(1,12), rShort("stages"), ""), + rParamPhaser(lrcross, rShort("cross"), "Channel routing"), + rParamPhaser(offset, rShort("off"), "Offset"), + rEffParTF(Poutsub, 10, rShort("sub"), "Invert output"), + rParamPhaser(phase, rShort("phase"), ""), + rParamPhaser(width, rShort("width"), ""), + rEffParTF(Phyper, 12, rShort("hyp."), "Square the LFO"), rEffPar(Pdistortion, 13, rShort("distort"), "Distortion"), - rEffParTF(Panalog, 14, rShort("analog"), ""), + rEffParTF(Panalog, 14, rShort("analog"), "Use analog phaser"), }; #undef rBegin #undef rEnd @@ -316,7 +336,7 @@ void Phaser::setoffset(unsigned char Poffset) offsetpct = (float)Poffset / 127.0f; } -void Phaser::setstages(unsigned char Pstages) +void Phaser::setstages(unsigned char Pstages_) { memory.devalloc(old.l); memory.devalloc(old.r); @@ -325,7 +345,7 @@ void Phaser::setstages(unsigned char Pstages) memory.devalloc(yn1.l); memory.devalloc(yn1.r); - this->Pstages = min(MAX_PHASER_STAGES, (int)Pstages); + Pstages = limit(Pstages_, 1, MAX_PHASER_STAGES); old = Stereo(memory.valloc(Pstages * 2), memory.valloc(Pstages * 2)); diff --git a/source/native-plugins/zynaddsubfx/Effects/Reverb.cpp b/source/native-plugins/zynaddsubfx/Effects/Reverb.cpp index cfc0b0fab..f1c5b8419 100644 --- a/source/native-plugins/zynaddsubfx/Effects/Reverb.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/Reverb.cpp @@ -21,7 +21,7 @@ #include #define rObject Reverb -#define rBegin [](const char *, rtosc::RtData &) { +#define rBegin [](const char *msg, rtosc::RtData &d) { #define rEnd } rtosc::Ports Reverb::ports = { @@ -31,16 +31,22 @@ rtosc::Ports Reverb::ports = { rProp(parameter) rDoc("Instrument Presets"), 0, rBegin; + rObject *o = (rObject*)d.obj; + if(rtosc_narguments(msg)) + o->setpreset(rtosc_argument(msg, 0).i); + else + d.reply(d.loc, "i", o->Ppreset); rEnd}, //Pvolume/Ppanning are common rEffPar(Ptime, 2, rShort("time"), "Length of Reverb"), rEffPar(Pidelay, 3, rShort("i.time"), "Delay for first impulse"), rEffPar(Pidelayfb,4, rShort("i.fb"), "Feedback for first impulse"), rEffPar(Plpf, 7, rShort("lpf"), "Low pass filter"), - rEffPar(Phpf, 8, rShort("lpf"), "High pass filter"), + rEffPar(Phpf, 8, rShort("hpf"), "High pass filter"), rEffPar(Plohidamp,9, rShort("damp"), "Dampening"), //Todo make this a selector - rEffPar(Ptype, 10,rShort("type"), "Type"), + rEffPar(Ptype, 10,rShort("type"), + rOptions(Random, Freeverb, Bandwidth), "Type"), rEffPar(Proomsize,11,rShort("size"), "Room Size"), rEffPar(Pbandwidth,12,rShort("bw"), "Bandwidth"), }; diff --git a/source/native-plugins/zynaddsubfx/Misc/Bank.cpp b/source/native-plugins/zynaddsubfx/Misc/Bank.cpp index 7e087fcf8..766974211 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Bank.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Bank.cpp @@ -364,9 +364,11 @@ void Bank::rescanforbanks() //sort the banks sort(banks.begin(), banks.end()); + for(int i = 0; i < (int) banks.size(); ++i) + db->addBankDir(banks[i].dir); + //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) { @@ -469,6 +471,20 @@ std::vector Bank::search(std::string s) const } return out; } + +std::vector Bank::blist(std::string s) +{ + std::vector out; + int result = loadbank(s); + for(int i=0; i<128; ++i) { + if(ins[i].filename.empty()) + out.push_back("Empty Preset"); + else + out.push_back(ins[i].name); + out.push_back(to_s(i)); + } + return out; +} int Bank::addtobank(int pos, string filename, string name) { diff --git a/source/native-plugins/zynaddsubfx/Misc/Bank.h b/source/native-plugins/zynaddsubfx/Misc/Bank.h index aa7684da9..3f99daf4d 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Bank.h +++ b/source/native-plugins/zynaddsubfx/Misc/Bank.h @@ -77,6 +77,7 @@ class Bank } ins[BANK_SIZE]; std::vector search(std::string) const; + std::vector blist(std::string); private: diff --git a/source/native-plugins/zynaddsubfx/Misc/BankDb.cpp b/source/native-plugins/zynaddsubfx/Misc/BankDb.cpp index ae136bfe3..a92bc111d 100644 --- a/source/native-plugins/zynaddsubfx/Misc/BankDb.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/BankDb.cpp @@ -1,8 +1,10 @@ #include "BankDb.h" #include "XMLwrapper.h" +#include "Util.h" #include "../globals.h" #include #include +#include #define INSTRUMENT_EXTENSION ".xiz" @@ -11,12 +13,34 @@ typedef BankDb::svec svec; typedef BankDb::bvec bvec; BankEntry::BankEntry(void) - :id(0), add(false), pad(false), sub(false) + :id(0), add(false), pad(false), sub(false), time(0) {} +bool platform_strcasestr(const char *hay, const char *needle) +{ + int n = strlen(hay); + int m = strlen(needle); + for(int i=0; ifile < b.file; +} + static svec split(string s) { svec vec; @@ -73,6 +102,8 @@ bvec BankDb::search(std::string ss) const vec.push_back(field); } + std::sort(vec.begin(), vec.end()); + return vec; } @@ -92,9 +123,77 @@ void BankDb::clear(void) fields.clear(); } +static std::string getCacheName(void) +{ + char name[512] = {0}; + snprintf(name, sizeof(name), "%s%s", getenv("HOME"), + "/.zynaddsubfx-bank-cache.xml"); + return name; +} + +static bvec loadCache(void) +{ + bvec cache; + XMLwrapper xml; + xml.loadXMLfile(getCacheName()); + if(xml.enterbranch("bank-cache")) { + auto nodes = xml.getBranch(); + + for(auto node:nodes) { + BankEntry be; +#define bind(x,y) if(node.has(#x)) {be.x = y(node[#x].c_str());} + bind(file, string); + bind(bank, string); + bind(name, string); + bind(comments, string); + bind(author, string); + bind(type, atoi); + bind(id, atoi); + bind(add, atoi); + bind(pad, atoi); + bind(sub, atoi); + bind(time, atoi); +#undef bind + cache.push_back(be); + } + } + return cache; +} + +static void saveCache(bvec vec) +{ + XMLwrapper xml; + xml.beginbranch("bank-cache"); + for(auto value:vec) { + XmlNode binding("instrument-entry"); +#define bind(x) binding[#x] = to_s(value.x); + bind(file); + bind(bank); + bind(name); + bind(comments); + bind(author); + bind(type); + bind(id); + bind(add); + bind(pad); + bind(sub); + bind(time); +#undef bind + xml.add(binding); + } + xml.endbranch(); + xml.saveXMLfile(getCacheName(), 0); +} + void BankDb::scanBanks(void) { fields.clear(); + bvec cache = loadCache(); + bmap cc; + for(auto c:cache) + cc[c.bank + c.file] = c; + + bvec ncache; for(auto bank:banks) { DIR *dir = opendir(bank.c_str()); @@ -102,6 +201,7 @@ void BankDb::scanBanks(void) if(!dir) continue; + struct dirent *fn; while((fn = readdir(dir))) { @@ -111,16 +211,36 @@ void BankDb::scanBanks(void) if(!strstr(filename, INSTRUMENT_EXTENSION)) continue; - auto xiz = processXiz(filename, bank); + auto xiz = processXiz(filename, bank, cc); fields.push_back(xiz); + ncache.push_back(xiz); } closedir(dir); + } + saveCache(ncache); } -BankEntry BankDb::processXiz(std::string filename, std::string bank) const +BankEntry BankDb::processXiz(std::string filename, + std::string bank, bmap &cache) const { + string fname = bank+filename; + + //Grab a timestamp + struct stat st; + int ret = lstat(fname.c_str(), &st); + int time = 0; + if(ret != -1) + time = st.st_mtim.tv_sec; + + //quickly check if the file exists in the cache and if it is up-to-date + if(cache.find(fname) != cache.end() && + cache[fname].time == time) + return cache[fname]; + + + //verify if the name is like this NNNN-name (where N is a digit) int no = 0; unsigned int startname = 0; @@ -153,6 +273,7 @@ BankEntry BankDb::processXiz(std::string filename, std::string bank) const entry.file = filename; entry.bank = bank; entry.id = no; + entry.time = time; if(no != 0) //the instrument position in the bank is found entry.name = name.substr(startname); @@ -179,10 +300,10 @@ BankEntry BankDb::processXiz(std::string filename, std::string bank) const "Sound Effects", }; + //Try to obtain other metadata (expensive) XMLwrapper xml; - string fname = bank+filename; - int ret = xml.loadXMLfile(fname); + ret = xml.loadXMLfile(fname); if(xml.enterbranch("INSTRUMENT")) { if(xml.enterbranch("INFO")) { char author[1024]; @@ -213,6 +334,7 @@ BankEntry BankDb::processXiz(std::string filename, std::string bank) const //printf("Bank Entry:\n"); //printf("\tname - %s\n", entry.name.c_str()); //printf("\tauthor - %s\n", line(entry.author).c_str()); + //printf("\tbank - %s\n", entry.bank.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 index b0acfe942..60b2a3823 100644 --- a/source/native-plugins/zynaddsubfx/Misc/BankDb.h +++ b/source/native-plugins/zynaddsubfx/Misc/BankDb.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include struct BankEntry { @@ -15,16 +16,20 @@ struct BankEntry bool add; bool pad; bool sub; + int time;//last update typedef std::vector svec; svec tags(void) const; bool match(std::string) const; + bool operator<(const BankEntry &b) const; }; + class BankDb { public: - typedef std::vector svec; - typedef std::vector bvec; + typedef std::vector svec; + typedef std::vector bvec; + typedef std::map bmap; //search for banks //uses a space separated list of keywords and @@ -44,7 +49,7 @@ class BankDb void scanBanks(void); private: - BankEntry processXiz(std::string, std::string) const; + BankEntry processXiz(std::string, std::string, bmap&) const; bvec fields; svec banks; }; diff --git a/source/native-plugins/zynaddsubfx/Misc/Config.cpp b/source/native-plugins/zynaddsubfx/Misc/Config.cpp index 00e62a6fa..a8ff54ecc 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Config.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Config.cpp @@ -19,7 +19,7 @@ #include #include "Config.h" -#include "globals.h" +#include "../globals.h" #include "XMLwrapper.h" #define rStdString(name, len, ...) \ @@ -136,6 +136,37 @@ static const rtosc::Ports ports = { c.cfg.OscilSize = val; d.broadcast(d.loc, "i", (int)(log(c.cfg.OscilSize*1.0)/log(2.0))); }}, + {"add-favorite:s", rDoc("Add favorite directory"), 0, + [](const char *msg, rtosc::RtData &d) + { + Config &c = *(Config*)d.obj; + for(int i=0; iaddparstr("presets_root", cfg.presetsDirList[i]); xmlcfg->endbranch(); } + + for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i) + if(!cfg.favoriteList[i].empty()) { + xmlcfg->beginbranch("FAVSROOT", i); + xmlcfg->addparstr("favoirtes_root", cfg.favoriteList[i]); + xmlcfg->endbranch(); + } xmlcfg->addpar("interpolation", cfg.Interpolation); diff --git a/source/native-plugins/zynaddsubfx/Misc/Config.h b/source/native-plugins/zynaddsubfx/Misc/Config.h index 3f453dcbb..c90b79a35 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Config.h +++ b/source/native-plugins/zynaddsubfx/Misc/Config.h @@ -46,6 +46,7 @@ class Config int Interpolation; std::string bankRootDirList[MAX_BANK_ROOT_DIRS], currentBankDir; std::string presetsDirList[MAX_BANK_ROOT_DIRS]; + std::string favoriteList[MAX_BANK_ROOT_DIRS]; int CheckPADsynth; int IgnoreProgramChange; int UserInterfaceMode; diff --git a/source/native-plugins/zynaddsubfx/Misc/Master.cpp b/source/native-plugins/zynaddsubfx/Misc/Master.cpp index d36a43020..b0ea444ee 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Master.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Master.cpp @@ -116,7 +116,7 @@ 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, rOpt(-1, Master), + rArrayI(Pinsparts, NUM_INS_EFX, rOpt(-2, Master), rOpt(-1, Off) rOptions(Part1, Part2, Part3, Part4, Part5, Part6, Part7, Part8, Part9, Part10, Part11, Part12, Part13, Part14, Part15, Part16), @@ -682,27 +682,8 @@ void dump_msg(const char* ptr, std::ostream& os = std::cerr) #endif int msg_id=0; -/* - * Master audio out (the final sound) - */ -bool Master::AudioOut(float *outr, float *outl) +bool Master::runOSC(float *outl, float *outr, bool offline) { - //Danger Limits - if(memory->lowMemory(2,1024*1024)) - printf("QUITE LOW MEMORY IN THE RT POOL BE PREPARED FOR WEIRD BEHAVIOR!!\n"); - //Normal Limits - if(!pendingMemory && memory->lowMemory(4,1024*1024)) { - printf("Requesting more memory\n"); - 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}; @@ -714,7 +695,8 @@ bool Master::AudioOut(float *outr, float *outl) if(!strcmp(msg, "/load-master")) { Master *this_master = this; Master *new_master = *(Master**)rtosc_argument(msg, 0).b.data; - new_master->AudioOut(outl, outr); + if(!offline) + new_master->AudioOut(outl, outr); Nio::masterSwap(new_master); if (mastercb) mastercb(mastercb_ptr, new_master); @@ -731,23 +713,55 @@ bool Master::AudioOut(float *outr, float *outl) } ports.dispatch(msg, d, true); events++; + if(!d.matches) { + //workaround for requesting voice status + int a=0, b=0, c=0; + char e=0; + if(4 == sscanf(msg, "/part%d/kit%d/adpars/VoicePar%d/Enable%c", &a, &b, &c, &e)) { + d.reply(msg, "F"); + d.matches++; + } + } if(!d.matches) {// && !ports.apropos(msg)) { fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40); - fprintf(stderr, "Unknown address '%s:%s'\n", uToB->peak(), rtosc_argument_string(uToB->peak())); -#if 0 - if(strstr(msg, "PFMVelocity")) - dump_msg(msg); - if(ports.apropos(msg)) - fprintf(stderr, " -> best match: '%s'\n", ports.apropos(msg)->name); - if(ports.apropos(msg+1)) - fprintf(stderr, " -> best match: '%s'\n", ports.apropos(msg+1)->name); -#endif + fprintf(stderr, "Unknown address '%s:%s'\n", + offline ? "offline" : "online", + uToB->peak(), + rtosc_argument_string(uToB->peak())); fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40); } } if(events>1 && false) fprintf(stderr, "backend: %d events per cycle\n",events); + return true; +} + +/* + * Master audio out (the final sound) + */ +bool Master::AudioOut(float *outr, float *outl) +{ + //Danger Limits + if(memory->lowMemory(2,1024*1024)) + printf("QUITE LOW MEMORY IN THE RT POOL BE PREPARED FOR WEIRD BEHAVIOR!!\n"); + //Normal Limits + if(!pendingMemory && memory->lowMemory(4,1024*1024)) { + printf("Requesting more memory\n"); + bToU->write("/request-memory", ""); + pendingMemory = true; + } + + //work through events + if(!runOSC(outl, outr, false)) + return false; + + + //Handle watch points + if(bToU) + watcher.write_back = bToU; + watcher.tick(); + //Swaps the Left channel with Right Channel if(swaplr) @@ -896,6 +910,22 @@ bool Master::AudioOut(float *outr, float *outl) //update the global frame timer time++; +#ifdef DEMO_VERSION + double seconds = time.time()*synth.buffersize_f/synth.samplerate_f; + if(seconds > 10*60) {//10 minute trial + shutup = true; + for(int i = 0; i < synth.buffersize; ++i) { + float tmp = (synth.buffersize_f - i) / synth.buffersize_f; + outl[i] *= 0.0f; + outr[i] *= 0.0f; + } + } +#endif + + //Update pulse + last_ack = last_beat; + + return true; } @@ -998,6 +1028,8 @@ void Master::ShutUp() insefx[nefx]->cleanup(); for(int nefx = 0; nefx < NUM_SYS_EFX; ++nefx) sysefx[nefx]->cleanup(); + for(int i = 0; i < int(sizeof(activeNotes)/sizeof(activeNotes[0])); ++i) + activeNotes[i] = 0; vuresetpeaks(); shutup = 0; } diff --git a/source/native-plugins/zynaddsubfx/Misc/Master.h b/source/native-plugins/zynaddsubfx/Misc/Master.h index 1256bd8bb..db5853d5f 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Master.h +++ b/source/native-plugins/zynaddsubfx/Misc/Master.h @@ -91,6 +91,9 @@ class Master void vuUpdate(const float *outl, const float *outr); + //Process a set of OSC events in the bToU buffer + bool runOSC(float *outl, float *outr, bool offline=false); + /**Audio Output*/ bool AudioOut(float *outl, float *outr) REALTIME; /**Audio Output (for callback mode). @@ -175,6 +178,11 @@ class Master bool pendingMemory; const SYNTH_T &synth; const int& gzip_compression; //!< value from config + + //Heartbeat for identifying plugin offline modes + //in units of 10 ms (done s.t. overflow is in 497 days) + uint32_t last_beat; + uint32_t last_ack; private: float sysefxvol[NUM_SYS_EFX][NUM_MIDI_PARTS]; float sysefxsend[NUM_SYS_EFX][NUM_SYS_EFX]; diff --git a/source/native-plugins/zynaddsubfx/Misc/Microtonal.cpp b/source/native-plugins/zynaddsubfx/Misc/Microtonal.cpp index 6cd936a70..3ce81dc07 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Microtonal.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Microtonal.cpp @@ -3,6 +3,7 @@ Microtonal.cpp - Tuning settings and microtonal capabilities Copyright (C) 2002-2005 Nasca Octavian Paul + Copyright (C) 2016 Mark McCurry Author: Nasca Octavian Paul This program is free software; you can redistribute it and/or @@ -39,25 +40,25 @@ using namespace rtosc; * A good lookup table should be a good finalization of this */ const rtosc::Ports Microtonal::ports = { - rToggle(Pinvertupdown, "key mapping inverse"), - rParamZyn(Pinvertupdowncenter, "center of the inversion"), - rToggle(Penabled, "Enable for microtonal mode"), - rParamZyn(PAnote, "The note for 'A'"), - rParamF(PAfreq, "Frequency of the 'A' note"), - rParamZyn(Pscaleshift, "UNDOCUMENTED"), - rParamZyn(Pfirstkey, "First key to retune"), - rParamZyn(Plastkey, "Last key to retune"), - rParamZyn(Pmiddlenote, "Scale degree 0 note"), + rToggle(Pinvertupdown, rShort("inv."), "key mapping inverse"), + rParamZyn(Pinvertupdowncenter, rShort("center"), "center of the inversion"), + rToggle(Penabled, rShort("enable"), "Enable for microtonal mode"), + rParamZyn(PAnote, rShort("A note"), "The note for 'A'"), + rParamF(PAfreq, rShort("A freq"), "Frequency of the 'A' note"), + rParamZyn(Pscaleshift, rShort("shift"), "UNDOCUMENTED"), + rParamZyn(Pfirstkey, rShort("first key"), "First key to retune"), + rParamZyn(Plastkey, rShort("last key"), "Last key to retune"), + rParamZyn(Pmiddlenote, rShort("middle"), "Scale degree 0 note"), //TODO check to see if this should be exposed rParamZyn(Pmapsize, "Size of key map"), rToggle(Pmappingenabled, "Mapping Enable"), rParams(Pmapping, 128, "Mapping of keys"), - rParamZyn(Pglobalfinedetune, "Fine detune for all notes"), + rParamZyn(Pglobalfinedetune, rShort("fine"), "Fine detune for all notes"), - rString(Pname, MICROTONAL_MAX_NAME_LEN, "Microtonal Name"), - rString(Pcomment, MICROTONAL_MAX_NAME_LEN, "Microtonal Name"), + rString(Pname, MICROTONAL_MAX_NAME_LEN, rShort("name"), "Microtonal Name"), + rString(Pcomment, MICROTONAL_MAX_NAME_LEN, rShort("comment"), "Microtonal comments"), {"octavesize:", rDoc("Get octave size"), 0, [](const char*, RtData &d) { @@ -790,6 +791,7 @@ void Microtonal::getfromXML(XMLwrapper& xml) } xml.exitbranch(); } + apply(); } @@ -819,3 +821,34 @@ int Microtonal::loadXML(const char *filename) return 0; } + +//roundabout function, but it works +void Microtonal::apply(void) +{ + { + char buf[100*MAX_OCTAVE_SIZE] = {0}; + char tmpbuf[100] = {0}; + for (int i=0;i #include #include +#include #include #include @@ -49,11 +50,14 @@ #include #include +#define errx(...) {} +#define warnx(...) {} #ifndef errx #include #endif using std::string; +int Pexitprogram = 0; /****************************************************************************** * LIBLO And Reflection Code * @@ -459,8 +463,16 @@ public: int preferred_port); ~MiddleWareImpl(void); + //Check offline vs online mode in plugins + void heartBeat(Master *m); + int64_t start_time_sec; + int64_t start_time_nsec; + bool offline; + //Apply function while parameters are write locked void doReadOnlyOp(std::function read_only_fn); + void doReadOnlyOpPlugin(std::function read_only_fn); + bool doReadOnlyOpNormal(std::function read_only_fn, bool canfail=false); void savePart(int npart, const char *filename) { @@ -489,7 +501,7 @@ public: assert(actual_load[npart] <= pending_load[npart]); //load part in async fashion when possible -#if HAVE_ASYNC +#ifndef WIN32 auto alloc = std::async(std::launch::async, [master,filename,this,npart](){ Part *p = new Part(*master->memory, synth, @@ -663,6 +675,13 @@ public: } autoSave.tick(); + + heartBeat(master); + + //XXX This might have problems with a master swap operation + if(offline) + master->runOSC(0,0,true); + } @@ -838,6 +857,47 @@ class MwDataObj:public rtosc::RtData MiddleWareImpl *mwi; }; +static std::vector getFiles(const char *folder, bool finddir) +{ + DIR *dir = opendir(folder); + + if(dir == NULL) { + return {}; + } + + struct dirent *fn; + std::vector files; + + while((fn = readdir(dir))) { +#ifndef WIN32 + bool is_dir = fn->d_type & DT_DIR; + //it could still be a symbolic link + if(!is_dir) { + string path = string(folder) + "/" + fn->d_name; + struct stat buf; + memset((void*)&buf, 0, sizeof(buf)); + int err = stat(path.c_str(), &buf); + if(err) + printf("[Zyn:Error] stat cannot handle <%s>:%d\n", path.c_str(), err); + if(S_ISDIR(buf.st_mode)) { + is_dir = true; + } + } +#else + std::string darn_windows = folder + std::string("/") + std::string(fn->d_name); + printf("attr on <%s> => %x\n", darn_windows.c_str(), GetFileAttributes(darn_windows.c_str())); + printf("desired mask = %x\n", mask); + printf("error = %x\n", INVALID_FILE_ATTRIBUTES); + bool is_dir = GetFileAttributes(darn_windows.c_str()) & FILE_ATTRIBUTE_DIRECTORY; +#endif + if(finddir == is_dir) + files.push_back(fn->d_name); + } + + closedir(dir); + std::sort(begin(files), end(files)); + return files; +} @@ -926,6 +986,25 @@ const rtosc::Ports bankPorts = { } d.replyArray("/bank/types", t, args); rEnd}, + {"tags:", 0, 0, + rBegin; + const char *types[8]; + types[ 0] = "fast"; + types[ 1] = "slow"; + types[ 2] = "saw"; + types[ 3] = "bell"; + types[ 4] = "lead"; + types[ 5] = "ambient"; + types[ 6] = "horn"; + types[ 7] = "alarm"; + char t[8+1]={0}; + rtosc_arg_t args[8]; + for(int i=0; i<8; ++i) { + t[i] = 's'; + args[i].s = types[i]; + } + d.replyArray(d.loc, t, args); + rEnd}, {"slot#1024:", 0, 0, rBegin; const int loc = extractInt(msg); @@ -1010,7 +1089,20 @@ const rtosc::Ports bankPorts = { {"search:s", 0, 0, rBegin; auto res = impl.search(rtosc_argument(msg, 0).s); -#define MAX_SEARCH 128 +#define MAX_SEARCH 300 + 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; + if(std::get<1>(val) == -1) + continue; + argt[4*j+0] = 'i'; + args[4*j+0].i = std::get<1>(val); + argt[4*j+1] = 's'; + args[4*j+1].s = key[i].c_str(); + argt[4*j+2] = 'i'; + args[4*j+2].i = 0; + argt[4*j+3] = 'i'; + args[4*j+3].i = 127; + j++; } d.replyArray(d.loc, argt, args); @@ -1274,7 +1435,8 @@ static rtosc::Ports middwareSnoopPorts = { midi.unMap(addr.c_str(), true); rEnd}, //drop this message into the abyss - {"ui/title:", 0, 0, [](const char *msg, RtData &d) {}} + {"ui/title:", 0, 0, [](const char *msg, RtData &d) {}}, + {"quit:", 0, 0, [](const char *, RtData&) {Pexitprogram = 1;}}, }; static rtosc::Ports middlewareReplyPorts = { @@ -1344,8 +1506,8 @@ MiddleWareImpl::MiddleWareImpl(MiddleWare *mw, SYNTH_T synth_, int res = master->saveXML(save_file.c_str()); (void)res;});}) { - bToU = new rtosc::ThreadLink(4096*2,1024); - uToB = new rtosc::ThreadLink(4096*2,1024); + bToU = new rtosc::ThreadLink(4096*2*16,1024/16); + uToB = new rtosc::ThreadLink(4096*2*16,1024/16); midi_mapper.base_ports = &Master::ports; midi_mapper.rt_cb = [this](const char *msg){handleMsg(msg);}; if(preferrred_port != -1) @@ -1390,6 +1552,14 @@ MiddleWareImpl::MiddleWareImpl(MiddleWare *mw, SYNTH_T synth_, rtosc_message(buf, 1024, "/undo_resume",""); handleMsg(buf); }); + + //Setup starting time + struct timespec time; + clock_gettime(CLOCK_MONOTONIC, &time); + start_time_sec = time.tv_sec; + start_time_nsec = time.tv_nsec; + + offline = false; } MiddleWareImpl::~MiddleWareImpl(void) @@ -1464,6 +1634,130 @@ void MiddleWareImpl::doReadOnlyOp(std::function read_only_fn) } } +//Offline detection code: +// - Assume that the audio callback should be run at least once every 50ms +// - Atomically provide the number of ms since start to Master +// - Every time middleware ticks provide a heart beat +// - If when the heart beat is provided the backend is more than 200ms behind +// the last heartbeat then it must be offline +// - When marked offline the backend doesn't receive another heartbeat until it +// registers the current beat that it's behind on +void MiddleWareImpl::heartBeat(Master *master) +{ + //Current time + //Last provided beat + //Last acknowledged beat + //Current offline status + + struct timespec time; + clock_gettime(CLOCK_MONOTONIC, &time); + uint32_t now = (time.tv_sec-start_time_sec)*100 + + (time.tv_nsec-start_time_nsec)*1e-9*100; + int32_t last_ack = master->last_ack; + int32_t last_beat = master->last_beat; + + //everything is considered online for the first second + if(now < 100) + return; + + if(offline) { + if(last_beat == last_ack) { + //XXX INSERT MESSAGE HERE ABOUT TRANSITION TO ONLINE + offline = false; + + //Send new heart beat + master->last_beat = now; + } + } else { + //it's unquestionably alive + if(last_beat == last_ack) { + + //Send new heart beat + master->last_beat = now; + return; + } + + //it's pretty likely dead + if(last_beat-last_ack > 0 && now-last_beat > 20) { + //The backend has had 200 ms to acquire a new beat + //The backend instead has an older beat + //XXX INSERT MESSAGE HERE ABOUT TRANSITION TO OFFLINE + offline = true; + return; + } + + //who knows if it's alive or not here, give it a few ms to acquire or + //not + } + +} + +void MiddleWareImpl::doReadOnlyOpPlugin(std::function read_only_fn) +{ + assert(uToB); + int offline = 0; + if(offline) { + std::atomic_thread_fence(std::memory_order_acquire); + + //Now it is safe to do any read only operation + read_only_fn(); + } else if(!doReadOnlyOpNormal(read_only_fn, true)) { + //check if we just transitioned to offline mode + + std::atomic_thread_fence(std::memory_order_acquire); + + //Now it is safe to do any read only operation + read_only_fn(); + } +} + +bool MiddleWareImpl::doReadOnlyOpNormal(std::function read_only_fn, bool canfail) +{ + assert(uToB); + uToB->write("/freeze_state",""); + + std::list fico; + int tries = 0; + while(tries++ < 2000) { + if(!bToU->hasNext()) { + usleep(500); + continue; + } + const char *msg = bToU->read(); + if(!strcmp("/state_frozen", msg)) + break; + size_t bytes = rtosc_message_length(msg, bToU->buffer_size()); + char *save_buf = new char[bytes]; + memcpy(save_buf, msg, bytes); + fico.push_back(save_buf); + } + + if(canfail) { + //Now to resume normal operations + uToB->write("/thaw_state",""); + for(auto x:fico) { + uToB->raw_write(x); + delete [] x; + } + return false; + } + + assert(tries < 10000);//if this happens, the backend must be dead + + std::atomic_thread_fence(std::memory_order_acquire); + + //Now it is safe to do any read only operation + read_only_fn(); + + //Now to resume normal operations + uToB->write("/thaw_state",""); + for(auto x:fico) { + uToB->raw_write(x); + delete [] x; + } + return true; +} + void MiddleWareImpl::broadcastToRemote(const char *rtmsg) { //Always send to the local UI @@ -1479,6 +1773,11 @@ void MiddleWareImpl::broadcastToRemote(const char *rtmsg) void MiddleWareImpl::sendToRemote(const char *rtmsg, std::string dest) { + if(!rtmsg || rtmsg[0] != '/' || !rtosc_message_length(rtmsg, -1)) { + printf("[Warning] Invalid message in sendToRemote <%s>...\n", rtmsg); + return; + } + //printf("sendToRemote(%s:%s,%s)\n", rtmsg, rtosc_argument_string(rtmsg), // dest.c_str()); if(dest == "GUI") { @@ -1486,6 +1785,10 @@ void MiddleWareImpl::sendToRemote(const char *rtmsg, std::string dest) } else if(!dest.empty()) { lo_message msg = lo_message_deserialise((void*)rtmsg, rtosc_message_length(rtmsg, bToU->buffer_size()), NULL); + if(!msg) { + printf("[ERROR] OSC to <%s> Failed To Parse In Liblo\n", rtmsg); + return; + } //Send to known url lo_address addr = lo_address_new_from_url(dest.c_str()); @@ -1522,6 +1825,11 @@ void MiddleWareImpl::bToUhandle(const char *rtmsg) MwDataObj d(this); middlewareReplyPorts.dispatch(rtmsg, d, true); + if(!rtmsg) { + fprintf(stderr, "[ERROR] Unexpected Null OSC In Zyn\n"); + return; + } + in_order = true; //Normal message not captured by the ports if(d.matches == 0) { diff --git a/source/native-plugins/zynaddsubfx/Misc/Part.cpp b/source/native-plugins/zynaddsubfx/Misc/Part.cpp index fd7c2d511..a8098aaba 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Part.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Part.cpp @@ -51,10 +51,10 @@ static const Ports partPorts = { rParamZyn(Pvolume, rShort("Vol"), "Part Volume"), #undef rChangeCb #define rChangeCb obj->setPpanning(obj->Ppanning); - rParamZyn(Ppanning, "Set Panning"), + rParamZyn(Ppanning, rShort("pan"), "Set Panning"), #undef rChangeCb #define rChangeCb obj->setkeylimit(obj->Pkeylimit); - rParamI(Pkeylimit, rProp(parameter), rMap(min,0), rMap(max, POLYPHONY), "Key limit per part"), + rParamI(Pkeylimit, rShort("limit"), rProp(parameter), rMap(min,0), rMap(max, POLYPHONY), "Key limit per part"), #undef rChangeCb #define rChangeCb rParamZyn(Pminkey, rShort("min"), "Min Used Key"), @@ -62,8 +62,8 @@ static const Ports partPorts = { rParamZyn(Pkeyshift, rShort("shift"), "Part keyshift"), 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"), + rParamZyn(Pvelsns, rShort("sense"), "Velocity sensing"), + rParamZyn(Pveloffs, rShort("offset"), "Velocity offset"), rToggle(Pnoteon, "If the channel accepts note on events"), rOption(Pkitmode, rOptions(Off, Multi-Kit, Single-Kit), "Kit mode/enable\n" "Off - Only the first kit is ever utilized\n" @@ -77,7 +77,8 @@ static const Ports partPorts = { rString(info.Pauthor, MAX_INFO_TEXT_SIZE, "Instrument author"), rString(info.Pcomments, MAX_INFO_TEXT_SIZE, "Instrument comments"), rString(Pname, PART_MAX_NAME_LEN, "User specified label"), - rArrayI(Pefxroute, NUM_PART_EFX, "Effect Routing"), + rArrayI(Pefxroute, NUM_PART_EFX, + rOptions(Next Effect,Part Out,Dry Out), "Effect Routing"), rArrayT(Pefxbypass, NUM_PART_EFX, "If an effect is bypassed"), {"captureMin:", rDoc("Capture minimum valid note"), NULL, [](const char *, RtData &r) @@ -100,7 +101,7 @@ static const Ports partPorts = { int res = 0; if(!p->Ppolymode) res = p->Plegatomode ? 2 : 1; - d.reply(d.loc, "c", res); + d.reply(d.loc, "i", res); return; } diff --git a/source/native-plugins/zynaddsubfx/Misc/Schema.cpp b/source/native-plugins/zynaddsubfx/Misc/Schema.cpp index b68caf779..6b3df02e5 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Schema.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Schema.cpp @@ -168,7 +168,7 @@ void dump_param_cb(const rtosc::Port *p, const char *full_name, void *v) const char *max = meta["max"]; for(auto m:meta) { - if(strlen(m.title) >= 5 && !bcmp(m.title, "map ", 4)) { + if(strlen(m.title) >= 5 && !memcmp(m.title, "map ", 4)) { int id = atoi(m.title+4); std::string val = m.value; options.push_back(std::make_pair(id, val)); diff --git a/source/native-plugins/zynaddsubfx/Misc/Util.cpp b/source/native-plugins/zynaddsubfx/Misc/Util.cpp index 626920b51..c9145fcba 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Util.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Util.cpp @@ -29,6 +29,7 @@ #include #endif +#define errx(...) {} #ifndef errx #include #endif diff --git a/source/native-plugins/zynaddsubfx/Misc/Util.h b/source/native-plugins/zynaddsubfx/Misc/Util.h index 9bd424afc..2a27d9e17 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Util.h +++ b/source/native-plugins/zynaddsubfx/Misc/Util.h @@ -20,6 +20,9 @@ #include #include +#include +#include + using std::min; using std::max; diff --git a/source/native-plugins/zynaddsubfx/Misc/XMLwrapper.cpp b/source/native-plugins/zynaddsubfx/Misc/XMLwrapper.cpp index 78fb0baea..a15b8bbb9 100644 --- a/source/native-plugins/zynaddsubfx/Misc/XMLwrapper.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/XMLwrapper.cpp @@ -102,11 +102,11 @@ XMLwrapper::XMLwrapper() node = root = addparams("ZynAddSubFX-data", 4, "version-major", stringFrom( - version.major()).c_str(), + version.get_major()).c_str(), "version-minor", stringFrom( - version.minor()).c_str(), + version.get_minor()).c_str(), "version-revision", - stringFrom(version.revision()).c_str(), + stringFrom(version.get_revision()).c_str(), "ZynAddSubFX-author", "Nasca Octavian Paul"); //make the empty branch that will contain the information parameters @@ -125,10 +125,21 @@ XMLwrapper::XMLwrapper() endbranch(); } -XMLwrapper::~XMLwrapper() +void +XMLwrapper::cleanup(void) { if(tree) mxmlDelete(tree); + + /* make sure freed memory is not referenced */ + tree = 0; + node = 0; + root = 0; +} + +XMLwrapper::~XMLwrapper() +{ + cleanup(); } void XMLwrapper::setPadSynth(bool enabled) @@ -297,9 +308,7 @@ const char *trimLeadingWhite(const char *c) int XMLwrapper::loadXMLfile(const string &filename) { - if(tree != NULL) - mxmlDelete(tree); - tree = NULL; + cleanup(); const char *xmldata = doloadfile(filename); if(xmldata == NULL) @@ -323,13 +332,13 @@ int XMLwrapper::loadXMLfile(const string &filename) return -3; //the XML doesnt embbed zynaddsubfx data //fetch version information - fileversion.set_major(stringTo(mxmlElementGetAttr(root, "version-major"))); - fileversion.set_minor(stringTo(mxmlElementGetAttr(root, "version-minor"))); - fileversion.set_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: " << fileversion << endl; + cout << "loadXMLfile() version: " << _fileversion << endl; return 0; } @@ -367,10 +376,8 @@ char *XMLwrapper::doloadfile(const string &filename) const bool XMLwrapper::putXMLdata(const char *xmldata) { - if(tree != NULL) - mxmlDelete(tree); + cleanup(); - tree = NULL; if(xmldata == NULL) return false; diff --git a/source/native-plugins/zynaddsubfx/Misc/XMLwrapper.h b/source/native-plugins/zynaddsubfx/Misc/XMLwrapper.h index ee2c32382..107adb1e7 100644 --- a/source/native-plugins/zynaddsubfx/Misc/XMLwrapper.h +++ b/source/native-plugins/zynaddsubfx/Misc/XMLwrapper.h @@ -16,7 +16,7 @@ #include #include #include -#include "../zyn-version.h" +#include "zyn-version.h" #ifndef XML_WRAPPER_H #define XML_WRAPPER_H @@ -233,6 +233,10 @@ class XMLwrapper std::vector getBranch(void) const; + const version_type& fileversion() const { + return _fileversion; + } + private: /** @@ -254,6 +258,11 @@ class XMLwrapper */ char *doloadfile(const std::string &filename) const; + /** + * Cleanup XML tree before loading new one. + */ + void cleanup(void); + mxml_node_t *tree; /** 0) { + while(1) { + if(midi.exiting) + break; + if(snd_seq_event_input_pending(midi.handle, 1) <= 0) { + usleep(10); + continue; + } + if(snd_seq_event_input(midi.handle, &event) < 0) + break; //ensure ev is empty ev.channel = 0; ev.num = 0; @@ -225,6 +233,7 @@ bool AlsaEngine::openMidi() if(alsaport < 0) return false; + midi.exiting = false; pthread_attr_t attr; pthread_attr_init(&attr); @@ -239,8 +248,10 @@ void AlsaEngine::stopMidi() return; snd_seq_t *handle = midi.handle; - if((NULL != midi.handle) && midi.pThread) - pthread_cancel(midi.pThread); + if((NULL != midi.handle) && midi.pThread) { + midi.exiting = true; + pthread_join(midi.pThread, 0); + } midi.handle = NULL; if(handle) snd_seq_close(handle); @@ -319,6 +330,18 @@ bool AlsaEngine::openAudio() snd_pcm_hw_params_set_periods_near(audio.handle, audio.params, &audio.periods, NULL); + /* Set buffer size (in frames). The resulting latency is given by */ + /* latency = periodsize * periods / (rate * bytes_per_frame) */ + snd_pcm_uframes_t alsa_buffersize = synth.buffersize; + rc = snd_pcm_hw_params_set_buffer_size_near(audio.handle, + audio.params, + &alsa_buffersize); + + /* At this place, ALSA's and zyn's buffer sizes may differ. */ + /* This should not be a problem. */ + if((int)alsa_buffersize != synth.buffersize) + cerr << "ALSA buffer size: " << alsa_buffersize << endl; + /* Write the parameters to the driver */ rc = snd_pcm_hw_params(audio.handle, audio.params); if(rc < 0) { @@ -328,12 +351,6 @@ bool AlsaEngine::openAudio() return false; } - /* Set buffer size (in frames). The resulting latency is given by */ - /* latency = periodsize * periods / (rate * bytes_per_frame) */ - snd_pcm_hw_params_set_buffer_size(audio.handle, - audio.params, - synth.buffersize); - //snd_pcm_hw_params_get_period_size(audio.params, &audio.frames, NULL); //snd_pcm_hw_params_get_period_time(audio.params, &val, NULL); @@ -371,8 +388,12 @@ void *AlsaEngine::processAudio() snd_pcm_prepare(handle); } else - if(rc < 0) - cerr << "error from writei: " << snd_strerror(rc) << endl; + if(rc < 0) { + cerr << "AlsaEngine: Recovering connection..." << endl; + rc = snd_pcm_recover(handle, rc, 0); + if(rc < 0) + throw "Could not recover ALSA connection"; + } } return NULL; } diff --git a/source/native-plugins/zynaddsubfx/Nio/AlsaEngine.h b/source/native-plugins/zynaddsubfx/Nio/AlsaEngine.h index c263f934e..f7ca43e9d 100644 --- a/source/native-plugins/zynaddsubfx/Nio/AlsaEngine.h +++ b/source/native-plugins/zynaddsubfx/Nio/AlsaEngine.h @@ -56,6 +56,7 @@ class AlsaEngine:public AudioOut, MidiIn std::string device; snd_seq_t *handle; int alsaId; + bool exiting; pthread_t pThread; } midi; diff --git a/source/native-plugins/zynaddsubfx/Nio/WavEngine.cpp b/source/native-plugins/zynaddsubfx/Nio/WavEngine.cpp index d223bc7ca..42489cc5d 100644 --- a/source/native-plugins/zynaddsubfx/Nio/WavEngine.cpp +++ b/source/native-plugins/zynaddsubfx/Nio/WavEngine.cpp @@ -119,7 +119,8 @@ void *WavEngine::AudioThread() -32768, 32767); } - file->writeStereoSamples(synth.buffersize, recordbuf_16bit); + if(file) + file->writeStereoSamples(synth.buffersize, recordbuf_16bit); } delete[] recordbuf_16bit; diff --git a/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.cpp b/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.cpp index 8f84820d4..6eb9efcbf 100644 --- a/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.cpp +++ b/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.cpp @@ -78,25 +78,29 @@ static const Ports voicePorts = { "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"), + rOption(Type, rShort("type"), rOptions(Sound,White,Pink,DC), "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"), + rMap(min, -1), rMap(max, 16), "External Oscillator Selection"), rParamI(PextFMoscil, rShort("ext."), - rMap(min, -1), rMap(max, 16), "External FM Oscilator Selection"), + rMap(min, -1), rMap(max, 16), "External FM Oscillator Selection"), rParamZyn(Poscilphase, rShort("phase"), "Oscillator Phase"), rParamZyn(PFMoscilphase, rShort("phase"), "FM Oscillator Phase"), rToggle(Pfilterbypass, rShort("bypass"), "Filter Bypass"), //Freq Stuff rToggle(Pfixedfreq, rShort("fixed"), "If frequency is fixed"), - rParamZyn(PfixedfreqET, rShort("e.t."), "Equal Tempermant Parameter"), + rParamZyn(PfixedfreqET, rShort("e.t."), "Equal Temperament Parameter"), rParamZyn(PBendAdjust, rShort("bend"), "Pitch bend adjustment"), rParamZyn(POffsetHz, rShort("offset"), "Voice constant offset"), - rParamI(PDetune, rShort("fine"), "Fine Detune"), + //nominally -8192..8191 + rParamI(PDetune, rShort("fine"), + rLinear(0, 16383), "Fine Detune"), rParamI(PCoarseDetune, rShort("coarse"), "Coarse Detune"), - rParamZyn(PDetuneType, rShort("type"), "Magnitude of Detune"), + rParamZyn(PDetuneType, rShort("type"), + rOptions(L35cents, L10cents, E100cents, E1200cents), + "Magnitude of Detune"), rToggle(PFreqEnvelopeEnabled, rShort("enable"), "Frequency Envelope Enable"), rToggle(PFreqLfoEnabled, rShort("enable"), "Frequency LFO Enable"), @@ -118,14 +122,18 @@ static const Ports voicePorts = { //Modulator Stuff rOption(PFMEnabled, rShort("mode"), rOptions(none, morph, ring, phase, - frequency, pitch), "Modulator mode"), + frequency, pulse), "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"), + //nominally -8192..8191 + rParamI(PFMDetune, rShort("fine"), + rLinear(0, 16383), "Modulator Fine Detune"), rParamI(PFMCoarseDetune, rShort("coarse"), "Modulator Coarse Detune"), - rParamZyn(PFMDetuneType, rShort("type"), "Modulator Detune Magnitude"), + rParamZyn(PFMDetuneType, rShort("type"), + rOptions(L35cents, L10cents, E100cents, E1200cents), + "Modulator Detune Magnitude"), rToggle(PFMFixedFreq, rShort("fixed"), "Modulator Frequency Fixed"), rToggle(PFMFreqEnvelopeEnabled, rShort("enable"), "Modulator Frequency Envelope"), rToggle(PFMAmpEnvelopeEnabled, rShort("enable"), "Modulator Amplitude Envelope"), @@ -143,7 +151,8 @@ static const Ports voicePorts = { //TODO do the same for the other engines d.reply(d.loc, "f", getdetune(detuneType, 0, obj->PDetune)); }}, - {"octave::c:i", rProp(parameter) rDoc("Octave note offset"), NULL, + {"octave::c:i", rProp(parameter) rShort("octave") rLinear(-8, 7) rDoc("Octave note offset"), + NULL, [](const char *msg, RtData &d) { rObject *obj = (rObject *)d.obj; @@ -157,7 +166,8 @@ static const Ports voicePorts = { obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; } }}, - {"coarsedetune::c:i", rProp(parameter) rDoc("Coarse note detune"), NULL, + {"coarsedetune::c:i", rProp(parameter) rShort("coarse") rLinear(-64,63) + rDoc("Coarse note detune"), NULL, [](const char *msg, RtData &d) { rObject *obj = (rObject *)d.obj; @@ -183,7 +193,7 @@ static const Ports voicePorts = { //TODO do the same for the other engines d.reply(d.loc, "f", getdetune(detuneType, 0, obj->PFMDetune)); }}, - {"FMoctave::c:i", rProp(parameter) rDoc("Octave note offset for modulator"), NULL, + {"FMoctave::c:i", rProp(parameter) rShort("octave") rLinear(-8,7) rDoc("Octave note offset for modulator"), NULL, [](const char *msg, RtData &d) { rObject *obj = (rObject *)d.obj; @@ -197,7 +207,8 @@ static const Ports voicePorts = { obj->PFMCoarseDetune = k*1024 + obj->PFMCoarseDetune%1024; } }}, - {"FMcoarsedetune::c:i", rProp(parameter) rDoc("Coarse note detune for modulator"), + {"FMcoarsedetune::c:i", rProp(parameter) rShort("coarse") rLinear(-64,63) + rDoc("Coarse note detune for modulator"), NULL, [](const char *msg, RtData &d) { rObject *obj = (rObject *)d.obj; @@ -239,7 +250,9 @@ static const Ports globalPorts = { rToggle(PStereo, rShort("stereo"), "Mono/Stereo Enable"), //Frequency - rParamI(PDetune, rShort("fine"), "Fine Detune"), + //nominally -8192..8191 + rParamI(PDetune, rShort("fine"), + rLinear(0, 16383), "Fine Detune"), rParamI(PCoarseDetune, rShort("coarse"), "Coarse Detune"), rParamZyn(PDetuneType, rShort("type"), rOptions(L35cents, L10cents, E100cents, E1200cents), @@ -272,7 +285,8 @@ static const Ports globalPorts = { rObject *obj = (rObject *)d.obj; d.reply(d.loc, "f", getdetune(obj->PDetuneType, 0, obj->PDetune)); }}, - {"octave::c:i", rProp(parameter) rDoc("Octave note offset"), NULL, + {"octave::c:i", rProp(parameter) rShort("octave") rLinear(-8,7) + rDoc("Octave note offset"), NULL, [](const char *msg, RtData &d) { rObject *obj = (rObject *)d.obj; @@ -286,7 +300,8 @@ static const Ports globalPorts = { obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; } }}, - {"coarsedetune::c:i", rProp(parameter) rDoc("Coarse note detune"), NULL, + {"coarsedetune::c:i", rProp(parameter) rShort("coarse") rLinear(-64, 63) + rDoc("Coarse note detune"), NULL, [](const char *msg, RtData &d) { rObject *obj = (rObject *)d.obj; diff --git a/source/native-plugins/zynaddsubfx/Params/Controller.cpp b/source/native-plugins/zynaddsubfx/Params/Controller.cpp index 985ce2668..40e1a0b4c 100644 --- a/source/native-plugins/zynaddsubfx/Params/Controller.cpp +++ b/source/native-plugins/zynaddsubfx/Params/Controller.cpp @@ -35,21 +35,22 @@ const rtosc::Ports Controller::ports = { 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, rShort("pch.d"), "Range of MIDI Pitch Wheel"), rParamI(pitchwheel.bendrange_down, "Lower Range of MIDI Pitch Wheel"), 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"), + rToggle(portamento.portamento, "Portamento Enable"), 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"), + rToggle(portamento.proportional, rShort("propt."), "Whether the portamento time is proportional" + "to the size of the interval between two notes."), + rParamZyn(portamento.propRate, rShort("scale"), "Portamento proportional scale"), 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(portamento.updowntimestretch, rShort("up/dwn"), "Relative length of glide up vs glide down"), 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"), diff --git a/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.cpp b/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.cpp index 0318dce2b..daefe8824 100644 --- a/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.cpp +++ b/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.cpp @@ -17,6 +17,7 @@ #include #include +#include "zyn-version.h" #include "EnvelopeParams.h" #include "../Misc/Util.h" #include "../Misc/Time.h" @@ -38,7 +39,7 @@ static const rtosc::Ports localPorts = { #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"), + rParamZyn(Penvsustain, "Location of the sustain point"), rParams(Penvdt, MAX_ENVELOPE_POINTS, "Envelope Delay Times"), rParams(Penvval, MAX_ENVELOPE_POINTS, "Envelope Values"), rParamZyn(Penvstretch, rShort("stretch"), @@ -54,28 +55,46 @@ static const rtosc::Ports localPorts = { 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, + + {"Envmode:", rDoc("Envelope variant type"), NULL, + rBegin; + d.reply(d.loc, "i", env->Envmode); + rEnd}, + + {"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'; + const int M = rtosc_narguments(msg); + if(M == 0) { + 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); + } else { + for(int i=0; iPenvdt[i] = env->inv_dt(rtosc_argument(msg, i).f); } - d.replyArray(d.loc, arg_types, args); rEnd}, - {"envval:", rDoc("Envelope Delay Times"), NULL, + {"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'; + const int M = rtosc_narguments(msg); + if(M == 0) { + 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); + } else { + for(int i=0; iPenvval[i] = limit(roundf(rtosc_argument(msg,i).f*127.0f), 0.0f, 127.0f); + } } - d.replyArray(d.loc, arg_types, args); rEnd}, {"addPoint:i", rProp(internal) rDoc("Add point to envelope"), NULL, @@ -190,6 +209,12 @@ float EnvelopeParams::dt(char val) return (powf(2.0f, val / 127.0f * 12.0f) - 1.0f) * 10.0f; //miliseconds } +char EnvelopeParams::inv_dt(float val) +{ + int ival = roundf(logf(val/10.0f + 1.0f)/logf(2.0f) * 127.0f/12.0f); + return limit(ival, 0, 127); +} + /* * ADSR/ASR... initialisations @@ -358,7 +383,42 @@ void EnvelopeParams::add2XML(XMLwrapper& xml) } } +float EnvelopeParams::env_dB2rap(float db) { + return (powf(10.0f, db / 20.0f) - 0.01)/.99f; +} +float EnvelopeParams::env_rap2dB(float rap) { + return 20.0f * log10f(rap * 0.99f + 0.01); +} + +/** + since commit 5334d94283a513ae42e472aa020db571a3589fb9, i.e. between + versions 2.4.3 and 2.4.4, the amplitude envelope has been converted + differently from dB to rap for AmplitudeEnvelope (mode 2) + this converts the values read from an XML file once +*/ +struct version_fixer_t +{ + const bool mismatch; +public: + int operator()(int input) const + { + return (mismatch) + // The errors occured when calling env_dB2rap. Let f be the + // conversion function for mode 2 (see Envelope.cpp), then we + // load values with (let "o" be the function composition symbol): + // f^{-1} o (env_dB2rap^{-1}) o dB2rap o f + // from the xml file. This results in the following formula: + ? roundf(127.0f * (0.5f * + log10f( 0.01f + 0.99f * + powf(100, input/127.0f - 1)) + + 1)) + : input; + } + version_fixer_t(const version_type& fileversion, int env_mode) : + mismatch(fileversion < version_type(2,4,4) && + (env_mode == 2)) {} +}; void EnvelopeParams::getfromXML(XMLwrapper& xml) { @@ -369,20 +429,22 @@ void EnvelopeParams::getfromXML(XMLwrapper& xml) Pforcedrelease = xml.getparbool("forced_release", Pforcedrelease); Plinearenvelope = xml.getparbool("linear_envelope", Plinearenvelope); + version_fixer_t version_fix(xml.fileversion(), Envmode); + PA_dt = xml.getpar127("A_dt", PA_dt); PD_dt = xml.getpar127("D_dt", PD_dt); PR_dt = xml.getpar127("R_dt", PR_dt); - PA_val = xml.getpar127("A_val", PA_val); - PD_val = xml.getpar127("D_val", PD_val); - PS_val = xml.getpar127("S_val", PS_val); - PR_val = xml.getpar127("R_val", PR_val); + PA_val = version_fix(xml.getpar127("A_val", PA_val)); + PD_val = version_fix(xml.getpar127("D_val", PD_val)); + PS_val = version_fix(xml.getpar127("S_val", PS_val)); + PR_val = version_fix(xml.getpar127("R_val", PR_val)); for(int i = 0; i < Penvpoints; ++i) { if(xml.enterbranch("POINT", i) == 0) continue; if(i != 0) Penvdt[i] = xml.getpar127("dt", Penvdt[i]); - Penvval[i] = xml.getpar127("val", Penvval[i]); + Penvval[i] = version_fix(xml.getpar127("val", Penvval[i])); xml.exitbranch(); } diff --git a/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.h b/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.h index 3bbe1ac10..41a08a937 100644 --- a/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.h +++ b/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.h @@ -44,6 +44,7 @@ class EnvelopeParams:public Presets float getdt(char i) const; static float dt(char val); + static char inv_dt(float val); /* MIDI Parameters */ unsigned char Pfreemode; //1 for free mode, 0 otherwise @@ -70,6 +71,10 @@ class EnvelopeParams:public Presets int64_t last_update_timestamp; static const rtosc::Ports &ports; + + static float env_rap2dB(float rap); + static float env_dB2rap(float db); + private: void store2defaults(); diff --git a/source/native-plugins/zynaddsubfx/Params/FilterParams.cpp b/source/native-plugins/zynaddsubfx/Params/FilterParams.cpp index ac72e13a6..7a869f393 100644 --- a/source/native-plugins/zynaddsubfx/Params/FilterParams.cpp +++ b/source/native-plugins/zynaddsubfx/Params/FilterParams.cpp @@ -32,9 +32,9 @@ constexpr int sizeof_pvowels = sizeof(FilterParams::Pvowels); #define rObject FilterParams::Pvowels_t::formants_t static const rtosc::Ports subsubports = { - rParamZyn(freq, "Formant frequency"), - rParamZyn(amp, "Strength of formant"), - rParamZyn(q, "Quality Factor"), + rParamZyn(freq, rShort("f.freq"), "Formant frequency"), + rParamZyn(amp, rShort("f.str"), "Strength of formant"), + rParamZyn(q, rShort("f.q"), "The formant's quality factor, also known as resonance bandwidth or Q for short"), }; #undef rObject @@ -68,28 +68,37 @@ const rtosc::Ports FilterParams::ports = { 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"), + rParamI(Pstages, rShort("stages"), + rLinear(0,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"), + rParamI(Pnumformants, rShort("formants"), + rLinear(1,12), "Number of formants to be used"), rParamZyn(Pformantslowness, rShort("slew"), "Rate that formants change"), rParamZyn(Pvowelclearness, rShort("clarity"), - "Cost for mixing vowels"), + "How much each vowel is smudged with the next in sequence. A high clarity will avoid smudging."), 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"), - rParamZyn(Psequencestretch, "How modulators stretch the sequence"), - rToggle(Psequencereversed, "If the modulator input is inverted"), + rParamI(Psequencesize, rShort("seq.size"), rLinear(0, FF_MAX_SEQUENCE), "Length of vowel sequence"), + rParamZyn(Psequencestretch, rShort("seq.str"), "How modulators stretch the sequence"), + rToggle(Psequencereversed, rShort("reverse"), "If the modulator input is inverted"), - //{"Psequence#" FF_MAX_SEQUENCE "/nvowel", "", NULL, [](){}}, + {"vowel_seq#" STRINGIFY(FF_MAX_SEQUENCE) "::i", rShort("vowel") rDoc("Vowel number of this sequence position"), NULL, + [](const char *msg, RtData &d){ + FilterParams *obj = (FilterParams *) d.obj; + const char *mm = msg; + while(*mm && !isdigit(*mm)) ++mm; + unsigned idx = atoi(mm); + if(rtosc_narguments(msg)) { + obj->Psequence[idx].nvowel = rtosc_argument(msg, 0).i; + } else + d.broadcast(d.loc, "i", obj->Psequence[idx].nvowel); + }}, {"type-svf::i", rProp(parameter) rShort("type") rOptions(low, high, band, notch) rDoc("Filter Type"), 0, rOptionCb(Ptype)}, diff --git a/source/native-plugins/zynaddsubfx/Params/PADnoteParameters.cpp b/source/native-plugins/zynaddsubfx/Params/PADnoteParameters.cpp index 8b5257bd0..b0fae260e 100644 --- a/source/native-plugins/zynaddsubfx/Params/PADnoteParameters.cpp +++ b/source/native-plugins/zynaddsubfx/Params/PADnoteParameters.cpp @@ -40,31 +40,33 @@ static const rtosc::Ports realtime_ports = rRecurp(GlobalFilter, "Post Filter"), //Volume - rToggle(PStereo, "Stereo/Mono Mode"), - rParamZyn(PPanning, "Left Right Panning"), - rParamZyn(PVolume, "Synth Volume"), - rParamZyn(PAmpVelocityScaleFunction, "Amplitude Velocity Sensing function"), + rToggle(PStereo, rShort("stereo"), "Stereo/Mono Mode"), + rParamZyn(PPanning, rShort("panning"), "Left Right Panning"), + rParamZyn(PVolume, rShort("vol"), "Synth Volume"), + rParamZyn(PAmpVelocityScaleFunction, rShort("sense"), "Amplitude Velocity Sensing function"), - rParamZyn(Fadein_adjustment, "Adjustment for anti-pop strategy."), + rParamZyn(Fadein_adjustment, rShort("a.pop."), "Adjustment for anti-pop strategy."), //Punch - 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("sense"), "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"), //Freq - rToggle(Pfixedfreq, "Base frequency fixed frequency enable"), - rParamZyn(PfixedfreqET, "Equal temeperate control for fixed frequency operation"), + rToggle(Pfixedfreq, rShort("fixed"), "Base frequency fixed frequency enable"), + rParamZyn(PfixedfreqET, rShort("f.ET"), "Equal temeperate control for fixed frequency operation"), rParamZyn(PBendAdjust, "Pitch bend adjustment"), - rParamZyn(POffsetHz, "Voice constant offset"), - rParamI(PDetune, "Fine Detune"), - rParamI(PCoarseDetune, "Coarse Detune"), - rParamZyn(PDetuneType, "Magnitude of Detune"), + rParamZyn(POffsetHz, rShort("offset"), "Voice constant offset"), + rParamI(PDetune, rShort("fine"), rLinear(0, 16383), "Fine Detune"), + rParamI(PCoarseDetune, rShort("coarse"), "Coarse Detune"), + rParamZyn(PDetuneType, rShort("type"), + rOptions(L35cents, L10cents, E100cents, E1200cents), + "Magnitude of Detune"), {"sample#64:ifb", rProp(internal) rDoc("Nothing to see here"), 0, [](const char *m, rtosc::RtData &d) @@ -86,7 +88,8 @@ static const rtosc::Ports realtime_ports = PADnoteParameters *obj = (PADnoteParameters *)d.obj; d.reply(d.loc, "f", getdetune(obj->PDetuneType, 0, obj->PDetune)); }}, - {"octave::c:i", rProp(parameter) rDoc("Octave note offset"), NULL, + {"octave::c:i", rProp(parameter) rShort("octave") rLinear(-8,7) + rDoc("Octave note offset"), NULL, [](const char *msg, RtData &d) { PADnoteParameters *obj = (PADnoteParameters *)d.obj; @@ -100,7 +103,8 @@ static const rtosc::Ports realtime_ports = obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; } }}, - {"coarsedetune::c:i", rProp(parameter) rDoc("Coarse note detune"), NULL, + {"coarsedetune::c:i", rProp(parameter) rShort("coarse") rLinear(-64, 63) + rDoc("Coarse note detune"), NULL, [](const char *msg, RtData &d) { PADnoteParameters *obj = (PADnoteParameters *)d.obj; diff --git a/source/native-plugins/zynaddsubfx/Params/SUBnoteParameters.cpp b/source/native-plugins/zynaddsubfx/Params/SUBnoteParameters.cpp index 2522de315..9fc4ba1cd 100644 --- a/source/native-plugins/zynaddsubfx/Params/SUBnoteParameters.cpp +++ b/source/native-plugins/zynaddsubfx/Params/SUBnoteParameters.cpp @@ -40,10 +40,11 @@ static const rtosc::Ports SUBnotePorts = { 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(PDetune, rShort("detune"), rLinear(0, 16383), "Detune in detune type units"), rParamI(PCoarseDetune, rShort("cdetune"), "Coarse Detune"), //Real values needed - rOption(PDetuneType, rShort("det. scl."), rOptions(100 cents, 200 cents, 500 cents), "Detune Scale"), + rOption(PDetuneType, rShort("det. scl."), + rOptions(L35 cents, L10 cents, E100 cents, E1200 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"), @@ -65,13 +66,13 @@ static const rtosc::Ports SUBnotePorts = { "Overtone Parameter"), rParamI(POvertoneSpread.par2, rMap(min, 0), rMap(max, 255), rShort("p2"), "Overtone Parameter"), - rParamI(POvertoneSpread.par3, rMap(min, 0), rMap(max, 255), rShort("p3"), - "Overtone Parameter"), + rParamI(POvertoneSpread.par3, rMap(min, 0), rMap(max, 255), rShort("forceH"), + "Force Overtones To Harmonics"), #undef rChangeCb #define rChangeCb if (obj->time) { obj->last_update_timestamp = obj->time->time(); } - rParamZyn(Pnumstages, rShort("stages"), rMap(min, 1), rMap(max, 5), "Number of filter stages"), + rParamI(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)"), + rParamZyn(Phmagtype, rShort("mag. type"), rOptions(linear, -40dB, -60dB, -80dB, -100dB), "Magnitude scale"), rArray(Phmag, MAX_SUB_HARMONICS, "Harmonic magnitudes"), rArray(Phrelbw, MAX_SUB_HARMONICS, "Relative bandwidth"), rParamZyn(Pbwscale, rShort("stretch"), "Bandwidth scaling with frequency"), @@ -95,7 +96,8 @@ static const rtosc::Ports SUBnotePorts = { 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, + {"octave::c:i", rProp(parameter) rShort("octave") rLinear(-8,7) + rDoc("Note octave shift"), NULL, rBegin; if(!rtosc_narguments(msg)) { int k=obj->PCoarseDetune/1024; @@ -107,7 +109,8 @@ static const rtosc::Ports SUBnotePorts = { obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; } rEnd}, - {"coarsedetune::c:i", rProp(parameter) rDoc("Note coarse detune"), NULL, + {"coarsedetune::c:i", rProp(parameter) rShort("coarse") rLinear(-64, 63) + rDoc("Note coarse detune"), NULL, rBegin; if(!rtosc_narguments(msg)) { int k=obj->PCoarseDetune%1024; diff --git a/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp b/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp index aff2887f3..a6e5e7927 100644 --- a/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp @@ -651,6 +651,8 @@ void ADnote::legatonote(LegatoParams lpars) * (1.0f - pars.VoicePar[nvoice].PVolume / 127.0f)) // -60 dB .. 0 dB * VelF(velocity, pars.VoicePar[nvoice].PAmpVelocityScaleFunction); //velocity + if(pars.VoicePar[nvoice].PVolume == 0) + NoteVoicePar[nvoice].Volume = 0; if(pars.VoicePar[nvoice].PVolumeminus != 0) NoteVoicePar[nvoice].Volume = -NoteVoicePar[nvoice].Volume; @@ -808,6 +810,8 @@ void ADnote::initparameters(WatchManager *wm, const char *prefix) /* Voice Amplitude Parameters Init */ vce.Volume = powf(0.1f, 3.0f * (1.0f - param.PVolume / 127.0f)) // -60dB..0dB * VelF(velocity, param.PAmpVelocityScaleFunction); + if(param.PVolume == 0) + vce.Volume = 0; if(param.PVolumeminus) vce.Volume = -vce.Volume; @@ -1523,6 +1527,15 @@ inline void ADnote::ComputeVoicePinkNoise(int nvoice) } } +inline void ADnote::ComputeVoiceDC(int nvoice) +{ + for(int k = 0; k < unison_size[nvoice]; ++k) { + float *tw = tmpwave_unison[k]; + for(int i = 0; i < synth.buffersize; ++i) + tw[i] = 1.0f; + } +} + /* @@ -1568,9 +1581,12 @@ int ADnote::noteout(float *outl, float *outr) case 1: ComputeVoiceWhiteNoise(nvoice); break; - default: + case 2: ComputeVoicePinkNoise(nvoice); break; + default: + ComputeVoiceDC(nvoice); + break; } // Voice Processing diff --git a/source/native-plugins/zynaddsubfx/Synth/ADnote.h b/source/native-plugins/zynaddsubfx/Synth/ADnote.h index 71bb019f8..0ed176c2c 100644 --- a/source/native-plugins/zynaddsubfx/Synth/ADnote.h +++ b/source/native-plugins/zynaddsubfx/Synth/ADnote.h @@ -95,6 +95,7 @@ class ADnote:public SynthNote /**Generate Noise Samples for Voice*/ inline void ComputeVoiceWhiteNoise(int nvoice); inline void ComputeVoicePinkNoise(int nvoice); + inline void ComputeVoiceDC(int nvoice); /**Fadein in a way that removes clicks but keep sound "punchy"*/ inline void fadein(float *smps) const; diff --git a/source/native-plugins/zynaddsubfx/Synth/Envelope.cpp b/source/native-plugins/zynaddsubfx/Synth/Envelope.cpp index 2a51707a4..be69ca760 100644 --- a/source/native-plugins/zynaddsubfx/Synth/Envelope.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/Envelope.cpp @@ -173,14 +173,6 @@ float Envelope::envout(bool doWatch) return out; } -inline float Envelope::env_dB2rap(float db) { - return (powf(10.0f, db / 20.0f) - 0.01)/.99f; -} - -inline float Envelope::env_rap2dB(float rap) { - return 20.0f * log10f(rap * 0.99f + 0.01); -} - /* * Envelope Output (dB) */ @@ -191,8 +183,8 @@ float Envelope::envout_dB() return envout(true); if((currentpoint == 1) && (!keyreleased || !forcedrelease)) { //first point is always lineary interpolated - float v1 = env_dB2rap(envval[0]); - float v2 = env_dB2rap(envval[1]); + float v1 = EnvelopeParams::env_dB2rap(envval[0]); + float v2 = EnvelopeParams::env_dB2rap(envval[1]); out = v1 + (v2 - v1) * t; t += inct; @@ -204,11 +196,11 @@ float Envelope::envout_dB() } if(out > 0.001f) - envoutval = env_rap2dB(out); + envoutval = EnvelopeParams::env_rap2dB(out); else envoutval = MIN_ENVELOPE_DB; } else - out = env_dB2rap(envout(false)); + out = EnvelopeParams::env_dB2rap(envout(false)); float pos[2] = {(float)currentpoint + t, out}; watchOut(pos, 2); diff --git a/source/native-plugins/zynaddsubfx/Synth/Envelope.h b/source/native-plugins/zynaddsubfx/Synth/Envelope.h index 9f5a1525c..5199145d6 100644 --- a/source/native-plugins/zynaddsubfx/Synth/Envelope.h +++ b/source/native-plugins/zynaddsubfx/Synth/Envelope.h @@ -36,8 +36,6 @@ class Envelope * @return returns 1 if the envelope is finished*/ bool finished(void) const; private: - float env_rap2dB(float rap); - float env_dB2rap(float db); int envpoints; int envsustain; //"-1" means disabled float envdt[MAX_ENVELOPE_POINTS]; //millisecons diff --git a/source/native-plugins/zynaddsubfx/Synth/OscilGen.cpp b/source/native-plugins/zynaddsubfx/Synth/OscilGen.cpp index 2228ec615..b718e7113 100644 --- a/source/native-plugins/zynaddsubfx/Synth/OscilGen.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/OscilGen.cpp @@ -27,8 +27,6 @@ #include #include -pthread_t main_thread; - #define rObject OscilGen const rtosc::Ports OscilGen::non_realtime_ports = { rSelf(OscilGen), @@ -57,7 +55,7 @@ const rtosc::Ports OscilGen::non_realtime_ports = { "Base function modulation parameter"), rParamZyn(Pbasefuncmodulationpar3, rShort("p3"), "Base function modulation parameter"), - rParamZyn(Pwaveshaping, "Degree Of Waveshaping"), + rParamZyn(Pwaveshaping, rShort("amount"), "Degree Of Waveshaping"), rOption(Pwaveshapingfunction, rShort("distort"), rOptions(Undistorted, Arctangent, Asymmetric, Pow, Sine, Quantisize, @@ -75,7 +73,7 @@ const rtosc::Ports OscilGen::non_realtime_ports = { rOption(Psatype, rShort("spec. adj."), rOptions(None, Pow, ThrsD, ThrsU), "Spectral Adjustment Type"), rParamZyn(Psapar, rShort("p1"), "Spectral Adjustment Parameter"), - rParamI(Pharmonicshift, rShort("shift"), "Amount of shift on harmonics"), + rParamI(Pharmonicshift, rLinear(-64,64), 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"), @@ -95,7 +93,8 @@ const rtosc::Ports OscilGen::non_realtime_ports = { else { phase = rtosc_argument(m,0).i; //XXX hack hack - char *repath = strdup(d.loc); + char repath[128]; + strcpy(repath, d.loc); char *edit = strrchr(repath, '/')+1; strcpy(edit, "prepare"); OscilGen &o = *((OscilGen*)d.obj); @@ -119,7 +118,8 @@ const rtosc::Ports OscilGen::non_realtime_ports = { mag = rtosc_argument(m,0).i; //printf("setting magnitude\n\n"); //XXX hack hack - char *repath = strdup(d.loc); + char repath[128]; + strcpy(repath, d.loc); char *edit = strrchr(repath, '/')+1; strcpy(edit, "prepare"); OscilGen &o = *((OscilGen*)d.obj); @@ -163,10 +163,22 @@ const rtosc::Ports OscilGen::non_realtime_ports = { {"convert2sine:", rProp(non-realtime) rDoc("Translates waveform into FS"), NULL, [](const char *, rtosc::RtData &d) { ((OscilGen*)d.obj)->convert2sine(); + //XXX hack hack + char repath[128]; + strcpy(repath, d.loc); + char *edit = strrchr(repath, '/')+1; + *edit = 0; + d.reply("/damage", "s", repath); }}, {"use-as-base:", rProp(non-realtime) rDoc("Translates current waveform into base"), NULL, [](const char *, rtosc::RtData &d) { ((OscilGen*)d.obj)->useasbase(); + //XXX hack hack + char repath[128]; + strcpy(repath, d.loc); + char *edit = strrchr(repath, '/')+1; + *edit = 0; + d.reply("/damage", "s", repath); }}}; #define rForwardCb [](const char *msg, rtosc::RtData &d) {\ @@ -174,7 +186,7 @@ const rtosc::Ports OscilGen::non_realtime_ports = { const rtosc::Ports OscilGen::realtime_ports{ rSelf(OscilGen), rPresetType, - rParamZyn(Prand, rShort("phase rnd"), "Oscilator Phase Randomness: smaller than 0 is \"" + rParamZyn(Prand, rLinear(-64, 63), rShort("phase rnd"), "Oscillator Phase Randomness: smaller than 0 is \"" "group\", larger than 0 is for each harmonic"), rParamZyn(Pamprandpower, rShort("variance"), "Variance of harmonic randomness"), @@ -187,7 +199,7 @@ const rtosc::Ports OscilGen::realtime_ports{ "Base frequency of adaptive harmonic (30..3000Hz)"), rParamI(Padaptiveharmonicspower, rShort("amount"), rLinear(0,200), "Adaptive Harmonic Strength"), - rParamZyn(Padaptiveharmonicspar, rShort("par"), + rParamI(Padaptiveharmonicspar, rShort("power"), rLinear(0,100), "Adaptive Harmonics Postprocessing Power"), {"waveform:", rDoc("Returns waveform points"), NULL, [](const char *, rtosc::RtData &d) { @@ -228,6 +240,10 @@ const rtosc::MergePorts OscilGen::ports{ &OscilGen::non_realtime_ports }; +#ifndef M_PI_2 +# define M_PI_2 1.57079632679489661923 /* pi/2 */ +#endif + //operations on FFTfreqs inline void clearAll(fft_t *freqs, int oscilsize) diff --git a/source/native-plugins/zynaddsubfx/Synth/SUBnote.cpp b/source/native-plugins/zynaddsubfx/Synth/SUBnote.cpp index d49c433a0..c064e90a0 100644 --- a/source/native-plugins/zynaddsubfx/Synth/SUBnote.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/SUBnote.cpp @@ -28,6 +28,10 @@ #include "../Misc/Util.h" #include "../Misc/Allocator.h" +#ifndef M_PI +# define M_PI 3.14159265358979323846 /* pi */ +#endif + SUBnote::SUBnote(const SUBnoteParameters *parameters, SynthParams &spars) :SynthNote(spars), pars(*parameters), AmpEnvelope(nullptr), diff --git a/source/native-plugins/zynaddsubfx/Synth/WatchPoint.cpp b/source/native-plugins/zynaddsubfx/Synth/WatchPoint.cpp index 5064c4b17..c91caa189 100644 --- a/source/native-plugins/zynaddsubfx/Synth/WatchPoint.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/WatchPoint.cpp @@ -69,6 +69,11 @@ WatchManager::WatchManager(thrlnk *link) void WatchManager::add_watch(const char *id) { + //Don't add duplicate watchs + for(int i=0; iinit("parameter11");} class Fl_Osc_Check @@ -599,7 +599,7 @@ if (filterwindow!=NULL){ class Fl_Osc_Dial } Fl_Check_Button phaserp10 { - label Substract + label Subtract tooltip {inverts output} xywh {200 10 74 20} box THIN_UP_BOX down_box DOWN_BOX color 51 labelfont 1 labelsize 10 code0 {o->init("parameter10");} class Fl_Osc_Check diff --git a/source/native-plugins/zynaddsubfx/UI/FilterUI.fl b/source/native-plugins/zynaddsubfx/UI/FilterUI.fl index ff467e351..3562d79dc 100644 --- a/source/native-plugins/zynaddsubfx/UI/FilterUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/FilterUI.fl @@ -333,7 +333,7 @@ formantfiltergraph->redraw();} } Fl_Counter {} { label {S.Pos.} - callback {nseqpos = o->value();update_formant_window();} + callback {nseqpos = o->value();update_formant_window();vowel_counter->oscMove("vowel_seq"+to_s(nseqpos));} tooltip {Current position from the sequence} xywh {595 97 40 15} type Simple labelfont 1 labelsize 10 align 9 minimum 0 maximum 127 step 1 textsize 10 code0 {o->bounds(0,FF_MAX_SEQUENCE-2);} code1 {o->value(nseqpos);} @@ -343,6 +343,8 @@ formantfiltergraph->redraw();} callback {(void)o;//pars->Psequence[nseqpos].nvowel=(int) o->value(); pars->changed=true;} xywh {640 97 40 15} type Simple labelsize 10 align 1 minimum 0 maximum 127 step 1 textsize 10 code0 {o->bounds(0,FF_MAX_VOWELS-1);} + code1 {o->init("vowel_seq0");} + class Fl_Osc_Counter } Fl_Check_Button {} { label {Neg.Input} diff --git a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Value.H b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Value.H index 3a0a4aa16..9fc722e0a 100644 --- a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Value.H +++ b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Value.H @@ -21,4 +21,7 @@ class Fl_Osc_Value: public Fl_Value_Input, public Fl_Osc_Widget //Normal Initialization void init(const char *path); + void OSC_value(float v); + + void cb(); }; diff --git a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Value.cpp b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Value.cpp index c7e2c82ff..426b3611c 100644 --- a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Value.cpp +++ b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Value.cpp @@ -11,16 +11,35 @@ */ #include "Fl_Osc_Value.H" +static void callback_fn_value(Fl_Widget *w, void *) +{ + ((Fl_Osc_Value*)w)->cb(); +} + Fl_Osc_Value::Fl_Osc_Value(int X, int Y, int W, int H, const char *label) :Fl_Value_Input(X,Y,W,H, label), Fl_Osc_Widget(this) { + Fl_Value_Input::callback(callback_fn_value); } Fl_Osc_Value::~Fl_Osc_Value(void) {} -void Fl_Osc_Value::init(const char *path) +void Fl_Osc_Value::init(const char *path_) +{ + ext = path_; + oscRegister(path_); +} + +void Fl_Osc_Value::OSC_value(float v) { - (void)path; + //static char value[1024]; + //snprintf(value, sizeof(value), "%f", v); + this->value(v); } +void Fl_Osc_Value::cb(void) +{ + assert(osc); + oscWrite(ext, "f", (float)(value())); +} diff --git a/source/native-plugins/zynaddsubfx/UI/MasterUI.fl b/source/native-plugins/zynaddsubfx/UI/MasterUI.fl index c942af511..02b3ca01e 100644 --- a/source/native-plugins/zynaddsubfx/UI/MasterUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/MasterUI.fl @@ -2,7 +2,7 @@ version 1.0302 header_name {.h} code_name {.cc} -decl {//Copyright (c) 2002-2009 Nasca Octavian Paul} {private local +decl {//Copyright (c) 2002-2009 Nasca Octavian Paul - (c) 2009-2016 Mark McCurry} {private local } decl {//License: GNU GPL version 2 or later} {private local @@ -869,7 +869,7 @@ panelwindow->show();} xywh {411 344 365 280} type Double hide } { Fl_Box {} { - label {Copyright (c) 2002-2009 Nasca O. PAUL and others. Please read AUTHORS.txt} + label {Copyright (c) 2002-2009 Nasca O. PAUL, 2009-2016 Mark McCurry, and others. Please read AUTHORS.txt} xywh {15 35 335 55} labeltype EMBOSSED_LABEL labelsize 15 align 208 } Fl_Box {} { diff --git a/source/native-plugins/zynaddsubfx/UI/NSM.C b/source/native-plugins/zynaddsubfx/UI/NSM.C index ddbf2d84a..ba6ef18ce 100644 --- a/source/native-plugins/zynaddsubfx/UI/NSM.C +++ b/source/native-plugins/zynaddsubfx/UI/NSM.C @@ -22,7 +22,7 @@ #include "../Nio/Nio.h" -#ifndef NO_UI +#if defined(FLTK_UI) || defined(NTK_UI) #include #endif #include @@ -30,9 +30,10 @@ #include #include #include +#include extern int Pexitprogram; -#ifndef NO_UI +#if defined(FLTK_UI) || defined(NTK_UI) #include "MasterUI.h" extern MasterUI *ui; #endif @@ -40,10 +41,11 @@ extern MasterUI *ui; extern NSM_Client *nsm; extern char *instance_name; -NSM_Client::NSM_Client() +NSM_Client::NSM_Client(MiddleWare *m) + :project_filename(0), + display_name(0), + middleware(m) { - project_filename = 0; - display_name = 0; } int command_open(const char *name, @@ -58,9 +60,7 @@ NSM_Client::command_save(char **out_msg) (void) out_msg; int r = ERR_OK; -#ifndef NO_UI - ui->do_save_master(project_filename); -#endif + middleware->transmitMsg("/save_xmz", "s", project_filename); return r; } @@ -82,24 +82,19 @@ NSM_Client::command_open(const char *name, char *new_filename; + //if you're on windows enjoy the undefined behavior... +#ifndef WIN32 asprintf(&new_filename, "%s.xmz", name); +#endif struct stat st; int r = ERR_OK; -#ifndef NO_UI - if(0 == stat(new_filename, &st)) { - if(ui->do_load_master_unconditional(new_filename, display_name) < 0) { - *out_msg = strdup("Failed to load for unknown reason"); - r = ERR_GENERAL; - - return r; - } - } + if(0 == stat(new_filename, &st)) + middleware->transmitMsg("/load_xmz", "s", new_filename); else - ui->do_new_master_unconditional(); -#endif + middleware->transmitMsg("/reset_master", ""); if(project_filename) free(project_filename); @@ -114,7 +109,7 @@ NSM_Client::command_open(const char *name, return r; } -#ifndef NO_UI +#if defined(FLTK_UI) || defined(NTK_UI) static void save_callback(Fl_Widget *, void *v) { MasterUI *ui = static_cast(v); @@ -125,7 +120,7 @@ static void save_callback(Fl_Widget *, void *v) void NSM_Client::command_active(bool active) { -#ifndef NO_UI +#if defined(FLTK_UI) || defined(NTK_UI) if(active) { Fl_Menu_Item *m; //TODO see if there is a cleaner way of doing this without voiding diff --git a/source/native-plugins/zynaddsubfx/UI/NSM.H b/source/native-plugins/zynaddsubfx/UI/NSM.H index ac4c0bb5f..8e3577ccd 100644 --- a/source/native-plugins/zynaddsubfx/UI/NSM.H +++ b/source/native-plugins/zynaddsubfx/UI/NSM.H @@ -21,6 +21,7 @@ #if USE_NSM #include "NSM/Client.H" +#include "../Misc/MiddleWare.h" class NSM_Client:public NSM::Client { @@ -28,8 +29,9 @@ class NSM_Client:public NSM::Client char *project_filename; char *display_name; + MiddleWare *middleware; - NSM_Client(); + NSM_Client(MiddleWare *m); ~NSM_Client() { } protected: diff --git a/source/native-plugins/zynaddsubfx/UI/guimain.cpp b/source/native-plugins/zynaddsubfx/UI/guimain.cpp index 77bbc62de..7190dffbe 100644 --- a/source/native-plugins/zynaddsubfx/UI/guimain.cpp +++ b/source/native-plugins/zynaddsubfx/UI/guimain.cpp @@ -46,7 +46,7 @@ bool fileexists(const char *filename) return false; } -int Pexitprogram = 0; +int Pexitprogram=0; #include "Connection.h" diff --git a/source/native-plugins/zynaddsubfx/main.cpp b/source/native-plugins/zynaddsubfx/main.cpp index ffb1c4ad3..42521ff3e 100644 --- a/source/native-plugins/zynaddsubfx/main.cpp +++ b/source/native-plugins/zynaddsubfx/main.cpp @@ -17,15 +17,17 @@ #include #include #include +#include #include #include +#include #include #include #include -#include +#include #include #include #include "Params/PADnoteParameters.h" @@ -39,6 +41,7 @@ //Nio System #include "Nio/Nio.h" +#include "Nio/InMgr.h" //GUI System #include "UI/Connection.h" @@ -53,7 +56,7 @@ using namespace std; Master *master; int swaplr = 0; //1 for left-right swapping -int Pexitprogram = 0; //if the UI set this to 1, the program will exit +extern int Pexitprogram; //if the UI set this to 1, the program will exit #if LASH #include "Misc/LASHClient.h" @@ -70,7 +73,6 @@ char *instance_name = 0; void exitprogram(const Config &config); -extern pthread_t main_thread; //cleanup on signaled exit void sigterm_exit(int /*sig*/) @@ -117,9 +119,88 @@ void exitprogram(const Config& config) FFT_cleanup(); } +//Windows MIDI OH WHAT A HACK... +#ifdef WIN32 +#include +#include +extern InMgr *in; +HMIDIIN winmidiinhandle = 0; + +void CALLBACK WinMidiInProc(HMIDIIN hMidiIn,UINT wMsg,DWORD dwInstance, + DWORD dwParam1,DWORD dwParam2) +{ + int midicommand=0; + if (wMsg==MIM_DATA) { + int cmd,par1,par2; + cmd=dwParam1&0xff; + if (cmd==0xfe) return; + par1=(dwParam1>>8)&0xff; + par2=dwParam1>>16; + int cmdchan=cmd&0x0f; + int cmdtype=(cmd>>4)&0x0f; + + int tmp=0; + MidiEvent ev; + switch (cmdtype) { + case(0x8)://noteon + ev.type = 1; + ev.num = par1; + ev.channel = cmdchan; + ev.value = 0; + in->putEvent(ev); + break; + case(0x9)://noteoff + ev.type = 1; + ev.num = par1; + ev.channel = cmdchan; + ev.value = par2&0xff; + in->putEvent(ev); + break; + case(0xb)://controller + ev.type = 2; + ev.num = par1; + ev.channel = cmdchan; + ev.value = par2&0xff; + in->putEvent(ev); + break; + case(0xe)://pitch wheel + //tmp=(par1+par2*(long int) 128)-8192; + //winmaster->SetController(cmdchan,C_pitchwheel,tmp); + break; + default: + break; + }; + + }; +}; + +void InitWinMidi(int midi) +{ +(void)midi; + for(int i=0; i<10; ++i) { + long int res=midiInOpen(&winmidiinhandle,i,(DWORD_PTR)(void*)WinMidiInProc,0,CALLBACK_FUNCTION); + if(res == MMSYSERR_NOERROR) { + res=midiInStart(winmidiinhandle); + printf("[INFO] Starting Windows MIDI At %d with code %d(noerror=%d)\n", i, res, MMSYSERR_NOERROR); + if(res == 0) + return; + } else + printf("[INFO] No Windows MIDI Device At id %d\n", i); + } +}; + +//void StopWinMidi() +//{ +// midiInStop(winmidiinhandle); +// midiInClose(winmidiinhandle); +//}; +#else +void InitWinMidi(int) {} +#endif + + int main(int argc, char *argv[]) { - main_thread = pthread_self(); SYNTH_T synth; Config config; config.init(); @@ -156,6 +237,9 @@ int main(int argc, char *argv[]) { "load-instrument", 2, NULL, 'L' }, + { + "midi-learn", 2, NULL, 'M' + }, { "sample-rate", 2, NULL, 'r' }, @@ -210,9 +294,6 @@ int main(int argc, char *argv[]) { "dump-json-schema", 2, NULL, 'D' }, - { - "ui-title", 1, NULL, 'u' - }, { 0, 0, 0, 0 } @@ -221,8 +302,9 @@ int main(int argc, char *argv[]) int option_index = 0, opt, exitwithhelp = 0, exitwithversion = 0; int prefered_port = -1; int auto_save_interval = 60; +int wmidi = -1; - string loadfile, loadinstrument, execAfterInit, ui_title; + string loadfile, loadinstrument, execAfterInit, loadmidilearn; while(1) { int tmp = 0; @@ -230,7 +312,7 @@ int main(int argc, char *argv[]) /**\todo check this process for a small memory leak*/ opt = getopt_long(argc, argv, - "l:L:r:b:o:I:O:N:e:P:A:u:D:hvapSDUY", + "l:L:M:r:b:o:I:O:N:e:P:A:D:hvapSDUYZ", opts, &option_index); char *optarguments = optarg; @@ -267,6 +349,9 @@ int main(int argc, char *argv[]) case 'L': GETOP(loadinstrument); break; + case 'M': + GETOP(loadmidilearn); + break; case 'r': GETOPNUM(synth.samplerate); if(synth.samplerate < 4000) { @@ -351,9 +436,9 @@ int main(int argc, char *argv[]) dump_json(outfile, Master::ports); } break; - case 'u': + case 'Z': if(optarguments) - ui_title = optarguments; + wmidi = atoi(optarguments); break; case '?': cerr << "ERROR:Bad option or parameter.\n" << endl; @@ -374,6 +459,7 @@ int main(int argc, char *argv[]) << " -v , --version \t\t\t Display version and exit\n" << " -l file, --load=FILE\t\t\t Loads a .xmz file\n" << " -L file, --load-instrument=FILE\t Loads a .xiz file\n" + << " -M file, --midi-learn=FILE\t\t Loads a .xlz file\n" << " -r SR, --sample-rate=SR\t\t Set the sample rate SR\n" << " -b BS, --buffer-size=SR\t\t Set the buffer size (granularity)\n" @@ -391,7 +477,6 @@ int main(int argc, char *argv[]) << " -I , --input\t\t\t\t Set Input Engine\n" << " -e , --exec-after-init\t\t Run post-initialization script\n" << " -d , --dump-oscdoc=FILE\t\t Dump oscdoc xml to file\n" - << " -u , --ui-title=TITLE\t\t Extend UI Window Titles\n" << endl; return 0; @@ -441,22 +526,34 @@ int main(int argc, char *argv[]) } } + if(!loadmidilearn.empty()) { + char msg[1024]; + rtosc_message(msg, sizeof(msg), "/load_xlz", + "s", loadmidilearn.c_str()); + middleware->transmitMsg(msg); + } + if(altered_master) middleware->updateResources(master); //Run the Nio system + printf("[INFO] Nio::start()\n"); bool ioGood = Nio::start(); + printf("[INFO] exec-after-init\n"); if(!execAfterInit.empty()) { cout << "Executing user supplied command: " << execAfterInit << endl; if(system(execAfterInit.c_str()) == -1) cerr << "Command Failed..." << endl; } + InitWinMidi(wmidi); + gui = NULL; //Capture Startup Responses + printf("[INFO] startup OSC\n"); typedef std::vector wait_t; wait_t msg_waitlist; middleware->setUiCallback([](void*v,const char*msg) { @@ -467,21 +564,19 @@ int main(int argc, char *argv[]) wait.push_back(copy); }, &msg_waitlist); + printf("[INFO] UI calbacks\n"); if(!noui) gui = GUI::createUi(middleware->spawnUiApi(), &Pexitprogram); middleware->setUiCallback(GUI::raiseUi, gui); middleware->setIdleCallback([](void*){GUI::tickUi(gui);}, NULL); //Replay Startup Responses + printf("[INFO] OSC replay\n"); for(auto msg:msg_waitlist) { GUI::raiseUi(gui, msg); delete [] msg; } - //set titles - if(!ui_title.empty()) - GUI::raiseUi(gui, "/ui/title", "s", ui_title.c_str()); - if(!noui) { GUI::raiseUi(gui, "/show", "i", config.cfg.UserInterfaceMode); @@ -490,18 +585,31 @@ int main(int argc, char *argv[]) "Default IO did not initialize.\nDefaulting to NULL backend."); } - if(auto_save_interval > 0) { + printf("[INFO] auto_save setup\n"); + if(auto_save_interval > 0 && false) { int old_save = middleware->checkAutoSave(); if(old_save > 0) GUI::raiseUi(gui, "/alert-reload", "i", old_save); middleware->enableAutoSave(auto_save_interval); } + printf("[INFO] NSM Stuff\n"); + + //TODO move this stuff into Cmake +#if USE_NSM && defined(WIN32) +#undef USE_NSM +#define USE_NSM 0 +#endif + +#if LASH && defined(WIN32) +#undef LASH +#define LASH 0 +#endif #if USE_NSM char *nsm_url = getenv("NSM_URL"); if(nsm_url) { - nsm = new NSM_Client; + nsm = new NSM_Client(middleware); if(!nsm->init(nsm_url)) nsm->announce("ZynAddSubFX", ":switch:", argv[0]); @@ -512,6 +620,7 @@ int main(int argc, char *argv[]) } #endif + printf("[INFO] LASH Stuff\n"); #if USE_NSM if(!nsm) #endif @@ -522,7 +631,22 @@ int main(int argc, char *argv[]) #endif } +#ifdef ZEST_GUI + if(!noui) { + printf("[INFO] Launching Zyn-Fusion...\n"); + const char *addr = middleware->getServerAddress(); + if(fork() == 0) { + execlp("zyn-fusion", "zyn-fusion", addr, "--builtin", "--no-hotload", 0); + execlp("./zyn-fusion", "zyn-fusion", addr, "--builtin", "--no-hotload", 0); + + err(1,"Failed to launch Zyn-Fusion"); + } + } +#endif + + printf("[INFO] Main Loop...\n"); while(Pexitprogram == 0) { +#ifndef WIN32 #if USE_NSM if(nsm) { nsm->check(); @@ -553,7 +677,11 @@ int main(int argc, char *argv[]) done: #endif GUI::tickUi(gui); +#endif middleware->tick(); +#ifdef WIN32 + Sleep(1); +#endif } exitprogram(config); diff --git a/source/native-plugins/zynaddsubfx/rtosc/cpp/ports.cpp b/source/native-plugins/zynaddsubfx/rtosc/cpp/ports.cpp index 688b56589..64db8b184 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/cpp/ports.cpp +++ b/source/native-plugins/zynaddsubfx/rtosc/cpp/ports.cpp @@ -19,6 +19,13 @@ RtData::RtData(void) :loc(NULL), loc_size(0), obj(NULL), matches(0), message(NULL) {} +void RtData::replyArray(const char *path, const char *args, + rtosc_arg_t *vals) +{ + (void) path; + (void) args; + (void) vals; +} void RtData::reply(const char *path, const char *args, ...) { va_list va; @@ -30,6 +37,23 @@ void RtData::reply(const char *path, const char *args, ...) }; void RtData::reply(const char *msg) {(void)msg;}; +void RtData::chain(const char *path, const char *args, ...) +{ + (void) path; + (void) args; +} + +void RtData::chain(const char *msg) +{ + (void) msg; +}; +void RtData::chainArray(const char *path, const char *args, + rtosc_arg_t *vals) +{ + (void) path; + (void) args; + (void) vals; +}; void RtData::broadcast(const char *path, const char *args, ...) { va_list va; @@ -41,9 +65,18 @@ void RtData::broadcast(const char *path, const char *args, ...) } void RtData::broadcast(const char *msg) {reply(msg);}; +void RtData::broadcastArray(const char *path, const char *args, + rtosc_arg_t *vals) +{ + (void) path; + (void) args; + (void) vals; +} void RtData::forward(const char *rational) -{} +{ + (void) rational; +} void metaiterator_advance(const char *&title, const char *&value) { diff --git a/source/native-plugins/zynaddsubfx/rtosc/port-sugar.h b/source/native-plugins/zynaddsubfx/rtosc/port-sugar.h index 54396ee4f..8bb828f2f 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/port-sugar.h +++ b/source/native-plugins/zynaddsubfx/rtosc/port-sugar.h @@ -91,7 +91,7 @@ struct rtosc_hack_decltype_t rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n) #define OPTIONS_IMP13(a,b,c,d,e,f,g,h,i,j,k,l,m) \ rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \ - rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n) + rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m) #define OPTIONS_IMP12(a,b,c,d,e,f,g,h,i,j,k,l) \ rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \ rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l) diff --git a/source/native-plugins/zynaddsubfx/rtosc/ports.h b/source/native-plugins/zynaddsubfx/rtosc/ports.h index 22506315d..65a7dc2e4 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/ports.h +++ b/source/native-plugins/zynaddsubfx/rtosc/ports.h @@ -55,17 +55,17 @@ struct RtData const char *message; virtual void replyArray(const char *path, const char *args, - rtosc_arg_t *vals){}; + 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 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){}; + 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){}; + rtosc_arg_t *vals); virtual void forward(const char *rational=NULL); }; diff --git a/source/native-plugins/zynaddsubfx/version.cpp b/source/native-plugins/zynaddsubfx/version.cpp index b5843fd32..2d5ce83e2 100644 --- a/source/native-plugins/zynaddsubfx/version.cpp +++ b/source/native-plugins/zynaddsubfx/version.cpp @@ -15,26 +15,12 @@ #include "zyn-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(); + return os << v.get_major() << '.' + << v.get_minor() << '.' + << v.get_revision(); } static_assert(!(version_type(3,1,1) < version_type(1,3,3)), diff --git a/source/native-plugins/zynaddsubfx/zyn-version.h b/source/native-plugins/zynaddsubfx/zyn-version.h index 04ec26859..4600b8e95 100644 --- a/source/native-plugins/zynaddsubfx/zyn-version.h +++ b/source/native-plugins/zynaddsubfx/zyn-version.h @@ -23,8 +23,15 @@ class version_type char version[3]; // strcmp-like comparison against another version_type - constexpr int v_strcmp(const version_type& v2, int i) const; - + constexpr int 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])); + } + public: constexpr version_type(char maj, char min, char rev) : version{maj, min, rev} @@ -33,7 +40,9 @@ public: //! constructs the current zynaddsubfx version constexpr version_type() : - version_type(2, 4, 4) + version_type(3, + 0, + 1) { } @@ -41,11 +50,14 @@ public: 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]; } + int get_major() const { return version[0]; } + int get_minor() const { return version[1]; } + int get_revision() const { return version[2]; } - constexpr bool operator<(const version_type& other) const; + constexpr bool operator<(const version_type& other) const + { + return v_strcmp(other, 0) < 0; + } //! prints version as .. friend std::ostream& operator<< (std::ostream& os,