| @@ -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 | |||
| @@ -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<uint>(fSampleRate), static_cast<int>(fBufferSize)); | |||
| EffectParams pars(fAllocator.getObject(), false, efxoutl, efxoutr, 0, | |||
| static_cast<uint>(fSampleRate), static_cast<int>(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; | |||
| @@ -17,16 +17,6 @@ | |||
| #include "CarlaDefines.h" | |||
| #ifdef CARLA_OS_WIN | |||
| # include <cmath> | |||
| # define errx(...) {} | |||
| # define warnx(...) {} | |||
| # define index strchr | |||
| # define rindex strrchr | |||
| #else | |||
| # include <err.h> | |||
| #endif | |||
| #define PLUGINVERSION | |||
| #define SOURCE_DIR "/usr/share/zynaddsubfx/examples" | |||
| #undef override | |||
| @@ -17,13 +17,6 @@ | |||
| #include "CarlaPipeUtils.cpp" | |||
| #ifdef CARLA_OS_WIN | |||
| # define errx(...) | |||
| # define warnx(...) | |||
| #else | |||
| # include <err.h> | |||
| #endif | |||
| #define PLUGINVERSION | |||
| #define SOURCE_DIR "/usr/share/zynaddsubfx" | |||
| #undef override | |||
| @@ -18,6 +18,8 @@ | |||
| #include "../Misc/Util.h" | |||
| #include "SVFilter.h" | |||
| #define errx(...) {} | |||
| #define warnx(...) {} | |||
| #ifndef errx | |||
| #include <err.h> | |||
| #endif | |||
| @@ -18,6 +18,8 @@ | |||
| #include "Unison.h" | |||
| #include "globals.h" | |||
| #define errx(...) {} | |||
| #define warnx(...) {} | |||
| #ifndef errx | |||
| #include <err.h> | |||
| #endif | |||
| @@ -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<int>(_Pdelay, 1, MAX_ALIENWAH_DELAY); | |||
| oldl = memory.valloc<complex<float>>(Pdelay); | |||
| oldr = memory.valloc<complex<float>>(Pdelay); | |||
| cleanup(); | |||
| @@ -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"), | |||
| @@ -20,25 +20,55 @@ | |||
| #include <rtosc/port-sugar.h> | |||
| #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 | |||
| @@ -11,6 +11,7 @@ | |||
| of the License, or (at your option) any later version. | |||
| */ | |||
| #include <cassert> | |||
| #include <cmath> | |||
| #include <iostream> | |||
| #include "DynamicFilter.h" | |||
| @@ -20,47 +21,29 @@ | |||
| #include <rtosc/port-sugar.h> | |||
| #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<FilterParams>(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); | |||
| } | |||
| @@ -25,7 +25,8 @@ class DynamicFilter:public Effect | |||
| ~DynamicFilter(); | |||
| void out(const Stereo<float *> &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 | |||
| @@ -12,10 +12,88 @@ | |||
| */ | |||
| #include <cmath> | |||
| #include <rtosc/ports.h> | |||
| #include <rtosc/port-sugar.h> | |||
| #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; i<MAX_EQ_BANDS*MAX_FILTER_STAGES*3; ++i) { | |||
| int stride = MAX_EQ_BANDS*MAX_FILTER_STAGES*3; | |||
| type[i] = type[i+stride] = 'f'; | |||
| val[i].f = b[i]; | |||
| val[i+stride].f = a[i]; | |||
| } | |||
| d.replyArray(d.loc, type, val); | |||
| }}, | |||
| }; | |||
| #undef rObject | |||
| #undef rBegin | |||
| #undef rEnd | |||
| EQ::EQ(EffectParams pars) | |||
| :Effect(pars) | |||
| { | |||
| @@ -31,6 +31,8 @@ class EQ:public Effect | |||
| void getFilter(float *a/*[MAX_EQ_BANDS*MAX_FILTER_STAGES*3]*/, | |||
| float *b/*[MAX_EQ_BANDS*MAX_FILTER_STAGES*3]*/) const; | |||
| static rtosc::Ports ports; | |||
| private: | |||
| //Parameters | |||
| unsigned char Pvolume; | |||
| @@ -41,7 +43,7 @@ class EQ:public Effect | |||
| //parameters | |||
| unsigned char Ptype, Pfreq, Pgain, Pq, Pstages; | |||
| //internal values | |||
| /* TODO | |||
| * The analog filters here really ought to be dumbed down some as | |||
| * you are just looking to do a batch convolution in the end | |||
| @@ -22,14 +22,20 @@ | |||
| #define MAX_DELAY 2 | |||
| #define rObject Echo | |||
| #define rBegin [](const char *, rtosc::RtData &) { | |||
| #define rBegin [](const char *msg, rtosc::RtData &d) { | |||
| #define rEnd } | |||
| rtosc::Ports Echo::ports = { | |||
| {"preset::i", rOptions(Echo 1, Echo 2, Echo 3, Simple Echo, Canyon, Panning Echo 1, Panning Echo 2, Panning Echo 3, Feedback Echo) | |||
| {"preset::i", rOptions(Echo 1, Echo 2, Echo 3, Simple Echo, Canyon, Panning Echo 1, | |||
| Panning Echo 2, Panning Echo 3, Feedback Echo) | |||
| rProp(parameter) | |||
| rDoc("Instrument Presets"), 0, | |||
| rBegin; | |||
| 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(Pdelay, 2, rShort("delay"), "Length of Echo"), | |||
| @@ -18,9 +18,11 @@ | |||
| #include <cmath> | |||
| 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), | |||
| @@ -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, ..)*/ | |||
| @@ -14,6 +14,7 @@ | |||
| #include <rtosc/ports.h> | |||
| #include <rtosc/port-sugar.h> | |||
| #include <iostream> | |||
| #include <cassert> | |||
| #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<name*>(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<DynamicFilter*>(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<DynamicFilter*>(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(); | |||
| @@ -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<int>(Pstages_, 1, MAX_PHASER_STAGES); | |||
| old = Stereo<float *>(memory.valloc<float>(Pstages * 2), | |||
| memory.valloc<float>(Pstages * 2)); | |||
| @@ -21,7 +21,7 @@ | |||
| #include <rtosc/port-sugar.h> | |||
| #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"), | |||
| }; | |||
| @@ -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<std::string> Bank::search(std::string s) const | |||
| } | |||
| return out; | |||
| } | |||
| std::vector<std::string> Bank::blist(std::string s) | |||
| { | |||
| std::vector<std::string> 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) | |||
| { | |||
| @@ -77,6 +77,7 @@ class Bank | |||
| } ins[BANK_SIZE]; | |||
| std::vector<std::string> search(std::string) const; | |||
| std::vector<std::string> blist(std::string); | |||
| private: | |||
| @@ -1,8 +1,10 @@ | |||
| #include "BankDb.h" | |||
| #include "XMLwrapper.h" | |||
| #include "Util.h" | |||
| #include "../globals.h" | |||
| #include <cstring> | |||
| #include <dirent.h> | |||
| #include <sys/stat.h> | |||
| #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; i<n; i++) { | |||
| int good = 1; | |||
| for(int j=0; j<m; ++j) { | |||
| if(toupper(hay[i+j]) != toupper(needle[j])) { | |||
| good = 0; | |||
| break; | |||
| } | |||
| } | |||
| if(good) | |||
| return 1; | |||
| } | |||
| return 0; | |||
| } | |||
| bool sfind(std::string hay, std::string needle) | |||
| { | |||
| return strcasestr(hay.c_str(), needle.c_str()); | |||
| //return strcasestr(hay.c_str(), needle.c_str()); | |||
| return platform_strcasestr(hay.c_str(), needle.c_str()); | |||
| return false; | |||
| } | |||
| bool BankEntry::match(string s) const | |||
| @@ -31,6 +55,11 @@ bool BankEntry::match(string s) const | |||
| sfind(type, s) || sfind(comments,s) || sfind(author,s); | |||
| } | |||
| bool BankEntry::operator<(const BankEntry &b) const | |||
| { | |||
| return this->file < 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; | |||
| @@ -1,6 +1,7 @@ | |||
| #pragma once | |||
| #include <string> | |||
| #include <vector> | |||
| #include <map> | |||
| struct BankEntry | |||
| { | |||
| @@ -15,16 +16,20 @@ struct BankEntry | |||
| bool add; | |||
| bool pad; | |||
| bool sub; | |||
| int time;//last update | |||
| typedef std::vector<std::string> svec; | |||
| svec tags(void) const; | |||
| bool match(std::string) const; | |||
| bool operator<(const BankEntry &b) const; | |||
| }; | |||
| class BankDb | |||
| { | |||
| public: | |||
| typedef std::vector<std::string> svec; | |||
| typedef std::vector<BankEntry> bvec; | |||
| typedef std::vector<std::string> svec; | |||
| typedef std::vector<BankEntry> bvec; | |||
| typedef std::map<std::string,BankEntry> 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; | |||
| }; | |||
| @@ -19,7 +19,7 @@ | |||
| #include <rtosc/port-sugar.h> | |||
| #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; i<MAX_BANK_ROOT_DIRS; ++i) { | |||
| if(c.cfg.favoriteList[i].empty()) { | |||
| c.cfg.favoriteList[i] = rtosc_argument(msg, 0).s; | |||
| return; | |||
| } | |||
| } | |||
| }}, | |||
| {"favorites:", rProp(parameter), 0, | |||
| [](const char *msg, rtosc::RtData &d) | |||
| { | |||
| Config &c = *(Config*)d.obj; | |||
| char *argt = new char[MAX_BANK_ROOT_DIRS+1]; | |||
| rtosc_arg_t *args = new rtosc_arg_t[MAX_BANK_ROOT_DIRS]; | |||
| memset(argt, 0, MAX_BANK_ROOT_DIRS+1); | |||
| int j = 0; | |||
| for(int i=0; i<MAX_BANK_ROOT_DIRS; ++i) { | |||
| if(!c.cfg.favoriteList[i].empty()) { | |||
| argt[j] = 's'; | |||
| args[j].s = c.cfg.favoriteList[i].c_str(); | |||
| j++; | |||
| } | |||
| } | |||
| d.replyArray(d.loc, argt, args); | |||
| delete [] argt; | |||
| delete [] args; | |||
| }}, | |||
| }; | |||
| const rtosc::Ports &Config::ports = ::ports; | |||
| #endif | |||
| @@ -319,6 +350,13 @@ void Config::readConfig(const char *filename) | |||
| cfg.presetsDirList[i] = xmlcfg.getparstr("presets_root", ""); | |||
| xmlcfg.exitbranch(); | |||
| } | |||
| //Get favs | |||
| for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i) | |||
| if(xmlcfg.enterbranch("FAVSROOT", i)) { | |||
| cfg.favoriteList[i] = xmlcfg.getparstr("favoirtes_root", ""); | |||
| xmlcfg.exitbranch(); | |||
| } | |||
| //linux stuff | |||
| xmlcfg.getparstr("linux_oss_wave_out_dev", | |||
| @@ -380,6 +418,13 @@ void Config::saveConfig(const char *filename) const | |||
| xmlcfg->addparstr("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); | |||
| @@ -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; | |||
| @@ -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<BACKEND> '%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<BACKEND:%s> '%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; | |||
| } | |||
| @@ -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]; | |||
| @@ -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<Pmapsize;i++) { | |||
| if (i!=0) | |||
| strncat(buf, "\n", sizeof(buf)-1); | |||
| if (Pmapping[i]==-1) | |||
| snprintf(tmpbuf,100,"x"); | |||
| else | |||
| snprintf(tmpbuf,100,"%d",Pmapping[i]); | |||
| strncat(buf, tmpbuf, sizeof(buf)-1); | |||
| } | |||
| texttomapping(buf); | |||
| } | |||
| { | |||
| char buf[100*MAX_OCTAVE_SIZE] = {0}; | |||
| char tmpbuf[100] = {0}; | |||
| for (int i=0;i<getoctavesize();i++){ | |||
| if (i!=0) | |||
| strncat(buf, "\n", sizeof(buf)-1); | |||
| tuningtoline(i,tmpbuf,100); | |||
| strncat(buf, tmpbuf, sizeof(buf)-1); | |||
| } | |||
| int err = texttotunings(buf); | |||
| } | |||
| } | |||
| @@ -149,6 +149,7 @@ class Microtonal | |||
| //Grab a 0..127 integer from the provided descriptor | |||
| static int linetotunings(struct OctaveTuning &tune, const char *line); | |||
| void apply(void); | |||
| const int& gzip_compression; | |||
| }; | |||
| @@ -17,6 +17,7 @@ | |||
| #include <fstream> | |||
| #include <iostream> | |||
| #include <dirent.h> | |||
| #include <sys/stat.h> | |||
| #include <rtosc/undo-history.h> | |||
| #include <rtosc/thread-link.h> | |||
| @@ -49,11 +50,14 @@ | |||
| #include <atomic> | |||
| #include <list> | |||
| #define errx(...) {} | |||
| #define warnx(...) {} | |||
| #ifndef errx | |||
| #include <err.h> | |||
| #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<void()> read_only_fn); | |||
| void doReadOnlyOpPlugin(std::function<void()> read_only_fn); | |||
| bool doReadOnlyOpNormal(std::function<void()> 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<std::string> getFiles(const char *folder, bool finddir) | |||
| { | |||
| DIR *dir = opendir(folder); | |||
| if(dir == NULL) { | |||
| return {}; | |||
| } | |||
| struct dirent *fn; | |||
| std::vector<string> 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<res.size() && i<MAX_SEARCH; ++i) { | |||
| res_type[i] = 's'; | |||
| res_dat[i].s = res[i].c_str(); | |||
| } | |||
| d.replyArray("/bank/search_results", res_type, res_dat); | |||
| #undef MAX_SEARCH | |||
| rEnd}, | |||
| {"blist:s", 0, 0, | |||
| rBegin; | |||
| auto res = impl.blist(rtosc_argument(msg, 0).s); | |||
| #define MAX_SEARCH 300 | |||
| char res_type[MAX_SEARCH+1] = {0}; | |||
| rtosc_arg_t res_dat[MAX_SEARCH] = {0}; | |||
| for(unsigned i=0; i<res.size() && i<MAX_SEARCH; ++i) { | |||
| @@ -1020,6 +1112,10 @@ const rtosc::Ports bankPorts = { | |||
| d.replyArray("/bank/search_results", res_type, res_dat); | |||
| #undef MAX_SEARCH | |||
| rEnd}, | |||
| {"search_results:", 0, 0, | |||
| rBegin; | |||
| d.reply("/bank/search_results", ""); | |||
| rEnd}, | |||
| }; | |||
| /****************************************************************************** | |||
| @@ -1157,6 +1253,63 @@ static rtosc::Ports middwareSnoopPorts = { | |||
| const char *file = rtosc_argument(msg,1).s; | |||
| impl.savePart(part_id, file); | |||
| rEnd}, | |||
| {"file_home_dir:", 0, 0, | |||
| rBegin; | |||
| const char *home = getenv("PWD"); | |||
| if(!home) | |||
| home = getenv("HOME"); | |||
| if(!home) | |||
| home = getenv("USERPROFILE"); | |||
| if(!home) | |||
| home = getenv("HOMEPATH"); | |||
| if(!home) | |||
| home = "/"; | |||
| string home_ = home; | |||
| #ifndef WIN32 | |||
| if(home_[home_.length()-1] != '/') | |||
| home_ += '/'; | |||
| #endif | |||
| d.reply(d.loc, "s", home_.c_str()); | |||
| rEnd}, | |||
| {"file_list_files:s", 0, 0, | |||
| rBegin; | |||
| const char *folder = rtosc_argument(msg, 0).s; | |||
| auto files = getFiles(folder, false); | |||
| const int N = files.size(); | |||
| rtosc_arg_t *args = new rtosc_arg_t[N]; | |||
| char *types = new char[N+1]; | |||
| types[N] = 0; | |||
| for(int i=0; i<N; ++i) { | |||
| args[i].s = files[i].c_str(); | |||
| types[i] = 's'; | |||
| } | |||
| d.replyArray(d.loc, types, args); | |||
| delete [] types; | |||
| delete [] args; | |||
| rEnd}, | |||
| {"file_list_dirs:s", 0, 0, | |||
| rBegin; | |||
| const char *folder = rtosc_argument(msg, 0).s; | |||
| auto files = getFiles(folder, true); | |||
| const int N = files.size(); | |||
| rtosc_arg_t *args = new rtosc_arg_t[N]; | |||
| char *types = new char[N+1]; | |||
| types[N] = 0; | |||
| for(int i=0; i<N; ++i) { | |||
| args[i].s = files[i].c_str(); | |||
| types[i] = 's'; | |||
| } | |||
| d.replyArray(d.loc, types, args); | |||
| delete [] types; | |||
| delete [] args; | |||
| rEnd}, | |||
| {"reload_auto_save:i", 0, 0, | |||
| rBegin | |||
| const int save_id = rtosc_argument(msg,0).i; | |||
| @@ -1180,10 +1333,12 @@ static rtosc::Ports middwareSnoopPorts = { | |||
| rBegin; | |||
| const char *file = rtosc_argument(msg, 0).s; | |||
| impl.loadMaster(file); | |||
| d.reply("/damage", "s", "/"); | |||
| rEnd}, | |||
| {"reset_master:", 0, 0, | |||
| rBegin; | |||
| impl.loadMaster(NULL); | |||
| d.reply("/damage", "s", "/"); | |||
| rEnd}, | |||
| {"load_xiz:is", 0, 0, | |||
| rBegin; | |||
| @@ -1221,7 +1376,9 @@ static rtosc::Ports middwareSnoopPorts = { | |||
| rEnd}, | |||
| {"part#16/clear:", 0, 0, | |||
| rBegin; | |||
| impl.loadClearPart(extractInt(msg)); | |||
| int id = extractInt(msg); | |||
| impl.loadClearPart(id); | |||
| d.reply("/damage", "s", ("/part"+to_s(id)).c_str()); | |||
| rEnd}, | |||
| {"undo:", 0, 0, | |||
| rBegin; | |||
| @@ -1240,16 +1397,20 @@ static rtosc::Ports middwareSnoopPorts = { | |||
| #define MAX_MIDI 32 | |||
| rtosc_arg_t args[MAX_MIDI*4]; | |||
| char argt[MAX_MIDI*4+1] = {0}; | |||
| int j=0; | |||
| for(unsigned i=0; i<key.size() && i<MAX_MIDI; ++i) { | |||
| auto val = midi.inv_map[key[i]]; | |||
| argt[4*i+0] = 'i'; | |||
| args[4*i+0].i = std::get<1>(val); | |||
| argt[4*i+1] = 's'; | |||
| args[4*i+1].s = key[i].c_str(); | |||
| argt[4*i+2] = 'i'; | |||
| args[4*i+2].i = 0; | |||
| argt[4*i+3] = 'i'; | |||
| args[4*i+3].i = 127; | |||
| 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<void()> 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<void()> 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<void()> read_only_fn, bool canfail) | |||
| { | |||
| assert(uToB); | |||
| uToB->write("/freeze_state",""); | |||
| std::list<const char *> 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) { | |||
| @@ -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; | |||
| } | |||
| @@ -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)); | |||
| @@ -29,6 +29,7 @@ | |||
| #include <sched.h> | |||
| #endif | |||
| #define errx(...) {} | |||
| #ifndef errx | |||
| #include <err.h> | |||
| #endif | |||
| @@ -20,6 +20,9 @@ | |||
| #include <algorithm> | |||
| #include <set> | |||
| #include <rtosc/ports.h> | |||
| #include <rtosc/port-sugar.h> | |||
| using std::min; | |||
| using std::max; | |||
| @@ -102,11 +102,11 @@ XMLwrapper::XMLwrapper() | |||
| node = root = addparams("ZynAddSubFX-data", 4, | |||
| "version-major", stringFrom<int>( | |||
| version.major()).c_str(), | |||
| version.get_major()).c_str(), | |||
| "version-minor", stringFrom<int>( | |||
| version.minor()).c_str(), | |||
| version.get_minor()).c_str(), | |||
| "version-revision", | |||
| stringFrom<int>(version.revision()).c_str(), | |||
| stringFrom<int>(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<int>(mxmlElementGetAttr(root, "version-major"))); | |||
| fileversion.set_minor(stringTo<int>(mxmlElementGetAttr(root, "version-minor"))); | |||
| fileversion.set_revision( | |||
| _fileversion.set_major(stringTo<int>(mxmlElementGetAttr(root, "version-major"))); | |||
| _fileversion.set_minor(stringTo<int>(mxmlElementGetAttr(root, "version-minor"))); | |||
| _fileversion.set_revision( | |||
| stringTo<int>(mxmlElementGetAttr(root, "version-revision"))); | |||
| if(verbose) | |||
| cout << "loadXMLfile() version: " << 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; | |||
| @@ -16,7 +16,7 @@ | |||
| #include <mxml.h> | |||
| #include <string> | |||
| #include <vector> | |||
| #include "../zyn-version.h" | |||
| #include "zyn-version.h" | |||
| #ifndef XML_WRAPPER_H | |||
| #define XML_WRAPPER_H | |||
| @@ -233,6 +233,10 @@ class XMLwrapper | |||
| std::vector<XmlNode> 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; /**<all xml data*/ | |||
| mxml_node_t *root; /**<xml data used by zynaddsubfx*/ | |||
| mxml_node_t *node; /**<current subtree in parsing or writing */ | |||
| @@ -272,8 +281,8 @@ class XMLwrapper | |||
| */ | |||
| mxml_node_t *addparams(const char *name, unsigned int params, | |||
| ...) const; | |||
| version_type fileversion; | |||
| public: | |||
| version_type _fileversion; | |||
| }; | |||
| #endif | |||
| @@ -105,7 +105,15 @@ void *AlsaEngine::MidiThread(void) | |||
| snd_seq_event_t *event; | |||
| MidiEvent ev; | |||
| set_realtime(); | |||
| while(snd_seq_event_input(midi.handle, &event) > 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; | |||
| } | |||
| @@ -56,6 +56,7 @@ class AlsaEngine:public AudioOut, MidiIn | |||
| std::string device; | |||
| snd_seq_t *handle; | |||
| int alsaId; | |||
| bool exiting; | |||
| pthread_t pThread; | |||
| } midi; | |||
| @@ -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; | |||
| @@ -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; | |||
| @@ -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"), | |||
| @@ -17,6 +17,7 @@ | |||
| #include <rtosc/ports.h> | |||
| #include <rtosc/port-sugar.h> | |||
| #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; i<N; ++i) { | |||
| args[i].f = env->getdt(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; i<N; ++i) { | |||
| args[i].f = env->getdt(i); | |||
| arg_types[i] = 'f'; | |||
| } | |||
| d.replyArray(d.loc, arg_types, args); | |||
| } else { | |||
| for(int i=0; i<N && i<M; ++i) | |||
| env->Penvdt[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; i<N; ++i) { | |||
| args[i].f = env->Penvval[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; i<N; ++i) { | |||
| args[i].f = env->Penvval[i]/127.0f; | |||
| arg_types[i] = 'f'; | |||
| } | |||
| d.replyArray(d.loc, arg_types, args); | |||
| } else { | |||
| for(int i=0; i<N && i<M; ++i) { | |||
| env->Penvval[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(); | |||
| } | |||
| @@ -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(); | |||
| @@ -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)}, | |||
| @@ -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; | |||
| @@ -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; | |||
| @@ -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 | |||
| @@ -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; | |||
| @@ -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); | |||
| @@ -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 | |||
| @@ -27,8 +27,6 @@ | |||
| #include <rtosc/ports.h> | |||
| #include <rtosc/port-sugar.h> | |||
| 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) | |||
| @@ -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), | |||
| @@ -69,6 +69,11 @@ WatchManager::WatchManager(thrlnk *link) | |||
| void WatchManager::add_watch(const char *id) | |||
| { | |||
| //Don't add duplicate watchs | |||
| for(int i=0; i<MAX_WATCH; ++i) | |||
| if(!strcmp(active_list[i], id)) | |||
| return; | |||
| //Apply to a free slot | |||
| for(int i=0; i<MAX_WATCH; ++i) { | |||
| if(!active_list[i][0]) { | |||
| @@ -27,7 +27,7 @@ void raiseUi(ui_handle_t, const char *, const char *, ...) | |||
| } | |||
| void tickUi(ui_handle_t) | |||
| { | |||
| usleep(100000); | |||
| usleep(1000); | |||
| } | |||
| Fl_Osc_Interface *genOscInterface(MiddleWare*) | |||
| { | |||
| @@ -445,7 +445,7 @@ if (filterwindow!=NULL){ | |||
| class Fl_Osc_Check | |||
| } | |||
| Fl_Check_Button chorusp11 { | |||
| label Substract | |||
| label Subtract | |||
| tooltip {inverts the output} xywh {185 10 70 20} box THIN_UP_BOX down_box DOWN_BOX color 51 labelsize 10 | |||
| code0 {o->init("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 | |||
| @@ -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} | |||
| @@ -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(); | |||
| }; | |||
| @@ -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())); | |||
| } | |||
| @@ -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 {} { | |||
| @@ -22,7 +22,7 @@ | |||
| #include "../Nio/Nio.h" | |||
| #ifndef NO_UI | |||
| #if defined(FLTK_UI) || defined(NTK_UI) | |||
| #include <FL/Fl.H> | |||
| #endif | |||
| #include <cstdio> | |||
| @@ -30,9 +30,10 @@ | |||
| #include <sys/stat.h> | |||
| #include <sys/types.h> | |||
| #include <unistd.h> | |||
| #include <stdlib.h> | |||
| 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<MasterUI*>(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 | |||
| @@ -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: | |||
| @@ -46,7 +46,7 @@ bool fileexists(const char *filename) | |||
| return false; | |||
| } | |||
| int Pexitprogram = 0; | |||
| int Pexitprogram=0; | |||
| #include "Connection.h" | |||
| @@ -17,15 +17,17 @@ | |||
| #include <map> | |||
| #include <cmath> | |||
| #include <cctype> | |||
| #include <ctime> | |||
| #include <algorithm> | |||
| #include <signal.h> | |||
| #include <err.h> | |||
| #include <unistd.h> | |||
| #include <pthread.h> | |||
| #include <getopt.h> | |||
| #include <lo/lo.h> | |||
| #include <rtosc/rtosc.h> | |||
| #include <rtosc/ports.h> | |||
| #include <rtosc/thread-link.h> | |||
| #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 <windows.h> | |||
| #include <mmsystem.h> | |||
| 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<const char *> 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); | |||
| @@ -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) | |||
| { | |||
| @@ -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) | |||
| @@ -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); | |||
| }; | |||
| @@ -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)), | |||
| @@ -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 <major>.<minor>.<revision> | |||
| friend std::ostream& operator<< (std::ostream& os, | |||