diff --git a/source/native-plugins/zynaddsubfx-src.cpp b/source/native-plugins/zynaddsubfx-src.cpp index 1e4d19992..ef711ff72 100644 --- a/source/native-plugins/zynaddsubfx-src.cpp +++ b/source/native-plugins/zynaddsubfx-src.cpp @@ -68,6 +68,7 @@ extern "C" { #include "zynaddsubfx/rtosc/cpp/undo-history.cpp" // zynaddsubfx includes +#include "zynaddsubfx/Containers/NotePool.cpp" #include "zynaddsubfx/DSP/AnalogFilter.cpp" #include "zynaddsubfx/DSP/FFTwrapper.cpp" #include "zynaddsubfx/DSP/Filter.cpp" diff --git a/source/native-plugins/zynaddsubfx-ui.cpp b/source/native-plugins/zynaddsubfx-ui.cpp index a78cd5b3d..c6daced98 100644 --- a/source/native-plugins/zynaddsubfx-ui.cpp +++ b/source/native-plugins/zynaddsubfx-ui.cpp @@ -91,6 +91,7 @@ extern "C" { #include "zynaddsubfx/UI/Fl_Osc_Pane.cpp" #include "zynaddsubfx/UI/Fl_Osc_Roller.cpp" #include "zynaddsubfx/UI/Fl_Osc_Slider.cpp" +#include "zynaddsubfx/UI/Fl_Osc_TSlider.cpp" #include "zynaddsubfx/UI/Fl_Osc_Value.cpp" #include "zynaddsubfx/UI/Fl_Osc_VSlider.cpp" #include "zynaddsubfx/UI/Fl_Osc_Widget.cpp" @@ -106,6 +107,7 @@ extern "C" { #include "zynaddsubfx/UI/PresetsUI.cpp" #include "zynaddsubfx/UI/ResonanceUI.cpp" #include "zynaddsubfx/UI/SUBnoteUI.cpp" +#include "zynaddsubfx/UI/TipWin.cpp" #include "zynaddsubfx/UI/VirKeyboard.cpp" #include "zynaddsubfx/UI/guimain.cpp" diff --git a/source/native-plugins/zynaddsubfx/Containers/NotePool.cpp b/source/native-plugins/zynaddsubfx/Containers/NotePool.cpp new file mode 100644 index 000000000..d33d1668a --- /dev/null +++ b/source/native-plugins/zynaddsubfx/Containers/NotePool.cpp @@ -0,0 +1,285 @@ +#include "NotePool.h" +//XXX eliminate dependence on Part.h +#include "../Misc/Part.h" +#include "../Misc/Allocator.h" +#include "../Synth/SynthNote.h" +#include +#include + +NotePool::NotePool(void) + :needs_cleaning(0) +{ + memset(ndesc, 0, sizeof(ndesc)); + memset(sdesc, 0, sizeof(ndesc)); +} +NotePool::activeNotesIter NotePool::activeNotes(NoteDescriptor &n) +{ + const int off_d1 = &n-ndesc; + int off_d2 = 0; + assert(off_d1 <= POLYPHONY); + for(int i=0; i(this)->cleanup(); + return constActiveDescIter{*this}; +} + +void NotePool::insertNote(uint8_t note, uint8_t sendto, SynthDescriptor desc, bool legato) +{ + //Get first free note descriptor + int desc_id = getMergeableDescriptor(note, sendto, legato, ndesc); + assert(desc_id != -1); + + ndesc[desc_id].note = note; + ndesc[desc_id].sendto = sendto; + ndesc[desc_id].size += 1; + ndesc[desc_id].status = Part::KEY_PLAYING; + ndesc[desc_id].legatoMirror = legato; + + //Get first free synth descriptor + int sdesc_id = 0; + while(sdesc[sdesc_id].note) + sdesc_id++; + sdesc[sdesc_id] = desc; +}; + +void NotePool::upgradeToLegato(void) +{ + for(auto &d:activeDesc()) + if(d.status == Part::KEY_PLAYING) + for(auto &s:activeNotes(d)) + insertLegatoNote(d.note, d.sendto, s); +} + +void NotePool::insertLegatoNote(uint8_t note, uint8_t sendto, SynthDescriptor desc) +{ + assert(desc.note); + desc.note = desc.note->cloneLegato(); + insertNote(note, sendto, desc, true); +}; + +//There should only be one pair of notes which are still playing +void NotePool::applyLegato(LegatoParams &par) +{ + for(auto &desc:activeDesc()) { + desc.note = par.midinote; + for(auto &synth:activeNotes(desc)) + synth.note->legatonote(par); + } +}; + +//Note that isn't KEY_PLAYING or KEY_RELASED_AND_SUSTAINING +bool NotePool::existsRunningNote(void) const +{ + //printf("runing note # =%d\n", getRunningNotes()); + return getRunningNotes(); +} + +int NotePool::getRunningNotes(void) const +{ + bool running[256] = {0}; + for(auto &desc:activeDesc()) { + //printf("note!(%d)\n", desc.note); + if(desc.status == Part::KEY_PLAYING) + running[desc.note] = true; + } + + int running_count = 0; + for(int i=0; i<256; ++i) + running_count += running[i]; + + return running_count; +} +int NotePool::enforceKeyLimit(int limit) const +{ + //{ + //int oldestnotepos = -1; + //if(notecount > keylimit) //find out the oldest note + // for(int i = 0; i < POLYPHONY; ++i) { + // int maxtime = 0; + // if(((partnote[i].status == KEY_PLAYING) || (partnote[i].status == KEY_RELEASED_AND_SUSTAINED)) && (partnote[i].time > maxtime)) { + // maxtime = partnote[i].time; + // oldestnotepos = i; + // } + // } + //if(oldestnotepos != -1) + // ReleaseNotePos(oldestnotepos); + //} + printf("Unimplemented enforceKeyLimit()\n"); + return -1; +} + +void NotePool::releasePlayingNotes(void) +{ + for(auto &d:activeDesc()) { + if(d.status == Part::KEY_PLAYING) { + d.status = Part::KEY_RELEASED; + for(auto s:activeNotes(d)) + s.note->releasekey(); + } + } +} + +void NotePool::release(NoteDescriptor &d) +{ + d.status = Part::KEY_RELEASED; + for(auto s:activeNotes(d)) + s.note->releasekey(); +} + +void NotePool::killAllNotes(void) +{ + for(auto &d:activeDesc()) + kill(d); +} + +void NotePool::killNote(uint8_t note) +{ + for(auto &d:activeDesc()) { + if(d.note == note) + kill(d); + } +} + +void NotePool::kill(NoteDescriptor &d) +{ + d.status = Part::KEY_OFF; + for(auto &s:activeNotes(d)) + kill(s); +} + +void NotePool::kill(SynthDescriptor &s) +{ + //printf("Kill synth...\n"); + s.note->memory.dealloc(s.note); + needs_cleaning = true; +} + +const char *getStatus(int status_bits) +{ + switch(status_bits) + { + case 0: return "OFF "; + case 1: return "PLAY"; + case 2: return "SUST"; + case 3: return "RELA"; + default: return "INVD"; + } +} + +void NotePool::cleanup(void) +{ + if(!needs_cleaning) + return; + needs_cleaning = false; + int new_length[POLYPHONY] = {0}; + int cur_length[POLYPHONY] = {0}; + //printf("Cleanup Start\n"); + //dump(); + + //Identify the current length of all segments + //and the lengths discarding invalid entries + + int last_valid_desc = 0; + for(int i=0; iNotePool::dump\n"); +} diff --git a/source/native-plugins/zynaddsubfx/Containers/NotePool.h b/source/native-plugins/zynaddsubfx/Containers/NotePool.h new file mode 100644 index 000000000..91e3f633e --- /dev/null +++ b/source/native-plugins/zynaddsubfx/Containers/NotePool.h @@ -0,0 +1,113 @@ +#pragma once +#include +#include +#include "../globals.h" + +//Expected upper bound of synths given that max polyphony is hit +#define EXPECTED_USAGE 3 + +struct LegatoParams; +class NotePool +{ + public: + typedef uint8_t note_t; + //Currently this wastes a ton of bits due ot the legatoMirror flag + struct NoteDescriptor { + //acceptable overlap after 2 minutes + //run time at 48kHz 8 samples per buffer + //19 bit minimum + uint32_t age; + uint8_t note; + uint8_t sendto; + //max of 16 kit elms and 3 kit items per + uint8_t size; + uint8_t status; + bool legatoMirror; + bool operator==(NoteDescriptor); + }; + + //To be pedantic this wastes 2 or 6 bytes per descriptor + //depending on 32bit/64bit alignment rules + struct SynthDescriptor { + SynthNote *note; + uint8_t type; + uint8_t kit; + }; + + + //Pool of notes + NoteDescriptor ndesc[POLYPHONY]; + SynthDescriptor sdesc[POLYPHONY*EXPECTED_USAGE]; + bool needs_cleaning; + + + //Iterators + struct activeNotesIter { + SynthDescriptor *begin() {return _b;}; + SynthDescriptor *end() {return _e;}; + SynthDescriptor *_b; + SynthDescriptor *_e; + }; + + struct activeDescIter { + activeDescIter(NotePool &_np):np(_np) + { + int off=0; + for(int i=0; igetgain()); oldinput = -1.0f; - Qfactor = 1.0f; + Qfactor = pars->getq(); oldQfactor = Qfactor; firsttime = 1; } diff --git a/source/native-plugins/zynaddsubfx/Effects/EffectMgr.cpp b/source/native-plugins/zynaddsubfx/Effects/EffectMgr.cpp index 72f8bda2a..a4553225e 100644 --- a/source/native-plugins/zynaddsubfx/Effects/EffectMgr.cpp +++ b/source/native-plugins/zynaddsubfx/Effects/EffectMgr.cpp @@ -45,7 +45,8 @@ static const rtosc::Ports local_ports = { rSelf(EffectMgr), rPaste, rRecurp(filterpars, "Filter Parameter for Dynamic Filter"), - {"parameter#128::i", rProp(alias) rDoc("Parameter Accessor"), NULL, + {"parameter#128::i:T:F", rProp(parameter) rProp(alias) rDoc("Parameter Accessor"), + NULL, [](const char *msg, rtosc::RtData &d) { EffectMgr *eff = (EffectMgr*)d.obj; @@ -54,20 +55,37 @@ static const rtosc::Ports local_ports = { if(!rtosc_narguments(msg)) d.reply(d.loc, "i", eff->geteffectparrt(atoi(mm))); - else { + else if(rtosc_type(msg, 0) == 'i'){ eff->seteffectparrt(atoi(mm), rtosc_argument(msg, 0).i); d.broadcast(d.loc, "i", eff->geteffectparrt(atoi(mm))); + } else if(rtosc_type(msg, 0) == 'T'){ + eff->seteffectparrt(atoi(mm), 127); + d.broadcast(d.loc, "i", eff->geteffectparrt(atoi(mm))); + } else if(rtosc_type(msg, 0) == 'F'){ + eff->seteffectparrt(atoi(mm), 0); + d.broadcast(d.loc, "i", eff->geteffectparrt(atoi(mm))); } }}, - {"preset::i", rProp(alias) rDoc("Effect Preset Selector"), NULL, + {"preset::i", rProp(parameter) rProp(alias) rDoc("Effect Preset Selector"), NULL, [](const char *msg, rtosc::RtData &d) { + char loc[1024]; EffectMgr *eff = (EffectMgr*)d.obj; if(!rtosc_narguments(msg)) d.reply(d.loc, "i", eff->getpreset()); else { eff->changepresetrt(rtosc_argument(msg, 0).i); d.broadcast(d.loc, "i", eff->getpreset()); + + //update parameters as well + strncpy(loc, d.loc, 1024); + char *tail = rindex(loc, '/'); + if(!tail) + return; + for(int i=0;i<128;++i) { + sprintf(tail+1, "parameter%d", i); + d.broadcast(loc, "i", eff->geteffectparrt(i)); + } } }}, {"eq-coeffs:", rProp(internal) rDoc("Get equalizer Coefficients"), NULL, @@ -84,7 +102,8 @@ static const rtosc::Ports local_ports = { eq->getFilter(a,b); d.reply(d.loc, "bb", sizeof(a), a, sizeof(b), b); }}, - {"efftype::i", rDoc("Get Effect Type"), NULL, [](const char *m, rtosc::RtData &d) + {"efftype::i", rProp(parameter) rDoc("Get Effect Type"), NULL, + [](const char *m, rtosc::RtData &d) { EffectMgr *eff = (EffectMgr*)d.obj; if(rtosc_narguments(m)) { @@ -385,7 +404,7 @@ void EffectMgr::paste(EffectMgr &e) changeeffectrt(e.nefx, true); changepresetrt(e.preset, true); for(int i=0;i<128;++i) - seteffectparrt(e.settings[i], i); + seteffectparrt(i, e.settings[i]); } void EffectMgr::add2XML(XMLwrapper *xml) diff --git a/source/native-plugins/zynaddsubfx/Effects/EffectMgr.h b/source/native-plugins/zynaddsubfx/Effects/EffectMgr.h index a7b53f53c..cc9968202 100644 --- a/source/native-plugins/zynaddsubfx/Effects/EffectMgr.h +++ b/source/native-plugins/zynaddsubfx/Effects/EffectMgr.h @@ -80,7 +80,6 @@ class EffectMgr:public Presets private: //Parameters Prior to initialization - char effect_id; char preset; char settings[128]; diff --git a/source/native-plugins/zynaddsubfx/Misc/Allocator.h b/source/native-plugins/zynaddsubfx/Misc/Allocator.h index c002b2c90..1f0d0d34a 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Allocator.h +++ b/source/native-plugins/zynaddsubfx/Misc/Allocator.h @@ -86,12 +86,14 @@ class Allocator //! the allocator for normal use class AllocatorClass : public Allocator { - void *alloc_mem(size_t mem_size); - void dealloc_mem(void *memory); - void addMemory(void *, size_t mem_size); - bool lowMemory(unsigned n, size_t chunk_size) const; - using Allocator::Allocator; + public: + void *alloc_mem(size_t mem_size); + void dealloc_mem(void *memory); + void addMemory(void *, size_t mem_size); + bool lowMemory(unsigned n, size_t chunk_size) const; + using Allocator::Allocator; }; +typedef AllocatorClass Alloc; //! the dummy allocator, which does not allow any allocation class DummyAllocator : public Allocator diff --git a/source/native-plugins/zynaddsubfx/Misc/Config.cpp b/source/native-plugins/zynaddsubfx/Misc/Config.cpp index e98a5c0a1..74535697d 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Config.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Config.cpp @@ -32,7 +32,7 @@ #include "XMLwrapper.h" #define rStdString(name, len, ...) \ - {STRINGIFY(name) "::s", rMap(length, len) DOC(__VA_ARGS__), NULL, rStringCb(name,len)} + {STRINGIFY(name) "::s", rMap(length, len) rProp(parameter) DOC(__VA_ARGS__), NULL, rStringCb(name,len)} #define rStdStringCb(name, length) rBOIL_BEGIN \ if(!strcmp("", args)) {\ data.reply(loc, "s", obj->name); \ @@ -57,7 +57,7 @@ static const rtosc::Ports ports = { rToggle(cfg.BankUIAutoClose, "Automatic Closing of BackUI After Patch Selection"), rParamI(cfg.GzipCompression, "Level of Gzip Compression For Save Files"), rParamI(cfg.Interpolation, "Level of Interpolation, Linear/Cubic"), - {"cfg.presetsDirList", rProp(parameter) rDoc("list of preset search directories"), 0, + {"cfg.presetsDirList", rDoc("list of preset search directories"), 0, [](const char *msg, rtosc::RtData &d) { Config &c = *(Config*)d.obj; @@ -90,7 +90,7 @@ static const rtosc::Ports ports = { rtosc_amessage(buffer, sizeof(buffer), d.loc, types, args); d.reply(buffer); }}, - {"cfg.bankRootDirList", rProp(parameter) rDoc("list of bank search directories"), 0, + {"cfg.bankRootDirList", rDoc("list of bank search directories"), 0, [](const char *msg, rtosc::RtData &d) { Config &c = *(Config*)d.obj; @@ -133,7 +133,7 @@ static const rtosc::Ports ports = { rParamI(cfg.VirKeybLayout, "Keyboard Layout For Virtual Piano Keyboard"), //rParamS(cfg.LinuxALSAaudioDev), //rParamS(cfg.nameTag) - {"cfg.OscilPower::i", rDoc("Size Of Oscillator Wavetable"), 0, + {"cfg.OscilPower::i", rProp(parameter) rDoc("Size Of Oscillator Wavetable"), 0, [](const char *msg, rtosc::RtData &d) { Config &c = *(Config*)d.obj; diff --git a/source/native-plugins/zynaddsubfx/Misc/Master.cpp b/source/native-plugins/zynaddsubfx/Misc/Master.cpp index 3f739c189..2e10dbe4f 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Master.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Master.cpp @@ -53,7 +53,9 @@ using namespace rtosc; static const Ports sysefxPort = { - {"part#" STRINGIFY(NUM_MIDI_PARTS) "::i", 0, 0, [](const char *m, RtData&d) + {"part#" STRINGIFY(NUM_MIDI_PARTS) "::i", rProp(parameter) + rDoc("gain on part to sysefx routing"), 0, + [](const char *m, RtData&d) { //ok, this is going to be an ugly workaround //we know that if we are here the message previously MUST have @@ -80,7 +82,8 @@ static const Ports sysefxPort = static const Ports sysefsendto = { - {"to#" STRINGIFY(NUM_SYS_EFX) "::i", 0, 0, [](const char *m, RtData&d) + {"to#" STRINGIFY(NUM_SYS_EFX) "::i", + rProp(parameter) rDoc("sysefx to sysefx routing gain"), 0, [](const char *m, RtData&d) { //same ugly workaround as before const char *index_1 = m; @@ -103,6 +106,7 @@ static const Ports sysefsendto = }; static const Ports master_ports = { + rString(last_xmz, XMZ_PATH_MAX, "File name for last name loaded if any."), rRecursp(part, 16, "Part"),//NUM_MIDI_PARTS rRecursp(sysefx, 4, "System Effect"),//NUM_SYS_EFX rRecursp(insefx, 8, "Insertion Effect"),//NUM_INS_EFX @@ -112,10 +116,10 @@ static const Ports master_ports = { rArrayI(Pinsparts, NUM_INS_EFX, "Part to insert part onto"), {"echo", rDoc("Hidden port to echo messages"), 0, [](const char *m, RtData&d) { d.reply(m-1);}}, - {"get-vu", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) { + {"get-vu:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) { Master *m = (Master*)d.obj; d.reply("/vu-meter", "bb", sizeof(m->vu), &m->vu, sizeof(float)*NUM_MIDI_PARTS, m->vuoutpeakpart);}}, - {"reset-vu", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) { + {"reset-vu:", rDoc("Grab VU Data"), 0, [](const char *, RtData &d) { Master *m = (Master*)d.obj; m->vuresetpeaks();}}, {"load-part:ib", rProp(internal) rDoc("Load Part From Middleware"), 0, [](const char *msg, RtData &d) { @@ -129,14 +133,14 @@ static const Ports master_ports = { p->initialize_rt(); //printf("part %d is now pointer %p\n", i, p); }}, - {"Pvolume::i", rDoc("Master Volume"), 0, + {"Pvolume::i", rProp(parameter) rDoc("Master Volume"), 0, [](const char *m, rtosc::RtData &d) { if(rtosc_narguments(m)==0) { d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume); } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') { ((Master*)d.obj)->setPvolume(limit(rtosc_argument(m,0).i,0,127)); d.broadcast(d.loc, "i", ((Master*)d.obj)->Pvolume);}}}, - {"volume::i", rDoc("Master Volume"), 0, + {"volume::i", rProp(parameter) rDoc("Master Volume"), 0, [](const char *m, rtosc::RtData &d) { if(rtosc_narguments(m)==0) { d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume); @@ -200,7 +204,8 @@ static const Ports master_ports = { [](const char *m, RtData &d){ Master *M = (Master*)d.obj; M->midi.clear_entry(rtosc_argument(m,0).s);}}, - {"close-ui", rDoc("Request to close any connection named \"GUI\""), 0, [](const char *, RtData &d) { + {"close-ui:", rDoc("Request to close any connection named \"GUI\""), 0, + [](const char *, RtData &d) { d.reply("/close-ui", "");}}, {"add-rt-memory:bi", rProp(internal) rDoc("Add Additional Memory To RT MemPool"), 0, [](const char *msg, RtData &d) @@ -220,12 +225,13 @@ static const Ports master_ports = { d.reply("/oscilsize", "f", m.synth.oscilsize_f); d.reply("/oscilsize", "i", m.synth.oscilsize); }}, - {"undo_pause",0,0,[](const char *, rtosc::RtData &d) - {d.reply("/undo_pause", "");}}, - {"undo_resume",0,0,[](const char *, rtosc::RtData &d) - {d.reply("/undo_resume", "");}}, - {"config/", 0, &Config::ports, [](const char *, rtosc::RtData &){}}, - {"presets/", 0, &preset_ports, rBOIL_BEGIN + {"undo_pause:",rProp(internal) rDoc("pause undo event recording"),0, + [](const char *, rtosc::RtData &d) {d.reply("/undo_pause", "");}}, + {"undo_resume:",rProp(internal) rDoc("resume undo event recording"),0, + [](const char *, rtosc::RtData &d) {d.reply("/undo_resume", "");}}, + {"config/", rDoc("Top Level Application Configuration Parameters"), &Config::ports, + [](const char *, rtosc::RtData &){}}, + {"presets/", rDoc("Parameter Presets"), &preset_ports, rBOIL_BEGIN SNIP preset_ports.dispatch(msg, data); rBOIL_END}, @@ -292,7 +298,7 @@ Master::Master(const SYNTH_T &synth_, Config* config) :HDDRecorder(synth_), ctl(synth_), microtonal(config->cfg.GzipCompression), bank(config), midi(Master::ports), frozenState(false), pendingMemory(false), - synth(synth_), gzip_compression(config->cfg.GzipCompression) + synth(synth_), time(synth), gzip_compression(config->cfg.GzipCompression) { bToU = NULL; uToB = NULL; @@ -306,6 +312,7 @@ Master::Master(const SYNTH_T &synth_, Config* config) the_master = this; #endif + last_xmz[0] = 0; fft = new FFTwrapper(synth.oscilsize); shutup = 0; @@ -315,7 +322,7 @@ Master::Master(const SYNTH_T &synth_, Config* config) } for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) - part[npart] = new Part(*memory, synth, config->cfg.GzipCompression, + part[npart] = new Part(*memory, synth, time, config->cfg.GzipCompression, config->cfg.Interpolation, µtonal, fft); //Insertion Effects init @@ -819,8 +826,8 @@ void Master::AudioOut(float *outl, float *outr) ShutUp(); } - //update the LFO's time - LFOParams::time++; + //update the global frame timer + time++; } //TODO review the respective code from yoshimi for this @@ -1056,7 +1063,6 @@ int Master::saveXML(const char *filename) } - int Master::loadXML(const char *filename) { XMLwrapper *xml = new XMLwrapper(); diff --git a/source/native-plugins/zynaddsubfx/Misc/Master.h b/source/native-plugins/zynaddsubfx/Misc/Master.h index ee47436a6..aa9632b41 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Master.h +++ b/source/native-plugins/zynaddsubfx/Misc/Master.h @@ -28,6 +28,7 @@ #include #include +#include "Time.h" #include "Bank.h" #include "Recorder.h" @@ -53,6 +54,8 @@ class Master /** Destructor*/ ~Master(); + char last_xmz[XMZ_PATH_MAX]; + void applyOscEvent(const char *event); /**Saves all settings to a XML file @@ -64,7 +67,6 @@ class Master void defaults(); - /**loads all settings from a XML file * @return 0 for ok or -1 if there is an error*/ int loadXML(const char *filename); @@ -99,7 +101,8 @@ class Master /**Audio Output*/ void AudioOut(float *outl, float *outr) REALTIME; - /**Audio Output (for callback mode). This allows the program to be controled by an external program*/ + /**Audio Output (for callback mode). + * This allows the program to be controled by an external program*/ void GetAudioOutSamples(size_t nsamples, unsigned samplerate, float *outl, @@ -171,6 +174,7 @@ class Master rtosc::ThreadLink *uToB; bool pendingMemory; const SYNTH_T &synth; + AbsTime time; const int& gzip_compression; //!< value from config private: float sysefxvol[NUM_SYS_EFX][NUM_MIDI_PARTS]; diff --git a/source/native-plugins/zynaddsubfx/Misc/Microtonal.cpp b/source/native-plugins/zynaddsubfx/Misc/Microtonal.cpp index 9ee39f7b1..3e61ad1c7 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Microtonal.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Microtonal.cpp @@ -67,7 +67,7 @@ const rtosc::Ports Microtonal::ports = { rString(Pname, MICROTONAL_MAX_NAME_LEN, "Microtonal Name"), rString(Pcomment, MICROTONAL_MAX_NAME_LEN, "Microtonal Name"), - {"octavesize:", 0, 0, [](const char*, RtData &d) + {"octavesize:", rDoc("Get octave size"), 0, [](const char*, RtData &d) { Microtonal &m = *(Microtonal*)d.obj; d.reply(d.loc, "i", m.getoctavesize()); diff --git a/source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp b/source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp index 5f41371f1..631ef1f96 100644 --- a/source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp @@ -236,6 +236,11 @@ void rescanForBanks(Bank &bank, std::function cb) void loadBank(Bank &bank, int pos, std::function cb) { + char response[2048]; + if(!rtosc_message(response, 2048, "/loadbank", "i", pos)) + errx(1, "Failure to handle bank update properly..."); + if(cb) + cb(response); if(bank.bankpos != pos) { bank.bankpos = pos; bank.loadbank(bank.banks[pos].dir); @@ -433,13 +438,13 @@ namespace Nio using std::get; using rtosc::rtMsg; rtosc::Ports ports = { - {"sink-list:", 0, 0, [](const char *msg, rtosc::RtData &d) { + {"sink-list:", 0, 0, [](const char *, rtosc::RtData &d) { auto list = Nio::getSinks(); char *ret = rtosc_splat(d.loc, list); d.reply(ret); delete [] ret; }}, - {"source-list:", 0, 0, [](const char *msg, rtosc::RtData &d) { + {"source-list:", 0, 0, [](const char *, rtosc::RtData &d) { auto list = Nio::getSources(); char *ret = rtosc_splat(d.loc, list); d.reply(ret); @@ -489,7 +494,7 @@ public: void doReadOnlyOp(std::function read_only_fn); - void saveBankSlot(int npart, int nslot, Master *master, Fl_Osc_Interface *osc) + void saveBankSlot(int npart, int nslot, Master *master) { int err = 0; doReadOnlyOp([master,nslot,npart,&err](){ @@ -502,7 +507,7 @@ public: } } - void renameBankSlot(int slot, string name, Master *master, Fl_Osc_Interface *osc) + void renameBankSlot(int slot, string name, Master *master) { int err = master->bank.setname(slot, name, -1); if(err) { @@ -513,7 +518,7 @@ public: } } - void swapBankSlot(int slota, int slotb, Master *master, Fl_Osc_Interface *osc) + void swapBankSlot(int slota, int slotb, Master *master) { int err = master->bank.swapslot(slota, slotb); if(err) { @@ -524,7 +529,7 @@ public: } } - void clearBankSlot(int slot, Master *master, Fl_Osc_Interface *osc) + void clearBankSlot(int slot, Master *master) { int err = master->bank.clearslot(slot); if(err) { @@ -557,7 +562,14 @@ public: /*printf("results: '%s' '%d'\n",fname.c_str(), res);*/}); } - void loadPart(int npart, const char *filename, Master *master, Fl_Osc_Interface *osc) + void loadPendingBank(int par, Bank &bank) + { + if(((unsigned int)par < bank.banks.size()) + && (bank.banks[par].dir != bank.bankfiletitle)) + bank.loadbank(bank.banks[par].dir); + } + + void loadPart(int npart, const char *filename, Master *master) { actual_load[npart]++; @@ -565,9 +577,12 @@ public: return; assert(actual_load[npart] <= pending_load[npart]); + //load part in async fashion when possible +#if HAVE_ASYNC auto alloc = std::async(std::launch::async, [master,filename,this,npart](){ Part *p = new Part(*master->memory, synth, + master->time, config->cfg.GzipCompression, config->cfg.Interpolation, &master->microtonal, master->fft); @@ -589,6 +604,20 @@ public: } Part *p = alloc.get(); +#else + Part *p = new Part(*master->memory, synth, master->time, + config->cfg.GzipCompression, + config->cfg.Interpolation, + &master->microtonal, master->fft); + if(p->loadXMLinstrument(filename)) + fprintf(stderr, "Warning: failed to load part<%s>!\n", filename); + + auto isLateLoad = [this,npart]{ + return actual_load[npart] != pending_load[npart]; + }; + + p->applyparameters(isLateLoad); +#endif obj_store.extractPart(p, npart); kits.extractPart(p, npart); @@ -605,6 +634,7 @@ public: if(npart == -1) return; Part *p = new Part(*master->memory, synth, + master->time, config->cfg.GzipCompression, config->cfg.Interpolation, &master->microtonal, master->fft); @@ -628,7 +658,10 @@ public: m->uToB = uToB; m->bToU = bToU; if(filename) { - m->loadXML(filename); + if ( m->loadXML(filename) ) { + delete m; + return; + } m->applyparameters(); } @@ -658,7 +691,8 @@ public: void tick(void) { - while(lo_server_recv_noblock(server, 0)); + if(server) + while(lo_server_recv_noblock(server, 0)); while(bToU->hasNext()) { const char *rtmsg = bToU->read(); bToUhandle(rtmsg); @@ -815,13 +849,19 @@ MiddleWareImpl::MiddleWareImpl(MiddleWare *mw, SYNTH_T synth_, LO_UDP, liblo_error_cb); else server = lo_server_new_with_proto(NULL, LO_UDP, liblo_error_cb); - lo_server_add_method(server, NULL, NULL, handler_function, mw); - fprintf(stderr, "lo server running on %d\n", lo_server_get_port(server)); + + if(server) { + lo_server_add_method(server, NULL, NULL, handler_function, mw); + fprintf(stderr, "lo server running on %d\n", lo_server_get_port(server)); + } else + fprintf(stderr, "lo server could not be started :-/\n"); + #ifndef PLUGINVERSION if(!isPlugin()) { clean_up_tmp_nams(); - create_tmp_file((unsigned)lo_server_get_port(server)); + if(server) + create_tmp_file((unsigned)lo_server_get_port(server)); } #endif @@ -869,7 +909,8 @@ MiddleWareImpl::~MiddleWareImpl(void) warnMemoryLeaks(); - lo_server_free(server); + if(server) + lo_server_free(server); delete master; delete osc; @@ -966,7 +1007,10 @@ void MiddleWareImpl::bToUhandle(const char *rtmsg, bool dummy) uToB->write("/add-rt-memory", "bi", sizeof(void*), &mem, N); } else if(!strcmp(rtmsg, "/setprogram") && !strcmp(rtosc_argument_string(rtmsg),"cc")) { - loadPart(rtosc_argument(rtmsg,0).i, master->bank.ins[rtosc_argument(rtmsg,1).i].filename.c_str(), master, osc); + loadPart(rtosc_argument(rtmsg,0).i, master->bank.ins[rtosc_argument(rtmsg,1).i].filename.c_str(), master); + } else if(!strcmp(rtmsg, "/setbank") + && !strcmp(rtosc_argument_string(rtmsg), "c")) { + loadPendingBank(rtosc_argument(rtmsg,0).i, master->bank); } else if(!strcmp("/undo_pause", rtmsg)) { recording_undo = false; } else if(!strcmp("/undo_resume", rtmsg)) { @@ -1148,7 +1192,7 @@ void MiddleWareImpl::handleMsg(const char *msg) if(last_url == "GUI") bank_cb = [this](const char *msg){if(osc)osc->tryLink(msg);}; else - bank_cb = [this](const char *msg){this->bToUhandle(msg, 1);}; + bank_cb = [this](const char *msg){if(osc)osc->tryLink(msg);this->bToUhandle(msg, 1);}; if(!strcmp(msg, "/refresh_bank") && !strcmp(rtosc_argument_string(msg), "i")) { refreshBankView(master->bank, rtosc_argument(msg,0).i, bank_cb); @@ -1184,22 +1228,22 @@ void MiddleWareImpl::handleMsg(const char *msg) loadMaster(NULL); } else if(!strcmp(msg, "/load_xiz") && !strcmp(rtosc_argument_string(msg), "is")) { pending_load[rtosc_argument(msg,0).i]++; - loadPart(rtosc_argument(msg,0).i, rtosc_argument(msg,1).s, master, osc); + loadPart(rtosc_argument(msg,0).i, rtosc_argument(msg,1).s, master); } else if(strstr(msg, "load-part") && !strcmp(rtosc_argument_string(msg), "is")) { pending_load[rtosc_argument(msg,0).i]++; - loadPart(rtosc_argument(msg,0).i, rtosc_argument(msg,1).s, master, osc); + loadPart(rtosc_argument(msg,0).i, rtosc_argument(msg,1).s, master); } else if(!strcmp(msg, "/setprogram") && !strcmp(rtosc_argument_string(msg),"c")) { pending_load[0]++; - loadPart(0, master->bank.ins[rtosc_argument(msg,0).i].filename.c_str(), master, osc); + loadPart(0, master->bank.ins[rtosc_argument(msg,0).i].filename.c_str(), master); } else if(strstr(msg, "save-bank-part") && !strcmp(rtosc_argument_string(msg), "ii")) { - saveBankSlot(rtosc_argument(msg,0).i, rtosc_argument(msg,1).i, master, osc); + saveBankSlot(rtosc_argument(msg,0).i, rtosc_argument(msg,1).i, master); } else if(strstr(msg, "bank-rename") && !strcmp(rtosc_argument_string(msg), "is")) { - renameBankSlot(rtosc_argument(msg,0).i, rtosc_argument(msg,1).s, master, osc); + renameBankSlot(rtosc_argument(msg,0).i, rtosc_argument(msg,1).s, master); } else if(strstr(msg, "swap-bank-slots") && !strcmp(rtosc_argument_string(msg), "ii")) { - swapBankSlot(rtosc_argument(msg,0).i, rtosc_argument(msg,1).i, master, osc); + swapBankSlot(rtosc_argument(msg,0).i, rtosc_argument(msg,1).i, master); } else if(strstr(msg, "clear-bank-slot") && !strcmp(rtosc_argument_string(msg), "i")) { - clearBankSlot(rtosc_argument(msg,0).i, master, osc); + clearBankSlot(rtosc_argument(msg,0).i, master); } else if(strstr(msg, "/config/")) { handleConfig(msg); } else if(strstr(msg, "/presets/")) { @@ -1215,6 +1259,8 @@ void MiddleWareImpl::handleMsg(const char *msg) undo.seekHistory(-1); } else if(!strcmp(msg, "/redo")) { undo.seekHistory(+1); + } else if(!strcmp(msg, "/ui/title")) { + ;//drop the message into the abyss } else uToB->raw_write(msg); } @@ -1316,6 +1362,10 @@ void MiddleWare::transmitMsg(const char *path, const char *args, va_list va) fprintf(stderr, "Error in transmitMsg(va)n"); } +void MiddleWare::pendingSetBank(int bank) +{ + impl->bToU->write("/setbank", "c", bank); +} void MiddleWare::pendingSetProgram(int part, int program) { impl->pending_load[part]++; @@ -1339,7 +1389,10 @@ const SYNTH_T &MiddleWare::getSynth(void) const const char* MiddleWare::getServerAddress(void) const { - return lo_server_get_url(impl->server); + if(impl->server) + return lo_server_get_url(impl->server); + else + return "NULL"; } const PresetsStore& MiddleWare::getPresetsStore() const diff --git a/source/native-plugins/zynaddsubfx/Misc/MiddleWare.h b/source/native-plugins/zynaddsubfx/Misc/MiddleWare.h index 3930da2a2..801fc90fa 100644 --- a/source/native-plugins/zynaddsubfx/Misc/MiddleWare.h +++ b/source/native-plugins/zynaddsubfx/Misc/MiddleWare.h @@ -4,15 +4,14 @@ #include struct SYNTH_T; -class Config; -class Master; +class Master; class PresetsStore; //Link between realtime and non-realtime layers class MiddleWare { public: - MiddleWare(SYNTH_T synth, Config *config, + MiddleWare(SYNTH_T synth, class Config *config, int preferred_port = -1); ~MiddleWare(void); void updateResources(Master *m); @@ -34,6 +33,8 @@ class MiddleWare void transmitMsg(const char *, const char *args, ...); //Handle a rtosc Message uToB void transmitMsg(const char *, const char *args, va_list va); + //Indicate that a bank will be loaded + void pendingSetBank(int bank); //Indicate that a program will be loaded on a known part void pendingSetProgram(int part, int program); //Get/Set the active bToU url diff --git a/source/native-plugins/zynaddsubfx/Misc/Part.cpp b/source/native-plugins/zynaddsubfx/Misc/Part.cpp index 23e997424..336824932 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Part.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Part.cpp @@ -73,7 +73,8 @@ static const Ports partPorts = { rToggle(Pnoteon, "If the channel accepts note on events"), //TODO FIXME Change to 0=OFF 1=MULTI 2=SINGLE rParamI(Pkitmode, "Kit mode enable"), - rToggle(Pdrummode, "Drum mode enable"), + rToggle(Pdrummode, "Drum mode enable\n" + "When drum mode is enabled all keys are mapped to 12tET and legato is disabled"), rToggle(Ppolymode, "Polyphoney mode"), rToggle(Plegatomode, "Legato enable"), rParamZyn(info.Ptype, "Class of Instrument"), @@ -82,11 +83,15 @@ static const Ports partPorts = { rString(Pname, PART_MAX_NAME_LEN, "Kit User Specified Label"), rArray(Pefxroute, NUM_PART_EFX, "Effect Routing"), rArrayT(Pefxbypass, NUM_PART_EFX, "If an effect is bypassed"), - {"captureMin:", NULL, NULL, [](const char *, RtData &r) + {"captureMin:", rDoc("Capture minimum valid note"), NULL, + [](const char *, RtData &r) {Part *p = (Part*)r.obj; p->Pminkey = p->lastnote;}}, - {"captureMax:", NULL, NULL, [](const char *, RtData &r) + {"captureMax:", rDoc("Capture maximum valid note"), NULL, + [](const char *, RtData &r) {Part *p = (Part*)r.obj; p->Pmaxkey = p->lastnote;}}, - {"polyType::c:i", NULL, NULL, [](const char *msg, RtData &d) + {"polyType::c:i", rProp(parameter) rOptions(Polyphonic, Monophonic, Legato) + rDoc("synthesis polyphony type"), NULL, + [](const char *msg, RtData &d) { Part *p = (Part*)d.obj; if(!rtosc_narguments(msg)) { @@ -108,7 +113,8 @@ static const Ports partPorts = { p->Ppolymode = 0; p->Plegatomode = 1; }}}, - {"clear:", rProp(internal) rDoc("Reset Part To Defaults"), 0, [](const char *, RtData &d) + {"clear:", rProp(internal) rDoc("Reset Part To Defaults"), 0, + [](const char *, RtData &d) { //XXX todo forward this event for middleware to handle //Part *p = (Part*)d.obj; @@ -159,23 +165,24 @@ static const Ports kitPorts = { rToggle(Ppadenabled, "PADsynth enable"), rParamZyn(Psendtoparteffect, "Effect Levels"), rString(Pname, PART_MAX_NAME_LEN, "Kit User Specified Label"), - {"captureMin:", NULL, NULL, [](const char *, RtData &r) + {"captureMin:", rDoc("Capture minimum valid note"), NULL, + [](const char *, RtData &r) {Part::Kit *p = (Part::Kit*)r.obj; p->Pminkey = p->parent->lastnote;}}, - {"captureMax:", NULL, NULL, [](const char *, RtData &r) + {"captureMax:", rDoc("Capture maximum valid note"), NULL, [](const char *, RtData &r) {Part::Kit *p = (Part::Kit*)r.obj; p->Pmaxkey = p->parent->lastnote;}}, - {"padpars-data:b", rProp(internal), 0, + {"padpars-data:b", rProp(internal) rDoc("Set PADsynth data pointer"), 0, [](const char *msg, RtData &d) { rObject &o = *(rObject*)d.obj; assert(o.padpars == NULL); o.padpars = *(decltype(o.padpars)*)rtosc_argument(msg, 0).b.data; }}, - {"adpars-data:b", rProp(internal), 0, + {"adpars-data:b", rProp(internal) rDoc("Set ADsynth data pointer"), 0, [](const char *msg, RtData &d) { rObject &o = *(rObject*)d.obj; assert(o.adpars == NULL); o.adpars = *(decltype(o.adpars)*)rtosc_argument(msg, 0).b.data; }}, - {"subpars-data:b", rProp(internal), 0, + {"subpars-data:b", rProp(internal) rDoc("Set SUBsynth data pointer"), 0, [](const char *msg, RtData &d) { rObject &o = *(rObject*)d.obj; assert(o.subpars == NULL); @@ -188,18 +195,23 @@ static const Ports kitPorts = { const Ports &Part::Kit::ports = kitPorts; const Ports &Part::ports = partPorts; -Part::Part(Allocator &alloc, const SYNTH_T &synth_, +Part::Part(Allocator &alloc, const SYNTH_T &synth_, const AbsTime &time_, const int &gzip_compression, const int &interpolation, Microtonal *microtonal_, FFTwrapper *fft_) - :ctl(synth_), memory(alloc), synth(synth_), + :Pdrummode(false), + Ppolymode(true), + Plegatomode(false), + partoutl(new float[synth_.buffersize]), + partoutr(new float[synth_.buffersize]), + ctl(synth_), + microtonal(microtonal_), + fft(fft_), + memory(alloc), + synth(synth_), + time(time_), gzip_compression(gzip_compression), interpolation(interpolation) { - microtonal = microtonal_; - fft = fft_; - partoutl = new float [synth.buffersize]; - partoutr = new float [synth.buffersize]; - monomemClear(); for(int n = 0; n < NUM_KIT_ITEMS; ++n) { @@ -217,6 +229,7 @@ Part::Part(Allocator &alloc, const SYNTH_T &synth_, partefx[nefx] = new EffectMgr(memory, synth, 1); Pefxbypass[nefx] = false; } + assert(partefx[0]); for(int n = 0; n < NUM_PART_EFX + 1; ++n) { partfxinputl[n] = new float [synth.buffersize]; @@ -226,28 +239,15 @@ Part::Part(Allocator &alloc, const SYNTH_T &synth_, killallnotes = false; oldfreq = -1.0f; - - for(int i = 0; i < POLYPHONY; ++i) { - partnote[i].status = KEY_OFF; - partnote[i].note = -1; - partnote[i].itemsplaying = 0; - for(int j = 0; j < NUM_KIT_ITEMS; ++j) { - partnote[i].kititem[j].adnote = NULL; - partnote[i].kititem[j].subnote = NULL; - partnote[i].kititem[j].padnote = NULL; - } - partnote[i].time = 0; - } cleanup(); Pname = new char[PART_MAX_NAME_LEN]; oldvolumel = oldvolumer = 0.5f; lastnote = -1; - lastpos = 0; // lastpos will store previously used NoteOn(...)'s pos. - lastlegatomodevalid = false; // To store previous legatomodevalid value. defaults(); + assert(partefx[0]); } void Part::cloneTraits(Part &p) const @@ -334,8 +334,7 @@ void Part::defaultsinstrument() */ void Part::cleanup(bool final_) { - for(int k = 0; k < POLYPHONY; ++k) - KillNotePos(k); + notePool.killAllNotes(); for(int i = 0; i < synth.buffersize; ++i) { partoutl[i] = final_ ? 0.0f : synth.denormalkillbuf[i]; partoutr[i] = final_ ? 0.0f : synth.denormalkillbuf[i]; @@ -371,6 +370,16 @@ Part::~Part() } } +void assert_kit_sanity(const Part::Kit *kits) +{ + for(int i=0; i POLYPHONY) - (Part.cpp::NoteOn(..))\n"); - else { - //start the note - partnote[pos].status = KEY_PLAYING; - partnote[pos].note = note; - if(legatomodevalid) { - partnote[posb].status = KEY_PLAYING; - partnote[posb].note = note; - } + verifyKeyMode(); + assert_kit_sanity(kit); - //this computes the velocity sensing of the part - float vel = VelF(velocity / 127.0f, Pvelsns); + //Preserve Note Stack + if(isMonoMode() || isLegatoMode()) { + monomemPush(note); + monomem[note].velocity = velocity; + monomem[note].mkeyshift = masterkeyshift; - //compute the velocity offset - vel = limit(vel + (Pveloffs - 64.0f) / 64.0f, 0.0f, 1.0f); + } else if(!monomemEmpty()) + monomemClear(); - //compute the keyshift - int partkeyshift = (int)Pkeyshift - 64; - int keyshift = masterkeyshift + partkeyshift; + //Mono/Legato Release old notes + if(isMonoMode() || (isLegatoMode() && !doingLegato)) + notePool.releasePlayingNotes(); - //initialise note frequency - float notebasefreq; - if(Pdrummode == 0) { - notebasefreq = microtonal->getnotefreq(note, keyshift); - if(notebasefreq < 0.0f) - return;//the key is no mapped - } - else - notebasefreq = 440.0f * powf(2.0f, (note - 69.0f) / 12.0f); - - //Portamento - if(oldfreq < 1.0f) - oldfreq = notebasefreq;//this is only the first note is played - - // For Mono/Legato: Force Portamento Off on first - // notes. That means it is required that the previous note is - // still held down or sustained for the Portamento to activate - // (that's like Legato). - bool portamento = false; - if(Ppolymode || !ismonofirstnote) - // I added a third argument to the - // ctl.initportamento(...) function to be able - // to tell it if we're doing a legato note. - portamento = ctl.initportamento(oldfreq, notebasefreq, doinglegato); - - if(portamento) - ctl.portamento.noteusing = pos; - oldfreq = notebasefreq; - - lastpos = pos; // Keep a trace of used pos. - - if(doinglegato) { - // Do Legato note - if(Pkitmode == 0) { // "normal mode" legato note - - auto note1 = partnote[pos].kititem[0]; - auto note2 = partnote[posb].kititem[0]; - LegatoParams pars = {notebasefreq, vel, portamento, note, true}; - if(kit[0].Padenabled && note1.adnote && note2.adnote) { - note1.adnote->legatonote(pars); - note2.adnote->legatonote(pars); - } + lastlegatomodevalid = isLegatoMode(); - if(kit[0].Psubenabled && note1.subnote && note2.subnote) { - note1.subnote->legatonote(pars); - note2.subnote->legatonote(pars); - } + //Compute Note Parameters + const float vel = getVelocity(velocity, Pvelsns, Pveloffs); + const int partkeyshift = (int)Pkeyshift - 64; + const int keyshift = masterkeyshift + partkeyshift; + const float notebasefreq = getBaseFreq(note, keyshift); - if(kit[0].Ppadenabled && note1.padnote && note2.padnote) { - note1.padnote->legatonote(pars); - note2.padnote->legatonote(pars); - } - } - else { // "kit mode" legato note - int ci = 0; - for(int item = 0; item < NUM_KIT_ITEMS; ++item) { - - //Make sure the key is valid and not across multiple ranges - if(kit[item].Pmuted || !inRange(note, kit[item].Pminkey, kit[item].Pmaxkey) - || !inRange((unsigned char)lastnotecopy, kit[item].Pminkey, kit[item].Pmaxkey)) - continue; - - auto note1 = partnote[pos].kititem[ci]; - auto note2 = partnote[posb].kititem[ci]; - LegatoParams pars = {notebasefreq, vel, portamento, note, true}; - note1.sendtoparteffect = limit((int)kit[item].Psendtoparteffect, 0, NUM_PART_EFX); - note2.sendtoparteffect = limit((int)kit[item].Psendtoparteffect, 0, NUM_PART_EFX); - - if(kit[item].Padenabled && kit[item].adpars && note1.adnote && note2.adnote) { - note1.adnote->legatonote(pars); - note2.adnote->legatonote(pars); - } - if(kit[item].Psubenabled && kit[item].subpars && note1.subnote && note2.subnote) { - note1.subnote->legatonote(pars); - note2.subnote->legatonote(pars); - } - if(kit[item].Ppadenabled && kit[item].padpars && note1.padnote && note2.padnote) { - note1.padnote->legatonote(pars); - note2.padnote->legatonote(pars); - } - - if(kit[item].adpars || kit[item].subpars || kit[item].padpars) { - ci++; - if((kit[item].Padenabled || kit[item].Psubenabled || kit[item].Ppadenabled) && (Pkitmode == 2)) - break; - } - } - if(ci == 0) { - // No legato were performed at all, so pretend nothing happened: - monomemPop(monomemBack()); // Remove last note from the list. - lastnote = lastnotecopy; // Set lastnote back to previous value. - } - } - return; // Ok, Legato note done, return. - } + if(notebasefreq < 0) + return; - partnote[pos].itemsplaying = 0; - if(legatomodevalid) - partnote[posb].itemsplaying = 0; - - if(Pkitmode == 0) { //init the notes for the "normal mode" - partnote[pos].kititem[0].sendtoparteffect = 0; - SynthParams pars{memory, ctl, synth, notebasefreq, vel, (bool) portamento, note, false}; - - if(kit[0].Padenabled) - partnote[pos].kititem[0].adnote = - memory.alloc(kit[0].adpars, pars); - if(kit[0].Psubenabled) - partnote[pos].kititem[0].subnote = - memory.alloc(kit[0].subpars, pars); - if(kit[0].Ppadenabled) - partnote[pos].kititem[0].padnote = - memory.alloc(kit[0].padpars, pars, - interpolation); - - - if(kit[0].Padenabled || kit[0].Psubenabled || kit[0].Ppadenabled) - partnote[pos].itemsplaying++; - - // Spawn another note (but silent) if legatomodevalid==true - if(legatomodevalid) { - partnote[posb].kititem[0].sendtoparteffect = 0; - pars.quiet = true; - - if(kit[0].Padenabled) - partnote[posb].kititem[0].adnote = - memory.alloc(kit[0].adpars, pars); - if(kit[0].Psubenabled) - partnote[posb].kititem[0].subnote = - memory.alloc(kit[0].subpars, pars); - if(kit[0].Ppadenabled) - partnote[posb].kititem[0].padnote = - memory.alloc(kit[0].padpars, pars, - interpolation); - - if(kit[0].Padenabled || kit[0].Psubenabled || kit[0].Ppadenabled) - partnote[posb].itemsplaying++; - } - } - else //init the notes for the "kit mode" - for(int item = 0; item < NUM_KIT_ITEMS; ++item) { - if(kit[item].Pmuted || !inRange(note, kit[item].Pminkey, kit[item].Pmaxkey)) - continue; + //Portamento + lastnote = note; + if(oldfreq < 1.0f) + oldfreq = notebasefreq;//this is only the first note is played + + // For Mono/Legato: Force Portamento Off on first + // notes. That means it is required that the previous note is + // still held down or sustained for the Portamento to activate + // (that's like Legato). + bool portamento = false; + if(Ppolymode || isRunningNote) + portamento = ctl.initportamento(oldfreq, notebasefreq, doingLegato); + + oldfreq = notebasefreq; + + //Adjust Existing Notes + if(doingLegato) { + LegatoParams pars = {notebasefreq, vel, portamento, note, true}; + notePool.applyLegato(pars); + return; + } - int ci = partnote[pos].itemsplaying; //ci=current item - auto ¬e1 = partnote[pos].kititem[ci]; - - //if this parameter is 127 for "unprocessed" - note1.sendtoparteffect = limit((int)kit[item].Psendtoparteffect, 0, NUM_PART_EFX); - - SynthParams pars{memory, ctl, synth, notebasefreq, vel, (bool) portamento, note, false}; - - if(kit[item].adpars && kit[item].Padenabled) - note1.adnote = - memory.alloc(kit[item].adpars, pars); - - if(kit[item].subpars && kit[item].Psubenabled) - note1.subnote = - memory.alloc(kit[item].subpars, pars); - - if(kit[item].padpars && kit[item].Ppadenabled) - note1.padnote = - memory.alloc(kit[item].padpars, pars, - interpolation); - - // Spawn another note (but silent) if legatomodevalid==true - if(legatomodevalid) { - auto ¬e2 = partnote[posb].kititem[ci]; - note2.sendtoparteffect = limit((int)kit[item].Psendtoparteffect, 0, NUM_PART_EFX); - - pars.quiet = true; - if(kit[item].adpars && kit[item].Padenabled) - note2.adnote = - memory.alloc(kit[item].adpars, pars); - if(kit[item].subpars && kit[item].Psubenabled) - note2.subnote = - memory.alloc(kit[item].subpars, pars); - if(kit[item].padpars && kit[item].Ppadenabled) - note2.padnote = - memory.alloc(kit[item].padpars, pars, - interpolation); - - if(kit[item].adpars || kit[item].subpars || kit[item].padpars) - partnote[posb].itemsplaying++; - } + //Create New Notes + for(uint8_t i = 0; i < NUM_KIT_ITEMS; ++i) { + auto &item = kit[i]; + if(Pkitmode != 0 && !item.validNote(note)) + continue; - if(kit[item].adpars || kit[item].subpars) { - partnote[pos].itemsplaying++; - if((kit[item].Padenabled || kit[item].Psubenabled || kit[item].Ppadenabled) && (Pkitmode == 2)) - break; - } - } + SynthParams pars{memory, ctl, synth, time, notebasefreq, vel, + portamento, note, false}; + const int sendto = Pkitmode ? item.sendto() : 0; + + if(item.Padenabled) + notePool.insertNote(note, sendto, + {memory.alloc(kit[i].adpars, pars), 0, i}); + if(item.Psubenabled) + notePool.insertNote(note, sendto, + {memory.alloc(kit[i].subpars, pars), 1, i}); + if(item.Ppadenabled) + notePool.insertNote(note, sendto, + {memory.alloc(kit[i].padpars, pars, interpolation), 2, i}); + + //Partial Kit Use + if(isNonKit() || (isSingleKit() && item.active())) + break; } - //this only release the keys if there is maximum number of keys allowed + if(isLegatoMode()) + notePool.upgradeToLegato(); + + //Enforce the key limit setkeylimit(Pkeylimit); } @@ -683,17 +485,18 @@ void Part::NoteOff(unsigned char note) //release the key if(!monomemEmpty()) monomemPop(note); - for(int i = POLYPHONY - 1; i >= 0; i--) //first note in, is first out if there are same note multiple times - if((partnote[i].status == KEY_PLAYING) && (partnote[i].note == note)) { - if(!ctl.sustain.sustain) { //the sustain pedal is not pushed - if(!Ppolymode && !monomemEmpty()) - MonoMemRenote();//Play most recent still active note - else - ReleaseNotePos(i); - } - else //the sustain pedal is pushed - partnote[i].status = KEY_RELEASED_AND_SUSTAINED; + for(auto &desc:notePool.activeDesc()) { + if(desc.note != note) + continue; + if(!ctl.sustain.sustain) { //the sustain pedal is not pushed + if((isMonoMode() || isLegatoMode()) && !monomemEmpty()) + MonoMemRenote();//Play most recent still active note + else + notePool.release(desc); } + else //the sustain pedal is pushed + desc.status = KEY_RELEASED_AND_SUSTAINED; + } } void Part::PolyphonicAftertouch(unsigned char note, @@ -709,36 +512,12 @@ void Part::PolyphonicAftertouch(unsigned char note, if(!Ppolymode) // if Poly is off monomem[note].velocity = velocity; // Store this note's velocity. - - for(int i = 0; i < POLYPHONY; ++i) - if((partnote[i].note == note) && (partnote[i].status == KEY_PLAYING)) { - /* update velocity */ - // compute the velocity offset - float vel = VelF(velocity / 127.0f, Pvelsns) + (Pveloffs - 64.0f) / 64.0f; - vel = limit(vel, 0.0f, 1.0f); - - if(!Pkitmode) { // "normal mode" - if(kit[0].Padenabled && partnote[i].kititem[0].adnote) - partnote[i].kititem[0].adnote->setVelocity(vel); - if(kit[0].Psubenabled && partnote[i].kititem[0].subnote) - partnote[i].kititem[0].subnote->setVelocity(vel); - if(kit[0].Ppadenabled && partnote[i].kititem[0].padnote) - partnote[i].kititem[0].padnote->setVelocity(vel); - } - else // "kit mode" - for(int item = 0; item < NUM_KIT_ITEMS; ++item) { - if(kit[item].Pmuted || !inRange(note, kit[item].Pminkey, kit[item].Pmaxkey)) - continue; - - if(kit[item].Padenabled && partnote[i].kititem[item].adnote) - partnote[i].kititem[item].adnote->setVelocity(vel); - if(kit[item].Psubenabled && partnote[i].kititem[item].subnote) - partnote[i].kititem[item].subnote->setVelocity(vel); - if(kit[item].Ppadenabled && partnote[i].kititem[item].padnote) - partnote[i].kititem[item].padnote->setVelocity(vel); - } - } - + const float vel = getVelocity(velocity, Pvelsns, Pveloffs); + for(auto &d:notePool.activeDesc()) { + if(d.note == note && d.status == KEY_PLAYING) + for(auto &s:notePool.activeNotes(d)) + s.note->setVelocity(vel); + } } /* @@ -839,13 +618,14 @@ void Part::SetController(unsigned int type, int par) void Part::ReleaseSustainedKeys() { // Let's call MonoMemRenote() on some conditions: - if(Ppolymode == 0 && !monomemEmpty()) + if((isMonoMode() || isLegatoMode()) && !monomemEmpty()) if(monomemBack() != lastnote) // Sustain controller manipulation would cause repeated same note respawn without this check. MonoMemRenote(); // To play most recent still held note. - for(int i = 0; i < POLYPHONY; ++i) - if(partnote[i].status == KEY_RELEASED_AND_SUSTAINED) - ReleaseNotePos(i); + for(auto &d:notePool.activeDesc()) + if(d.status == KEY_RELEASED_AND_SUSTAINED) + for(auto &s:notePool.activeNotes(d)) + s.note->releasekey(); } /* @@ -854,10 +634,10 @@ void Part::ReleaseSustainedKeys() void Part::ReleaseAllKeys() { - for(int i = 0; i < POLYPHONY; ++i) - if((partnote[i].status != KEY_RELEASED) - && (partnote[i].status != KEY_OFF)) //thanks to Frank Neumann - ReleaseNotePos(i); + for(auto &d:notePool.activeDesc()) + if(d.status != KEY_RELEASED) + for(auto &s:notePool.activeNotes(d)) + s.note->releasekey(); } // Call NoteOn(...) with the most recent still held key as new note @@ -866,50 +646,36 @@ void Part::MonoMemRenote() { unsigned char mmrtempnote = monomemBack(); // Last list element. monomemPop(mmrtempnote); // We remove it, will be added again in NoteOn(...). - if(Pnoteon == 0) - ReleaseNotePos(lastpos); - else - NoteOn(mmrtempnote, monomem[mmrtempnote].velocity, - monomem[mmrtempnote].mkeyshift); + NoteOn(mmrtempnote, monomem[mmrtempnote].velocity, + monomem[mmrtempnote].mkeyshift); } -/* - * Release note at position - */ -void Part::ReleaseNotePos(int pos) +float Part::getBaseFreq(int note, int keyshift) const { - for(int j = 0; j < NUM_KIT_ITEMS; ++j) { - if(partnote[pos].kititem[j].adnote) - partnote[pos].kititem[j].adnote->releasekey(); + if(Pdrummode) + return 440.0f * powf(2.0f, (note - 69.0f) / 12.0f); + else + return microtonal->getnotefreq(note, keyshift); +} - if(partnote[pos].kititem[j].subnote) - partnote[pos].kititem[j].subnote->releasekey(); +float Part::getVelocity(uint8_t velocity, uint8_t velocity_sense, + uint8_t velocity_offset) const +{ + //compute sense function + const float vel = VelF(velocity / 127.0f, velocity_sense); - if(partnote[pos].kititem[j].padnote) - partnote[pos].kititem[j].padnote->releasekey(); - } - partnote[pos].status = KEY_RELEASED; + //compute the velocity offset + return limit(vel + (velocity_offset - 64.0f) / 64.0f, 0.0f, 1.0f); } - -/* - * Kill note at position - */ -void Part::KillNotePos(int pos) +void Part::verifyKeyMode(void) { - partnote[pos].status = KEY_OFF; - partnote[pos].note = -1; - partnote[pos].time = 0; - partnote[pos].itemsplaying = 0; - - for(int j = 0; j < NUM_KIT_ITEMS; ++j) { - memory.dealloc(partnote[pos].kititem[j].adnote); - memory.dealloc(partnote[pos].kititem[j].subnote); - memory.dealloc(partnote[pos].kititem[j].padnote); - } - if(pos == ctl.portamento.noteusing) { - ctl.portamento.noteusing = -1; - ctl.portamento.used = 0; + if(Plegatomode && !Pdrummode && Ppolymode) { + fprintf(stderr, + "WARNING: Poly & Legato modes are On, that shouldn't happen\n" + "Disabling Legato mode...\n" + "(Part.cpp::NoteOn(..))\n"); + Plegatomode = 0; } } @@ -917,32 +683,15 @@ void Part::KillNotePos(int pos) /* * Set Part's key limit */ -void Part::setkeylimit(unsigned char Pkeylimit) +void Part::setkeylimit(unsigned char Pkeylimit_) { - this->Pkeylimit = Pkeylimit; + Pkeylimit = Pkeylimit_; int keylimit = Pkeylimit; if(keylimit == 0) keylimit = POLYPHONY - 5; - //release old keys if the number of notes>keylimit - if(Ppolymode != 0) { - int notecount = 0; - for(int i = 0; i < POLYPHONY; ++i) - if((partnote[i].status == KEY_PLAYING) || (partnote[i].status == KEY_RELEASED_AND_SUSTAINED)) - notecount++; - - int oldestnotepos = -1; - if(notecount > keylimit) //find out the oldest note - for(int i = 0; i < POLYPHONY; ++i) { - int maxtime = 0; - if(((partnote[i].status == KEY_PLAYING) || (partnote[i].status == KEY_RELEASED_AND_SUSTAINED)) && (partnote[i].time > maxtime)) { - maxtime = partnote[i].time; - oldestnotepos = i; - } - } - if(oldestnotepos != -1) - ReleaseNotePos(oldestnotepos); - } + if(notePool.getRunningNotes() > keylimit) + notePool.enforceKeyLimit(keylimit); } @@ -954,65 +703,35 @@ void Part::AllNotesOff() killallnotes = true; } -void Part::RunNote(unsigned int k) +/* + * Compute Part samples and store them in the partoutl[] and partoutr[] + */ +void Part::ComputePartSmps() { - unsigned noteplay = 0; - for(int item = 0; item < partnote[k].itemsplaying; ++item) { - int sendcurrenttofx = partnote[k].kititem[item].sendtoparteffect; - - for(unsigned type = 0; type < 3; ++type) { - //Select a note - SynthNote **note = NULL; - if(type == 0) - note = &partnote[k].kititem[item].adnote; - else if(type == 1) - note = &partnote[k].kititem[item].subnote; - else if(type == 2) - note = &partnote[k].kititem[item].padnote; - - //Process if it exists - if(!(*note)) - continue; - noteplay++; + assert(partefx[0]); + for(unsigned nefx = 0; nefx < NUM_PART_EFX + 1; ++nefx) { + memset(partfxinputl[nefx], 0, synth.bufferbytes); + memset(partfxinputr[nefx], 0, synth.bufferbytes); + } + for(auto &d:notePool.activeDesc()) { + d.age++; + for(auto &s:notePool.activeNotes(d)) { float tmpoutr[synth.buffersize]; float tmpoutl[synth.buffersize]; - (*note)->noteout(&tmpoutl[0], &tmpoutr[0]); + auto ¬e = *s.note; + note.noteout(&tmpoutl[0], &tmpoutr[0]); - if((*note)->finished()) - memory.dealloc(*note); for(int i = 0; i < synth.buffersize; ++i) { //add the note to part(mix) - partfxinputl[sendcurrenttofx][i] += tmpoutl[i]; - partfxinputr[sendcurrenttofx][i] += tmpoutr[i]; + partfxinputl[d.sendto][i] += tmpoutl[i]; + partfxinputr[d.sendto][i] += tmpoutr[i]; } - } - } - //Kill note if there is no synth on that note - if(!noteplay) - KillNotePos(k); -} - -/* - * Compute Part samples and store them in the partoutl[] and partoutr[] - */ -void Part::ComputePartSmps() -{ - for(unsigned nefx = 0; nefx < NUM_PART_EFX + 1; ++nefx) - for(int i = 0; i < synth.buffersize; ++i) { - partfxinputl[nefx][i] = 0.0f; - partfxinputr[nefx][i] = 0.0f; + if(note.finished()) + notePool.kill(s); } - - for(unsigned k = 0; k < POLYPHONY; ++k) { - if(partnote[k].status == KEY_OFF) - continue; - partnote[k].time++; - //get the sampledata of the note and kill it if it's finished - RunNote(k); } - //Apply part's effects and mix them for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) { if(!Pefxbypass[nefx]) { @@ -1040,8 +759,8 @@ void Part::ComputePartSmps() partoutl[i] *= tmp; partoutr[i] *= tmp; } - for(int k = 0; k < POLYPHONY; ++k) - KillNotePos(k); + notePool.killAllNotes(); + monomemClear(); killallnotes = false; for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) partefx[nefx]->cleanup(); @@ -1087,9 +806,7 @@ void Part::setkititemstatus(unsigned kititem, bool Penabled_) delete kkit.padpars; kkit.Pname[0] = '\0'; - //Reset notes s.t. stale buffers will not get read - for(int k = 0; k < POLYPHONY; ++k) - KillNotePos(k); + notePool.killAllNotes(); } else { //All parameters must be NULL in this case @@ -1247,8 +964,7 @@ void Part::kill_rt(void) { for(int i=0; ikill(); - for(int k = 0; k < POLYPHONY; ++k) - KillNotePos(k); + notePool.killAllNotes(); } void Part::monomemPush(char note) @@ -1414,3 +1130,19 @@ void Part::getfromXML(XMLwrapper *xml) xml->exitbranch(); } } + +bool Part::Kit::active(void) const +{ + return Padenabled || Psubenabled || Ppadenabled; +} + +uint8_t Part::Kit::sendto(void) const +{ + return limit((int)Psendtoparteffect, 0, NUM_PART_EFX); + +} + +bool Part::Kit::validNote(char note) const +{ + return !Pmuted && inRange((uint8_t)note, Pminkey, Pmaxkey); +} diff --git a/source/native-plugins/zynaddsubfx/Misc/Part.h b/source/native-plugins/zynaddsubfx/Misc/Part.h index f5b97de81..8b7d38ea2 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Part.h +++ b/source/native-plugins/zynaddsubfx/Misc/Part.h @@ -27,6 +27,7 @@ #include "../globals.h" #include "../Params/Controller.h" +#include "../Containers/NotePool.h" #include @@ -37,7 +38,7 @@ class Part /**Constructor * @param microtonal_ Pointer to the microtonal object * @param fft_ Pointer to the FFTwrapper*/ - Part(Allocator &alloc, const SYNTH_T &synth, + Part(Allocator &alloc, const SYNTH_T &synth, const AbsTime &time, const int& gzip_compression, const int& interpolation, Microtonal *microtonal_, FFTwrapper *fft_); /**Destructor*/ @@ -97,6 +98,10 @@ class Part SUBnoteParameters *subpars; PADnoteParameters *padpars; + bool active(void) const; + uint8_t sendto(void) const; + bool validNote(char note) const; + const static rtosc::Ports &ports; } kit[NUM_KIT_ITEMS]; @@ -118,6 +123,8 @@ class Part unsigned char Pveloffs; //velocity offset bool Pnoteon; //if the part receives NoteOn messages int Pkitmode; //if the kitmode is enabled + + //XXX consider deprecating drum mode bool Pdrummode; //if all keys are mapped and the system is 12tET (used for drums) bool Ppolymode; //Part mode - 0=monophonic , 1=polyphonic @@ -156,25 +163,22 @@ class Part const static rtosc::Ports &ports; private: - void RunNote(unsigned k); - void KillNotePos(int pos); - void ReleaseNotePos(int pos); void MonoMemRenote(); // MonoMem stuff. + float getBaseFreq(int note, int keyshift) const; + float getVelocity(uint8_t velocity, uint8_t velocity_sense, + uint8_t velocity_offset) const; + void verifyKeyMode(void); + bool isPolyMode(void) const {return Ppolymode;} + bool isMonoMode(void) const {return !Ppolymode && !Plegatomode;}; + bool isLegatoMode(void) const {return Plegatomode && !Pdrummode;} + bool isNonKit(void) const {return Pkitmode == 0;} + bool isMultiKit(void) const {return Pkitmode == 1;} + bool isSingleKit(void) const {return Pkitmode == 2;} - int killallnotes; //is set to 1 if I want to kill all notes - - struct PartNotes { - NoteStatus status; - int note; //if there is no note playing, the "note"=-1 - int itemsplaying; - struct { - SynthNote *adnote, *subnote, *padnote; - int sendtoparteffect; - } kititem[NUM_KIT_ITEMS]; - int time; - }; + bool killallnotes; + + NotePool notePool; - int lastpos, lastposb; // To keep track of previously used pos and posb. bool lastlegatomodevalid; // To keep track of previous legatomodevalid. // MonoMem stuff @@ -194,13 +198,12 @@ class Part store the velocity and masterkeyshift values of a given note (the list only store note values). For example 'monomem[note].velocity' would be the velocity value of the note 'note'.*/ - PartNotes partnote[POLYPHONY]; - float oldfreq; //this is used for portamento Microtonal *microtonal; FFTwrapper *fft; Allocator &memory; const SYNTH_T &synth; + const AbsTime &time; const int &gzip_compression, &interpolation; }; diff --git a/source/native-plugins/zynaddsubfx/Misc/PresetExtractor.cpp b/source/native-plugins/zynaddsubfx/Misc/PresetExtractor.cpp index eebe2053e..aa5ff7d23 100644 --- a/source/native-plugins/zynaddsubfx/Misc/PresetExtractor.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/PresetExtractor.cpp @@ -30,7 +30,7 @@ static void dummy(const char *, rtosc::RtData&) {} const rtosc::Ports real_preset_ports = { {"scan-for-presets:", 0, 0, - [](const char *msg, rtosc::RtData &d) { + [](const char *, rtosc::RtData &d) { MiddleWare &mw = *(MiddleWare*)d.obj; mw.getPresetsStore().scanforpresets(); auto &pre = mw.getPresetsStore().presets; @@ -83,7 +83,7 @@ const rtosc::Ports real_preset_ports = assert(false && "bad arguments"); }}, {"clipboard-type:", 0, 0, - [](const char *msg, rtosc::RtData &d) { + [](const char *, rtosc::RtData &d) { const MiddleWare &mw = *(MiddleWare*)d.obj; d.reply(d.loc, "s", mw.getPresetsStore().clipboard.type.c_str()); }}, @@ -99,8 +99,8 @@ const rtosc::Ports real_preset_ports = const rtosc::Ports preset_ports { {"scan-for-presets:", rDoc("Scan For Presets"), 0, dummy}, - {"copy:s:ss:si:ssi", rDoc("Copy URL to Name/Clipboard from subfield "), 0, dummy}, - {"paste:s:ss:si:ssi", rDoc("Paste URL to File-Name/Clipboard from subfield "), 0, dummy}, + {"copy:s:ss:si:ssi", rDoc("Copy (s)URL to (s) Name/Clipboard from subfield (i)"), 0, dummy}, + {"paste:s:ss:si:ssi", rDoc("Paste (s) URL to (s) File-Name/Clipboard from subfield (i)"), 0, dummy}, {"clipboard-type:", rDoc("Type Stored In Clipboard"), 0, dummy}, {"delete:s", rDoc("Delete the given preset file"), 0, dummy}, }; @@ -123,14 +123,6 @@ const rtosc::Ports preset_ports //Synth/Resonance.cpp: setpresettype("Presonance"); -//Translate newer symbols to old preset types -std::vector translate_preset_types(std::string metatype) -{ - std::vector results; - return results; -} - - /***************************************************************************** * Implementation Methods * *****************************************************************************/ diff --git a/source/native-plugins/zynaddsubfx/Misc/Time.h b/source/native-plugins/zynaddsubfx/Misc/Time.h new file mode 100644 index 000000000..0f8c46b0a --- /dev/null +++ b/source/native-plugins/zynaddsubfx/Misc/Time.h @@ -0,0 +1,42 @@ +#pragma once +#include +#include "../globals.h" + +class AbsTime +{ + public: + AbsTime(const SYNTH_T &synth) + :frames(0), + s(synth){}; + void operator++(){++frames;}; + void operator++(int){frames++;}; + int64_t time() const {return frames;}; + float dt() const { return s.dt(); } + float framesPerSec() const { return 1/s.dt();} + int samplesPerFrame() const {return s.buffersize;} + private: + int64_t frames; + const SYNTH_T &s; +}; + +//Marker for an event relative to some position of the absolute timer +class RelTime +{ + public: + RelTime(const AbsTime &t_, float sec) + :t(t_) + { + //Calculate time of event + double deltaFrames = sec*t.framesPerSec(); + int64_t tmp = (int64_t)deltaFrames; + frame = t.time() + tmp; + sample = t.samplesPerFrame()*(deltaFrames-tmp); + } + bool inThisFrame() {return t.time() == frame;}; + bool inPast() {return t.time() > frame;} + bool inFuture() {return t.time() < frame;} + private: + int64_t frame; + int32_t sample; + const AbsTime &t; +}; diff --git a/source/native-plugins/zynaddsubfx/Misc/Util.h b/source/native-plugins/zynaddsubfx/Misc/Util.h index 1a9cf6e68..682894cc5 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Util.h +++ b/source/native-plugins/zynaddsubfx/Misc/Util.h @@ -157,12 +157,12 @@ char *rtosc_splat(const char *path, std::set); {STRINGIFY(name) "::i", rProp(parameter) rMap(min, 0) rMap(max, 127) DOC(__VA_ARGS__), NULL, rParamICb(name)} #define rSelf(type) \ -{"self", rProp(internal) rMap(class, type) rDoc("port metadata"), 0, \ +{"self:", rProp(internal) rMap(class, type) rDoc("port metadata"), 0, \ [](const char *, rtosc::RtData &d){ \ d.reply(d.loc, "b", sizeof(d.obj), &d.obj);}}\ #define rPaste \ -{"preset-type", rProp(internal), 0, \ +{"preset-type:", rProp(internal) rDoc("clipboard type of object"), 0, \ [](const char *, rtosc::RtData &d){ \ rObject *obj = (rObject*)d.obj; \ d.reply(d.loc, "s", obj->type);}},\ diff --git a/source/native-plugins/zynaddsubfx/Misc/WavFile.cpp b/source/native-plugins/zynaddsubfx/Misc/WavFile.cpp index 78db9d52e..43d665542 100644 --- a/source/native-plugins/zynaddsubfx/Misc/WavFile.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/WavFile.cpp @@ -10,7 +10,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License (version 2) for more details. + GNU General Public License (version 2 or later) for more details. You should have received a copy of the GNU General Public License (version 2) along with this program; if not, write to the Free Software Foundation, diff --git a/source/native-plugins/zynaddsubfx/Misc/WavFile.h b/source/native-plugins/zynaddsubfx/Misc/WavFile.h index 4b29efa02..b33a75f0e 100644 --- a/source/native-plugins/zynaddsubfx/Misc/WavFile.h +++ b/source/native-plugins/zynaddsubfx/Misc/WavFile.h @@ -13,7 +13,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License (version 2) for more details. + GNU General Public License (version 2 or later) for more details. You should have received a copy of the GNU General Public License (version 2) along with this program; if not, write to the Free Software Foundation, diff --git a/source/native-plugins/zynaddsubfx/Nio/WavEngine.cpp b/source/native-plugins/zynaddsubfx/Nio/WavEngine.cpp index efc899bf5..ef54c85b8 100644 --- a/source/native-plugins/zynaddsubfx/Nio/WavEngine.cpp +++ b/source/native-plugins/zynaddsubfx/Nio/WavEngine.cpp @@ -9,7 +9,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License (version 2) for more details. + GNU General Public License (version 2 or later) for more details. You should have received a copy of the GNU General Public License (version 2) along with this program; if not, write to the Free Software Foundation, diff --git a/source/native-plugins/zynaddsubfx/Nio/WavEngine.h b/source/native-plugins/zynaddsubfx/Nio/WavEngine.h index 6ec6d7433..aeb26394e 100644 --- a/source/native-plugins/zynaddsubfx/Nio/WavEngine.h +++ b/source/native-plugins/zynaddsubfx/Nio/WavEngine.h @@ -13,7 +13,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License (version 2) for more details. + GNU General Public License (version 2 or later) for more details. You should have received a copy of the GNU General Public License (version 2) along with this program; if not, write to the Free Software Foundation, diff --git a/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.cpp b/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.cpp index 405fdc923..cf7aa0fbb 100644 --- a/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.cpp +++ b/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.cpp @@ -62,11 +62,11 @@ static const Ports voicePorts = { rParamZyn(Unison_vibratto, "Subvoice vibratto"), rParamZyn(Unison_vibratto_speed, "Subvoice vibratto speed"), rOption(Unison_invert_phase, rOptions(none, random, 50%, 33%, 25%), "Subvoice Phases"), - rOption(Type, rOptions(Sound,Noise), "Type of Sound"), + rOption(Type, rOptions(Sound,White,Pink), "Type of Sound"), rParamZyn(PDelay, "Voice Startup Delay"), rToggle(Presonance, "Resonance Enable"), - rParamZyn(Pextoscil, "External Oscilator Selection"), - rParamZyn(PextFMoscil, "External FM Oscilator Selection"), + rParamI(Pextoscil, rMap(min, -1), rMap(max, 16), "External Oscilator Selection"), + rParamI(PextFMoscil, rMap(min, -1), rMap(max, 16), "External FM Oscilator Selection"), rParamZyn(Poscilphase, "Oscillator Phase"), rParamZyn(PFMoscilphase, "FM Oscillator Phase"), rToggle(Pfilterbypass, "Filter Bypass"), @@ -108,14 +108,19 @@ static const Ports voicePorts = { //weird stuff for PCoarseDetune - {"detunevalue:", NULL, NULL, [](const char *, RtData &d) + {"detunevalue:", rMap(unit,cents) rDoc("Get detune in cents"), NULL, + [](const char *, RtData &d) { rObject *obj = (rObject *)d.obj; + unsigned detuneType = + obj->PDetuneType == 0 ? *(obj->GlobalPDetuneType) + : obj->PDetuneType; //TODO check if this is accurate or if PCoarseDetune is utilized //TODO do the same for the other engines - d.reply(d.loc, "f", getdetune(obj->PDetuneType, 0, obj->PDetune)); + d.reply(d.loc, "f", getdetune(detuneType, 0, obj->PDetune)); }}, - {"octave::c:i", NULL, NULL, [](const char *msg, RtData &d) + {"octave::c:i", rProp(parameter) rDoc("Octave note offset"), NULL, + [](const char *msg, RtData &d) { rObject *obj = (rObject *)d.obj; if(!rtosc_narguments(msg)) { @@ -128,7 +133,8 @@ static const Ports voicePorts = { obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; } }}, - {"coarsedetune::c:i", NULL, NULL, [](const char *msg, RtData &d) + {"coarsedetune::c:i", rProp(parameter) rDoc("Coarse note detune"), NULL, + [](const char *msg, RtData &d) { rObject *obj = (rObject *)d.obj; if(!rtosc_narguments(msg)) { @@ -143,14 +149,18 @@ static const Ports voicePorts = { }}, //weird stuff for PCoarseDetune - {"FMdetunevalue:", NULL, NULL, [](const char *, RtData &d) + {"FMdetunevalue:", rMap(unit,cents) rDoc("Get modulator detune"), NULL, [](const char *, RtData &d) { rObject *obj = (rObject *)d.obj; + unsigned detuneType = + obj->PFMDetuneType == 0 ? *(obj->GlobalPDetuneType) + : obj->PFMDetuneType; //TODO check if this is accurate or if PCoarseDetune is utilized //TODO do the same for the other engines - d.reply(d.loc, "f", getdetune(obj->PFMDetuneType, 0, obj->PFMDetune)); + d.reply(d.loc, "f", getdetune(detuneType, 0, obj->PFMDetune)); }}, - {"FMoctave::c:i", NULL, NULL, [](const char *msg, RtData &d) + {"FMoctave::c:i", rProp(parameter) rDoc("Octave note offset for modulator"), NULL, + [](const char *msg, RtData &d) { rObject *obj = (rObject *)d.obj; if(!rtosc_narguments(msg)) { @@ -163,7 +173,8 @@ static const Ports voicePorts = { obj->PFMCoarseDetune = k*1024 + obj->PFMCoarseDetune%1024; } }}, - {"FMcoarsedetune::c:i", NULL, NULL, [](const char *msg, RtData &d) + {"FMcoarsedetune::c:i", rProp(parameter) rDoc("Coarse note detune for modulator"), + NULL, [](const char *msg, RtData &d) { rObject *obj = (rObject *)d.obj; if(!rtosc_narguments(msg)) { @@ -178,7 +189,8 @@ static const Ports voicePorts = { }}, //Reader - {"unisonFrequencySpreadCents:", NULL, NULL, [](const char *, RtData &d) + {"unisonFrequencySpreadCents:", rMap(unit,cents) rDoc("Unison Frequency Spread"), + NULL, [](const char *, RtData &d) { rObject *obj = (rObject *)d.obj; d.reply(d.loc, "f", obj->getUnisonFrequencySpreadCents()); @@ -224,12 +236,14 @@ static const Ports globalPorts = { rParamZyn(Hrandgrouping, "How randomness is applied to multiple voices using the same oscil"), //weird stuff for PCoarseDetune - {"detunevalue:", NULL, NULL, [](const char *, RtData &d) + {"detunevalue:", rMap(unit,cents) rDoc("Get detune in cents"), NULL, + [](const char *, RtData &d) { rObject *obj = (rObject *)d.obj; d.reply(d.loc, "f", getdetune(obj->PDetuneType, 0, obj->PDetune)); }}, - {"octave::c:i", NULL, NULL, [](const char *msg, RtData &d) + {"octave::c:i", rProp(parameter) rDoc("Octave note offset"), NULL, + [](const char *msg, RtData &d) { rObject *obj = (rObject *)d.obj; if(!rtosc_narguments(msg)) { @@ -242,7 +256,8 @@ static const Ports globalPorts = { obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; } }}, - {"coarsedetune::c:i", NULL, NULL, [](const char *msg, RtData &d) + {"coarsedetune::c:i", rProp(parameter) rDoc("Coarse note detune"), NULL, + [](const char *msg, RtData &d) { rObject *obj = (rObject *)d.obj; if(!rtosc_narguments(msg)) { @@ -279,8 +294,10 @@ ADnoteParameters::ADnoteParameters(const SYNTH_T &synth, FFTwrapper *fft_) fft = fft_; - for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) + for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + VoicePar[nvoice].GlobalPDetuneType = &GlobalPar.PDetuneType; EnableVoice(synth, nvoice); + } defaults(); } @@ -524,40 +541,6 @@ ADnoteParameters::~ADnoteParameters() KillVoice(nvoice); } -int ADnoteParameters::get_unison_size_index(int nvoice) const -{ - int index = 0; - if(nvoice >= NUM_VOICES) - return 0; - int unison = VoicePar[nvoice].Unison_size; - - while(1) { - if(ADnote_unison_sizes[index] >= unison) - return index; - - if(ADnote_unison_sizes[index] == 0) - return index - 1; - - index++; - } - return 0; -} - -void ADnoteParameters::set_unison_size_index(int nvoice, int index) { - int unison = 1; - for(int i = 0; i <= index; ++i) { - unison = ADnote_unison_sizes[i]; - if(unison == 0) { - unison = ADnote_unison_sizes[i - 1]; - break; - } - } - - VoicePar[nvoice].Unison_size = unison; -} - - - void ADnoteParameters::add2XMLsection(XMLwrapper *xml, int n) { int nvoice = n; diff --git a/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.h b/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.h index 75544875d..b73720a86 100644 --- a/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.h +++ b/source/native-plugins/zynaddsubfx/Params/ADnoteParameters.h @@ -29,8 +29,6 @@ enum FMTYPE { NONE, MORPH, RING_MOD, PHASE_MOD, FREQ_MOD, PITCH_MOD }; -static const int ADnote_unison_sizes[] = -{1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 25, 30, 40, 50, 0}; /*****************************************************************/ /* GLOBAL PARAMETERS */ @@ -284,6 +282,8 @@ struct ADnoteVoiceParam { unsigned char PFMAmpEnvelopeEnabled; EnvelopeParams *FMAmpEnvelope; + unsigned char *GlobalPDetuneType; + static const rtosc::Ports &ports; }; @@ -306,8 +306,6 @@ class ADnoteParameters:public PresetsArray float getBandwidthDetuneMultiplier() const; float getUnisonFrequencySpreadCents(int nvoice) const; - int get_unison_size_index(int nvoice) const; - void set_unison_size_index(int nvoice, int index); static const rtosc::Ports &ports; void defaults(int n); //n is the nvoice void add2XMLsection(XMLwrapper *xml, int n); diff --git a/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.cpp b/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.cpp index a88a57f0d..bf5f1d72f 100644 --- a/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.cpp +++ b/source/native-plugins/zynaddsubfx/Params/EnvelopeParams.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -131,7 +132,13 @@ void EnvelopeParams::paste(const EnvelopeParams &ep) //Avoid undefined behavior if(&ep == this) return; - memcpy((char*)this, (const char*)&ep, sizeof(*this)); + + + char *base = (char*)&this->Pfreemode; + char *end = (char*)&this->DR_val; + assert(end-base > 0); + + memcpy((char*)&this->Pfreemode, (const char*)&ep.Pfreemode, 1+end-base); } float EnvelopeParams::getdt(char i) const diff --git a/source/native-plugins/zynaddsubfx/Params/FilterParams.cpp b/source/native-plugins/zynaddsubfx/Params/FilterParams.cpp index a14a799d1..c0224a2b3 100644 --- a/source/native-plugins/zynaddsubfx/Params/FilterParams.cpp +++ b/source/native-plugins/zynaddsubfx/Params/FilterParams.cpp @@ -105,13 +105,14 @@ const rtosc::Ports FilterParams::ports = { if(rtosc_narguments(msg)) rChangeCb; }}, - {"centerfreq:", NULL, NULL, - [](const char *, RtData &d) { + {"centerfreq:", rDoc("Get the center frequency of the formant's graph"), + NULL, [](const char *, RtData &d) { FilterParams *obj = (FilterParams *) d.obj; d.reply(d.loc, "f", obj->getcenterfreq()); }}, - {"octavesfreq:", NULL, NULL, - [](const char *, RtData &d) { + {"octavesfreq:", + rDoc("Get the number of octave that the formant functions applies to"), + NULL, [](const char *, RtData &d) { FilterParams *obj = (FilterParams *) d.obj; d.reply(d.loc, "f", obj->getoctavesfreq()); }}, diff --git a/source/native-plugins/zynaddsubfx/Params/LFOParams.cpp b/source/native-plugins/zynaddsubfx/Params/LFOParams.cpp index d7444e10f..27fc0b98d 100644 --- a/source/native-plugins/zynaddsubfx/Params/LFOParams.cpp +++ b/source/native-plugins/zynaddsubfx/Params/LFOParams.cpp @@ -49,8 +49,6 @@ static const rtosc::Ports _ports = { const rtosc::Ports &LFOParams::ports = _ports; -int LFOParams::time; - LFOParams::LFOParams() { Dfreq = 64; @@ -61,7 +59,6 @@ LFOParams::LFOParams() Ddelay = 0; Dcontinous = 0; fel = 0; - time = 0; defaults(); } @@ -94,7 +91,6 @@ LFOParams::LFOParams(char Pfreq_, Ddelay = Pdelay_; Dcontinous = Pcontinous_; fel = fel_; - time = 0; defaults(); } diff --git a/source/native-plugins/zynaddsubfx/Params/LFOParams.h b/source/native-plugins/zynaddsubfx/Params/LFOParams.h index 289a12f74..08e5aa43e 100644 --- a/source/native-plugins/zynaddsubfx/Params/LFOParams.h +++ b/source/native-plugins/zynaddsubfx/Params/LFOParams.h @@ -28,6 +28,14 @@ class XMLwrapper; +#define LFO_SINE 0 +#define LFO_TRIANGLE 1 +#define LFO_SQUARE 2 +#define LFO_RAMPUP 3 +#define LFO_RAMPDOWN 4 +#define LFO_EXP_DOWN1 5 +#define LFO_EXP_DOWN2 6 + class LFOParams:public Presets { public: @@ -60,7 +68,6 @@ class LFOParams:public Presets unsigned char Pstretch; /**Pbandwidth); }}}, - {"bandwidthvalue:", NULL, NULL, + {"bandwidthvalue:", rMap(unit, cents) rDoc("Get Bandwidth"), NULL, [](const char *, rtosc::RtData &d) { PADnoteParameters *p = ((PADnoteParameters*)d.obj); d.reply(d.loc, "f", p->setPbandwidth(p->Pbandwidth)); @@ -143,7 +141,7 @@ static const rtosc::Ports PADnotePorts = d.reply(d.loc, "b", n*sizeof(float), tmp); d.reply(d.loc, "i", realbw); delete[] tmp;}}, - {"sample#64:ifb", rDoc("Nothing to see here"), 0, + {"sample#64:ifb", rProp(internal) rDoc("Nothing to see here"), 0, [](const char *m, rtosc::RtData &d) { PADnoteParameters *p = (PADnoteParameters*)d.obj; @@ -157,12 +155,14 @@ static const rtosc::Ports PADnotePorts = //XXX TODO memory managment (deallocation of smp buffer) }}, //weird stuff for PCoarseDetune - {"detunevalue:", NULL, NULL, [](const char *, RtData &d) + {"detunevalue:", rMap(unit,cents) rDoc("Get detune value"), NULL, + [](const char *, RtData &d) { PADnoteParameters *obj = (PADnoteParameters *)d.obj; d.reply(d.loc, "f", getdetune(obj->PDetuneType, 0, obj->PDetune)); }}, - {"octave::c:i", NULL, NULL, [](const char *msg, RtData &d) + {"octave::c:i", rProp(parameter) rDoc("Octave note offset"), NULL, + [](const char *msg, RtData &d) { PADnoteParameters *obj = (PADnoteParameters *)d.obj; if(!rtosc_narguments(msg)) { @@ -175,7 +175,8 @@ static const rtosc::Ports PADnotePorts = obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; } }}, - {"coarsedetune::c:i", NULL, NULL, [](const char *msg, RtData &d) + {"coarsedetune::c:i", rProp(parameter) rDoc("Coarse note detune"), NULL, + [](const char *msg, RtData &d) { PADnoteParameters *obj = (PADnoteParameters *)d.obj; if(!rtosc_narguments(msg)) { diff --git a/source/native-plugins/zynaddsubfx/Params/SUBnoteParameters.cpp b/source/native-plugins/zynaddsubfx/Params/SUBnoteParameters.cpp index 7b5faf448..91b3ead12 100644 --- a/source/native-plugins/zynaddsubfx/Params/SUBnoteParameters.cpp +++ b/source/native-plugins/zynaddsubfx/Params/SUBnoteParameters.cpp @@ -93,7 +93,8 @@ static const rtosc::Ports SUBnotePorts = { d.reply(d.loc, "f", getdetune(obj->PDetuneType, 0, obj->PDetune)); }}, //weird stuff for PCoarseDetune - {"octave::c:i", rDoc("Note octave shift"), NULL, [](const char *msg, RtData &d) + {"octave::c:i", rProp(parameter) rDoc("Note octave shift"), NULL, + [](const char *msg, RtData &d) { SUBnoteParameters *obj = (SUBnoteParameters *)d.obj; if(!rtosc_narguments(msg)) { @@ -106,7 +107,8 @@ static const rtosc::Ports SUBnotePorts = { obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; } }}, - {"coarsedetune::c:i", rDoc("Note coarse detune"), NULL, [](const char *msg, RtData &d) + {"coarsedetune::c:i", rProp(parameter) rDoc("Note coarse detune"), NULL, + [](const char *msg, RtData &d) { SUBnoteParameters *obj = (SUBnoteParameters *)d.obj; if(!rtosc_narguments(msg)) { diff --git a/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp b/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp index 5c9881625..bf285eb2a 100644 --- a/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp @@ -49,7 +49,6 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) NoteEnabled = ON; basefreq = spars.frequency; velocity = spars.velocity; - time = 0.0f; stereo = pars.GlobalPar.PStereo; NoteGlobalPar.Detune = getdetune(pars.GlobalPar.PDetuneType, @@ -87,6 +86,9 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) NoteGlobalPar.Punch.Enabled = 0; for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { + for (int i = 0; i < 14; i++) + pinking[nvoice][i] = 0.0; + pars.VoicePar[nvoice].OscilSmp->newrandseed(prng()); NoteVoicePar[nvoice].OscilSmp = NULL; NoteVoicePar[nvoice].FMSmp = NULL; @@ -106,6 +108,10 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) if(unison < 1) unison = 1; + // Since noise unison of greater than two is touch goofy... + if (pars.VoicePar[nvoice].Type != 0 && unison > 2) + unison = 2; + //compute unison unison_size[nvoice] = unison; @@ -397,6 +403,13 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) initparameters(); } +SynthNote *ADnote::cloneLegato(void) +{ + SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity, + (bool)portamento, legato.param.midinote, true}; + return memory.alloc(&pars, sp); +} + // ADlegatonote: This function is (mostly) a copy of ADnote(...) and // initparameters() stuck together with some lines removed so that it // only alter the already playing note (to perform legato). It is @@ -711,6 +724,7 @@ void ADnote::initparameters() // Global Parameters NoteGlobalPar.initparameters(pars.GlobalPar, synth, + time, memory, basefreq, velocity, stereo); @@ -753,7 +767,7 @@ void ADnote::initparameters() } if(param.PAmpLfoEnabled) { - vce.AmpLfo = memory.alloc(*param.AmpLfo, basefreq, synth.dt()); + vce.AmpLfo = memory.alloc(*param.AmpLfo, basefreq, time); newamplitude[nvoice] *= vce.AmpLfo->amplfoout(); } @@ -762,7 +776,7 @@ void ADnote::initparameters() vce.FreqEnvelope = memory.alloc(*param.FreqEnvelope, basefreq, synth.dt()); if(param.PFreqLfoEnabled) - vce.FreqLfo = memory.alloc(*param.FreqLfo, basefreq, synth.dt()); + vce.FreqLfo = memory.alloc(*param.FreqLfo, basefreq, time); /* Voice Filter Parameters Init */ if(param.PFilterEnabled != 0) { @@ -776,7 +790,7 @@ void ADnote::initparameters() vce.FilterEnvelope = memory.alloc(*param.FilterEnvelope, basefreq, synth.dt()); if(param.PFilterLfoEnabled) - vce.FilterLfo = memory.alloc(*param.FilterLfo, basefreq, synth.dt()); + vce.FilterLfo = memory.alloc(*param.FilterLfo, basefreq, time); vce.FilterFreqTracking = param.VoiceFilter->getfreqtracking(basefreq); @@ -1071,7 +1085,6 @@ void ADnote::computecurrentparameters() } } } - time += synth.buffersize_f / synth.samplerate_f; } @@ -1417,7 +1430,7 @@ inline void ADnote::ComputeVoiceOscillatorPitchModulation(int /*nvoice*/) /* * Computes the Noise */ -inline void ADnote::ComputeVoiceNoise(int nvoice) +inline void ADnote::ComputeVoiceWhiteNoise(int nvoice) { for(int k = 0; k < unison_size[nvoice]; ++k) { float *tw = tmpwave_unison[k]; @@ -1426,6 +1439,25 @@ inline void ADnote::ComputeVoiceNoise(int nvoice) } } +inline void ADnote::ComputeVoicePinkNoise(int nvoice) +{ + for(int k = 0; k < unison_size[nvoice]; ++k) { + float *tw = tmpwave_unison[k]; + float *f = &pinking[nvoice][k > 0 ? 7 : 0]; + for(int i = 0; i < synth.buffersize; ++i) { + float white = (RND-0.5)/4.0; + f[0] = 0.99886*f[0]+white*0.0555179; + f[1] = 0.99332*f[1]+white*0.0750759; + f[2] = 0.96900*f[2]+white*0.1538520; + f[3] = 0.86650*f[3]+white*0.3104856; + f[4] = 0.55000*f[4]+white*0.5329522; + f[5] = -0.7616*f[5]-white*0.0168980; + tw[i] = f[0]+f[1]+f[2]+f[3]+f[4]+f[5]+f[6]+white*0.5362; + f[6] = white*0.115926; + } + } +} + /* @@ -1448,27 +1480,34 @@ int ADnote::noteout(float *outl, float *outr) if((NoteVoicePar[nvoice].Enabled != ON) || (NoteVoicePar[nvoice].DelayTicks > 0)) continue; - if(NoteVoicePar[nvoice].noisetype == 0) //voice mode=sound - switch(NoteVoicePar[nvoice].FMEnabled) { - case MORPH: - ComputeVoiceOscillatorMorph(nvoice); - break; - case RING_MOD: - ComputeVoiceOscillatorRingModulation(nvoice); - break; - case PHASE_MOD: - ComputeVoiceOscillatorFrequencyModulation(nvoice, 0); - break; - case FREQ_MOD: - ComputeVoiceOscillatorFrequencyModulation(nvoice, 1); - break; - //case PITCH_MOD:ComputeVoiceOscillatorPitchModulation(nvoice);break; - default: - ComputeVoiceOscillator_LinearInterpolation(nvoice); - //if (config.cfg.Interpolation) ComputeVoiceOscillator_CubicInterpolation(nvoice); - } - else - ComputeVoiceNoise(nvoice); + switch (NoteVoicePar[nvoice].noisetype) { + case 0: //voice mode=sound + switch(NoteVoicePar[nvoice].FMEnabled) { + case MORPH: + ComputeVoiceOscillatorMorph(nvoice); + break; + case RING_MOD: + ComputeVoiceOscillatorRingModulation(nvoice); + break; + case PHASE_MOD: + ComputeVoiceOscillatorFrequencyModulation(nvoice, 0); + break; + case FREQ_MOD: + ComputeVoiceOscillatorFrequencyModulation(nvoice, 1); + break; + //case PITCH_MOD:ComputeVoiceOscillatorPitchModulation(nvoice);break; + default: + ComputeVoiceOscillator_LinearInterpolation(nvoice); + //if (config.cfg.Interpolation) ComputeVoiceOscillator_CubicInterpolation(nvoice); + } + break; + case 1: + ComputeVoiceWhiteNoise(nvoice); + break; + default: + ComputeVoicePinkNoise(nvoice); + break; + } // Voice Processing @@ -1772,15 +1811,16 @@ void ADnote::Global::kill(Allocator &memory) void ADnote::Global::initparameters(const ADnoteGlobalParam ¶m, const SYNTH_T &synth, + const AbsTime &time, class Allocator &memory, float basefreq, float velocity, bool stereo) { FreqEnvelope = memory.alloc(*param.FreqEnvelope, basefreq, synth.dt()); - FreqLfo = memory.alloc(*param.FreqLfo, basefreq, synth.dt()); + FreqLfo = memory.alloc(*param.FreqLfo, basefreq, time); AmpEnvelope = memory.alloc(*param.AmpEnvelope, basefreq, synth.dt()); - AmpLfo = memory.alloc(*param.AmpLfo, basefreq, synth.dt()); + AmpLfo = memory.alloc(*param.AmpLfo, basefreq, time); Volume = 4.0f * powf(0.1f, 3.0f * (1.0f - param.PVolume / 96.0f)) //-60 dB .. 0 dB * VelF(velocity, param.PAmpVelocityScaleFunction); //sensing @@ -1794,7 +1834,7 @@ void ADnote::Global::initparameters(const ADnoteGlobalParam ¶m, GlobalFilterR = NULL; FilterEnvelope = memory.alloc(*param.FilterEnvelope, basefreq, synth.dt()); - FilterLfo = memory.alloc(*param.FilterLfo, basefreq, synth.dt()); + FilterLfo = memory.alloc(*param.FilterLfo, basefreq, time); FilterQ = param.GlobalFilter->getq(); FilterFreqTracking = param.GlobalFilter->getfreqtracking(basefreq); } diff --git a/source/native-plugins/zynaddsubfx/Synth/ADnote.h b/source/native-plugins/zynaddsubfx/Synth/ADnote.h index 6cfc00c4b..14ebde744 100644 --- a/source/native-plugins/zynaddsubfx/Synth/ADnote.h +++ b/source/native-plugins/zynaddsubfx/Synth/ADnote.h @@ -53,6 +53,8 @@ class ADnote:public SynthNote int noteout(float *outl, float *outr); void releasekey(); int finished() const; + + virtual SynthNote *cloneLegato(void) override; private: /**Changes the frequency of an oscillator. @@ -97,7 +99,8 @@ class ADnote:public SynthNote inline void ComputeVoiceOscillatorPitchModulation(int nvoice); /**Generate Noise Samples for Voice*/ - inline void ComputeVoiceNoise(int nvoice); + inline void ComputeVoiceWhiteNoise(int nvoice); + inline void ComputeVoicePinkNoise(int nvoice); /**Fadein in a way that removes clicks but keep sound "punchy"*/ inline void fadein(float *smps) const; @@ -119,6 +122,7 @@ class ADnote:public SynthNote void kill(Allocator &memory); void initparameters(const ADnoteGlobalParam ¶m, const SYNTH_T &synth, + const AbsTime &time, class Allocator &memory, float basefreq, float velocity, bool stereo); @@ -246,8 +250,8 @@ class ADnote:public SynthNote /* INTERNAL VALUES OF THE NOTE AND OF THE VOICES */ /********************************************************/ - //time from the start of the note - float time; + //pinking filter (Paul Kellet) + float pinking[NUM_VOICES][14]; //the size of unison for a single voice int unison_size[NUM_VOICES]; diff --git a/source/native-plugins/zynaddsubfx/Synth/LFO.cpp b/source/native-plugins/zynaddsubfx/Synth/LFO.cpp index 14d6ac2c8..37449959c 100644 --- a/source/native-plugins/zynaddsubfx/Synth/LFO.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/LFO.cpp @@ -28,8 +28,10 @@ #include #include -LFO::LFO(const LFOParams &lfopars, float basefreq, float dt_) - :dt(dt_) +LFO::LFO(const LFOParams &lfopars, float basefreq, const AbsTime &t) + :delayTime(t, lfopars.Pdelay / 127.0f * 4.0f), //0..4 sec + waveShape(lfopars.PLFOtype), + deterministic(!lfopars.Pfreqrand) { int stretch = lfopars.Pstretch; if(stretch == 0) @@ -38,24 +40,24 @@ LFO::LFO(const LFOParams &lfopars, float basefreq, float dt_) //max 2x/octave const float lfostretch = powf(basefreq / 440.0f, (stretch - 64.0f) / 63.0f); - float lfofreq = + const float lfofreq = (powf(2, lfopars.Pfreq * 10.0f) - 1.0f) / 12.0f * lfostretch; - incx = fabs(lfofreq) * dt; + phaseInc = fabs(lfofreq) * t.dt(); if(!lfopars.Pcontinous) { if(lfopars.Pstartphase == 0) - x = RND; + phase = RND; else - x = fmod((lfopars.Pstartphase - 64.0f) / 127.0f + 1.0f, 1.0f); + phase = fmod((lfopars.Pstartphase - 64.0f) / 127.0f + 1.0f, 1.0f); } else { - const float tmp = fmod(lfopars.time * incx, 1.0f); - x = fmod((lfopars.Pstartphase - 64.0f) / 127.0f + 1.0f + tmp, 1.0f); + const float tmp = fmod(t.time() * phaseInc, 1.0f); + phase = fmod((lfopars.Pstartphase - 64.0f) / 127.0f + 1.0f + tmp, 1.0f); } //Limit the Frequency(or else...) - if(incx > 0.49999999f) - incx = 0.499999999f; + if(phaseInc > 0.49999999f) + phaseInc = 0.499999999f; lfornd = limit(lfopars.Prandomness / 127.0f, 0.0f, 1.0f); @@ -70,88 +72,72 @@ LFO::LFO(const LFOParams &lfopars, float basefreq, float dt_) break; //in octave default: lfointensity = powf(2, lfopars.Pintensity / 127.0f * 11.0f) - 1.0f; //in centi - x -= 0.25f; //chance the starting phase + phase -= 0.25f; //chance the starting phase break; } amp1 = (1 - lfornd) + lfornd * RND; amp2 = (1 - lfornd) + lfornd * RND; - lfotype = lfopars.PLFOtype; - lfodelay = lfopars.Pdelay / 127.0f * 4.0f; //0..4 sec incrnd = nextincrnd = 1.0f; - freqrndenabled = (lfopars.Pfreqrand != 0); - computenextincrnd(); - computenextincrnd(); //twice because I want incrnd & nextincrnd to be random + computeNextFreqRnd(); + computeNextFreqRnd(); //twice because I want incrnd & nextincrnd to be random } LFO::~LFO() {} -/* - * LFO out - */ -float LFO::lfoout() +float LFO::baseOut(const char waveShape, const float phase) const { - float out; - switch(lfotype) { - case 1: //LFO_TRIANGLE - if((x >= 0.0f) && (x < 0.25f)) - out = 4.0f * x; + switch(waveShape) { + case LFO_TRIANGLE: + if(phase >= 0.0f && phase < 0.25f) + return 4.0f * phase; + else if(phase > 0.25f && phase < 0.75f) + return 2 - 4 * phase; else - if((x > 0.25f) && (x < 0.75f)) - out = 2 - 4 * x; - else - out = 4.0f * x - 4.0f; + return 4.0f * phase - 4.0f; break; - case 2: //LFO_SQUARE - if(x < 0.5f) - out = -1; + case LFO_SQUARE: + if(phase < 0.5f) + return -1; else - out = 1; - break; - case 3: //LFO_RAMPUP - out = (x - 0.5f) * 2.0f; + return 1; break; - case 4: //LFO_RAMPDOWN - out = (0.5f - x) * 2.0f; - break; - case 5: //LFO_EXP_DOWN 1 - out = powf(0.05f, x) * 2.0f - 1.0f; - break; - case 6: //LFO_EXP_DOWN 2 - out = powf(0.001f, x) * 2.0f - 1.0f; - break; - default: - out = cosf(x * 2.0f * PI); //LFO_SINE + case LFO_RAMPUP: return (phase - 0.5f) * 2.0f; + case LFO_RAMPDOWN: return (0.5f - phase) * 2.0f; + case LFO_EXP_DOWN1: return powf(0.05f, phase) * 2.0f - 1.0f; + case LFO_EXP_DOWN2: return powf(0.001f, phase) * 2.0f - 1.0f; + default: return cosf(phase * 2.0f * PI); //LFO_SINE } +} - if((lfotype == 0) || (lfotype == 1)) - out *= lfointensity * (amp1 + x * (amp2 - amp1)); +float LFO::lfoout() +{ + float out = baseOut(waveShape, phase); + + if(waveShape == LFO_SINE || waveShape == LFO_TRIANGLE) + out *= lfointensity * (amp1 + phase * (amp2 - amp1)); else out *= lfointensity * amp2; - if(lfodelay < 0.00001f) { - if(freqrndenabled == 0) - x += incx; - else { - float tmp = (incrnd * (1.0f - x) + nextincrnd * x); - if(tmp > 1.0f) - tmp = 1.0f; - else - if(tmp < 0.0f) - tmp = 0.0f; - x += incx * tmp; - } - if(x >= 1) { - x = fmod(x, 1.0f); - amp1 = amp2; - amp2 = (1 - lfornd) + lfornd * RND; - - computenextincrnd(); - } + + if(delayTime.inFuture()) + return out; + + //Start oscillating + if(deterministic) + phase += phaseInc; + else { + const float tmp = (incrnd * (1.0f - phase) + nextincrnd * phase); + phase += phaseInc * limit(tmp, 0.0f, 1.0f); + } + if(phase >= 1) { + phase = fmod(phase, 1.0f); + amp1 = amp2; + amp2 = (1 - lfornd) + lfornd * RND; + + computeNextFreqRnd(); } - else - lfodelay -= dt; return out; } @@ -164,9 +150,9 @@ float LFO::amplfoout() } -void LFO::computenextincrnd() +void LFO::computeNextFreqRnd() { - if(freqrndenabled == 0) + if(deterministic) return; incrnd = nextincrnd; nextincrnd = powf(0.5f, lfofreqrnd) + RND * (powf(2.0f, lfofreqrnd) - 1.0f); diff --git a/source/native-plugins/zynaddsubfx/Synth/LFO.h b/source/native-plugins/zynaddsubfx/Synth/LFO.h index 6c53fc630..ebe5707a8 100644 --- a/source/native-plugins/zynaddsubfx/Synth/LFO.h +++ b/source/native-plugins/zynaddsubfx/Synth/LFO.h @@ -24,8 +24,9 @@ #define LFO_H #include "../globals.h" +#include "../Misc/Time.h" -/**Class for creating Low Frequency Ocillators*/ +/**Class for creating Low Frequency Oscillators*/ class LFO { public: @@ -34,24 +35,36 @@ class LFO * @param lfopars pointer to a LFOParams object * @param basefreq base frequency of LFO */ - LFO(const LFOParams &lfopars, float basefreq, float dt); - /**Deconstructor*/ + LFO(const LFOParams &lfopars, float basefreq, const AbsTime &t); ~LFO(); + float lfoout(); float amplfoout(); private: - float x; - float incx, incrnd, nextincrnd; - float amp1, amp2; // used for randomness + float baseOut(const char waveShape, const float phase) const; + //Phase of Oscillator + float phase; + //Phase Increment Per Frame + float phaseInc; + //Frequency Randomness + float incrnd, nextincrnd; + //Amplitude Randomness + float amp1, amp2; + + //Intensity of the wave float lfointensity; + //Amount Randomness float lfornd, lfofreqrnd; - float lfodelay; - const float dt; - /**\todo see if an enum would be better here*/ - char lfotype; - int freqrndenabled; - void computenextincrnd(); + //Delay before starting + RelTime delayTime; + + char waveShape; + + //If After initialization there are no calls to random number gen. + bool deterministic; + + void computeNextFreqRnd(void); }; #endif diff --git a/source/native-plugins/zynaddsubfx/Synth/OscilGen.cpp b/source/native-plugins/zynaddsubfx/Synth/OscilGen.cpp index 4022fa125..ab224783c 100644 --- a/source/native-plugins/zynaddsubfx/Synth/OscilGen.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/OscilGen.cpp @@ -44,38 +44,96 @@ pthread_t main_thread; const rtosc::Ports OscilGen::ports = { rSelf(OscilGen), rPaste, - PC(hmagtype), - PC(currentbasefunc), - PC(basefuncpar), - PC(basefuncmodulation), - PC(basefuncmodulationpar1), - PC(basefuncmodulationpar2), - PC(basefuncmodulationpar3), - PC(waveshaping), - PC(waveshapingfunction), - PC(filtertype), + //TODO ensure min/max + rOption(Phmagtype, + rOptions(linear,dB scale (-40), + dB scale (-60), dB scale (-80), + dB scale (-100)), + "Type of magnitude for harmonics"), + rOption(Pcurrentbasefunc, + rOptions(sine, triangle, + pulse, + saw, + power, + gauss, + diode, + abssine, + pulsesine, + stretchsine, + chirp, + absstretchsine, + chebyshev, + sqr, + spike, + circle), rOpt(127,use-as-base waveform), + "Base Waveform for harmonics"), + rParamZyn(Pbasefuncpar, + "Morph between possible base function shapes " + "(e.g. rising sawtooth vs a falling sawtooth)"), + rOption(Pbasefuncmodulation, + rOptions(None, Rev, Sine, Power), + "Modulation applied to Base function spectra"), + rParamZyn(Pbasefuncmodulationpar1, + "Base function modulation parameter"), + rParamZyn(Pbasefuncmodulationpar2, + "Base function modulation parameter"), + rParamZyn(Pbasefuncmodulationpar3, + "Base function modulation parameter"), + rParamZyn(Pwaveshaping, "Degree Of Waveshaping"), + rOption(Pwaveshapingfunction, + rOptions(Undistorted, + Arctangent, + Asymmetric, + Pow, + Sine, + Quantisize, + Zigzag, + Limiter, + Upper Limiter, + Lower Limiter, + Inverse Limiter, + Clip, + Asym2, + Pow2, + sigmoid), "Shape of distortion to be applied"), + rOption(Pfiltertype, rOptions(No Filter, + lp, hp1, hp1b, bp1, bs1, lp2, hp2, bp2, bs2, + cos, sin, low_shelf, s), "Harmonic Filter"), PC(filterpar1), PC(filterpar2), - PC(filterbeforews), + rToggle(Pfilterbeforews, "Filter before waveshaping spectra;" + "When enabled oscilfilter(freqs); then waveshape(freqs);, " + "otherwise waveshape(freqs); then oscilfilter(freqs);"), PC(satype), - PC(sapar), + rParamZyn(Psapar, "Spectral Adjustment Parameter"), rParamI(Pharmonicshift, "Amount of shift on harmonics"), rToggle(Pharmonicshiftfirst, "If harmonics are shifted before waveshaping/filtering"), - PC(modulation), - PC(modulationpar1), - PC(modulationpar2), - PC(modulationpar3), + rOption(Pmodulation, rOptions(None, Rev, Sine, Power), + "Frequency Modulation To Combined Spectra"), + rParamZyn(Pmodulationpar1, + "modulation parameter"), + rParamZyn(Pmodulationpar2, + "modulation parameter"), + rParamZyn(Pmodulationpar3, + "modulation parameter"), //FIXME realtime parameters lurking below PC(rand), - PC(amprandpower), - PC(amprandtype), - PC(adaptiveharmonics), - PC(adaptiveharmonicsbasefreq), - PC(adaptiveharmonicspower), - PC(adaptiveharmonicspar), + rParamZyn(Pamprandpower, + "Variance of harmonic randomness"), + rOption(Pamprandtype, rOptions(None, Pow, Sin), + "Harmonic random distribution to select from"), + rOption(Padaptiveharmonics, + rOptions(OFF, ON, Square, 2xSub, 2xAdd, 3xSub, 3xAdd, 4xSub, 4xAdd), + "Adaptive Harmonics Mode"), + rParamI(Padaptiveharmonicsbasefreq, rLinear(0,255), + "Base frequency of adaptive harmonic (30..3000Hz)"), + rParamI(Padaptiveharmonicspower,rLinear(0,200), + "Adaptive Harmonic Strength"), + rParamZyn(Padaptiveharmonicspar, + "Adaptive Harmonics Postprocessing Power"), //TODO update to rArray and test - {"phase#128::c", rDoc("Sets harmonic phase"), + {"phase#128::c", rProp(parameter) rDoc("Sets harmonic phase"), NULL, [](const char *m, rtosc::RtData &d) { const char *mm = m; while(*mm && !isdigit(*mm)) ++mm; @@ -86,7 +144,7 @@ const rtosc::Ports OscilGen::ports = { phase = rtosc_argument(m,0).i; }}, //TODO update to rArray and test - {"magnitude#128::c", rDoc("Sets harmonic magnitude"), + {"magnitude#128::c", rProp(parameter) rDoc("Sets harmonic magnitude"), NULL, [](const char *m, rtosc::RtData &d) { //printf("I'm at '%s'\n", d.loc); const char *mm = m; @@ -153,6 +211,10 @@ const rtosc::Ports OscilGen::ports = { NULL, [](const char *, rtosc::RtData &d) { ((OscilGen*)d.obj)->convert2sine(); }}, + {"use-as-base:", rProp(non-realtime) rDoc("Translates current waveform into base"), + NULL, [](const char *, rtosc::RtData &d) { + ((OscilGen*)d.obj)->useasbase(); + }}, {"prepare:b", rProp(internal) rProp(non-realtime) rProp(pointer) rDoc("Sets prepared fft data"), NULL, [](const char *m, rtosc::RtData &d) { //fprintf(stderr, "prepare:b got a message from '%s'\n", m); @@ -385,64 +447,45 @@ void OscilGen::convert2sine() */ void OscilGen::getbasefunction(float *smps) { - int i; float par = (Pbasefuncpar + 0.5f) / 128.0f; if(Pbasefuncpar == 64) par = 0.5f; - float basefuncmodulationpar1 = Pbasefuncmodulationpar1 / 127.0f, - basefuncmodulationpar2 = Pbasefuncmodulationpar2 / 127.0f, - basefuncmodulationpar3 = Pbasefuncmodulationpar3 / 127.0f; + float p1 = Pbasefuncmodulationpar1 / 127.0f, + p2 = Pbasefuncmodulationpar2 / 127.0f, + p3 = Pbasefuncmodulationpar3 / 127.0f; switch(Pbasefuncmodulation) { case 1: - basefuncmodulationpar1 = - (powf(2, basefuncmodulationpar1 * 5.0f) - 1.0f) / 10.0f; - basefuncmodulationpar3 = - floor((powf(2, basefuncmodulationpar3 * 5.0f) - 1.0f)); - if(basefuncmodulationpar3 < 0.9999f) - basefuncmodulationpar3 = -1.0f; + p1 = (powf(2, p1 * 5.0f) - 1.0f) / 10.0f; + p3 = floor(powf(2, p3 * 5.0f) - 1.0f); + if(p3 < 0.9999f) + p3 = -1.0f; break; case 2: - basefuncmodulationpar1 = - (powf(2, basefuncmodulationpar1 * 5.0f) - 1.0f) / 10.0f; - basefuncmodulationpar3 = 1.0f - + floor((powf(2, basefuncmodulationpar3 - * 5.0f) - 1.0f)); + p1 = (powf(2, p1 * 5.0f) - 1.0f) / 10.0f; + p3 = 1.0f + floor(powf(2, p3 * 5.0f) - 1.0f); break; case 3: - basefuncmodulationpar1 = - (powf(2, basefuncmodulationpar1 * 7.0f) - 1.0f) / 10.0f; - basefuncmodulationpar3 = 0.01f - + (powf(2, basefuncmodulationpar3 - * 16.0f) - 1.0f) / 10.0f; + p1 = (powf(2, p1 * 7.0f) - 1.0f) / 10.0f; + p3 = 0.01f + (powf(2, p3 * 16.0f) - 1.0f) / 10.0f; break; } base_func func = getBaseFunction(Pcurrentbasefunc); - for(i = 0; i < synth.oscilsize; ++i) { + for(int i = 0; i < synth.oscilsize; ++i) { float t = i * 1.0f / synth.oscilsize; switch(Pbasefuncmodulation) { - case 1: - t = t * basefuncmodulationpar3 + sinf( - (t - + basefuncmodulationpar2) * 2.0f - * PI) * basefuncmodulationpar1; //rev + case 1: //rev + t = t * p3 + sinf((t + p2) * 2.0f * PI) * p1; break; - case 2: - t = t + sinf( - (t * basefuncmodulationpar3 - + basefuncmodulationpar2) * 2.0f - * PI) * basefuncmodulationpar1; //sine + case 2: //sine + t += sinf( (t * p3 + p2) * 2.0f * PI) * p1; break; - case 3: - t = t + powf((1.0f - cosf( - (t - + basefuncmodulationpar2) * 2.0f - * PI)) * 0.5f, - basefuncmodulationpar3) * basefuncmodulationpar1; //power + case 3: //power + t += powf((1.0f - cosf((t + p2) * 2.0f * PI)) * 0.5f, p3) * p1; break; } @@ -652,12 +695,11 @@ void OscilGen::spectrumadjust(fft_t *freqs) break; } - normalize(freqs, synth.oscilsize); for(int i = 0; i < synth.oscilsize / 2; ++i) { - float mag = abs(oscilFFTfreqs, i); - float phase = M_PI_2 - arg(oscilFFTfreqs, i); + float mag = abs(freqs, i); + float phase = M_PI_2 - arg(freqs, i); switch(Psatype) { case 1: @@ -783,13 +825,12 @@ void OscilGen::prepare(fft_t *freqs) if(Pharmonicshiftfirst != 0) shiftharmonics(freqs); - if(Pfilterbeforews == 0) { - waveshape(freqs); - oscilfilter(freqs); - } - else { + if(Pfilterbeforews) { oscilfilter(freqs); waveshape(freqs); + } else { + waveshape(freqs); + oscilfilter(freqs); } modulation(freqs); @@ -876,9 +917,9 @@ void OscilGen::adaptiveharmonicpostprocess(fft_t *f, int size) if(Padaptiveharmonics == 2) { //2n+1 for(int i = 0; i < size; ++i) if((i % 2) == 0) - f[i] += inf[i]; //i=0 pt prima armonica,etc. + f[i] += inf[i]; //i=0 first harmonic,etc. } - else { //celelalte moduri + else { //other ways int nh = (Padaptiveharmonics - 3) / 2 + 2; int sub_vs_add = (Padaptiveharmonics - 3) % 2; if(sub_vs_add == 0) { diff --git a/source/native-plugins/zynaddsubfx/Synth/OscilGen.h b/source/native-plugins/zynaddsubfx/Synth/OscilGen.h index a68689d84..707efa67d 100644 --- a/source/native-plugins/zynaddsubfx/Synth/OscilGen.h +++ b/source/native-plugins/zynaddsubfx/Synth/OscilGen.h @@ -84,7 +84,7 @@ class OscilGen:public Presets unsigned char Pwaveshaping, Pwaveshapingfunction; unsigned char Pfiltertype, Pfilterpar1, Pfilterpar2; - unsigned char Pfilterbeforews; + bool Pfilterbeforews; unsigned char Psatype, Psapar; //spectrum adjust int Pharmonicshift; //how the harmonics are shifted diff --git a/source/native-plugins/zynaddsubfx/Synth/PADnote.cpp b/source/native-plugins/zynaddsubfx/Synth/PADnote.cpp index 2e2321644..4fc84e9f1 100644 --- a/source/native-plugins/zynaddsubfx/Synth/PADnote.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/PADnote.cpp @@ -28,7 +28,7 @@ #include "../Params/FilterParams.h" #include "../Misc/Util.h" -PADnote::PADnote(PADnoteParameters *parameters, +PADnote::PADnote(const PADnoteParameters *parameters, SynthParams pars, const int& interpolation) :SynthNote(pars), pars(*parameters), interpolation(interpolation) { @@ -121,9 +121,9 @@ void PADnote::setup(float freq, ((powf(10, 1.5f * pars.PPunchStrength / 127.0f) - 1.0f) * VelF(velocity, pars.PPunchVelocitySensing)); - float time = + const float time = powf(10, 3.0f * pars.PPunchTime / 127.0f) / 10000.0f; //0.1f .. 100 ms - float stretch = powf(440.0f / freq, pars.PPunchStretch / 64.0f); + const float stretch = powf(440.0f / freq, pars.PPunchStretch / 64.0f); NoteGlobalPar.Punch.dt = 1.0f / (time * synth.samplerate_f * stretch); } @@ -131,10 +131,10 @@ void PADnote::setup(float freq, NoteGlobalPar.Punch.Enabled = 0; NoteGlobalPar.FreqEnvelope = memory.alloc(*pars.FreqEnvelope, basefreq, synth.dt()); - NoteGlobalPar.FreqLfo = memory.alloc(*pars.FreqLfo, basefreq, synth.dt()); + NoteGlobalPar.FreqLfo = memory.alloc(*pars.FreqLfo, basefreq, time); NoteGlobalPar.AmpEnvelope = memory.alloc(*pars.AmpEnvelope, basefreq, synth.dt()); - NoteGlobalPar.AmpLfo = memory.alloc(*pars.AmpLfo, basefreq, synth.dt()); + NoteGlobalPar.AmpLfo = memory.alloc(*pars.AmpLfo, basefreq, time); } NoteGlobalPar.Volume = 4.0f @@ -154,7 +154,7 @@ void PADnote::setup(float freq, synth.samplerate, synth.buffersize); NoteGlobalPar.FilterEnvelope = memory.alloc(*pars.FilterEnvelope, basefreq, synth.dt()); - NoteGlobalPar.FilterLfo = memory.alloc(*pars.FilterLfo, basefreq, synth.dt()); + NoteGlobalPar.FilterLfo = memory.alloc(*pars.FilterLfo, basefreq, time); } NoteGlobalPar.FilterQ = pars.GlobalFilter->getq(); NoteGlobalPar.FilterFreqTracking = pars.GlobalFilter->getfreqtracking( @@ -166,6 +166,13 @@ void PADnote::setup(float freq, } } +SynthNote *PADnote::cloneLegato(void) +{ + SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity, + (bool)portamento, legato.param.midinote, true}; + return memory.alloc(&pars, sp, interpolation); +} + void PADnote::legatonote(LegatoParams pars) { // Manage legato stuff diff --git a/source/native-plugins/zynaddsubfx/Synth/PADnote.h b/source/native-plugins/zynaddsubfx/Synth/PADnote.h index dda0641f6..aa2b4f767 100644 --- a/source/native-plugins/zynaddsubfx/Synth/PADnote.h +++ b/source/native-plugins/zynaddsubfx/Synth/PADnote.h @@ -30,10 +30,11 @@ class PADnote:public SynthNote { public: - PADnote(PADnoteParameters *parameters, SynthParams pars, + PADnote(const PADnoteParameters *parameters, SynthParams pars, const int &interpolation); ~PADnote(); + SynthNote *cloneLegato(void); void legatonote(LegatoParams pars); int noteout(float *outl, float *outr); diff --git a/source/native-plugins/zynaddsubfx/Synth/Resonance.cpp b/source/native-plugins/zynaddsubfx/Synth/Resonance.cpp index 4e34fd67d..0601ece9a 100644 --- a/source/native-plugins/zynaddsubfx/Synth/Resonance.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/Resonance.cpp @@ -44,7 +44,7 @@ const rtosc::Ports Resonance::ports = { rAction(smooth, "Smooth out frequency response"), rAction(zero, "Reset frequency response"), //UI Value listeners - {"centerfreq:", rDoc("Get center frequency"), NULL, + {"centerfreq:", rDoc("Get center frequency") rMap(unit, Hz), NULL, [](const char *, RtData &d) {d.reply(d.loc, "f", ((rObject*)d.obj)->getcenterfreq());}}, {"octavesfreq:", rDoc("Get center freq of graph"), NULL, diff --git a/source/native-plugins/zynaddsubfx/Synth/SUBnote.cpp b/source/native-plugins/zynaddsubfx/Synth/SUBnote.cpp index 85c6e6621..ea09d0d88 100644 --- a/source/native-plugins/zynaddsubfx/Synth/SUBnote.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/SUBnote.cpp @@ -33,7 +33,7 @@ #include "../Misc/Util.h" #include "../Misc/Allocator.h" -SUBnote::SUBnote(SUBnoteParameters *parameters, SynthParams &spars) +SUBnote::SUBnote(const SUBnoteParameters *parameters, SynthParams &spars) :SynthNote(spars), pars(*parameters) { NoteEnabled = ON; @@ -46,6 +46,7 @@ void SUBnote::setup(float freq, int midinote, bool legato) { + this->velocity = velocity; portamento = portamento_; NoteEnabled = ON; volume = powf(0.1f, 3.0f * (1.0f - pars.PVolume / 96.0f)); //-60 dB .. 0 dB @@ -210,6 +211,13 @@ void SUBnote::setup(float freq, oldamplitude = newamplitude; } +SynthNote *SUBnote::cloneLegato(void) +{ + SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity, + (bool)portamento, legato.param.midinote, true}; + return memory.alloc(&pars, sp); +} + void SUBnote::legatonote(LegatoParams pars) { // Manage legato stuff diff --git a/source/native-plugins/zynaddsubfx/Synth/SUBnote.h b/source/native-plugins/zynaddsubfx/Synth/SUBnote.h index 17e648da2..ac1c6c68e 100644 --- a/source/native-plugins/zynaddsubfx/Synth/SUBnote.h +++ b/source/native-plugins/zynaddsubfx/Synth/SUBnote.h @@ -30,9 +30,10 @@ class SUBnote:public SynthNote { public: - SUBnote(SUBnoteParameters *parameters, SynthParams &pars); + SUBnote(const SUBnoteParameters *parameters, SynthParams &pars); ~SUBnote(); + SynthNote *cloneLegato(void); void legatonote(LegatoParams pars); int noteout(float *outl, float *outr); //note output,return 0 if the note is finished @@ -100,6 +101,7 @@ class SUBnote:public SynthNote int oldpitchwheel, oldbandwidth; float globalfiltercenterq; + float velocity; }; #endif diff --git a/source/native-plugins/zynaddsubfx/Synth/SynthNote.cpp b/source/native-plugins/zynaddsubfx/Synth/SynthNote.cpp index 4e88ca477..45054dfcb 100644 --- a/source/native-plugins/zynaddsubfx/Synth/SynthNote.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/SynthNote.cpp @@ -3,9 +3,9 @@ #include SynthNote::SynthNote(SynthParams &pars) - :legato(pars.synth, pars.frequency, pars.velocity, pars.portamento, - pars.note, pars.quiet), - memory(pars.memory), ctl(pars.ctl), synth(pars.synth) + :memory(pars.memory), + legato(pars.synth, pars.frequency, pars.velocity, pars.portamento, + pars.note, pars.quiet), ctl(pars.ctl), synth(pars.synth), time(pars.time) {} SynthNote::Legato::Legato(const SYNTH_T &synth_, float freq, float vel, int port, diff --git a/source/native-plugins/zynaddsubfx/Synth/SynthNote.h b/source/native-plugins/zynaddsubfx/Synth/SynthNote.h index a7ccae50e..38e98a554 100644 --- a/source/native-plugins/zynaddsubfx/Synth/SynthNote.h +++ b/source/native-plugins/zynaddsubfx/Synth/SynthNote.h @@ -28,8 +28,9 @@ class Controller; struct SynthParams { Allocator &memory; //Memory Allocator for the Note to use - Controller &ctl; + const Controller &ctl; const SYNTH_T &synth; + const AbsTime &time; float frequency; //Note base frequency float velocity; //Velocity of the Note bool portamento;//True if portamento is used for this note @@ -65,8 +66,14 @@ class SynthNote virtual int finished() const = 0; virtual void legatonote(LegatoParams pars) = 0; + + virtual SynthNote *cloneLegato(void) = 0; + /* For polyphonic aftertouch needed */ void setVelocity(float velocity_); + + //Realtime Safe Memory Allocator For notes + class Allocator &memory; protected: // Legato transitions class Legato @@ -87,6 +94,7 @@ class SynthNote int length; float m, step; } fade; + public: struct { // Note parameters float freq, vel; bool portamento; @@ -103,10 +111,9 @@ class SynthNote void setDecounter(int decounter_) {decounter = decounter_; } } legato; - //Realtime Safe Memory Allocator For notes - class Allocator &memory; const Controller &ctl; const SYNTH_T &synth; + const AbsTime &time; }; #endif diff --git a/source/native-plugins/zynaddsubfx/UI/ADnoteUI.fl b/source/native-plugins/zynaddsubfx/UI/ADnoteUI.fl index 79e56cdd0..fe78c188b 100644 --- a/source/native-plugins/zynaddsubfx/UI/ADnoteUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/ADnoteUI.fl @@ -70,12 +70,21 @@ decl {\#include "OscilGenUI.h"} {public local decl {\#include "PresetsUI.h"} {public local } +decl {\#include "PartUI.h"} {private local +} + +decl {\#include "MasterUI.h"} {private local +} + +decl {extern class MasterUI *ui;} {private local +} + class ADvoicelistitem {open : {public Fl_Osc_Group} } { Function {make_window()} {open private } { Fl_Window ADnoteVoiceListItem {open - private xywh {346 881 615 100} type Double box UP_FRAME + private xywh {117 90 670 100} type Double box NO_BOX class Fl_Osc_Group visible } { Fl_Box {} { @@ -83,61 +92,61 @@ class ADvoicelistitem {open : {public Fl_Osc_Group} code0 {ADnoteVoiceListItem->base = loc;} } Fl_Group voicelistitemgroup {open - private xywh {50 0 570 25} + private xywh {0 0 670 25} class Fl_Osc_Group } { Fl_Value_Slider voicevolume { - tooltip Volume xywh {90 5 115 20} type {Horz Knob} box NO_BOX labelsize 8 align 5 maximum 127 step 1 + tooltip Volume xywh {132 5 115 20} type {Horz Knob} box NO_BOX labelsize 8 align 5 maximum 127 step 1 code1 {o->init("PVolume");} class Fl_Osc_VSlider } Fl_Check_Button voiceresonanceenabled { - tooltip {Resonance On/Off} xywh {245 7 15 17} down_box DOWN_BOX labeltype EMBOSSED_LABEL labelfont 1 labelsize 11 align 4 + tooltip {Resonance On/Off} xywh {287 7 15 17} down_box DOWN_BOX labeltype EMBOSSED_LABEL labelfont 1 labelsize 11 align 4 code0 {o->init("Presonance");} class Fl_Osc_Check } Fl_Value_Slider voicelfofreq { - tooltip {Frequency LFO amount} xywh {500 5 115 20} type {Horz Knob} box NO_BOX labelsize 8 align 5 maximum 127 step 1 + tooltip {Frequency LFO amount} xywh {542 5 115 20} type {Horz Knob} box NO_BOX labelsize 8 align 5 maximum 127 step 1 code0 {o->init("FreqLfo/Pintensity", 'i');} - class Fl_Osc_Slider + class Fl_Osc_VSlider } Fl_Dial voicepanning { - tooltip {Panning (leftmost is Random)} xywh {215 5 20 20} box ROUND_UP_BOX labelsize 10 align 4 maximum 127 step 1 + tooltip {Panning (leftmost is Random)} xywh {257 5 20 20} box ROUND_UP_BOX labelsize 10 align 4 maximum 127 step 1 code0 {o->init("PPanning");} class Fl_Osc_Dial } Fl_Group voiceoscil {open - xywh {60 5 30 20} box THIN_DOWN_BOX color 32 selection_color 71 labelcolor 179 + xywh {102 5 30 20} box THIN_DOWN_BOX color 32 selection_color 71 labelcolor 179 code0 {voiceoscil->ext = "OscilSmp/";} code1 {oscil=new Fl_Oscilloscope(o->x(),o->y(),o->w(),o->h(),"");} code2 {oscil->init(false);} class Fl_Osc_Group } {} Fl_Value_Output detunevalueoutput { - xywh {265 5 45 20} labelsize 10 align 5 minimum -5000 maximum 5000 step 0.01 textfont 1 textsize 10 + xywh {307 5 45 20} labelsize 10 align 5 minimum -5000 maximum 5000 step 0.01 textfont 1 textsize 10 code0 {o->init("detunevalue");} class Fl_Osc_Output } Fl_Slider voicedetune { callback {detunevalueoutput->update();} - tooltip {Fine Detune (cents)} xywh {315 5 185 20} type {Horz Knob} box NO_BOX minimum -8192 maximum 8191 step 1 + tooltip {Fine Detune (cents)} xywh {357 5 185 20} type {Horz Knob} box NO_BOX minimum -8192 maximum 8191 step 1 code0 {o->init("PDetune",'i');} class Fl_Osc_Slider } - Fl_Box noiselabel { + Fl_Box whitenoiselabel { label N - xywh {65 5 20 20} labelfont 1 labelsize 13 labelcolor 7 + xywh {107 5 20 20} labelfont 1 labelsize 13 labelcolor 7 } Fl_Check_Button noisehack { callback {if (o->value()==0) { - noiselabel->hide(); + whitenoiselabel->hide(); voiceresonanceenabled->activate(); detunevalueoutput->activate(); voicedetune->activate(); voicelfofreq->activate(); voiceoscil->activate(); } else { - noiselabel->show(); + whitenoiselabel->show(); voiceresonanceenabled->deactivate(); detunevalueoutput->deactivate(); voicedetune->deactivate(); @@ -159,6 +168,21 @@ o->redraw();} code1 {o->init("Enabled");} class Fl_Osc_Check } + Fl_Button {} { + label edit + callback { +class ADnoteUI *adnoteui = ui->partui->adnoteui; +adnoteui->ADnoteVoice->show(); +adnoteui->currentvoicecounter->value(nvoice+1); +adnoteui->currentvoicecounter->do_callback(); +class ADvoiceUI *advoice = adnoteui->advoice; +if (advoice->mod_type->value() == 0) + advoice->voiceFMparametersgroup->deactivate(); +else + advoice->voiceFMparametersgroup->activate(); +} + xywh {53 6 40 15} box THIN_UP_BOX labelsize 11 + } } } Function {ADvoicelistitem(int x,int y, int w, int h, const char *label=0):Fl_Osc_Group(x,y,w,h,label)} {open @@ -281,7 +305,7 @@ o->redraw();} label Vol tooltip Volume xywh {540 80 160 15} type {Horz Knob} box NO_BOX labelsize 11 align 8 maximum 127 step 1 code0 {o->init("PFMVolume", 'i');} - class Fl_Osc_Slider + class Fl_Osc_VSlider } Fl_Value_Slider {} { label {V.Sns} @@ -309,7 +333,7 @@ o->redraw();} label {F.Damp} tooltip {Modulator Damp at Higher frequency} xywh {540 120 160 15} type {Horz Knob} box NO_BOX labelsize 11 align 8 minimum -64 maximum 63 step 1 code0 {o->init("PFMVolumeDamp",'i');} - class Fl_Osc_Slider + class Fl_Osc_VSlider } } Fl_Group modoscil {open @@ -361,11 +385,11 @@ voiceFMparametersgroup->redraw();} open xywh {560 410 75 20} down_box BORDER_BOX labelsize 10 textfont 1 textsize 10 code0 {o->add("Internal");} code1 {char tmp[50]; for (int i=0;iadd(tmp);};} - code3 {o->init("PextFMoscil",1);} + code3 {o->init("PextFMoscil",-1);} class Fl_Osc_Choice } {} } - Fl_Choice {} { + Fl_Choice extMod { label {External Mod.} callback {if ((int) o->value() != 0) { modoscil->deactivate(); @@ -383,7 +407,7 @@ voiceFMparametersgroup->redraw();} open class Fl_Osc_Choice } {} } - Fl_Choice {} { + Fl_Choice mod_type { label {Type:} callback {if (o->value()==0) voiceFMparametersgroup->deactivate(); else voiceFMparametersgroup->activate(); @@ -551,25 +575,12 @@ voiceonbutton->redraw();} open xywh {5 470 65 15} down_box BORDER_BOX labelsize 10 align 5 textfont 1 textsize 10 code0 {o->add("Internal");} code1 {char tmp[50]; for (int i=0;iadd(tmp);};} - code3 {o->init("Pextoscil",1);} + code3 {o->init("Pextoscil",-1);} class Fl_Osc_Choice } {} Fl_Group {} {open xywh {5 540 520 50} box UP_FRAME } { - Fl_Dial {} { - label Stereo - tooltip {Stereo Spread} xywh {322 555 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 - code0 {o->init("Unison_stereo_spread");} - class Fl_Osc_Dial - } - Fl_Choice {} { - label Unison - tooltip {Unison size} xywh {10 560 75 20} down_box BORDER_BOX labelfont 1 align 5 textfont 1 textsize 10 - code0 {o->add("OFF");char tmp[100];for (int i=1;ADnote_unison_sizes[i];i++){snprintf(tmp,100,"size %d",ADnote_unison_sizes[i]);o->add(tmp);};} - code1 {o->init("Unison_size");} - class Fl_Osc_Choice - } {} Fl_Dial {} { label Vibrato tooltip Vibrato xywh {364 555 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 @@ -718,8 +729,11 @@ o->redraw();} code0 {char tmp[10];snprintf(tmp,10,"%d",nvoice+1);o->label(strdup(tmp));} } {} Fl_Choice {} { - callback {if (o->value()==0){ voicemodegroup->activate(); noiselabel->hide();} - else{ voicemodegroup->deactivate(); noiselabel->show();}} + callback {switch (o->value()) { + case 0: voicemodegroup->activate(); whitenoiselabel->hide(); pinknoiselabel->hide(); break; + case 1: voicemodegroup->deactivate(); whitenoiselabel->show(); pinknoiselabel->hide(); break; + default: voicemodegroup->deactivate(); whitenoiselabel->hide(); pinknoiselabel->show(); break; +}} open tooltip {Oscillator Type (sound/noise)} xywh {5 515 65 20} down_box BORDER_BOX labelsize 10 textfont 1 textsize 10 code0 {o->init("Type");} class Fl_Osc_Choice @@ -729,8 +743,12 @@ o->redraw();} xywh {5 5 100 20} labelfont 1 labelsize 11 } MenuItem {} { - label NOISE - xywh {15 15 100 20} labelfont 1 labelsize 11 labelcolor 1 + label White + xywh {15 15 100 20} labelfont 1 labelsize 11 labelcolor 55 + } + MenuItem {} { + label Pink + xywh {25 25 100 20} labelfont 1 labelsize 11 labelcolor 211 } } Fl_Check_Button bypassfiltercheckbutton { @@ -759,10 +777,27 @@ bypassfiltercheckbutton->redraw();} code0 {o->init("PFilterEnabled");} class Fl_Osc_Check } - Fl_Box noiselabel { + Fl_Box whitenoiselabel { label {White Noise} xywh {150 430 300 65} labelfont 1 labelsize 50 labelcolor 53 hide } + Fl_Box pinknoiselabel { + label {Pink Noise} + xywh {150 430 300 65} labelfont 1 labelsize 50 labelcolor 212 hide + } + Fl_Dial {} { + label Stereo + tooltip {Stereo Spread} xywh {327 560 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 + code0 {o->init("Unison_stereo_spread");} + class Fl_Osc_Dial + } + Fl_Counter {} { + label Unison selected + tooltip {Unison size} xywh {20 568 65 18} labelfont 1 align 5 minimum 1 maximum 64 step 1 value 1 textfont 1 textsize 11 + code0 {o->init("Unison_size", 1);} + code1 {o->lstep(5);} + class Fl_Osc_Counter + } } Fl_Check_Button voiceonbutton { label On @@ -802,9 +837,29 @@ delete(oscedit); } { code {nvoice = nvoice_; loc = base+"VoicePar"+to_s(nvoice)+"/"; - char tmp[10];snprintf(tmp,10,"%d",nvoice+1); + char tmp[50];snprintf(tmp,10,"%d",nvoice+1); activeVoiceID->label(strdup(tmp)); -ADnoteVoiceParameters->rebase(base+"VoicePar"+to_s(nvoice)+"/");} {selected + extoscil->clear(); + extoscil->add("Internal"); + for (int i=0;iadd(tmp); + }; + + extFMoscil->clear(); + extFMoscil->add("Internal"); + for (int i=0;iadd(tmp); + } + + extMod->clear(); + extMod->add("OFF"); + for (int i=0;iadd(tmp); + } + ADnoteVoiceParameters->rebase(base+"VoicePar"+to_s(nvoice)+"/");} {selected } } decl {int nvoice;} {private local @@ -834,7 +889,7 @@ class ADnoteUI {open : {public PresetsUI_} } { Fl_Box {} { xywh {0 0 0 0} - code0 {ADnoteGlobalParameters->base = loc + "GlobalPar/"; ADnoteGlobalParameters->osc = osc;} + code0 {ADnoteGlobalParameters->init(osc, loc + "GlobalPar/");} } Fl_Group {} { label FREQUENCY open @@ -879,7 +934,10 @@ class ADnoteUI {open : {public PresetsUI_} } Fl_Choice detunetype { label {Detune Type} - callback {detunevalueoutput->update();} open + callback {detunevalueoutput->update(); + ui->partui->adnoteui->advoice->detunevalueoutput->update(); + ui->partui->adnoteui->advoice->fmdetunevalueoutput->update(); + } open xywh {455 340 75 15} down_box BORDER_BOX labelsize 10 align 5 textfont 1 textsize 10 code0 {o->add("L35cents");o->add("L10cents");o->add("E100cents");o->add("E1200cents");} code1 {o->init("PDetuneType",1);} @@ -1000,7 +1058,12 @@ ADnoteVoiceList->show();} } Fl_Button {} { label {Show Voice Parameters} - callback {ADnoteVoice->show();} + callback { + if (advoice->mod_type->value() == 0) + advoice->voiceFMparametersgroup->deactivate(); + else + advoice->voiceFMparametersgroup->activate(); + ADnoteVoice->show();} xywh {5 400 170 25} labelsize 12 } Fl_Button {} { @@ -1032,8 +1095,7 @@ resui->resonancewindow->show();} } { Fl_Box {} { xywh {0 0 0 0} - code0 {ADnoteVoice->base = loc;} - code1 {ADnoteVoice->osc = osc;} + code0 {ADnoteVoice->init(osc,loc);} } Fl_Group advoice {open xywh {0 0 765 595} @@ -1066,49 +1128,48 @@ advoice->change_voice(nvoice);} } Fl_Window ADnoteVoiceList { label {ADsynth Voices list} open - xywh {32 266 650 260} type Double hide + xywh {6 263 670 260} type Double hide class Fl_Osc_Window } { Fl_Box {} { xywh {0 0 0 0} - code0 {ADnoteVoiceList->base = loc;} - code1 {ADnoteVoiceList->osc = osc;} + code0 {ADnoteVoiceList->init(osc, loc);} } Fl_Text_Display {} { label {No.} - xywh {10 15 30 10} box NO_BOX labelfont 1 labelsize 11 + xywh {17 15 30 10} box NO_BOX labelfont 1 labelsize 11 } Fl_Text_Display {} { label Vol - xywh {145 15 30 10} box NO_BOX labelfont 1 labelsize 11 + xywh {190 15 30 10} box NO_BOX labelfont 1 labelsize 11 } Fl_Text_Display {} { label Detune - xywh {384 15 25 10} box NO_BOX labelfont 1 labelsize 11 + xywh {431 15 25 10} box NO_BOX labelfont 1 labelsize 11 } Fl_Text_Display {} { label Pan - xywh {210 15 30 10} box NO_BOX labelfont 1 labelsize 11 + xywh {253 15 30 10} box NO_BOX labelfont 1 labelsize 11 } Fl_Text_Display {} { - label {Vib. Depth} - xywh {560 15 30 10} box NO_BOX labelfont 1 labelsize 11 + label {Vib. Depth} selected + xywh {600 15 30 10} box NO_BOX labelfont 1 labelsize 11 } Fl_Text_Display {} { label {R.} - xywh {245 15 25 10} box NO_BOX labelfont 1 labelsize 11 + xywh {285 15 25 10} box NO_BOX labelfont 1 labelsize 11 } Fl_Button {} { label {Hide Voice List} callback {ADnoteVoiceList->hide();} - xywh {255 237 125 20} + xywh {271 237 125 20} } Fl_Scroll {} {open - xywh {0 15 640 220} type VERTICAL box THIN_UP_BOX + xywh {0 15 670 220} type VERTICAL box THIN_UP_BOX } { Fl_Pack {} {open - xywh {0 20 620 210} - code0 {o->begin();for (int i=0;iinit(i,loc+"VoicePar"+to_s(i)+"/",osc);}o->end();} + xywh {0 20 670 210} + code0 {o->begin();for (int i=0;iinit(i,loc+"VoicePar"+to_s(i)+"/",osc);}o->end();} } {} } } diff --git a/source/native-plugins/zynaddsubfx/UI/BankUI.fl b/source/native-plugins/zynaddsubfx/UI/BankUI.fl index 6d4013698..76b825e78 100644 --- a/source/native-plugins/zynaddsubfx/UI/BankUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/BankUI.fl @@ -29,6 +29,9 @@ decl {\#include "Fl_Osc_Interface.h"} {public local decl {\#include "Fl_Osc_Check.H"} {public local } +decl {\#include "Fl_Osc_Pane.H"} {public local +} + decl {\#include "../Misc/Util.h"} {public local } @@ -46,8 +49,7 @@ class BankUI {open } { Fl_Box {} { xywh {0 0 0 0} - code0 {bankuiwindow->osc = osc;} - code1 {bankuiwindow->base = "/";} + code0 {bankuiwindow->init(osc, "/");} } Fl_Button {} { label Close diff --git a/source/native-plugins/zynaddsubfx/UI/BankView.cpp b/source/native-plugins/zynaddsubfx/UI/BankView.cpp index 5946ef7ae..003b86a7d 100644 --- a/source/native-plugins/zynaddsubfx/UI/BankView.cpp +++ b/source/native-plugins/zynaddsubfx/UI/BankView.cpp @@ -21,7 +21,7 @@ void BankList::init(std::string path) void BankList::OSC_raw(const char *msg) { - if(!strcmp(msg, "/bank-list")) { + if(!strcmp(msg, "/bank-list") && !strcmp(rtosc_argument_string(msg),"iss")) { const int pos = rtosc_argument(msg, 0).i; const char *path = rtosc_argument(msg, 1).s; @@ -33,7 +33,7 @@ void BankList::OSC_raw(const char *msg) this->add(path); osc->write("/loadbank"); } - if(!strcmp(msg, "/loadbank")) { + if(!strcmp(msg, "/loadbank")&& !strcmp(rtosc_argument_string(msg),"i")) { value(rtosc_argument(msg, 0).i); } } diff --git a/source/native-plugins/zynaddsubfx/UI/ConfigUI.fl b/source/native-plugins/zynaddsubfx/UI/ConfigUI.fl index 83272e6d4..5d01b634e 100644 --- a/source/native-plugins/zynaddsubfx/UI/ConfigUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/ConfigUI.fl @@ -44,6 +44,9 @@ decl {\#include "Fl_Osc_Numeric_Input.H"} {public local decl {\#include "Fl_Osc_ListView.H"} {public local } +decl {\#include "Fl_Osc_Pane.H"} {public local +} + decl {\#include "../globals.h"} {public local } @@ -60,8 +63,7 @@ class ConfigUI {} { } { Fl_Box dummy { xywh {25 25 25 25} - code0 {configwindow->osc = osc;} - code1 {configwindow->base = "/config/";} + code0 {configwindow->init(osc, "/config/");} } Fl_Tabs {} { xywh {5 5 500 330} @@ -291,7 +293,7 @@ activatebutton_presetdir(true);} oscilsize->callback = [this](int i){ oscilsize_widget->value(i-7); }; - oscilsize->update("/config/cfg.OscilPower"); + oscilsize->doUpdate("/config/cfg.OscilPower"); } {} } diff --git a/source/native-plugins/zynaddsubfx/UI/Connection.cpp b/source/native-plugins/zynaddsubfx/UI/Connection.cpp index f80451f47..aca03910b 100644 --- a/source/native-plugins/zynaddsubfx/UI/Connection.cpp +++ b/source/native-plugins/zynaddsubfx/UI/Connection.cpp @@ -345,14 +345,18 @@ class UI_Interface:public Fl_Osc_Interface { #ifndef NO_UI printf("\n\nDamage(\"%s\")\n", path); + std::set to_update; for(auto pair:map) { if(strstr(pair.first.c_str(), path)) { auto *tmp = dynamic_cast(pair.second); if(!tmp || tmp->visible_r()) { - pair.second->update(); + to_update.insert(pair.second); } } } + + for(auto elm:to_update) + elm->update(); #endif } diff --git a/source/native-plugins/zynaddsubfx/UI/ConnectionDummy.cpp b/source/native-plugins/zynaddsubfx/UI/ConnectionDummy.cpp index 3aef4a2be..15a702b99 100644 --- a/source/native-plugins/zynaddsubfx/UI/ConnectionDummy.cpp +++ b/source/native-plugins/zynaddsubfx/UI/ConnectionDummy.cpp @@ -1,7 +1,7 @@ #include "Connection.h" #include namespace GUI { -ui_handle_t createUi(Fl_Osc_Interface*, void *exit) +ui_handle_t createUi(Fl_Osc_Interface*, void *) { return 0; } diff --git a/source/native-plugins/zynaddsubfx/UI/EffUI.fl b/source/native-plugins/zynaddsubfx/UI/EffUI.fl index 2839815c8..9e868b6c9 100644 --- a/source/native-plugins/zynaddsubfx/UI/EffUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/EffUI.fl @@ -29,6 +29,9 @@ decl {\#include "Fl_Osc_Check.H"} {public local decl {\#include "Fl_EQGraph.H"} {public local } +decl {\#include "Fl_Osc_Pane.H"} {public local +} + decl {\#include "EnvelopeUI.h"} {public local } @@ -150,7 +153,7 @@ if (filterwindow!=NULL){ label Type callback {if(o->value()==2) revp12->activate(); else revp12->deactivate();} - xywh {110 15 85 15} down_box BORDER_BOX color 14 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + xywh {110 15 85 15} box UP_BOX down_box BORDER_BOX color 14 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 code0 {o->init("parameter10");} class Fl_Osc_Choice } { diff --git a/source/native-plugins/zynaddsubfx/UI/EnvelopeFreeEdit.cpp b/source/native-plugins/zynaddsubfx/UI/EnvelopeFreeEdit.cpp index 5c084d40b..8b7f28009 100644 --- a/source/native-plugins/zynaddsubfx/UI/EnvelopeFreeEdit.cpp +++ b/source/native-plugins/zynaddsubfx/UI/EnvelopeFreeEdit.cpp @@ -21,6 +21,12 @@ void EnvelopeFreeEdit::init(void) oscRegister("Penvdt"); oscRegister("Penvval"); oscRegister("Penvsustain"); + + //register for non-bulk types + for(int i=0; icreateLink(loc+string("Penvdt") + to_s(i), this); + osc->createLink(loc+string("Penvval") + to_s(i), this); + } } void EnvelopeFreeEdit::OSC_raw(const char *msg) @@ -36,6 +42,16 @@ void EnvelopeFreeEdit::OSC_raw(const char *msg) rtosc_blob_t b = rtosc_argument(msg, 0).b; assert(b.len == MAX_ENVELOPE_POINTS); memcpy(Penvval, b.data, MAX_ENVELOPE_POINTS); + } else if(strstr(msg, "Penvval") && !strcmp(args, "c")) { + const char *str = strstr(msg, "Penvval"); + int id = atoi(str+7); + assert(0 <= id && id < MAX_ENVELOPE_POINTS); + Penvval[id] = rtosc_argument(msg, 0).i; + } else if(strstr(msg, "Penvdt") && !strcmp(args, "c")) { + const char *str = strstr(msg, "Penvdt"); + int id = atoi(str+6); + assert(0 <= id && id < MAX_ENVELOPE_POINTS); + Penvdt[id] = rtosc_argument(msg, 0).i; } else if(strstr(msg,"Penvsustain") && !strcmp(args, "i")) { Penvsustain = rtosc_argument(msg, 0).i; } @@ -98,6 +114,8 @@ float EnvelopeFreeEdit::getdt(int i) const return dt(Penvdt[i]); } +static bool ctrldown; + void EnvelopeFreeEdit::draw(void) { int ox=x(),oy=y(),lx=w(),ly=h(); @@ -129,8 +147,10 @@ void EnvelopeFreeEdit::draw(void) for (int i=1; i=0){ + snprintf((char *)&tmpstr,20,"%d", Penvval[lastpoint]); + fl_draw(tmpstr,ox+lx-20,oy+ly-23,20,10,FL_ALIGN_RIGHT,NULL,0); + } } int EnvelopeFreeEdit::handle(int event) { const int x_=Fl::event_x()-x(); const int y_=Fl::event_y()-y(); - - if (event==FL_PUSH) { - currentpoint=getnearest(x_,y_); - cpx=x_; - cpdt=Penvdt[currentpoint]; - lastpoint=currentpoint; - redraw(); - if (pair) - pair->redraw(); - } - - if (event==FL_RELEASE){ - currentpoint=-1; - redraw(); - if (pair) - pair->redraw(); - } - - if (event==FL_DRAG && currentpoint>=0){ - int ny=limit(127-(int) (y_*127.0/h()), 0, 127); - - Penvval[currentpoint]=ny; - - const int dx=(int)((x_-cpx)*0.1); - const int newdt=limit(cpdt+dx,0,127); - - if(currentpoint!=0) - Penvdt[currentpoint]=newdt; - else - Penvdt[currentpoint]=0; - - oscWrite(to_s("Penvval")+to_s(currentpoint), "c", ny); - oscWrite(to_s("Penvdt")+to_s(currentpoint), "c", newdt); - oscWrite("Penvdt",""); - oscWrite("Penvval",""); - redraw(); - - if(pair) - pair->redraw(); + static Fl_Widget *old_focus; + int key; + + switch(event) { + case FL_ENTER: + old_focus=Fl::focus(); + Fl::focus(this); + // Otherwise the underlying window seems to regrab focus, + // and I can't see the KEYDOWN action. + return 1; + case FL_LEAVE: + Fl::focus(old_focus); + break; + case FL_KEYDOWN: + case FL_KEYUP: + key = Fl::event_key(); + if (key==FL_Control_L || key==FL_Control_R){ + ctrldown = (event==FL_KEYDOWN); + redraw(); + if (pair!=NULL) pair->redraw(); + } + break; + case FL_PUSH: + currentpoint=getnearest(x_,y_); + cpx=x_; + cpdt=Penvdt[currentpoint]; + lastpoint=currentpoint; + redraw(); + if (pair) + pair->redraw(); + return 1; + case FL_RELEASE: + currentpoint=-1; + redraw(); + if (pair) + pair->redraw(); + return 1; + case FL_MOUSEWHEEL: + if (lastpoint>=0) { + if (!ctrldown) { + int ny = Penvval[lastpoint] - Fl::event_dy(); + ny = ny < 0 ? 0 : ny > 127 ? 127 : ny; + Penvval[lastpoint] = ny; + oscWrite(to_s("Penvval")+to_s(lastpoint), "c", ny); + oscWrite("Penvval",""); + } else if (lastpoint > 0) { + int newdt = Fl::event_dy() + Penvdt[lastpoint]; + newdt = newdt < 0 ? 0 : newdt > 127 ? 127 : newdt; + Penvdt[lastpoint] = newdt; + oscWrite(to_s("Penvdt")+to_s(lastpoint), "c", newdt); + oscWrite("Penvdt",""); + } + redraw(); + if (pair!=NULL) pair->redraw(); + return 1; + } + case FL_DRAG: + if (currentpoint>=0){ + int ny=limit(127-(int) (y_*127.0/h()), 0, 127); + + Penvval[currentpoint]=ny; + + const int dx=(int)((x_-cpx)*0.1); + const int newdt=limit(cpdt+dx,0,127); + + if(currentpoint!=0) + Penvdt[currentpoint]=newdt; + else + Penvdt[currentpoint]=0; + + oscWrite(to_s("Penvval")+to_s(currentpoint), "c", ny); + oscWrite(to_s("Penvdt")+to_s(currentpoint), "c", newdt); + oscWrite("Penvdt",""); + oscWrite("Penvval",""); + redraw(); + + if(pair) + pair->redraw(); + return 1; + } } - - - return 1; + // Needed to propagate undo/redo keys. + return 0; } void EnvelopeFreeEdit::update(void) @@ -230,6 +294,12 @@ void EnvelopeFreeEdit::rebase(std::string new_base) osc->renameLink(loc+"Penvdt", new_base+"Penvdt", this); osc->renameLink(loc+"Penvval", new_base+"Penvval", this); osc->renameLink(loc+"Penvsustain", new_base+"Penvsustain", this); + for(int i=0; irenameLink(loc+dt, new_base+dt, this); + osc->renameLink(loc+val, new_base+val, this); + } loc = new_base; update(); } diff --git a/source/native-plugins/zynaddsubfx/UI/EnvelopeUI.fl b/source/native-plugins/zynaddsubfx/UI/EnvelopeUI.fl index 713a34bf9..1e990597d 100644 --- a/source/native-plugins/zynaddsubfx/UI/EnvelopeUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/EnvelopeUI.fl @@ -20,6 +20,9 @@ decl {\#include "Fl_Osc_Button.H"} {public local decl {\#include "Fl_Osc_Counter.H"} {public local } +decl {\#include "Fl_Osc_Pane.H"} {public local +} + decl {\#include } {public local } @@ -53,6 +56,21 @@ decl {\#include "common.H"} {public local decl {\#include "EnvelopeFreeEdit.h"} {public local } +class PointButton {open : {public Fl_Button, public Fl_Osc_Widget}} +{ + Function {PointButton(int x,int y, int w, int h, const char *label=0):Fl_Button(x,y,w,h,label),Fl_Osc_Widget(this)} {open + } { + code {} {} + } + Function {rebase(std::string new_base)} {open + } { + code {loc = new_base;} {} + } + Function {init(std::string path_)} {open + } { + code {ext = path_;} {} + } +} class EnvelopeUI {open : {public Fl_Osc_Group,PresetsUI_} } { Function {EnvelopeUI(int x,int y, int w, int h, const char *label=0):Fl_Osc_Group(x,y,w,h,label)} {} { @@ -79,13 +97,13 @@ delete (freemodeeditwindow);} {} Fl_Button {} { label C callback {presetsui->copy(freemodeeditwindow->loc());} - xywh {465 160 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 - code0 {freemodeeditwindow->osc = osc; freemodeeditwindow->base = loc();} + xywh {465 160 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 55 + code0 {freemodeeditwindow->init(osc, loc());} } Fl_Button {} { label P callback {presetsui->paste(freemodeeditwindow->loc(),this);} - xywh {482 160 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + xywh {482 160 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 55 } Fl_Button addpoint { label {Add point} @@ -101,8 +119,8 @@ sustaincounter->update(); //sustaincounter->value(Penvsustain); //sustaincounter->maximum(Penvpoints-2);} xywh {115 155 80 20} box THIN_UP_BOX labelsize 11 - code0 {(void)o;//if (Pfreemode==0) o->hide();} - class Fl_Osc_Button + code0 {(void)o->init("addPoint");} + class PointButton } Fl_Box freeedit { label Envelope @@ -121,20 +139,8 @@ sustaincounter->update(); //sustaincounter->value(Penvsustain); //sustaincounter->maximum(Penvpoints-2);} xywh {200 155 80 20} box THIN_UP_BOX labelsize 11 - code0 {(void)o;//if (Pfreemode==0) o->hide();} - class Fl_Osc_Button - } - Fl_Check_Button freemodebutton { - label FreeMode - callback { - o->oscWrite("Pfreemode", o->value() ? "T" : "F"); - reinit(o->value()); - -freeedit->lastpoint=-1; -freeedit->redraw();} - tooltip {Enable or disable the freemode} xywh {10 155 95 20} labelsize 11 - code0{o->init("Pfreemode");} - class Fl_Osc_Check + code0 {(void)o->init("delPoint");} + class PointButton } Fl_Check_Button forcedreleasecheck { label frcR @@ -170,6 +176,18 @@ envfree->redraw();} code3 {o->init("Penvsustain");} class Fl_Osc_Counter } + Fl_Check_Button freemodehack { + xywh {0 0 0 0} down_box DOWN_BOX + callback{refresh_display();} + code0 {o->init("Pfreemode");o->hide();} + class Fl_Osc_Check + } + Fl_Button {} { + label {Cancel Freemode} + callback {disable_freemode();} + xywh {5 155 105 20} box THIN_UP_BOX labelsize 11 labelcolor 1 + class Fl_Osc_Button + } } } Function {make_ADSR_window()} {} { @@ -185,12 +203,12 @@ envfree->redraw();} Fl_Button {} { label C callback {presetsui->copy(envADSR->loc());} - xywh {150 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + xywh {150 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 55 } Fl_Button {} { label P callback {presetsui->paste(envADSR->loc(),this);} - xywh {167 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + xywh {167 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 55 } Fl_Dial e1adt { label {A.dt} @@ -234,8 +252,9 @@ envfree->redraw();} } Fl_Button {} { label E - callback {freemodeeditwindow->show();} + callback {open_as_freemode();} tooltip {Envelope window} xywh {185 5 15 15} labelfont 1 labelsize 10 + class Fl_Osc_Button } Fl_Check_Button e1linearenvelope { label L @@ -259,12 +278,12 @@ envfree->redraw();} Fl_Button {} { label C callback {presetsui->copy(envASR->loc());} - xywh {155 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + xywh {155 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 55 } Fl_Button {} { label P callback {presetsui->paste(envASR->loc(),this);} - xywh {172 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + xywh {172 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 55 } Fl_Dial e2aval { label {A.val} @@ -309,8 +328,9 @@ envfree->redraw();} } Fl_Button {} { label E - callback {freemodeeditwindow->show();} + callback {open_as_freemode();} tooltip {Envelope window} xywh {190 5 15 15} labelfont 1 labelsize 10 + class Fl_Osc_Button } } } @@ -327,12 +347,12 @@ envfree->redraw();} Fl_Button {} { label C callback {presetsui->copy(envADSRfilter->loc());} - xywh {220 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + xywh {220 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 55 } Fl_Button {} { label P callback {presetsui->paste(envADSRfilter->loc(),this);} - xywh {237 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + xywh {237 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 55 } Fl_Dial e3aval { label {A.val} @@ -390,8 +410,9 @@ envfree->redraw();} } Fl_Button {} { label E - callback {freemodeeditwindow->show();} + callback {open_as_freemode();} xywh {255 5 15 15} labelfont 1 labelsize 10 + class Fl_Osc_Button } } } @@ -410,12 +431,12 @@ envfree->redraw();} Fl_Button {} { label C callback {presetsui->copy(envASRbw->loc());} - xywh {155 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + xywh {155 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 55 } Fl_Button {} { label P callback {presetsui->paste(envASRbw->loc(),this);} - xywh {172 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 7 + xywh {172 5 15 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 10 labelcolor 55 } Fl_Dial e4aval { label {A.val} @@ -460,8 +481,9 @@ envfree->redraw();} } Fl_Button {} { label E - callback {freemodeeditwindow->show();} + callback {open_as_freemode();} xywh {190 5 15 15} labelfont 1 labelsize 10 + class Fl_Osc_Button } } } @@ -478,8 +500,9 @@ envfree->redraw();} } { Fl_Button {} { label E - callback {freemodeeditwindow->show();} + callback {open_as_freemode();} xywh {185 5 15 15} labelfont 1 labelsize 10 + class Fl_Osc_Button } Fl_Box freeeditsmall { label Envelope @@ -501,6 +524,18 @@ envfree->redraw();} } } } + Function {open_as_freemode()} {open + } { + code { + if (!freemodehack->value() && + fl_choice("Convert envelope to free mode data?","No","Yes",NULL)==0) + return; + freemodehack->oscWrite("Pfreemode", "T"); + freeedit->update(); + freemodeeditwindow->show(); + freemodeeditwindow->position(Fl::event_x_root()-20, Fl::event_y_root()+20); +} {} +} Function {init(int env_type, Fl_Osc_Interface *osc_, std::string base_, std::string ext_)} {open } { code {osc = osc_; @@ -533,113 +568,68 @@ freeeditsmall->setpair(freeedit); freeedit->setpair(freeeditsmall); -refresh();} {} +refresh_display();} {} } Function {rebase(std::string new_base)} {open } { code {Fl_Osc_Group::rebase(new_base); freemodeeditwindow->rebase(new_base+ext);} {} } - Function {reinit(bool Pfreemode)} {open + Function {disable_freemode()} {open } { - code { - //if(!Pfreemode){ - // int answer=fl_choice("Disable the free mode of the Envelope?","No","Yes",NULL); - // freemodebutton->value(Pfreemode); - // if (answer==0) - // return; - //}; - freeedit->update(); + code {if (fl_choice("Discard free mode data?","No","Yes",NULL)==0) + return; hide(); - const int winx=freemodeeditwindow->x(); - const int winy=freemodeeditwindow->y(); - - bool reshow = freemodeeditwindow->visible(); + int winx = Fl::event_x_root()-10; + int winy = Fl::event_y_root()-155; + winx = winx < 1 ? 1 : winx; + winy = winy < 1 ? 1 : winy; freemodeeditwindow->hide(); envwindow->hide(); Fl_Group *par=envwindow->parent(); par->hide(); - - refresh(); + freemodehack->oscWrite("Pfreemode", "F"); + freeedit->update(); envwindow->show(); par->redraw(); par->show(); show(); - freemodeeditwindow->position(winx,winy); - if(reshow) - freemodeeditwindow->show(); - - if (Pfreemode) { - freemodebutton->value(1); - addpoint->show(); - deletepoint->show(); - forcedreleasecheck->show(); - sustaincounter->show(); - envstretchdial->show(); - } else{ - freemodebutton->value(0); - addpoint->hide(); - deletepoint->hide(); - forcedreleasecheck->hide(); - sustaincounter->hide(); - envstretchdial->hide(); - }; -} {selected - } + freemodeeditwindow->position(winx,winy);} {} + } + Function {refresh()} { open } + { code { + freemodehack->oscWrite("Pfreemode"); + } {} } - Function {refresh()} {open + Function {refresh_display()} {open } { code { + sustaincounter->value(Penvsustain); sustaincounter->maximum(Penvpoints-2); envstretchdial->value(Penvstretch); -linearenvelopecheck->value(Plinearenvelope); - -//Conditionally display widgets - if(freemodebutton->value()) { - freemodebutton->value(1); - addpoint->show(); - deletepoint->show(); - forcedreleasecheck->show(); - sustaincounter->show(); - envstretchdial->show(); - } else { - freemodebutton->value(0); - addpoint->hide(); - deletepoint->hide(); - forcedreleasecheck->hide(); - sustaincounter->hide(); - envstretchdial->hide(); - } - - if(freemodebutton->value() || Envmode>2) - linearenvelopecheck->hide(); - else - linearenvelopecheck->show(); + linearenvelopecheck->value(Plinearenvelope); + linearenvelopecheck->show(); forcedreleasecheck->value(Pforcedrelease); -if (freemodebutton->value()==0){ - addpoint->hide(); - deletepoint->hide(); -} else { - addpoint->show(); - deletepoint->show(); -} - envADSR->hide(); envASR->hide(); envADSRfilter->hide(); envASRbw->hide(); envfree->hide(); -if (freemodebutton->value()==0){ +if (freemodehack->value()) { + envwindow=envfree; + freeedit->update(); +} else { + freemodeeditwindow->hide(); switch(Envmode){ case 1: case 2: @@ -656,10 +646,8 @@ if (freemodebutton->value()==0){ break; default: break; - }; -}else{ - envwindow=envfree; -}; + } +} assert(envwindow); envwindow->resize(this->x(),this->y(),this->w(),this->h()); diff --git a/source/native-plugins/zynaddsubfx/UI/FilterUI.fl b/source/native-plugins/zynaddsubfx/UI/FilterUI.fl index d4c66fe72..e2ad38f06 100644 --- a/source/native-plugins/zynaddsubfx/UI/FilterUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/FilterUI.fl @@ -26,6 +26,9 @@ decl {\#include "Fl_Osc_Output.H"} {public local decl {\#include "Fl_Osc_Slider.H"} {public local } +decl {\#include "Fl_Osc_Pane.H"} {public local +} + decl {\#include } {private local } diff --git a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_DialF.cpp b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_DialF.cpp index ccc656dc2..5b3390cff 100644 --- a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_DialF.cpp +++ b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_DialF.cpp @@ -39,6 +39,7 @@ void Fl_Osc_DialF::init(const char *path) ext = path; loc = pane->base; oscRegister(path); + integer_step = false; }; Fl_Osc_DialF::~Fl_Osc_DialF(void) diff --git a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_ListView.cpp b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_ListView.cpp index d2ca916af..8ca03a721 100644 --- a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_ListView.cpp +++ b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_ListView.cpp @@ -1,4 +1,5 @@ #include "Fl_Osc_ListView.H" +#include "Fl_Osc_Pane.H" #include #include @@ -21,7 +22,7 @@ void Fl_Osc_ListView::init(const char *path_) path = path_; data = new Osc_SimpleListModel(osc); data->callback = [this](Osc_SimpleListModel::list_t l){this->doUpdate(l);}; - data->update(loc+path_); + data->doUpdate(loc+path_); } void Fl_Osc_ListView::doUpdate(Osc_SimpleListModel::list_t l) @@ -33,7 +34,7 @@ void Fl_Osc_ListView::doUpdate(Osc_SimpleListModel::list_t l) } void Fl_Osc_ListView::update(void) { - data->update(loc+path); + data->doUpdate(loc+path); } void Fl_Osc_ListView::insert(std::string s, int offset) diff --git a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Numeric_Input.cpp b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Numeric_Input.cpp index 0afc68429..00015dd4e 100644 --- a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Numeric_Input.cpp +++ b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Numeric_Input.cpp @@ -1,3 +1,4 @@ +#include #include "Fl_Osc_Numeric_Input.H" Fl_Osc_Numeric_Input::Fl_Osc_Numeric_Input(int X, int Y, int W, int H, const char *label) diff --git a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Pane.H b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Pane.H index 14bcf190b..9d9e9b803 100644 --- a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Pane.H +++ b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Pane.H @@ -3,6 +3,7 @@ #include #include #include +#include "Osc_DataModel.h" class Fl_Osc_Pane { @@ -16,8 +17,12 @@ class Fl_Osc_Pane class Fl_Osc_Window:public Fl_Double_Window, public Fl_Osc_Pane { + Osc_DataModel *title_ext; + std::string title_orig; + std::string title_new; public: Fl_Osc_Window(int w, int h, const char *L=0); + ~Fl_Osc_Window(void); void init(Fl_Osc_Interface *osc_, std::string loc_); virtual std::string loc(void) const; diff --git a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Pane.cpp b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Pane.cpp index 2ca971576..012d3cef2 100644 --- a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Pane.cpp +++ b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Pane.cpp @@ -9,15 +9,42 @@ Fl_Osc_Pane::Fl_Osc_Pane(void) Fl_Osc_Window::Fl_Osc_Window(int w, int h, const char *L) - :Fl_Double_Window(w,h,L) + :Fl_Double_Window(w,h,L), title_ext(NULL) {} - + void Fl_Osc_Window::init(Fl_Osc_Interface *osc_, std::string loc_) { + title_ext = new Osc_DataModel(osc_); + title_ext->doUpdate("/ui/title"); + title_ext->callback = [this](string next) { + //printf("old: %s\n", title_orig.c_str()); + const char *orig = title_orig.c_str(); + // 12345678901 + const char *sub = strstr(orig, "zynaddsubfx"); + if(!sub) + sub = strstr(orig, "ZynAddSubFX"); + title_new = ""; + while(*orig) { + if(orig == sub) { + title_new += next; + orig += 11; + } else + title_new += *orig++; + } + //title_new = title_orig + next; + this->label(title_new.c_str()); + }; + title_orig = label(); + osc = osc_; base = loc_; } +Fl_Osc_Window::~Fl_Osc_Window(void) +{ + delete title_ext; +} + std::string Fl_Osc_Window::loc(void) const { return base; diff --git a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Slider.H b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Slider.H index b743de8dd..7ecf42989 100644 --- a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Slider.H +++ b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Slider.H @@ -5,7 +5,6 @@ class Fl_Osc_Slider:public Fl_Slider, public Fl_Osc_Widget { - public: Fl_Osc_Slider(int X, int Y, int W, int H, const char *label = NULL); // string name, @@ -22,10 +21,12 @@ class Fl_Osc_Slider:public Fl_Slider, public Fl_Osc_Widget void callback(Fl_Callback *cb, void *p = NULL); //MIDI Learn - int handle(int); + int handle(int ev, int X, int Y, int W, int H); + int handle(int ev); void cb(void); static void _cb(Fl_Widget *w, void *); + float reset_value; private: char osc_type; std::pair cb_data; diff --git a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Slider.cpp b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Slider.cpp index 24ed44f7c..4148182fa 100644 --- a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Slider.cpp +++ b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Slider.cpp @@ -7,6 +7,7 @@ #include #include #include +#include "../Misc/Util.h" static double min__(double a, double b) { @@ -14,7 +15,8 @@ static double min__(double a, double b) } Fl_Osc_Slider::Fl_Osc_Slider(int X, int Y, int W, int H, const char *label) - :Fl_Slider(X,Y,W,H,label), Fl_Osc_Widget(this), cb_data(NULL, NULL) + :Fl_Slider(X,Y,W,H,label), Fl_Osc_Widget(this), reset_value(0), + cb_data(NULL, NULL) { //bounds(0.0f,1.0f); Fl_Slider::callback(Fl_Osc_Slider::_cb); @@ -72,7 +74,7 @@ void Fl_Osc_Slider::callback(Fl_Callback *cb, void *p) cb_data.second = p; } -int Fl_Osc_Slider::handle(int ev) +int Fl_Osc_Slider::handle(int ev, int X, int Y, int W, int H) { bool middle_mouse = (ev == FL_PUSH && Fl::event_state(FL_BUTTON2) && !Fl::event_shift()); bool ctl_click = (ev == FL_PUSH && Fl::event_state(FL_BUTTON1) && Fl::event_ctrl()); @@ -85,7 +87,70 @@ int Fl_Osc_Slider::handle(int ev) osc->write("/unlearn", "s", (loc+ext).c_str()); return 1; } - return Fl_Slider::handle(ev); + + int handled, rounded; + bool reset_requested = false; + switch (ev) { + case FL_MOUSEWHEEL: + if (this == Fl::belowmouse() && Fl::e_dy != 0) { + int step = 1, divisor = 16; + switch (Fl::event_state() & ( FL_CTRL | FL_SHIFT)) { + case FL_SHIFT: + step = 8; + case FL_SHIFT | FL_CTRL: + break; + case FL_CTRL: + divisor = 128; + default: + step = (fabs(maximum() - minimum()) + 1) / divisor; + if (step < 1) + step = 1; + } + int dy = minimum() <= maximum() ? Fl::e_dy : -Fl::e_dy; + handle_drag(clamp(value() + step * dy)); + } + return 1; + case FL_RELEASE: + rounded = value() + 0.5; + value(clamp((double)rounded)); + if (Fl::event_clicks() == 1) { + Fl::event_clicks(0); + reset_requested = true; + } + } + + if (!Fl::event_shift()) { + handled = Fl_Slider::handle(ev, X, Y, W, H); + if (reset_requested) { + value(reset_value); + value_damage(); + if (this->when() != 0) + do_callback(); + } + return handled; + } + + // Slow down the drag. + // Handy if the slider has a large delta bigger than a mouse quantum. + // Somewhat tricky to use with OSC feedback. + // To change direction of movement, one must reclick the handle. + int old_value = value(); + handled = Fl_Slider::handle(ev, X, Y, W, H); + int delta = value() - old_value; + if (ev == FL_DRAG && (delta < -1 || delta > 1)) { + value(clamp((old_value + (delta > 0 ? 1 : -1)))); + value_damage(); + do_callback(); + } + return handled; +} + +int Fl_Osc_Slider::handle(int ev) { + return handle(ev, + x()+Fl::box_dx(box()), + y()+Fl::box_dy(box()), + w()-Fl::box_dw(box()), + h()-Fl::box_dh(box())); } void Fl_Osc_Slider::update(void) diff --git a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_TSlider.H b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_TSlider.H new file mode 100644 index 000000000..2f4964144 --- /dev/null +++ b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_TSlider.H @@ -0,0 +1,21 @@ +#ifndef FL_OSC_TSLIDER_H +#define FL_OSC_TSLIDER_H +#include "Fl_Osc_Slider.H" +#include "TipWin.h" + + +class Fl_Osc_TSlider:public Fl_Osc_Slider +{ + public: + Fl_Osc_TSlider(int x, int y, int w, int h, const char *label = 0); + ~Fl_Osc_TSlider(); + int handle(int event); + void set_transform(float scale = 1.0, float offset = 0.0); + float transform(float x); + void setRounding(unsigned int digits = 0); + private: + class TipWin * tipwin; + float value_offset; + float value_scale; +}; +#endif diff --git a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_TSlider.cpp b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_TSlider.cpp new file mode 100644 index 000000000..cf8c3751b --- /dev/null +++ b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_TSlider.cpp @@ -0,0 +1,65 @@ +#include +#include "Fl_Osc_TSlider.H" +//Copyright (c) 2015 Christopher Oliver +//License: GNU GPL version 2 or later + + +Fl_Osc_TSlider::Fl_Osc_TSlider(int x, int y, int w, int h, const char *label) + :Fl_Osc_Slider(x, y, w, h, label), value_offset(0.0), value_scale(1.0) +{ + Fl_Group *save = Fl_Group::current(); + tipwin = new TipWin(); + tipwin->hide(); + Fl_Group::current(save); + tipwin->setRounding(); +} + +Fl_Osc_TSlider::~Fl_Osc_TSlider() +{ + if (tipwin) + delete tipwin; +} + +void Fl_Osc_TSlider::setRounding(unsigned int digits) +{ + tipwin->setRounding(digits); +} + + +int Fl_Osc_TSlider::handle(int event) +{ + int super = 1; + + super = Fl_Osc_Slider::handle(event); + + switch(event) { + case FL_PUSH: + case FL_MOUSEWHEEL: + if (!Fl::event_inside(x(),y(),w(),h())) + return(1); + tipwin->position(Fl::event_x_root()-Fl::event_x()+x(), + Fl::event_y_root()-Fl::event_y()+h()+y()+5); + case FL_DRAG: + tipwin->showValue(transform(value())); + break; + case FL_RELEASE: + case FL_HIDE: + case FL_LEAVE: + if (tipwin) + tipwin->hide(); + return 1; + } + + return super; +} + +void Fl_Osc_TSlider::set_transform(float scale, float offset) +{ + value_offset = offset; + value_scale = scale; +} + +float Fl_Osc_TSlider::transform(float x) +{ + return value_scale * x + value_offset; +} diff --git a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_VSlider.H b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_VSlider.H index edbdc1b81..4ba3dbed7 100644 --- a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_VSlider.H +++ b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_VSlider.H @@ -1,29 +1,35 @@ #pragma once #include -#include "Fl_Osc_Widget.H" +#include "Fl_Osc_Slider.H" #include -class Fl_Osc_VSlider:public Fl_Value_Slider, public Fl_Osc_Widget +class Fl_Osc_VSlider:public Fl_Osc_Slider { public: Fl_Osc_VSlider(int X, int Y, int W, int H, const char *label = NULL); virtual ~Fl_Osc_VSlider(void); - void OSC_value(char); - void OSC_value(int); - void OSC_value(float); - void init(std::string, char type = 'i'); + Fl_Font textfont_; + Fl_Fontsize textsize_; + Fl_Color textcolor_; + void init(std::string, char type = 'i'); //Refetch parameter information - void update(void); - void callback(Fl_Callback *cb, void *p = NULL); //MIDI Learn int handle(int); - void cb(void); - static void _cb(Fl_Widget *w, void *); + // Value Slider add-ins. + Fl_Font textfont() const {return textfont_;} + void textfont(Fl_Font s) {textfont_ = s;} + Fl_Fontsize textsize() const {return textsize_;} + void textsize(Fl_Fontsize s) {textsize_ = s;} + Fl_Color textcolor() const {return textcolor_;} + void textcolor(Fl_Color s) {textcolor_ = s;} + + protected: + void draw(void); private: char osc_type; std::pair cb_data; diff --git a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_VSlider.cpp b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_VSlider.cpp index 1c438c076..1f5994afb 100644 --- a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_VSlider.cpp +++ b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_VSlider.cpp @@ -1,4 +1,5 @@ #include +#include #include "Fl_Osc_VSlider.H" #include "Fl_Osc_Interface.h" #include "Fl_Osc_Pane.H" @@ -9,82 +10,60 @@ #include Fl_Osc_VSlider::Fl_Osc_VSlider(int X, int Y, int W, int H, const char *label) - :Fl_Value_Slider(X,Y,W,H,label), Fl_Osc_Widget(this), cb_data(NULL, NULL) + :Fl_Osc_Slider(X,Y,W,H,label), cb_data(NULL, NULL) { //bounds(0.0f,1.0f); - Fl_Slider::callback(Fl_Osc_VSlider::_cb); -} - -void Fl_Osc_VSlider::init(std::string path_, char type_) -{ - osc_type = type_; - ext = path_; - oscRegister(ext.c_str()); + Fl_Slider::callback(Fl_Osc_Slider::_cb); + textfont_ = FL_HELVETICA; + textsize_ = 10; + textcolor_ = FL_FOREGROUND_COLOR; } Fl_Osc_VSlider::~Fl_Osc_VSlider(void) {} -void Fl_Osc_VSlider::OSC_value(char v) -{ - Fl_Slider::value(v+minimum()+fmodf(value(), 1.0f)); -} - -void Fl_Osc_VSlider::OSC_value(int v) -{ - Fl_Slider::value(v+minimum()+fmodf(value(), 1.0f)); -} - -void Fl_Osc_VSlider::OSC_value(float v) +void Fl_Osc_VSlider::init(std::string path_, char type_) { - Fl_Slider::value(v+minimum()); + Fl_Osc_Slider::init(path_, type_); } -void Fl_Osc_VSlider::cb(void) -{ - const float val = Fl_Slider::value(); - if(osc_type == 'f') - oscWrite(ext, "f", val-minimum()); - else if(osc_type == 'i') - oscWrite(ext, "i", (int)(val-minimum())); - else { - fprintf(stderr, "invalid `c' from vslider %s%s, using `i'\n", loc.c_str(), ext.c_str()); - oscWrite(ext, "i", (int)(val-minimum())); +void Fl_Osc_VSlider::draw() { + int sxx = x(), syy = y(), sww = w(), shh = h(); + int bxx = x(), byy = y(), bww = w(), bhh = h(); + if (horizontal()) { + bww = 35; sxx += 35; sww -= 35; + } else { + syy += 25; bhh = 25; shh -= 25; } - //OSC_value(val); - - if(cb_data.first) - cb_data.first(this, cb_data.second); -} - -void Fl_Osc_VSlider::callback(Fl_Callback *cb, void *p) -{ - cb_data.first = cb; - cb_data.second = p; + if (damage()&FL_DAMAGE_ALL) draw_box(box(),sxx,syy,sww,shh,color()); + Fl_Osc_Slider::draw(sxx+Fl::box_dx(box()), + syy+Fl::box_dy(box()), + sww-Fl::box_dw(box()), + shh-Fl::box_dh(box())); + draw_box(box(),bxx,byy,bww,bhh,color()); + char buf[128]; + format(buf); + fl_font(textfont(), textsize()); + fl_color(active_r() ? textcolor() : fl_inactive(textcolor())); + fl_draw(buf, bxx, byy, bww, bhh, FL_ALIGN_CLIP); } int Fl_Osc_VSlider::handle(int ev) { - bool middle_mouse = (ev == FL_PUSH && Fl::event_state(FL_BUTTON2) && !Fl::event_shift()); - bool ctl_click = (ev == FL_PUSH && Fl::event_state(FL_BUTTON1) && Fl::event_ctrl()); - bool shift_middle = (ev == FL_PUSH && Fl::event_state(FL_BUTTON2) && Fl::event_shift()); - if(middle_mouse || ctl_click) { - printf("Trying to learn...\n"); - osc->write("/learn", "s", (loc+ext).c_str()); - return 1; - } else if(shift_middle) { - osc->write("/unlearn", "s", (loc+ext).c_str()); - return 1; + if (ev == FL_PUSH && Fl::visible_focus()) { + Fl::focus(this); + redraw(); + } + int sxx = x(), syy = y(), sww = w(), shh = h(); + if (horizontal()) { + sxx += 35; sww -= 35; + } else { + syy += 25; shh -= 25; } - return Fl_Value_Slider::handle(ev); -} - -void Fl_Osc_VSlider::update(void) -{ - oscWrite(ext, ""); -} -void Fl_Osc_VSlider::_cb(Fl_Widget *w, void *) -{ - static_cast(w)->cb(); + return Fl_Osc_Slider::handle(ev, + sxx+Fl::box_dx(box()), + syy+Fl::box_dy(box()), + sww-Fl::box_dw(box()), + shh-Fl::box_dh(box())); } diff --git a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Widget.H b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Widget.H index 39a8894c2..6ed7fa0f9 100644 --- a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Widget.H +++ b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Widget.H @@ -4,7 +4,6 @@ #include #include #include "Fl_Osc_Interface.h" -#include "Fl_Osc_Pane.H" #include class Fl_Osc_Widget @@ -54,5 +53,5 @@ class Fl_Osc_Widget std::string ext; Fl_Osc_Interface *osc; protected: - Fl_Osc_Pane *fetch_osc_pane(Fl_Widget *w); + class Fl_Osc_Pane *fetch_osc_pane(Fl_Widget *w); }; diff --git a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Widget.cpp b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Widget.cpp index 2ab925f22..dda9e9901 100644 --- a/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Widget.cpp +++ b/source/native-plugins/zynaddsubfx/UI/Fl_Osc_Widget.cpp @@ -1,4 +1,5 @@ #include "Fl_Osc_Widget.H" +#include "Fl_Osc_Pane.H" #include Fl_Osc_Widget::Fl_Osc_Widget(void) //Deprecated diff --git a/source/native-plugins/zynaddsubfx/UI/LFOUI.fl b/source/native-plugins/zynaddsubfx/UI/LFOUI.fl index c45cbc032..29e6105ce 100644 --- a/source/native-plugins/zynaddsubfx/UI/LFOUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/LFOUI.fl @@ -20,6 +20,9 @@ decl {\#include "Fl_Osc_Choice.H"} {public local decl {\#include "Fl_Osc_Check.H"} {public local } +decl {\#include "Fl_Osc_Pane.H"} {public local +} + decl {\#include "../globals.h"} {private global } diff --git a/source/native-plugins/zynaddsubfx/UI/MasterUI.fl b/source/native-plugins/zynaddsubfx/UI/MasterUI.fl index 80e2dd9b1..220d2f7d4 100644 --- a/source/native-plugins/zynaddsubfx/UI/MasterUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/MasterUI.fl @@ -61,6 +61,9 @@ decl {\#include "VuPartMeter.h"} {public local decl {\#include "Fl_Osc_Dial.H"} {private local } +decl {\#include "Osc_DataModel.h"} {private local +} + decl {\#include "VuMasterMeter.h"} {public local } @@ -241,7 +244,13 @@ class MasterUI {open close(); };} open xywh {330 365 390 525} type Double xclass zynaddsubfx visible + class Fl_Osc_Window } { + Fl_Box dummy_again { + xywh {25 25 25 25} + code0 {masterwindow->init(osc, "");} + } + Fl_Group win_root {open xywh {0 0 390 525} class Fl_Osc_Group @@ -258,6 +267,11 @@ class MasterUI {open callback {do_new_master();} xywh {25 25 100 20} } + MenuItem {} { + label {&Revert changes...} + callback {do_revert_changes();} + xywh {25 25 100 20} + } MenuItem {} { label {&Open Parameters...} callback {\#if USE_NSM @@ -568,7 +582,7 @@ syseffectui->refresh();} Fl_Button {} { label C callback {presetsui->copy("/sysefx"+to_s(nsyseff)+"/");} - xywh {180 187 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 7 + xywh {180 187 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 55 } Fl_Button {} { label P @@ -855,8 +869,7 @@ GNU General Public License for details.} } { Fl_Box {} { xywh {0 0 0 0} - code0 {syseffsendwindow->osc = osc; assert(osc);} - code1 {syseffsendwindow->base = "";} + code0 {syseffsendwindow->init(osc,"");} } Fl_Scroll syseffscroll {open xywh {0 45 120 170} box FLAT_BOX resizable @@ -880,8 +893,7 @@ GNU General Public License for details.} } { Fl_Box {} { xywh {0 0 0 0} - code0 {panelwindow->osc = osc;} - code1 {panelwindow->base = "/";} + code0 {panelwindow->init(osc,"/");} } Fl_Scroll {} {open xywh {0 5 570 310} type HORIZONTAL box THIN_UP_BOX @@ -924,8 +936,7 @@ if (fl_choice("Exit and leave the unsaved data?","No","Yes",NULL)) } { Fl_Box {} { xywh {0 0 0 0} - code0 {simplemasterwindow->osc = osc;} - code1 {simplemasterwindow->base = "/";} + code0 {simplemasterwindow->init(osc, "/");} } Fl_Menu_Bar simplemastermenu { xywh {0 0 600 25} @@ -939,6 +950,11 @@ if (fl_choice("Exit and leave the unsaved data?","No","Yes",NULL)) callback {do_new_master();} xywh {30 30 100 20} } + MenuItem {} { + label {&Revert changes...} + callback {do_revert_changes();} + xywh {30 30 100 20} + } MenuItem {} { label {&Open Parameters...} callback {do_load_master();} @@ -1427,6 +1443,7 @@ virkeys->take_focus();} label {User Interface mode} callback {*exitprogram=1;} xywh {342 246 430 250} type Double hide non_modal + class Fl_Osc_Window } { Fl_Box {} { label {Welcome to ZynAddSubFX} @@ -1489,6 +1506,25 @@ osc=osc_; ninseff=0; nsyseff=0; npart=0; +last_loaded[0]=0; +loading_next[0]=0; +last_xmz = new Osc_DataModel(osc); +last_xmz->callback = [this](std::string filestr) { + const char *filename = filestr.c_str(); + if (filename[0] != 0) + strncpy(last_loaded, filename, XMZ_PATH_MAX); + else if (loading_next[0] != 0) { + strncpy(last_loaded, loading_next, XMZ_PATH_MAX); + loading_next[0] = 0; + } else + last_loaded[0] = 0; + last_loaded[XMZ_PATH_MAX - 1] = 0; + char *label = last_loaded[0] == 0 ? NULL + : ((label = strrchr(last_loaded, '/'))) ? label+1 + : last_loaded; + setfilelabel(label); +}; +last_xmz->doUpdate("/last_xmz"); for (int i=0;iredraw();} {} microtonalui=new MicrotonalUI(osc, "/microtonal/"); osc->write("/reset_master"); + osc->write("/last_xmz"); npartcounter->value(1); refresh_master_ui(); updatepanel();} {} @@ -1565,9 +1602,29 @@ microtonalui=new MicrotonalUI(osc, "/microtonal/"); do_new_master_unconditional(); }} {} } + Function {do_revert_changes_unconditional()} {return_type int + } { + code {strncpy(loading_next, last_loaded, XMZ_PATH_MAX); + osc->write("/load_xmz", "s", last_loaded); + osc->write("/last_xmz"); + refresh_master_ui(); + updatepanel(); + +return 1;} {} + } + Function {do_revert_changes()} {} { + code {if (last_loaded[0] == 0) + do_new_master(); + else + if (fl_choice("Revert *ALL* the parameters ?","No","Yes",NULL)){ + do_revert_changes_unconditional(); + }} {} + } Function {do_load_master_unconditional(const char *filename, const char *display_name)} {return_type int } { - code {osc->write("/load_xmz", "s", filename); + code {strncpy(loading_next, filename, XMZ_PATH_MAX); + osc->write("/load_xmz", "s", filename); + osc->write("/last_xmz"); refresh_master_ui(); updatepanel(); @@ -1648,6 +1705,8 @@ simpleinseffnocounter->do_callback(); simplerefresh(); bankui->hide();} {} + } + decl {class Osc_DataModel *last_xmz;} {public local } decl {MicrotonalUI *microtonalui;} {private local } @@ -1675,6 +1734,10 @@ bankui->hide();} {} } decl {class Fl_Osc_Interface *osc;} {public local } + decl {char last_loaded[XMZ_PATH_MAX];} {public local + } + decl {char loading_next[XMZ_PATH_MAX];} {public local + } Function {close()} {open return_type void } { code {*exitprogram=1;} {} diff --git a/source/native-plugins/zynaddsubfx/UI/MicrotonalUI.fl b/source/native-plugins/zynaddsubfx/UI/MicrotonalUI.fl index 889217f43..6ca5676b8 100644 --- a/source/native-plugins/zynaddsubfx/UI/MicrotonalUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/MicrotonalUI.fl @@ -51,8 +51,7 @@ class MicrotonalUI {} { } { Fl_Box {} { xywh {0 0 0 0} - code0 {microtonaluiwindow->osc = osc;} - code1 {microtonaluiwindow->base = base;} + code0 {microtonaluiwindow->init(osc, base);} } Fl_Group {} { tooltip {Center where the note's freqs. are turned upside-down} xywh {249 2 155 45} box ENGRAVED_FRAME diff --git a/source/native-plugins/zynaddsubfx/UI/NioUI.cpp b/source/native-plugins/zynaddsubfx/UI/NioUI.cpp index b08793705..a379d8692 100644 --- a/source/native-plugins/zynaddsubfx/UI/NioUI.cpp +++ b/source/native-plugins/zynaddsubfx/UI/NioUI.cpp @@ -11,6 +11,7 @@ #include #include #include "Osc_SimpleListModel.h" +#include "Fl_Osc_Pane.H" using namespace std; @@ -56,7 +57,7 @@ class Fl_Osc_StrChoice:public Fl_Choice, public Fl_Osc_Widget cb_data.first = cb; cb_data.second = p; } - + void cb(void) { assert(osc); @@ -66,7 +67,6 @@ class Fl_Osc_StrChoice:public Fl_Choice, public Fl_Osc_Widget cb_data.first(this, cb_data.second); } private: - int min; std::pair cb_data; }; static void callback_fn_choice_nio(Fl_Widget *w, void *) @@ -110,9 +110,9 @@ NioUI::NioUI(Fl_Osc_Interface *osc) for(auto io:list) audio->add(io.c_str()); }; - - midi_opt->update("/io/source-list"); - audio_opt->update("/io/sink-list"); + + midi_opt->doUpdate("/io/source-list"); + audio_opt->doUpdate("/io/sink-list"); resizable(this); size_range(400, 300); @@ -123,19 +123,19 @@ NioUI::~NioUI() void NioUI::refresh() { - midi_opt->update("/io/source-list"); - audio_opt->update("/io/sink-list"); + midi_opt->doUpdate("/io/source-list"); + audio_opt->doUpdate("/io/sink-list"); midi->update(); audio->update(); } -void NioUI::midiCallback(Fl_Widget *c) +void NioUI::midiCallback(Fl_Widget *) { //bool good = Nio::setSource(static_cast(c)->text()); //static_cast(c)->textcolor(fl_rgb_color(255 * !good, 0, 0)); } -void NioUI::audioCallback(Fl_Widget *c) +void NioUI::audioCallback(Fl_Widget *) { //bool good = Nio::setSink(static_cast(c)->text()); //static_cast(c)->textcolor(fl_rgb_color(255 * !good, 0, 0)); diff --git a/source/native-plugins/zynaddsubfx/UI/Osc_DataModel.h b/source/native-plugins/zynaddsubfx/UI/Osc_DataModel.h index ebde39d11..72ef62cad 100644 --- a/source/native-plugins/zynaddsubfx/UI/Osc_DataModel.h +++ b/source/native-plugins/zynaddsubfx/UI/Osc_DataModel.h @@ -17,7 +17,7 @@ class Osc_DataModel:public Fl_Osc_Widget value_t value; std::function callback; - void update(std::string url) + void doUpdate(std::string url) { if(!ext.empty()) osc->removeLink(this); @@ -26,7 +26,7 @@ class Osc_DataModel:public Fl_Osc_Widget oscRegister(ext.c_str()); } - + //Raw messages virtual void OSC_raw(const char *msg) { diff --git a/source/native-plugins/zynaddsubfx/UI/Osc_IntModel.h b/source/native-plugins/zynaddsubfx/UI/Osc_IntModel.h index 4fe297b6f..e01678166 100644 --- a/source/native-plugins/zynaddsubfx/UI/Osc_IntModel.h +++ b/source/native-plugins/zynaddsubfx/UI/Osc_IntModel.h @@ -23,7 +23,7 @@ class Osc_IntModel:public Fl_Osc_Widget oscWrite(ext, "i", v); } - void update(std::string url) + void doUpdate(std::string url) { if(!ext.empty()) osc->removeLink(this); @@ -31,7 +31,7 @@ class Osc_IntModel:public Fl_Osc_Widget oscRegister(ext.c_str()); } - + //Raw messages virtual void OSC_raw(const char *msg) { diff --git a/source/native-plugins/zynaddsubfx/UI/Osc_ListModel.h b/source/native-plugins/zynaddsubfx/UI/Osc_ListModel.h index 3e096ddb5..c00de0b96 100644 --- a/source/native-plugins/zynaddsubfx/UI/Osc_ListModel.h +++ b/source/native-plugins/zynaddsubfx/UI/Osc_ListModel.h @@ -18,7 +18,7 @@ class Osc_ListModel:public Fl_Osc_Widget std::function callback; unsigned list_size; - void update(std::string url) + void doUpdate(std::string url) { if(!ext.empty()) osc->removeLink(this); @@ -26,7 +26,7 @@ class Osc_ListModel:public Fl_Osc_Widget oscRegister(ext.c_str()); } - + //Raw messages virtual void OSC_raw(const char *msg) { diff --git a/source/native-plugins/zynaddsubfx/UI/Osc_SimpleListModel.h b/source/native-plugins/zynaddsubfx/UI/Osc_SimpleListModel.h index db0f51712..3d8a07e59 100644 --- a/source/native-plugins/zynaddsubfx/UI/Osc_SimpleListModel.h +++ b/source/native-plugins/zynaddsubfx/UI/Osc_SimpleListModel.h @@ -18,7 +18,7 @@ class Osc_SimpleListModel:public Fl_Osc_Widget std::function callback; unsigned list_size; - void update(std::string url) + void doUpdate(std::string url) { if(!ext.empty()) osc->removeLink(this); @@ -47,7 +47,7 @@ class Osc_SimpleListModel:public Fl_Osc_Widget rtosc_amessage(buffer, sizeof(buffer), ext.c_str(), types, args); osc->writeRaw(buffer); } - + //Raw messages virtual void OSC_raw(const char *msg) { diff --git a/source/native-plugins/zynaddsubfx/UI/OscilGenUI.fl b/source/native-plugins/zynaddsubfx/UI/OscilGenUI.fl index 49368aa0b..14b3b7809 100644 --- a/source/native-plugins/zynaddsubfx/UI/OscilGenUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/OscilGenUI.fl @@ -346,7 +346,7 @@ oscildisplaygroup->redraw();} code1 {oscilo_base->parent(o);oscilo_base->init(true);} } {} Fl_Dial bfslider { - callback {redrawoscil();} + callback {redrawoscil(); bfparval->value(o->value());} tooltip {Base Function Parameter} xywh {525 285 20 20} minimum -64 maximum 63 step 1 code0 {o->init("Pbasefuncpar");} class Fl_Osc_Dial @@ -492,9 +492,9 @@ redrawoscil();} } Fl_Button {} { label {Use as base} - callback {//oscil->useasbase(); + callback {osc->requestValue(loc+"use-as-base"); if (autoclearbutton->value()){ - for (int i=0;imag->value(64); h[i]->mag->do_callback(); h[i]->phase->value(64); @@ -528,7 +528,7 @@ redrawoscil();} label Clear callback {if (!fl_choice("Clear the harmonics settings?","No","Yes",NULL)) return; -for (int i=0;imag->value(64); h[i]->mag->do_callback(); h[i]->phase->value(64); @@ -537,7 +537,7 @@ for (int i=0;imag->value(0); h[0]->mag->do_callback(); -//for (int i=0;iPhmag[i]==64) h[i]->mag->selection_color(0); // else h[i]->mag->selection_color(222); //}; @@ -620,7 +620,7 @@ redrawoscil();} } } Fl_Dial wshpar { - callback {redrawoscil();} + callback {redrawoscil(); wsparval->value(o->value());} tooltip {Waveshaping Parameter} xywh {265 318 20 20} minimum -64 maximum 63 step 1 code0 {o->init("Pwaveshaping");} class Fl_Osc_Dial @@ -916,7 +916,7 @@ refresh();} } { Fl_Pack harmonics {open xywh {15 350 650 225} type HORIZONTAL - code0 {for (int i=0;ih(),"");h[i]->init(i,oscildisplaygroup,loc,osc);}} + code0 {for (int i=0;i<(MAX_AD_HARMONICS - 1);i++){h[i]=new Oscilharmonic(0,0,20,o->h(),"");h[i]->init(i,oscildisplaygroup,loc,osc);}} } {} } } @@ -958,7 +958,7 @@ osceditUI->show();} {} Function {~OscilEditor()} {open } { code {osceditUI->hide(); -for (int i=0; iupdate(); - for (int i=0;irefresh(); + for (int i=0;i<(MAX_AD_HARMONICS - 1);i++) h[i]->refresh(); osc->requestValue(loc+"prepare"); @@ -989,7 +989,7 @@ oscils->update(); oscilo_base->update(); oscils_base->update();} {} } - decl {Oscilharmonic *h[MAX_AD_HARMONICS];} {private local + decl {Oscilharmonic *h[(MAX_AD_HARMONICS - 1)];} {private local } decl {std::string loc;} {private local } diff --git a/source/native-plugins/zynaddsubfx/UI/PADnoteUI.fl b/source/native-plugins/zynaddsubfx/UI/PADnoteUI.fl index 239ede047..d9e6afd49 100644 --- a/source/native-plugins/zynaddsubfx/UI/PADnoteUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/PADnoteUI.fl @@ -84,8 +84,7 @@ initialized = true;} {selected } { Fl_Box dummy { - code0 {padnotewindow->osc = osc_i; padnotewindow->base = location;} - code1 {puts("dummy setup done...");} + code0 {padnotewindow->init(osc_i, location);} } Fl_Tabs {} { callback {if (o->value()!=harmonicstructuregroup) applybutton->hide(); diff --git a/source/native-plugins/zynaddsubfx/UI/PartUI.fl b/source/native-plugins/zynaddsubfx/UI/PartUI.fl index d2a55c425..119dba12a 100644 --- a/source/native-plugins/zynaddsubfx/UI/PartUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/PartUI.fl @@ -418,8 +418,7 @@ if (event==FL_RIGHT_MOUSE){ } { Fl_Box {} { xywh {0 0 0 0} - code0 {ctlwindow->osc = osc;} - code1 {ctlwindow->base = "/part"+to_s(npart)+"/ctl/";} + code0 {ctlwindow->init(osc,"/part"+to_s(npart)+"/ctl/");} } Fl_Check_Button {} { label Expr @@ -610,8 +609,7 @@ else {propta->deactivate();proptb->deactivate();}} } { Fl_Box {} { xywh {0 0 0 0} - code0 {partfx->osc = osc;} - code1 {partfx->base = part_path;} + code0 {partfx->init(osc, part_path);} } Fl_Counter inseffnocounter { label {FX No.} @@ -726,12 +724,12 @@ if (x==2) part->partefx[ninseff]->setdryonly(true); } Fl_Button {} { label C - callback {presetsui->copy(partfx->loc());} + callback {presetsui->copy(partfx->loc()+"partefx"+to_s(ninseff)+"/");} xywh {90 127 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 55 } Fl_Button {} { label P - callback {presetsui->paste(partfx->loc(),inseffectui);} + callback {presetsui->paste(partfx->loc()+"partefx"+to_s(ninseff)+"/",inseffectui);} xywh {120 127 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 55 } } @@ -742,8 +740,7 @@ if (x==2) part->partefx[ninseff]->setdryonly(true); } { Fl_Box {} { xywh {0 0 0 0} - code0 {instrumentkitlist->osc = osc;} - code1 {instrumentkitlist->base = "/part"+to_s(npart)+"/";} + code0 {instrumentkitlist->init(osc, "/part"+to_s(npart)+"/");} } Fl_Button {} { label {Close Window} @@ -824,8 +821,7 @@ if (x==2) part->partefx[ninseff]->setdryonly(true); } { Fl_Box {} { xywh {0 0 0 0} - code0 {instrumenteditwindow->osc = osc;} - code1 {instrumenteditwindow->base = "/part"+to_s(npart)+"/";} + code0 {instrumenteditwindow->init(osc, "/part"+to_s(npart)+"/");} } Fl_Group editgroup { xywh {0 220 395 110} box UP_FRAME @@ -1093,7 +1089,7 @@ delete(instrumenteditwindow);} {} } decl {BankUI *bankui;} {private local } - decl {ADnoteUI *adnoteui;} {private local + decl {ADnoteUI *adnoteui;} {selected public local } decl {SUBnoteUI *subnoteui;} {private local } diff --git a/source/native-plugins/zynaddsubfx/UI/PresetsUI.fl b/source/native-plugins/zynaddsubfx/UI/PresetsUI.fl index 3b67efa4c..f329d5a00 100644 --- a/source/native-plugins/zynaddsubfx/UI/PresetsUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/PresetsUI.fl @@ -60,7 +60,7 @@ class PresetsUI {} { printf("Value = %s\\n", val.c_str()); copytypetext->label(val.c_str()); pastetypetext->label(val.c_str()); - listmodel.update("/presets/scan-for-presets"); + listmodel.doUpdate("/presets/scan-for-presets"); }; } {} @@ -268,7 +268,7 @@ class PresetsUI {} { pastebrowse->clear(); printf("Datamodel Update..."); - datamodel.update(url+"preset-type"); + datamodel.doUpdate(url+"preset-type"); } {} } decl {std::string url;} {public local diff --git a/source/native-plugins/zynaddsubfx/UI/SUBnoteUI.fl b/source/native-plugins/zynaddsubfx/UI/SUBnoteUI.fl index d257b392e..a4edafaa2 100644 --- a/source/native-plugins/zynaddsubfx/UI/SUBnoteUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/SUBnoteUI.fl @@ -184,7 +184,7 @@ class SUBnoteUI {open : {public PresetsUI_} } { Fl_Box {} { xywh {0 0 0 0} box FLAT_BOX color 45 - code0 {SUBparameters->osc = osc; SUBparameters->base = loc;} + code0 {SUBparameters->init(osc, loc);} } Fl_Scroll {} { label scroll open diff --git a/source/native-plugins/zynaddsubfx/UI/TipWin.cpp b/source/native-plugins/zynaddsubfx/UI/TipWin.cpp new file mode 100644 index 000000000..594d00f29 --- /dev/null +++ b/source/native-plugins/zynaddsubfx/UI/TipWin.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include "TipWin.h" + +TipWin::TipWin(void):Fl_Menu_Window(1, 1) +{ + strcpy(format, "%0.2f"); + set_override(); + end(); +} + +void TipWin::setRounding(unsigned int digits) +{ + format[3] = "0123456789"[digits < 9 ? digits : 9]; +} + +void TipWin::draw() +{ + //setup window + draw_box(FL_BORDER_BOX, 0, 0, w(), h(), Fl_Color(175)); + fl_color(Fl_Tooltip::textcolor()); + fl_font(labelfont(), labelsize()); + + //Draw the current string + fl_draw(getStr(), 3, 3, w() - 6, h() - 6, + Fl_Align(FL_ALIGN_LEFT | FL_ALIGN_WRAP)); +} + +void TipWin::showValue(float f) +{ + //convert the value to a string + char tmp[10]; + snprintf(tmp, 9, format, f); + tip = tmp; + + textmode = false; + redraw(); + show(); +} + +void TipWin::setText(const char *c) +{ + text = c; + textmode = true; + redraw(); +} + +void TipWin::showText() +{ + if(!text.empty()) { + textmode = true; + redraw(); + show(); + } +} + +void TipWin::redraw() +{ + // Recalc size of window + fl_font(labelfont(), labelsize()); + int W = 0, H = 0; + fl_measure(getStr(), W, H, 0); + //provide a bit of extra space + W += 8; + H += 4; + size(W, H); + Fl_Menu_Window::redraw(); +} + +const char *TipWin::getStr() const +{ + return (textmode ? text : tip).c_str(); +} diff --git a/source/native-plugins/zynaddsubfx/UI/TipWin.h b/source/native-plugins/zynaddsubfx/UI/TipWin.h new file mode 100644 index 000000000..cb6bcf886 --- /dev/null +++ b/source/native-plugins/zynaddsubfx/UI/TipWin.h @@ -0,0 +1,25 @@ +#ifndef TIPWIN_H +#include +#include +#include +#define TIPWIN_H +using namespace std; + +class TipWin:public Fl_Menu_Window +{ + public: + TipWin(); + void draw(); + void showValue(float f); + void setText(const char *c); + void showText(); + void setRounding(unsigned int digits = 0); + private: + void redraw(); + const char *getStr() const; + string tip; + string text; + bool textmode; + char format[6]; +}; +#endif diff --git a/source/native-plugins/zynaddsubfx/UI/WidgetPDial.cpp b/source/native-plugins/zynaddsubfx/UI/WidgetPDial.cpp index ef752ce70..82570677c 100644 --- a/source/native-plugins/zynaddsubfx/UI/WidgetPDial.cpp +++ b/source/native-plugins/zynaddsubfx/UI/WidgetPDial.cpp @@ -1,104 +1,19 @@ // generated by Fast Light User Interface Designer (fluid) version 1.0107f -#include "WidgetPDial.h" -#include -#include #include -#include -#include #include -#include -#include #include "../Misc/Util.h" +#include "WidgetPDial.h" //Copyright (c) 2003-2005 Nasca Octavian Paul //License: GNU GPL version 2 or later -using namespace std; - -class TipWin:public Fl_Menu_Window -{ - public: - TipWin(); - void draw(); - void showValue(float f); - void setText(const char *c); - void showText(); - private: - void redraw(); - const char *getStr() const; - string tip; - string text; - bool textmode; -}; - -TipWin::TipWin():Fl_Menu_Window(1, 1) -{ - set_override(); - end(); -} - -void TipWin::draw() -{ - //setup window - draw_box(FL_BORDER_BOX, 0, 0, w(), h(), Fl_Color(175)); - fl_color(Fl_Tooltip::textcolor()); - fl_font(labelfont(), labelsize()); - - //Draw the current string - fl_draw(getStr(), 3, 3, w() - 6, h() - 6, - Fl_Align(FL_ALIGN_LEFT | FL_ALIGN_WRAP)); -} - -void TipWin::showValue(float f) -{ - //convert the value to a string - char tmp[10]; - snprintf(tmp, 9, "%.2f", f); - tip = tmp; - - textmode = false; - redraw(); - show(); -} - -void TipWin::setText(const char *c) -{ - text = c; - textmode = true; - redraw(); -} - -void TipWin::showText() -{ - if(!text.empty()) { - textmode = true; - redraw(); - show(); - } -} - -void TipWin::redraw() -{ - // Recalc size of window - fl_font(labelfont(), labelsize()); - int W = 0, H = 0; - fl_measure(getStr(), W, H, 0); - //provide a bit of extra space - W += 8; - H += 4; - size(W, H); - Fl_Menu_Window::redraw(); -} - -const char *TipWin::getStr() const -{ - return (textmode ? text : tip).c_str(); -} //static int numobj = 0; WidgetPDial::WidgetPDial(int x, int y, int w, int h, const char *label) - :Fl_Dial(x, y, w, h, label), oldvalue(0.0f), pos(false), textset(false) + :Fl_Dial(x, y, w, h, label), reset_value(0), integer_step(true), + oldvalue(0.0f), pos(false), textset(false), value_offset(0.0), + value_scale(1.0) { //cout << "[" << label << "] There are now " << ++numobj << endl; Fl_Group *save = Fl_Group::current(); @@ -113,27 +28,46 @@ WidgetPDial::~WidgetPDial() delete tipwin; } +void WidgetPDial::setRounding(unsigned int digits) +{ + tipwin->setRounding(digits); +} + int WidgetPDial::handle(int event) { -//#ifdef NTK_GUI -// return Fl_Dial::handle( event ); -//#else - double dragsize, min = minimum(), max = maximum(); - int my; + double dragsize, min = minimum(), max = maximum(), result; + int dy; + if (event == FL_RELEASE && Fl::event_clicks() == 1) { + Fl::event_clicks(0); + value(reset_value); + value_damage(); + if (this->when() != 0) + do_callback(); + return 1; + } + switch(event) { case FL_PUSH: + if (integer_step) + setRounding(0); + else if (Fl::event_shift()) + setRounding(4); + else + setRounding(Fl::event_button1() ? 2 : 3); oldvalue = value(); + old_y = Fl::event_y(); case FL_DRAG: getPos(); - my = -(Fl::event_y() - y() - h() / 2); + dy = old_y - Fl::event_y(); - dragsize = 200.0f; - if(Fl::event_state(FL_BUTTON1) == 0) - dragsize *= 10; + if (Fl::event_shift()) + dragsize = 20000.0f; + else + dragsize = Fl::event_button1() ? 200.0f : 2000.0f; - value(limit(oldvalue + my / dragsize * (max - min), min, max)); - tipwin->showValue(value()); + value(clamp(oldvalue + dy / dragsize * (max - min))); + tipwin->showValue(transform(value())); value_damage(); if(this->when() != 0) do_callback(); @@ -141,14 +75,28 @@ int WidgetPDial::handle(int event) case FL_MOUSEWHEEL: if (Fl::belowmouse() != this) return 1; - my = - Fl::event_dy(); - - dragsize = 200.0f; - if(Fl::event_state(FL_CTRL) != 0) - dragsize *= 10; - - value(limit(value() + my / dragsize * (max - min), min, max)); - tipwin->showValue(value()); + dy = - Fl::event_dy(); + + if (integer_step) { + setRounding(0); + result = (int)(value() + dy * (Fl::event_ctrl() ? 1 : 8)); + } else { + float dragsize; + if (Fl::event_shift()) { + dragsize = 10000.0; + setRounding(4); + } else if (Fl::event_ctrl()) { + dragsize = 1000.0; + setRounding(3); + } else { + dragsize = 100.0; + setRounding(2); + } + result = value() + dy / dragsize * (max - min); + } + value(clamp(result)); + + tipwin->showValue(transform(value())); value_damage(); if(this->when() != 0) do_callback(); @@ -163,6 +111,10 @@ int WidgetPDial::handle(int event) resetPos(); break; case FL_RELEASE: + if (integer_step) { + int rounded = value() + 0.5; + value(clamp(rounded)); + } tipwin->hide(); resetPos(); if(this->when() == 0) @@ -262,3 +214,14 @@ void WidgetPDial::resetPos() { pos = false; } + +void WidgetPDial::set_transform(float scale, float offset) +{ + value_offset = offset; + value_scale = scale; +} + +float WidgetPDial::transform(float x) +{ + return value_scale * x + value_offset; +} diff --git a/source/native-plugins/zynaddsubfx/UI/WidgetPDial.h b/source/native-plugins/zynaddsubfx/UI/WidgetPDial.h index b386b542e..534e3d7c1 100644 --- a/source/native-plugins/zynaddsubfx/UI/WidgetPDial.h +++ b/source/native-plugins/zynaddsubfx/UI/WidgetPDial.h @@ -3,6 +3,7 @@ #ifndef WIDGETPDIAL_h #define WIDGETPDIAL_h #include +#include "TipWin.h" class WidgetPDial:public Fl_Dial @@ -14,12 +15,21 @@ class WidgetPDial:public Fl_Dial void draw(); void pdialcolor(int r, int g, int b); void tooltip(const char *c); + void set_transform(float scale = 1.0, float offset = 0.0); + float transform(float x); + void setRounding(unsigned int digits = 0); + float reset_value; + protected: + bool integer_step; private: void getPos(); void resetPos(); double oldvalue; + int old_y; bool pos; bool textset; class TipWin * tipwin; + float value_offset; + float value_scale; }; #endif diff --git a/source/native-plugins/zynaddsubfx/UI/guimain.cpp b/source/native-plugins/zynaddsubfx/UI/guimain.cpp index a22eb33d9..4849d0f74 100644 --- a/source/native-plugins/zynaddsubfx/UI/guimain.cpp +++ b/source/native-plugins/zynaddsubfx/UI/guimain.cpp @@ -541,6 +541,7 @@ int main(int argc, char *argv[]) lo_server_add_method(server, NULL, NULL, handler_function, 0); sendtourl = argv[1]; } + fprintf(stderr, "ext client running on %d\n", lo_server_get_port(server)); gui = GUI::createUi(new UI_Interface(), &Pexitprogram); diff --git a/source/native-plugins/zynaddsubfx/globals.cpp b/source/native-plugins/zynaddsubfx/globals.cpp index 9e80eb199..14a161be3 100644 --- a/source/native-plugins/zynaddsubfx/globals.cpp +++ b/source/native-plugins/zynaddsubfx/globals.cpp @@ -24,7 +24,7 @@ #include "Misc/Util.h" #include "globals.h" -void SYNTH_T::alias() +void SYNTH_T::alias(bool randomize) { halfsamplerate_f = (samplerate_f = samplerate) / 2.0f; buffersize_f = buffersize; @@ -36,5 +36,8 @@ void SYNTH_T::alias() // for deleting the buffers and also call it in the dtor denormalkillbuf.resize(buffersize); for(int i = 0; i < buffersize; ++i) - denormalkillbuf[i] = (RND - 0.5f) * 1e-16; + if(randomize) + denormalkillbuf[i] = (RND - 0.5f) * 1e-16; + else + denormalkillbuf[i] = 0; } diff --git a/source/native-plugins/zynaddsubfx/globals.h b/source/native-plugins/zynaddsubfx/globals.h index 8c1dadaa5..eb93455e4 100644 --- a/source/native-plugins/zynaddsubfx/globals.h +++ b/source/native-plugins/zynaddsubfx/globals.h @@ -43,6 +43,8 @@ class PADnoteParameters; class SynthNote; class Allocator; +class AbsTime; +class RelTime; class Microtonal; class XMLwrapper; @@ -142,6 +144,14 @@ typedef std::complex fft_t; */ #define PART_MAX_NAME_LEN 30 +/* + * The maximum we allow for an XMZ path + * + * Note that this is an ugly hack. Finding a compile time path + * max portably is painful. + */ +#define XMZ_PATH_MAX 1024 + /* * The maximum number of bands of the equaliser */ @@ -270,8 +280,8 @@ public: operator T*() { return ptr; } operator const T*() const { return ptr; } - T& operator[](unsigned idx) { return ptr[idx]; } - const T& operator[](unsigned idx) const { return ptr[idx]; } + //T& operator[](unsigned idx) { return ptr[idx]; } + //const T& operator[](unsigned idx) const { return ptr[idx]; } }; //temporary include for synth->{samplerate/buffersize} members @@ -280,7 +290,7 @@ struct SYNTH_T { SYNTH_T(void) :samplerate(44100), buffersize(256), oscilsize(1024) { - alias(); + alias(false); } SYNTH_T(const SYNTH_T& ) = delete; @@ -320,7 +330,7 @@ struct SYNTH_T { { return buffersize_f / samplerate_f; } - void alias(void); + void alias(bool randomize=true); static float numRandom(void); //defined in Util.cpp for now }; #endif diff --git a/source/native-plugins/zynaddsubfx/main.cpp b/source/native-plugins/zynaddsubfx/main.cpp index eab03bbcc..ab8cdacbd 100644 --- a/source/native-plugins/zynaddsubfx/main.cpp +++ b/source/native-plugins/zynaddsubfx/main.cpp @@ -138,7 +138,7 @@ int main(int argc, char *argv[]) << " Copyright (c) 2009-2014 Mark McCurry [active maintainer]" << endl; cerr << "Compiled: " << __DATE__ << " " << __TIME__ << endl; - cerr << "This program is free software (GNU GPL v.2 or later) and \n"; + cerr << "This program is free software (GNU GPL v2 or later) and \n"; cerr << "it comes with ABSOLUTELY NO WARRANTY.\n" << endl; if(argc == 1) cerr << "Try 'zynaddsubfx --help' for command-line options." << endl; @@ -211,6 +211,9 @@ int main(int argc, char *argv[]) { "dump-oscdoc", 2, NULL, 'd' }, + { + "ui-title", 1, NULL, 'u' + }, { 0, 0, 0, 0 } @@ -219,7 +222,7 @@ int main(int argc, char *argv[]) int option_index = 0, opt, exitwithhelp = 0, exitwithversion = 0; int prefered_port = -1; - string loadfile, loadinstrument, execAfterInit; + string loadfile, loadinstrument, execAfterInit, ui_title; while(1) { int tmp = 0; @@ -227,7 +230,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:hvapSDUY", + "l:L:r:b:o:I:O:N:e:P:u:hvapSDUY", opts, &option_index); char *optarguments = optarg; @@ -335,6 +338,10 @@ int main(int argc, char *argv[]) outfile << s; } break; + case 'u': + if(optarguments) + ui_title = optarguments; + break; case '?': cerr << "ERROR:Bad option or parameter.\n" << endl; exitwithhelp = 1; @@ -370,6 +377,7 @@ 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; @@ -384,20 +392,26 @@ int main(int argc, char *argv[]) initprogram(std::move(synth), &config, prefered_port); + bool altered_master = false; if(!loadfile.empty()) { - int tmp = master->loadXML(loadfile.c_str()); + altered_master = true; + const char *filename = loadfile.c_str(); + int tmp = master->loadXML(filename); if(tmp < 0) { cerr << "ERROR: Could not load master file " << loadfile << "." << endl; exit(1); } else { + strncpy(master->last_xmz, filename, XMZ_PATH_MAX); + master->last_xmz[XMZ_PATH_MAX-1] = 0; master->applyparameters(); cout << "Master file loaded." << endl; } } if(!loadinstrument.empty()) { + altered_master = true; int loadtopart = 0; int tmp = master->part[loadtopart]->loadXMLinstrument( loadinstrument.c_str()); @@ -413,6 +427,9 @@ int main(int argc, char *argv[]) } } + if(altered_master) + middleware->updateResources(master); + //Run the Nio system bool ioGood = Nio::start(); @@ -447,6 +464,10 @@ int main(int argc, char *argv[]) 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); diff --git a/source/native-plugins/zynaddsubfx/rtosc/cpp/midimapper.cpp b/source/native-plugins/zynaddsubfx/rtosc/cpp/midimapper.cpp index 019b91ee7..32d1774b1 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/cpp/midimapper.cpp +++ b/source/native-plugins/zynaddsubfx/rtosc/cpp/midimapper.cpp @@ -1,5 +1,5 @@ -#include "ports.h" +#include #include #include #include @@ -8,7 +8,7 @@ #include #include -#include "miditable.h" +#include using namespace rtosc; using std::string; diff --git a/source/native-plugins/zynaddsubfx/rtosc/cpp/miditable.cpp b/source/native-plugins/zynaddsubfx/rtosc/cpp/miditable.cpp index 6530584b5..0414c20b8 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/cpp/miditable.cpp +++ b/source/native-plugins/zynaddsubfx/rtosc/cpp/miditable.cpp @@ -1,5 +1,5 @@ #include -#include "miditable.h" +#include using namespace rtosc; diff --git a/source/native-plugins/zynaddsubfx/rtosc/cpp/ports.cpp b/source/native-plugins/zynaddsubfx/rtosc/cpp/ports.cpp index 5b233343c..9d68cfd22 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/cpp/ports.cpp +++ b/source/native-plugins/zynaddsubfx/rtosc/cpp/ports.cpp @@ -1,4 +1,4 @@ -#include "ports.h" +#include "../ports.h" #include #include #include @@ -255,15 +255,15 @@ bool has(T &t, Z&z) return false; } -int rtosc_max(int a, int b) { return a str, Port_Matcher &pm) +static void generate_minimal_hash(std::vector str, Port_Matcher &pm) { pm.pos = find_pos(str); if(pm.pos.empty()) { @@ -382,7 +382,7 @@ void generate_minimal_hash(std::vector str, Port_Matcher &pm) pm.remap = find_remap(str, pm.pos, pm.assoc); } -void generate_minimal_hash(Ports &p, Port_Matcher &pm) +static void generate_minimal_hash(Ports &p, Port_Matcher &pm) { svec_t keys; cvec_t args; @@ -575,6 +575,71 @@ const Port *Ports::apropos(const char *path) const return NULL; } +static bool parent_path_p(char *read, char *start) +{ + if(read-start<2) + return false; + return read[0]=='.' && read[-1]=='.' && read[-2]=='/'; +} + +static void read_path(char *&r, char *start) +{ + while(1) + { + if(r= p) { + //per path chunk either + //(1) find a parent ref and inc consuming + //(2) find a normal ref and consume + //(3) find a normal ref and write through + bool ppath = parent_path_p(read_pos, p); + if(ppath) { + read_path(read_pos, p); + consuming++; + } else if(consuming) { + read_path(read_pos, p); + consuming--; + } else + move_path(read_pos, write_pos, p); + } + //return last written location, not next to write + return write_pos+1; +}; + void rtosc::walk_ports(const Ports *base, char *name_buffer, size_t buffer_size, @@ -670,7 +735,7 @@ void walk_ports2(const rtosc::Ports *base, //for(unsigned i=0; i\n"; + o << " Enable " << doc << "\n"; + o << " \n"; + o << " \n"; + o << " \n"; + o << " Disable " << doc << "\n"; + o << " \n"; + o << " \n"; + o << " \n"; + o << " Get state of " << doc << "\n"; + o << " \n"; + o << " \n"; + o << " Value of " << doc << "\n"; + o << " "; + o << " \n"; + o << " \n"; + o << " Value of " << doc << "\n"; + o << " "; + o << " \n"; + return o; +} +static ostream &dump_any_port(ostream &o, string name, string doc) +{ + o << " \n"; + o << " " << doc << "\n"; + o << " \n"; + return o; +} + +static ostream &dump_generic_port(ostream &o, string name, string doc, string type) +{ + const char *t = type.c_str(); + string arg_names = "xyzabcdefghijklmnopqrstuvw"; + + //start out with argument separator + if(*t++ != ':') + return o; + //now real arguments (assume [] don't exist) + string args; + while(*t && *t != ':') + args += *t++; + + o << " \n"; + o << " " << doc << "\n"; + + assert(args.length()\n"; + o << " \n"; + + if(*t == ':') + return dump_generic_port(o, name, doc, t); + else + return o; +} + void dump_ports_cb(const rtosc::Port *p, const char *name, void *v) { - std::ostream &o = *(std::ostream*)v; - auto meta = p->meta(); - if(meta.find("parameter") != p->meta().end()) { + std::ostream &o = *(std::ostream*)v; + auto meta = p->meta(); + const char *args = strchr(p->name, ':'); + auto mparameter = meta.find("parameter"); + auto mdoc = meta.find("documentation"); + string doc; + + if(mdoc != p->meta().end()) + doc = mdoc.value; + if(meta.find("internal") != meta.end()) { + doc += "[INTERNAL]"; + } + + if(mparameter != p->meta().end()) { char type = 0; - const char *foo = strchr(p->name, ':'); - if(strchr(foo, 'f')) - type = 'f'; - else if(strchr(foo, 'i')) - type = 'i'; - else if(strchr(foo, 'c')) - type = 'c'; - else if(strchr(foo, 'T')) - type = 't'; + if(args) { + if(strchr(args, 'f')) + type = 'f'; + else if(strchr(args, 'i')) + type = 'i'; + else if(strchr(args, 'c')) + type = 'c'; + else if(strchr(args, 'T')) + type = 't'; + else if(strchr(args, 's')) + type = 's'; + } + if(!type) { - fprintf(stderr, "rtosc port dumper: Cannot handle '%s'\n", p->name); + fprintf(stderr, "rtosc port dumper: Cannot handle '%s'\n", name); + fprintf(stderr, " args = <%s>\n", args); return; } - if(type == 't') - { - o << " \n"; - o << " Enable " << p->meta()["documentation"] << "\n"; - o << " \n"; - o << " \n"; - o << " \n"; - o << " Disable " << p->meta()["documentation"] << "\n"; - o << " \n"; - o << " \n"; - o << " \n"; - o << " Get state of " << p->meta()["documentation"] << "\n"; - o << " \n"; - o << " \n"; - o << " Value of " << p->meta()["documentation"] << "\n"; - o << " "; - o << " \n"; - o << " \n"; - o << " Value of %s\n", p->meta()["documentation"]; - o << " "; - o << " \n"; + if(type == 't') { + dump_t_f_port(o, name, doc); return; } o << " \n"; - o << " Set Value of " << p->meta()["documentation"] << "\n"; - if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') - { + o << " Set Value of " << doc << "\n"; + if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') { o << " \n"; @@ -785,12 +906,11 @@ void dump_ports_cb(const rtosc::Port *p, const char *name, void *v) } o << " \n"; o << " \n"; - o << " Get Value of " << p->meta()["documentation"] << "\n"; + o << " Get Value of " << doc << "\n"; o << " \n"; o << " \n"; - o << " Value of " << p->meta()["documentation"] << "\n"; - if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') - { + o << " Value of " << doc << "\n"; + if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') { o << " \n"; @@ -803,10 +923,17 @@ void dump_ports_cb(const rtosc::Port *p, const char *name, void *v) o << "/>\n"; } o << " \n"; - }// else if(meta.find("documentation") != meta.end()) - // fprintf(stderr, "Skipping \"%s\"\n", name); - //else - // fprintf(stderr, "Skipping [UNDOCUMENTED] \"%s\"\n", name); + } else if(mdoc != meta.end() && (!args || args == std::string(""))) { + dump_any_port(o, name, doc); + } else if(mdoc != meta.end() && args) { + dump_generic_port(o, name, doc, args); + } else if(mdoc != meta.end()) { + fprintf(stderr, "Skipping \"%s\"\n", name); + if(args) { + fprintf(stderr, " type = %s\n", args); + } + } else + fprintf(stderr, "Skipping [UNDOCUMENTED] \"%s\"\n", name); } std::ostream &rtosc::operator<<(std::ostream &o, rtosc::OscDocFormatter &formatter) diff --git a/source/native-plugins/zynaddsubfx/rtosc/cpp/subtree-serialize.cpp b/source/native-plugins/zynaddsubfx/rtosc/cpp/subtree-serialize.cpp index ad425d198..63d853f58 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/cpp/subtree-serialize.cpp +++ b/source/native-plugins/zynaddsubfx/rtosc/cpp/subtree-serialize.cpp @@ -1,12 +1,20 @@ -#include "subtree-serialize.h" -#include "ports.h" -#include "rtosc.h" +#include +#include +#include #include #include using namespace rtosc; +static void emplace_uint32_cpp(uint8_t *buffer, uint32_t d) +{ + buffer[0] = ((d>>24) & 0xff); + buffer[1] = ((d>>16) & 0xff); + buffer[2] = ((d>>8) & 0xff); + buffer[3] = ((d>>0) & 0xff); +} + /* * Append another message onto a bundle if the space permits it. * If insufficient space is available, then zero is returned and the buffer is @@ -26,7 +34,7 @@ static size_t append_bundle(char *dst, const char *src, if(max_len < dst_len + src_len + 4 || dst_len == 0 || src_len == 0) return 0; - *(int32_t*)(dst+dst_len) = (int32_t)src_len; + emplace_uint32_cpp((uint8_t*)(dst+dst_len), src_len); memcpy(dst+dst_len+4, src, src_len); diff --git a/source/native-plugins/zynaddsubfx/rtosc/cpp/thread-link.cpp b/source/native-plugins/zynaddsubfx/rtosc/cpp/thread-link.cpp index 646b3e912..c57873d93 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/cpp/thread-link.cpp +++ b/source/native-plugins/zynaddsubfx/rtosc/cpp/thread-link.cpp @@ -1,4 +1,4 @@ -#include "thread-link.h" +#include "../thread-link.h" namespace rtosc { #ifdef off_t @@ -17,7 +17,6 @@ struct internal_ringbuffer_t { }; typedef internal_ringbuffer_t ringbuffer_t; -#define static static size_t ring_read_size(ringbuffer_t *ring) { @@ -178,5 +177,3 @@ char *ThreadLink::buffer(void) {return write_buffer;} size_t ThreadLink::buffer_size(void) const {return BufferSize;} }; - -#undef static diff --git a/source/native-plugins/zynaddsubfx/rtosc/cpp/undo-history.cpp b/source/native-plugins/zynaddsubfx/rtosc/cpp/undo-history.cpp index e4bdc3cf3..e5aa8c4ea 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/cpp/undo-history.cpp +++ b/source/native-plugins/zynaddsubfx/rtosc/cpp/undo-history.cpp @@ -3,8 +3,8 @@ #include #include #include -#include "rtosc.h" -#include "undo-history.h" +#include +#include using std::pair; using std::make_pair; diff --git a/source/native-plugins/zynaddsubfx/rtosc/dispatch.c b/source/native-plugins/zynaddsubfx/rtosc/dispatch.c index 95e48e0e2..dcce04b1b 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/dispatch.c +++ b/source/native-plugins/zynaddsubfx/rtosc/dispatch.c @@ -1,7 +1,13 @@ -#include "rtosc.h" +#include #include +#include +#include +#include #include + +#include + static bool rtosc_match_number(const char **pattern, const char **msg) { //Verify both hold digits @@ -74,3 +80,207 @@ bool rtosc_match(const char *pattern, const char *msg) return rtosc_match_args(arg_pattern, msg); return true; } + + + +/* + * Special characters from the specification: + * ' ' space 32 + * # number sign 35 + * * asterisk 42 + * , comma 44 + * / forward slash 47 + * ? question mark 63 + * [ open bracket 91 + * ] close bracket 93 + * { open curly brace 123 + * } close curly brace 125 + */ + +#if 0 +QUOTE FROM OSC 1.0 SPEC + +'?' in the OSC Address Pattern matches any single character +'*' in the OSC Address Pattern matches any sequence of zero or more characters +A string of characters in square brackets (e.g., "[string]") in the OSC Address Pattern matches any character in the string. +Inside square brackets, the minus sign (-) and exclamation point (!) have special meanings: + two characters separated by a minus sign indicate the range of characters between the given two +in ASCII collating sequence. (A minus sign at the end of the string has no special meaning.) + An exclamation point at the beginning of a bracketed string negates the sense of the list, + meaning that the list matches any character not in the list. +(An exclamation point anywhere besides the first character after the open bracket has no special meaning.) + A comma-separated list of strings enclosed in curly braces (e.g., "{foo,bar}") in the OSC Address Pattern + matches any of the strings in the list. +#endif + + + + +//for literal string X +//for X+?+[] Y +//for Y+single{} Z +//for numeric string N +//assume a is of the form X +//assume b is a pattern of the form: +//* (1) +//Y (2) +//Y* (3) +//*X* (4) +//Z (5) +//Z* (6) +//Y#N (7) +#define RTOSC_MATCH_ALL 1 +#define RTOSC_MATCH_CHAR 2 +#define RTOSC_MATCH_PARTIAL_CHAR 3 +#define RTOSC_MATCH_SUBSTRING 4 +#define RTOSC_MATCH_OPTIONS 5 +#define RTOSC_MATCH_PARTIAL_OPTIONS 6 +#define RTOSC_MATCH_ENUMERATED 7 +static bool is_charwise(char c) +{ + return (c>=0 && c<=0x7f) && c != ' ' && c != '#' && + c != '/' && c != '{' && c != '}'; +} + +int rtosc_subpath_pat_type(const char *pattern) +{ + int charwise_only = 1; + const char *last_star = rindex(pattern, '*'); + const char *pound = index(pattern, '#'); + if(!strcmp("*", pattern)) + return RTOSC_MATCH_ALL; + + for(const char *p = pattern;*p;++p) + charwise_only &= is_charwise(*p); + if(charwise_only && !last_star) + return RTOSC_MATCH_CHAR; + if(pound) + return RTOSC_MATCH_ENUMERATED; + + + return 2; +} + +static bool rtosc_match_char(const char **path, const char **pattern) +{ + //printf("rtosc_match_char('%s','%s')\n", *path, *pattern); + if(**path == **pattern && **path) { + ++*path; + ++*pattern; + return true; + } else if(**pattern == '?' && *path) { + ++*path; + ++*pattern; + return true; + } else if(**pattern == '[') { + bool matched = false; + bool negation = false; + char last_range = '\0'; + char to_match = **path; + ++*pattern; + if(**pattern == '!') { + negation = true; + ++*pattern; + } + while(**pattern && **pattern != ']') { + last_range = **pattern; + if(**pattern == to_match) { + matched = true; + } else if(**pattern == '-') {//range + ++*pattern; + char range_high = **pattern; + if(range_high == ']' || !range_high) + break; + if(to_match <= range_high && to_match >= last_range) + matched = true; + } + ++*pattern; + } + if(**pattern == ']') + ++*pattern; + ++*path; + return negation ^ matched; + } + return false; +} + +bool rtosc_match_partial(const char *a, const char *b) +{ + //assume a is of the form X + //assume b is a pattern of the form: (1..6) + //This is done to avoid backtracking of any kind + //This is an OSC serialization library, not a regex + //implementation + + char patternbuf[256]; + (void) patternbuf; + int type = rtosc_subpath_pat_type(b); + + if(type == RTOSC_MATCH_ALL) + return true; + else if(type == RTOSC_MATCH_CHAR || type == RTOSC_MATCH_PARTIAL_CHAR) { + while(rtosc_match_char(&a,&b)); + if(!*a && !*b) + return true; + else if(*a && *b=='*' && b[1] == '\0') + return true; + else + return false; + } else if(type == 4) { + //extract substring + const char *sub=NULL; + return strstr(a,sub); + } else if(type == RTOSC_MATCH_OPTIONS || type == 6) { + } else if(type == RTOSC_MATCH_ENUMERATED) { + while(rtosc_match_char(&a,&b)); + if(*a && *b=='#' && b[1] != '\0') + return atoi(a) < atoi(b+1); + return false; + } else + return 0; + assert(false); +} + +int rtosc_matchable_path(const char *pattern) +{ + (void) pattern; + return 0; + +} + +int chunk_path(const char *a, int b, const char *c) +{ + (void) a; + (void) b; + (void) c; + return 0; +} +void advance_path(const char **a) +{ + (void) a; +} + +bool rtosc_match_full_path(const char *pattern, const char *message) +{ + assert(false && "This API is a WIP"); + char subpattern[256]; + char submessage[256]; + const char *p = pattern; + const char *m = message; + +step: + if(*p != *m) + return 0; + if(chunk_path(subpattern, sizeof(subpattern), p)) + return 0; + if(chunk_path(submessage, sizeof(submessage), m)) + return 0; + + advance_path(&p); + advance_path(&m); + + if(*p == 0 && *m == 0) + return 1; + else + goto step; +} diff --git a/source/native-plugins/zynaddsubfx/rtosc/miditable.h b/source/native-plugins/zynaddsubfx/rtosc/miditable.h index 125f3c93c..115a99cb1 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/miditable.h +++ b/source/native-plugins/zynaddsubfx/rtosc/miditable.h @@ -21,9 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ +#ifndef RTOSC_MIDITABLE_H +#define RTOSC_MIDITABLE_H -#pragma once -#include "ports.h" +#include #include #include #include @@ -276,3 +277,4 @@ class MidiTable }; }; +#endif diff --git a/source/native-plugins/zynaddsubfx/rtosc/port-sugar.h b/source/native-plugins/zynaddsubfx/rtosc/port-sugar.h index d955c0a61..f750834f6 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/port-sugar.h +++ b/source/native-plugins/zynaddsubfx/rtosc/port-sugar.h @@ -43,8 +43,8 @@ struct rtosc_hack_decltype_t #define STRINGIFY(a) STRINGIFY2(a) //Helper for documenting varargs -#define IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9, N, ...) N -#define LAST_IMP(...) IMPL(__VA_ARGS__,9,8,7,6,5,4,3,2,1,0,0,0,0) +#define IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,N, ...) N +#define LAST_IMP(...) IMPL(__VA_ARGS__,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0,0,0) #define DOC_IMP9(a,b,c,d,e,f,g,h,i) a b c d e f g h rDoc(i) #define DOC_IMP8(a,b,c,d,e,f,g,h) a b c d e f g rDoc(h) #define DOC_IMP7(a,b,c,d,e,f,g) a b c d e f rDoc(g) @@ -75,6 +75,30 @@ struct rtosc_hack_decltype_t #define MAC_EACH_I(mac, count, ...) MAC_EACH_IMP(mac, count, __VA_ARGS__) #define MAC_EACH(mac, ...) MAC_EACH_I(mac, LAST_IMP(__VA_ARGS__), __VA_ARGS__) +// 1 2 3 4 5 6 7 8 910111213141516 +#define OPTIONS_IMP16(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ + 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(14,o)rOpt(15,p) +#define OPTIONS_IMP15(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) \ + 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(14,o) +#define OPTIONS_IMP14(a,b,c,d,e,f,g,h,i,j,k,l,m,n) \ + 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) +#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) +#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) +#define OPTIONS_IMP11(a,b,c,d,e,f,g,h,i,j,k) \ + 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) +#define OPTIONS_IMP10(a,b,c,d,e,f,g,h,i,j) \ + 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) #define OPTIONS_IMP9(a,b,c,d,e,f,g,h,i) \ 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) @@ -137,7 +161,7 @@ struct rtosc_hack_decltype_t //Alias operators #define rParams(name, length, ...) \ rArray(name, length, __VA_ARGS__), \ -{STRINGIFY(name) ":", rProp(alias), NULL, rParamsCb(name, length)} +{STRINGIFY(name) ":", rProp(alias) rDoc("get all data from aliased array"), NULL, rParamsCb(name, length)} template constexpr T spice(T*t) {return *t;} @@ -145,7 +169,7 @@ template constexpr T spice(T*t) {return *t;} //Recursion [two ports in one for pointer manipulation] #define rRecur(name, ...) \ {STRINGIFY(name) "/", DOC(__VA_ARGS__), &decltype(rObject::name)::ports, rRecurCb(name)}, \ - {STRINGIFY(name) ":", rProp(internal), NULL, rRecurPtrCb(name)} + {STRINGIFY(name) ":", rProp(internal) rDoc("get obj pointer"), NULL, rRecurPtrCb(name)} #define rRecurp(name, ...) \ {STRINGIFY(name) "/", DOC(__VA_ARGS__), \ @@ -168,7 +192,7 @@ template constexpr T spice(T*t) {return *t;} //Misc #define rDummy(name, ...) {STRINIFY(name), rProp(dummy), NULL, [](msg_t, rtosc::RtData &){}} #define rString(name, len, ...) \ - {STRINGIFY(name) "::s", rMap(length, len) DOC(__VA_ARGS__), NULL, rStringCb(name,len)} + {STRINGIFY(name) "::s", rMap(length, len) rProp(parameter) DOC(__VA_ARGS__), NULL, rStringCb(name,len)} //General property operators #define rMap(name, value) ":" STRINGIFY(name) "\0=" STRINGIFY(value) "\0" diff --git a/source/native-plugins/zynaddsubfx/rtosc/ports.h b/source/native-plugins/zynaddsubfx/rtosc/ports.h index 5440e1ed7..2fab58647 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/ports.h +++ b/source/native-plugins/zynaddsubfx/rtosc/ports.h @@ -28,7 +28,7 @@ #include #include #include -#include "rtosc.h" +#include #include #include #include @@ -180,6 +180,13 @@ struct Ports */ const Port *apropos(const char *path) const; + /** + * Collapse path with parent path identifiers "/.." + * + * e.g. /foo/bar/../baz => /foo/baz + */ + static char *collapsePath(char *p); + private: //Performance hacks class Port_Matcher *impl; diff --git a/source/native-plugins/zynaddsubfx/rtosc/rtosc.c b/source/native-plugins/zynaddsubfx/rtosc/rtosc.c index 0f45fd030..783c6c834 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/rtosc.c +++ b/source/native-plugins/zynaddsubfx/rtosc/rtosc.c @@ -6,7 +6,7 @@ #include #include -#include "rtosc.h" +#include const char *rtosc_argument_string(const char *msg) { @@ -78,6 +78,58 @@ char rtosc_type(const char *msg, unsigned nargument) } } +static unsigned arg_start(const char *msg_) +{ + const uint8_t *msg = (const uint8_t*)msg_; + //Iterate to the right position + const uint8_t *args = (const uint8_t*) rtosc_argument_string(msg_); + const uint8_t *aligned_ptr = args-1; + const uint8_t *arg_pos = args; + + while(*++arg_pos); + //Alignment + arg_pos += 4-(arg_pos-aligned_ptr)%4; + return arg_pos-msg; +} + +static unsigned arg_size(const uint8_t *arg_mem, char type) +{ + if(!has_reserved(type)) + return 0; + const uint8_t *arg_pos=arg_mem; + uint32_t blob_length = 0; + switch(type) + { + case 'h': + case 't': + case 'd': + return 8; + case 'm': + case 'r': + case 'f': + case 'c': + case 'i': + return 4; + case 'S': + case 's': + while(*++arg_pos); + arg_pos += 4-(arg_pos-arg_mem)%4; + return arg_pos-arg_mem; + case 'b': + blob_length |= (*arg_pos++ << 24); + blob_length |= (*arg_pos++ << 16); + blob_length |= (*arg_pos++ << 8); + blob_length |= (*arg_pos++); + if(blob_length%4) + blob_length += 4-blob_length%4; + arg_pos += blob_length; + return arg_pos-arg_mem; + default: + assert("Invalid Type"); + } + return -1; +} + static unsigned arg_off(const char *msg, unsigned idx) { if(!has_reserved(rtosc_type(msg,idx))) @@ -97,45 +149,11 @@ static unsigned arg_off(const char *msg, unsigned idx) ++args; while(idx--) { - uint32_t bundle_length = 0; - switch(*args++) - { - case 'h': - case 't': - case 'd': - arg_pos +=8; - break; - case 'm': - case 'r': - case 'f': - case 'c': - case 'i': - arg_pos += 4; - break; - case 'S': - case 's': - while(*++arg_pos); - arg_pos += 4-(arg_pos-((uint8_t*)aligned_ptr))%4; - break; - case 'b': - bundle_length |= (*arg_pos++ << 24); - bundle_length |= (*arg_pos++ << 16); - bundle_length |= (*arg_pos++ << 8); - bundle_length |= (*arg_pos++); - if(bundle_length%4) - bundle_length += 4-bundle_length%4; - arg_pos += bundle_length; - break; - case '[': - case ']': - //completely ignore array chars - ++idx; - break; - case 'T': - case 'F': - case 'I': - ; - } + char type = *args++; + if(type == '[' || type == ']') + idx++;//not a valid arg idx + else + arg_pos += arg_size(arg_pos, type); } return arg_pos-(uint8_t*)msg; } @@ -385,10 +403,9 @@ size_t rtosc_amessage(char *buffer, return pos; } -rtosc_arg_t rtosc_argument(const char *msg, unsigned idx) +static rtosc_arg_t extract_arg(const uint8_t *arg_pos, char type) { rtosc_arg_t result = {0}; - char type = rtosc_type(msg, idx); //trivial case if(!has_reserved(type)) { switch(type) @@ -403,7 +420,6 @@ rtosc_arg_t rtosc_argument(const char *msg, unsigned idx) ; } } else { - const unsigned char *arg_pos = (const unsigned char*)msg+arg_off(msg,idx); switch(type) { case 'h': @@ -450,6 +466,52 @@ rtosc_arg_t rtosc_argument(const char *msg, unsigned idx) return result; } +static const char *advance_past_dummy_args(const char *args) +{ + while(*args == '[' || *args == ']') + args++; + return args; +} + +rtosc_arg_itr_t rtosc_itr_begin(const char *msg) +{ + rtosc_arg_itr_t itr; + itr.type_pos = advance_past_dummy_args(rtosc_argument_string(msg)); + itr.value_pos = (uint8_t*)(msg+arg_start(msg)); + + return itr; +} + +rtosc_arg_val_t rtosc_itr_next(rtosc_arg_itr_t *itr) +{ + //current position provides the value + rtosc_arg_val_t result = {0}; + result.type = *itr->type_pos; + if(result.type) + result.val = extract_arg(itr->value_pos, result.type); + + //advance + itr->type_pos = advance_past_dummy_args(itr->type_pos+1); + char type = result.type; + int size = arg_size(itr->value_pos, type); + itr->value_pos += size; + + + return result; +} + +int rtosc_itr_end(rtosc_arg_itr_t itr) +{ + return !itr.type_pos || !*itr.type_pos; +} + +rtosc_arg_t rtosc_argument(const char *msg, unsigned idx) +{ + char type = rtosc_type(msg, idx); + uint8_t *arg_mem = (uint8_t*)msg + arg_off(msg, idx); + return extract_arg(arg_mem, type); +} + static unsigned char deref(unsigned pos, ring_t *ring) { return pos>56) & 0xff); + buffer[1] = ((d>>48) & 0xff); + buffer[2] = ((d>>40) & 0xff); + buffer[3] = ((d>>32) & 0xff); + buffer[4] = ((d>>24) & 0xff); + buffer[5] = ((d>>16) & 0xff); + buffer[6] = ((d>>8) & 0xff); + buffer[7] = ((d>>0) & 0xff); +} + +static void emplace_uint32(uint8_t *buffer, uint32_t d) +{ + buffer[0] = ((d>>24) & 0xff); + buffer[1] = ((d>>16) & 0xff); + buffer[2] = ((d>>8) & 0xff); + buffer[3] = ((d>>0) & 0xff); +} size_t rtosc_bundle(char *buffer, size_t len, uint64_t tt, int elms, ...) { @@ -605,15 +710,15 @@ size_t rtosc_bundle(char *buffer, size_t len, uint64_t tt, int elms, ...) memset(buffer, 0, len); strcpy(buffer, "#bundle"); buffer += 8; - (*(uint64_t*)buffer) = tt; - buffer +=8; + emplace_uint64((uint8_t*)buffer, tt); + buffer += 8; va_list va; va_start(va, elms); for(int i=0; i len) break; @@ -644,7 +749,10 @@ const char *rtosc_bundle_fetch(const char *buffer, unsigned elm) { const uint32_t *lengths = (const uint32_t*) (buffer+16); size_t elm_pos = 0; - while(elm_pos!=elm && *lengths) ++elm_pos, lengths+=*lengths/4+1; + while(elm_pos!=elm && extract_uint32((const uint8_t*)lengths)) { + ++elm_pos; + lengths += extract_uint32((const uint8_t*)lengths)/4+1; + } return (const char*) (elm==elm_pos?lengths+1:NULL); } @@ -654,9 +762,9 @@ size_t rtosc_bundle_size(const char *buffer, unsigned elm) const uint32_t *lengths = (const uint32_t*) (buffer+16); size_t elm_pos = 0; size_t last_len = 0; - while(elm_pos!=elm && *lengths) { - last_len = *lengths; - ++elm_pos, lengths+=*lengths/4+1; + while(elm_pos!=elm && extract_uint32((const uint8_t*)lengths)) { + last_len = extract_uint32((const uint8_t*)lengths); + ++elm_pos, lengths+=extract_uint32((const uint8_t*)lengths)/4+1; } return last_len; @@ -669,5 +777,5 @@ int rtosc_bundle_p(const char *msg) uint64_t rtosc_bundle_timetag(const char *msg) { - return *(uint64_t*)(msg+8); + return extract_uint64((const uint8_t*)msg+8); } diff --git a/source/native-plugins/zynaddsubfx/rtosc/rtosc.h b/source/native-plugins/zynaddsubfx/rtosc/rtosc.h index 4ad705e78..3a8aa35ed 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/rtosc.h +++ b/source/native-plugins/zynaddsubfx/rtosc/rtosc.h @@ -115,6 +115,37 @@ unsigned rtosc_narguments(const char *msg); */ char rtosc_type(const char *msg, unsigned i); +typedef struct { + const char *type_pos; + const uint8_t *value_pos; +} rtosc_arg_itr_t; + +typedef struct { + char type; + rtosc_arg_t val; +} rtosc_arg_val_t; + +/** + * Create an argument iterator for a message + * @param msg OSC message + * @returns an initialized iterator + */ +rtosc_arg_itr_t rtosc_itr_begin(const char *msg); + +/** + * Gets the next argument in a message + * @param itr OSC message iterator + * @returns a type value pair from the message + */ +rtosc_arg_val_t rtosc_itr_next(rtosc_arg_itr_t *itr); + +/** + * Determines if the iterator is at the end of the argument list + * @param itr OSC message iterator + * @returns 1 if there are no more elements, 0 otherwise + */ +int rtosc_itr_end(rtosc_arg_itr_t itr); + /** * Blob data may be safely written to * @param msg OSC message diff --git a/source/native-plugins/zynaddsubfx/rtosc/subtree-serialize.h b/source/native-plugins/zynaddsubfx/rtosc/subtree-serialize.h index e87c7be06..66e1a11e7 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/subtree-serialize.h +++ b/source/native-plugins/zynaddsubfx/rtosc/subtree-serialize.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef RTOSC_SUBTREE_H +#define RTOSC_SUBTREE_H #include namespace rtosc{struct Ports; struct RtData;} @@ -8,3 +9,4 @@ size_t subtree_serialize(char *buffer, size_t buffer_size, void subtree_deserialize(char *buffer, size_t buffer_size, void *object, rtosc::Ports *ports, rtosc::RtData &d); +#endif diff --git a/source/native-plugins/zynaddsubfx/rtosc/thread-link.h b/source/native-plugins/zynaddsubfx/rtosc/thread-link.h index d8e21057a..662cc1e14 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/thread-link.h +++ b/source/native-plugins/zynaddsubfx/rtosc/thread-link.h @@ -28,7 +28,7 @@ #include #include #include -#include "rtosc.h" +#include namespace rtosc { typedef const char *msg_t; diff --git a/source/native-plugins/zynaddsubfx/rtosc/typed-message.h b/source/native-plugins/zynaddsubfx/rtosc/typed-message.h index bb31d0333..d1a5a6b35 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/typed-message.h +++ b/source/native-plugins/zynaddsubfx/rtosc/typed-message.h @@ -1,10 +1,14 @@ -#pragma once -#include "rtosc.h" +#ifndef RTOSC_TYPED_MESSAGE_H +#define RTOSC_TYPED_MESSAGE_H +#include +#include #include #include namespace rtosc { +struct match_exact{}; +struct match_partial{}; template class rtMsg; @@ -25,6 +29,17 @@ template<> class rtMsg<> const char *msg; }; +template +struct advance_size +{ + static std::true_type size; +}; + +template +struct advance_size> +{ + static std::false_type size; +}; template bool valid_char(char) { return false;} @@ -44,13 +59,28 @@ bool validate(const char *arg) return rtosc_narguments(arg) == i; } +template +bool match_path(std::false_type, const char *arg) +{ + return rtosc_match_path(T::data(), arg); +} + +template +bool match_path(std::true_type, const char *) +{ + return true; +} + template bool validate(const char *arg) { - if(!valid_char(rtosc_type(arg,i))) + auto size = advance_size::size; + if(size && !valid_char(rtosc_type(arg,i))) + return false; + else if(!size && !match_path(size, arg)) return false; else - return validate(arg); + return validate::size,Rest...>(arg); } //Tuple Like Template Class Definition @@ -58,6 +88,7 @@ template class rtMsg:public rtMsg { public: + typedef This This_; typedef rtMsg T; rtMsg(const char *arg = NULL, const char *spec=NULL) :T(arg, spec, false) @@ -69,6 +100,7 @@ class rtMsg:public rtMsg rtMsg(const char *arg, const char *spec, bool) :T(arg, spec, false) {} + }; @@ -114,4 +146,20 @@ get(rtMsg& Tuple) typedef typename std::remove_reference>::type>::type T; return rt_get_impl(Tuple.msg, Index); } + +template inline + typename osc_element<0, rtMsg>::type +first(rtMsg&Tuple) +{ + return get<0>(Tuple); +} + +template inline + typename osc_element<1, rtMsg>::type +second(rtMsg&Tuple) +{ + return get<1>(Tuple); +} + }; +#endif diff --git a/source/native-plugins/zynaddsubfx/rtosc/typestring.hh b/source/native-plugins/zynaddsubfx/rtosc/typestring.hh new file mode 100644 index 000000000..138d3bf6a --- /dev/null +++ b/source/native-plugins/zynaddsubfx/rtosc/typestring.hh @@ -0,0 +1,256 @@ +/*~ + * Copyright (C) 2015 George Makrydakis + * + * The 'typestring' header is a single header C++ library for creating types + * to use as type parameters in template instantiations, repository available + * at https://github.com/irrequietus/typestring. Conceptually stemming from + * own implementation of the same thing (but in a more complicated manner to + * be revised) in 'clause': https://github.com/irrequietus/clause. + * + * File subject to the terms and conditions of the Mozilla Public License v 2.0. + * If a copy of the MPLv2 license text was not distributed with this file, you + * can obtain it at: http://mozilla.org/MPL/2.0/. + */ + +#ifndef IRQUS_TYPESTRING_HH_ +#define IRQUS_TYPESTRING_HH_ + +namespace irqus { + +/*~ + * @desc A class 'storing' strings into distinct, reusable compile-time types that + * can be used as type parameters in a template parameter list. + * @tprm C... : char non-type parameter pack whose ordered sequence results + * into a specific string. + * @note Could have wrapped up everything in a single class, eventually will, + * once some compilers fix their class scope lookups! I have added some + * utility functions because asides being a fun little project, it is of + * use in certain constructs related to template metaprogramming + * nonetheless. + */ +template +struct typestring final { +private: + static constexpr char const vals[sizeof...(C)+1] = { C...,'\0' }; + static constexpr unsigned int sval = sizeof...(C); +public: + + static constexpr char const * data() noexcept + { return &vals[0]; } + + static constexpr unsigned int size() noexcept + { return sval; }; + + static constexpr char const * cbegin() noexcept + { return &vals[0]; } + + static constexpr char const * cend() noexcept + { return &vals[sval]; } +}; + +template +constexpr char const typestring::vals[sizeof...(C)+1]; + +//*~ part 1: preparing the ground, because function templates are awesome. + +/*~ + * @note While it is easy to resort to constexpr strings for use in constexpr + * metaprogramming, what we want is to convert compile time string in situ + * definitions into reusable, distinct types, for use in advanced template + * metaprogramming techniques. We want such features because this kind of + * metaprogramming constitutes a pure, non-strict, untyped functional + * programming language with pattern matching where declarative semantics + * can really shine. + * + * Currently, there is no feature in C++ that offers the opportunity to + * use strings as type parameter types themselves, despite there are + * several, different library implementations. This implementation is a + * fast, short, single-header, stupid-proof solution that works with any + * C++11 compliant compiler and up, with the resulting type being easily + * reusable throughout the code. + * + * @usge Just include the header and enable -std=c++11 or -std=c++14 etc, use + * like in the following example: + * + * typestring_is("Hello!") + * + * is essentially identical to the following template instantiation: + * + * irqus::typestring<'H', 'e', 'l', 'l', 'o', '!'> + * + * By passing -DUSE_TYPESTRING= during compilation, you can + * set the maximum length of the 'typestring' from 1 to 1024 (2^0 to 2^10). + * Although all preprocessor implementations tested are capable of far + * more with this method, exceeding this limit may cause internal compiler + * errors in most, with at times rather hilarious results. + */ + +template +constexpr char tygrab(char const(&c)[M]) noexcept +{ return c[N < M ? N : M-1]; } + +//*~ part2: Function template type signatures for type deduction purposes. In +// other words, exploiting the functorial nature of parameter packs +// while mixing them with an obvious catamorphism through pattern +// matching galore (partial ordering in this case in C++ "parlance"). + +template +auto typoke(typestring) // as is... + -> typestring; + +template +auto typoke(typestring, typestring<'\0'>, typestring...) + -> typestring; + +template +auto typoke(typestring, typestring, typestring...) + -> decltype(typoke(typestring(), typestring()...)); + +template +auto typeek(typestring) + -> decltype(typoke(typestring()...)); + +} /* irqus */ + + +//*~ part3: some necessary code generation using preprocessor metaprogramming! +// There is functional nature in preprocessor metaprogramming as well. + +/*~ + * @note Code generation block. Undoubtedly, the preprocessor implementations + * of both clang++ and g++ are relatively competent in producing a + * relatively adequate amount of boilerplate for implementing features + * that the language itself will probably be having as features in a few + * years. At times, like herein, the preprocessor is able to generate + * boilerplate *extremely* fast, but over a certain limit the compiler is + * incapable of compiling it. For the record, only certain versions of + * g++ where capable of going beyond 4K, so I thought of going from base + * 16 to base 2 for USE_TYPESTRING power base. For the record, it takes + * a few milliseconds to generate boilerplate for several thousands worth + * of "string" length through such an 'fmap' like procedure. + */ + +/* 2^0 = 1 */ +#define TYPESTRING1(n,x) irqus::tygrab<0x##n##0>(x) + +/* 2^1 = 2 */ +#define TYPESTRING2(n,x) irqus::tygrab<0x##n##0>(x), irqus::tygrab<0x##n##1>(x) + +/* 2^2 = 2 */ +#define TYPESTRING4(n,x) \ + irqus::tygrab<0x##n##0>(x), irqus::tygrab<0x##n##1>(x) \ + , irqus::tygrab<0x##n##2>(x), irqus::tygrab<0x##n##3>(x) + +/* 2^3 = 8 */ +#define TYPESTRING8(n,x) \ + irqus::tygrab<0x##n##0>(x), irqus::tygrab<0x##n##1>(x) \ + , irqus::tygrab<0x##n##2>(x), irqus::tygrab<0x##n##3>(x) \ + , irqus::tygrab<0x##n##4>(x), irqus::tygrab<0x##n##5>(x) \ + , irqus::tygrab<0x##n##6>(x), irqus::tygrab<0x##n##7>(x) + +/* 2^4 = 16 */ +#define TYPESTRING16(n,x) \ + irqus::tygrab<0x##n##0>(x), irqus::tygrab<0x##n##1>(x) \ + , irqus::tygrab<0x##n##2>(x), irqus::tygrab<0x##n##3>(x) \ + , irqus::tygrab<0x##n##4>(x), irqus::tygrab<0x##n##5>(x) \ + , irqus::tygrab<0x##n##6>(x), irqus::tygrab<0x##n##7>(x) \ + , irqus::tygrab<0x##n##8>(x), irqus::tygrab<0x##n##9>(x) \ + , irqus::tygrab<0x##n##A>(x), irqus::tygrab<0x##n##B>(x) \ + , irqus::tygrab<0x##n##C>(x), irqus::tygrab<0x##n##D>(x) \ + , irqus::tygrab<0x##n##E>(x), irqus::tygrab<0x##n##F>(x) + +/* 2^5 = 32 */ +#define TYPESTRING32(n,x) \ + TYPESTRING16(n##0,x),TYPESTRING16(n##1,x) + +/* 2^6 = 64 */ +#define TYPESTRING64(n,x) \ + TYPESTRING16(n##0,x), TYPESTRING16(n##1,x), TYPESTRING16(n##2,x) \ + , TYPESTRING16(n##3,x) + +/* 2^7 = 128 */ +#define TYPESTRING128(n,x) \ + TYPESTRING16(n##0,x), TYPESTRING16(n##1,x), TYPESTRING16(n##2,x) \ + , TYPESTRING16(n##3,x), TYPESTRING16(n##4,x), TYPESTRING16(n##5,x) \ + , TYPESTRING16(n##6,x), TYPESTRING16(n##7,x) + +/* 2^8 = 256 */ +#define TYPESTRING256(n,x) \ + TYPESTRING16(n##0,x), TYPESTRING16(n##1,x), TYPESTRING16(n##2,x) \ + , TYPESTRING16(n##3,x), TYPESTRING16(n##4,x), TYPESTRING16(n##5,x) \ + , TYPESTRING16(n##6,x), TYPESTRING16(n##7,x), TYPESTRING16(n##8,x) \ + , TYPESTRING16(n##9,x), TYPESTRING16(n##A,x), TYPESTRING16(n##B,x) \ + , TYPESTRING16(n##C,x), TYPESTRING16(n##D,x), TYPESTRING16(n##E,x) \ + , TYPESTRING16(n##F,x) + +/* 2^9 = 512 */ +#define TYPESTRING512(n,x) \ + TYPESTRING256(n##0,x), TYPESTRING256(n##1,x) + +/* 2^10 = 1024 */ +#define TYPESTRING1024(n,x) \ + TYPESTRING256(n##0,x), TYPESTRING256(n##1,x), TYPESTRING256(n##2,x) \ + , TYPESTRING128(n##3,x), TYPESTRING16(n##38,x), TYPESTRING16(n##39,x) \ + , TYPESTRING16(n##3A,x), TYPESTRING16(n##3B,x), TYPESTRING16(n##3C,x) \ + , TYPESTRING16(n##3D,x), TYPESTRING16(n##3E,x), TYPESTRING16(n##3F,x) + +//*~ part4 : Let's give some logic with a -DUSE_TYPESTRING flag! + +#ifdef USE_TYPESTRING +#if USE_TYPESTRING == 0 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 1 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 2 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 3 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 4 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 5 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 6 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 7 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 8 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 9 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING == 10 +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#elif USE_TYPESTRING > 10 + +#warning !!!: custom typestring length exceeded allowed (1024) !!! +#warning !!!: all typestrings to default maximum typestring length of 64 !!! +#warning !!!: you can use -DUSE_TYPESTRING= to set length !!! + +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) + +#elif USE_TYPESTRING < 0 + +#warning !!!: You used USE_TYPESTRING with a negative size specified !!! +#warning !!!: all typestrings to default maximum typestring length of 64 !!! +#warning !!!: you can use -DUSE_TYPESTRING= to set length !!! + +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) + +#endif +#else +#define typestring_is(x) \ + decltype(irqus::typeek(irqus::typestring())) +#endif +#endif /* IRQUS_TYPESTRING_HH_ */ diff --git a/source/native-plugins/zynaddsubfx/rtosc/undo-history.h b/source/native-plugins/zynaddsubfx/rtosc/undo-history.h index 111560556..072b15a88 100644 --- a/source/native-plugins/zynaddsubfx/rtosc/undo-history.h +++ b/source/native-plugins/zynaddsubfx/rtosc/undo-history.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef RTOSC_UNDO_H +#define RTOSC_UNDO_H #include namespace rtosc @@ -32,3 +33,4 @@ class UndoHistory class UndoHistoryImpl *impl; }; }; +#endif