| @@ -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" | |||
| @@ -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" | |||
| @@ -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 <cstring> | |||
| #include <cassert> | |||
| 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<off_d1; ++i) | |||
| off_d2 += ndesc[i].size; | |||
| return NotePool::activeNotesIter{sdesc+off_d2,sdesc+off_d2+n.size}; | |||
| } | |||
| bool NotePool::NoteDescriptor::operator==(NoteDescriptor nd) | |||
| { | |||
| return age == nd.age && note == nd.note && sendto == nd.sendto && size == nd.size && status == nd.status; | |||
| } | |||
| //return either the first unused descriptor or the last valid descriptor which | |||
| //matches note/sendto | |||
| static int getMergeableDescriptor(uint8_t note, uint8_t sendto, bool legato, | |||
| NotePool::NoteDescriptor *ndesc) | |||
| { | |||
| int desc_id = 0; | |||
| for(int i=0; i<POLYPHONY; ++i, ++desc_id) | |||
| if(ndesc[desc_id].status == Part::KEY_OFF) | |||
| break; | |||
| //Out of free descriptors | |||
| if(ndesc[desc_id].status != Part::KEY_OFF) | |||
| return -1; | |||
| if(desc_id != 0) { | |||
| auto &nd = ndesc[desc_id-1]; | |||
| if(nd.age == 0 && nd.note == note && nd.sendto == sendto | |||
| && nd.status == Part::KEY_PLAYING && nd.legatoMirror == legato) | |||
| return desc_id-1; | |||
| } | |||
| return desc_id; | |||
| } | |||
| NotePool::activeDescIter NotePool::activeDesc(void) | |||
| { | |||
| cleanup(); | |||
| return activeDescIter{*this}; | |||
| } | |||
| NotePool::constActiveDescIter NotePool::activeDesc(void) const | |||
| { | |||
| const_cast<NotePool*>(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; i<POLYPHONY; ++i) | |||
| if(ndesc[i].status != Part::KEY_OFF) | |||
| last_valid_desc = i; | |||
| //Find the real numbers of allocated notes | |||
| { | |||
| int cum_old = 0; | |||
| for(int i=0; i<=last_valid_desc; ++i) { | |||
| cur_length[i] = ndesc[i].size; | |||
| for(int j=0; j<ndesc[i].size; ++j) | |||
| new_length[i] += (bool)sdesc[cum_old++].note; | |||
| } | |||
| } | |||
| //Move the note descriptors | |||
| { | |||
| int cum_new = 0; | |||
| for(int i=0; i<=last_valid_desc; ++i) { | |||
| ndesc[i].size = new_length[i]; | |||
| if(new_length[i] != 0) | |||
| ndesc[cum_new++] = ndesc[i]; | |||
| else | |||
| ndesc[i].status = Part::KEY_OFF; | |||
| } | |||
| memset(ndesc+cum_new, 0, sizeof(*ndesc)*(POLYPHONY-cum_new)); | |||
| } | |||
| //Move the synth descriptors | |||
| { | |||
| int total_notes=0; | |||
| for(int i=0; i<=last_valid_desc; ++i) | |||
| total_notes+=cur_length[i]; | |||
| int cum_new = 0; | |||
| for(int i=0; i<total_notes; ++i) | |||
| if(sdesc[i].note) | |||
| sdesc[cum_new++] = sdesc[i]; | |||
| memset(sdesc+cum_new, 0, sizeof(*sdesc)*(POLYPHONY*EXPECTED_USAGE-cum_new)); | |||
| } | |||
| //printf("Cleanup Done\n"); | |||
| //dump(); | |||
| } | |||
| void NotePool::dump(void) | |||
| { | |||
| printf("NotePool::dump<\n"); | |||
| const char *format = | |||
| " Note %d:%d age(%d) note(%d) sendto(%d) status(%s) legato(%d) type(%d) kit(%d) ptr(%p)\n"; | |||
| int note_id=0; | |||
| int descriptor_id=0; | |||
| for(auto &d:activeDesc()) { | |||
| descriptor_id += 1; | |||
| for(auto &s:activeNotes(d)) { | |||
| note_id += 1; | |||
| printf(format, | |||
| note_id, descriptor_id, | |||
| d.age, d.note, d.sendto, | |||
| getStatus(d.status), d.legatoMirror, s.type, s.kit, s.note); | |||
| } | |||
| } | |||
| printf(">NotePool::dump\n"); | |||
| } | |||
| @@ -0,0 +1,113 @@ | |||
| #pragma once | |||
| #include <stdint.h> | |||
| #include <functional> | |||
| #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; i<POLYPHONY; ++i, ++off) | |||
| if(np.ndesc[i].status == 0) | |||
| break; | |||
| _end = np.ndesc+off; | |||
| } | |||
| NoteDescriptor *begin() {return np.ndesc;}; | |||
| NoteDescriptor *end() { return _end; }; | |||
| NoteDescriptor *_end; | |||
| NotePool &np; | |||
| }; | |||
| struct constActiveDescIter { | |||
| constActiveDescIter(const NotePool &_np):np(_np) | |||
| { | |||
| int off=0; | |||
| for(int i=0; i<POLYPHONY; ++i, ++off) | |||
| if(np.ndesc[i].status == 0) | |||
| break; | |||
| _end = np.ndesc+off; | |||
| } | |||
| const NoteDescriptor *begin() const {return np.ndesc;}; | |||
| const NoteDescriptor *end() const { return _end; }; | |||
| const NoteDescriptor *_end; | |||
| const NotePool &np; | |||
| }; | |||
| activeNotesIter activeNotes(NoteDescriptor &n); | |||
| activeDescIter activeDesc(void); | |||
| constActiveDescIter activeDesc(void) const; | |||
| NotePool(void); | |||
| //Operations | |||
| void insertNote(uint8_t note, uint8_t sendto, SynthDescriptor desc, bool legato=false); | |||
| void insertLegatoNote(uint8_t note, uint8_t sendto, SynthDescriptor desc); | |||
| void upgradeToLegato(void); | |||
| void applyLegato(LegatoParams &par); | |||
| //Note that isn't KEY_PLAYING or KEY_RELASED_AND_SUSTAINING | |||
| bool existsRunningNote(void) const; | |||
| int getRunningNotes(void) const; | |||
| int enforceKeyLimit(int limit) const; | |||
| void releasePlayingNotes(void); | |||
| void releaseNote(note_t note); | |||
| void release(NoteDescriptor &d); | |||
| void killAllNotes(void); | |||
| void killNote(note_t note); | |||
| void kill(NoteDescriptor &d); | |||
| void kill(SynthDescriptor &s); | |||
| void cleanup(void); | |||
| void dump(void); | |||
| }; | |||
| @@ -71,7 +71,7 @@ FormantFilter::FormantFilter(FilterParams *pars, Allocator *alloc, unsigned int | |||
| outgain = dB2rap(pars->getgain()); | |||
| oldinput = -1.0f; | |||
| Qfactor = 1.0f; | |||
| Qfactor = pars->getq(); | |||
| oldQfactor = Qfactor; | |||
| firsttime = 1; | |||
| } | |||
| @@ -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) | |||
| @@ -80,7 +80,6 @@ class EffectMgr:public Presets | |||
| private: | |||
| //Parameters Prior to initialization | |||
| char effect_id; | |||
| char preset; | |||
| char settings[128]; | |||
| @@ -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 | |||
| @@ -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; | |||
| @@ -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<char>(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(); | |||
| @@ -28,6 +28,7 @@ | |||
| #include <rtosc/miditable.h> | |||
| #include <rtosc/ports.h> | |||
| #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]; | |||
| @@ -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()); | |||
| @@ -236,6 +236,11 @@ void rescanForBanks(Bank &bank, std::function<void(const char*)> cb) | |||
| void loadBank(Bank &bank, int pos, std::function<void(const char*)> 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<void()> 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 | |||
| @@ -4,15 +4,14 @@ | |||
| #include <string> | |||
| 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 | |||
| @@ -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<NUM_KIT_ITEMS; ++i) { | |||
| //an enabled kit must have a corresponding parameter object | |||
| assert(!kits[i].Padenabled || kits[i].adpars); | |||
| assert(!kits[i].Ppadenabled || kits[i].padpars); | |||
| assert(!kits[i].Psubenabled || kits[i].subpars); | |||
| } | |||
| } | |||
| /* | |||
| * Note On Messages | |||
| */ | |||
| @@ -378,299 +387,92 @@ void Part::NoteOn(unsigned char note, | |||
| unsigned char velocity, | |||
| int masterkeyshift) | |||
| { | |||
| // Legato and MonoMem used vars: | |||
| int posb = POLYPHONY - 1; // Just a dummy initial value. | |||
| bool legatomodevalid = false; //true when legato mode is determined applicable. | |||
| bool doinglegato = false; // true when we determined we do a legato note. | |||
| bool ismonofirstnote = false; /*(In Mono/Legato) true when we determined | |||
| no other notes are held down or sustained.*/ | |||
| int lastnotecopy = lastnote; //Useful after lastnote has been changed. | |||
| //Verify Basic Mode and sanity | |||
| const bool isRunningNote = notePool.existsRunningNote(); | |||
| const bool doingLegato = isRunningNote && isLegatoMode() && | |||
| lastlegatomodevalid; | |||
| if(!Pnoteon || !inRange(note, Pminkey, Pmaxkey)) | |||
| return; | |||
| // MonoMem stuff: | |||
| if(!Ppolymode) { // If Poly is off | |||
| monomemPush(note); // Add note to the list. | |||
| monomem[note].velocity = velocity; // Store this note's velocity. | |||
| monomem[note].mkeyshift = masterkeyshift; /* Store masterkeyshift too*/ | |||
| if((partnote[lastpos].status != KEY_PLAYING) | |||
| && (partnote[lastpos].status != KEY_RELEASED_AND_SUSTAINED)) | |||
| ismonofirstnote = true; // No other keys are held or sustained. | |||
| } else if(!monomemEmpty()) | |||
| monomemClear(); | |||
| lastnote = note; | |||
| int pos = -1; | |||
| for(int i = 0; i < POLYPHONY; ++i) | |||
| if(partnote[i].status == KEY_OFF) { | |||
| pos = i; | |||
| break; | |||
| } | |||
| if(Plegatomode && !Pdrummode) { | |||
| if(Ppolymode != 0) { | |||
| fprintf( | |||
| stderr, | |||
| "ZynAddSubFX WARNING: Poly and Legato modes are both On, that should not happen ! ... Disabling Legato mode ! - (Part.cpp::NoteOn(..))\n"); | |||
| Plegatomode = 0; | |||
| } | |||
| else { | |||
| // Legato mode is on and applicable. | |||
| legatomodevalid = true; | |||
| if((not ismonofirstnote) && (lastlegatomodevalid)) { | |||
| // At least one other key is held or sustained, and the | |||
| // previous note was played while in valid legato mode. | |||
| doinglegato = true; // So we'll do a legato note. | |||
| pos = lastpos; // A legato note uses same pos as previous.. | |||
| posb = lastposb; // .. same goes for posb. | |||
| } | |||
| else { | |||
| // Legato mode is valid, but this is only a first note. | |||
| for(int i = 0; i < POLYPHONY; ++i) | |||
| if((partnote[i].status == KEY_PLAYING) | |||
| || (partnote[i].status == KEY_RELEASED_AND_SUSTAINED)) | |||
| ReleaseNotePos(i); | |||
| // Set posb | |||
| posb = (pos + 1) % POLYPHONY; //We really want it (if the following fails) | |||
| for(int i = 0; i < POLYPHONY; ++i) | |||
| if((partnote[i].status == KEY_OFF) && (pos != i)) { | |||
| posb = i; | |||
| break; | |||
| } | |||
| } | |||
| lastposb = posb; // Keep a trace of used posb | |||
| } | |||
| } | |||
| else // Legato mode is either off or non-applicable. | |||
| if(!Ppolymode) { //if the mode is 'mono' turn off all other notes | |||
| for(int i = 0; i < POLYPHONY; ++i) | |||
| if(partnote[i].status == KEY_PLAYING) | |||
| ReleaseNotePos(i); | |||
| ReleaseSustainedKeys(); | |||
| } | |||
| lastlegatomodevalid = legatomodevalid; | |||
| if(pos == -1) | |||
| fprintf(stderr, | |||
| "%s", | |||
| "NOTES TOO MANY (> 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<ADnote>(kit[0].adpars, pars); | |||
| if(kit[0].Psubenabled) | |||
| partnote[pos].kititem[0].subnote = | |||
| memory.alloc<SUBnote>(kit[0].subpars, pars); | |||
| if(kit[0].Ppadenabled) | |||
| partnote[pos].kititem[0].padnote = | |||
| memory.alloc<PADnote>(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<ADnote>(kit[0].adpars, pars); | |||
| if(kit[0].Psubenabled) | |||
| partnote[posb].kititem[0].subnote = | |||
| memory.alloc<SUBnote>(kit[0].subpars, pars); | |||
| if(kit[0].Ppadenabled) | |||
| partnote[posb].kititem[0].padnote = | |||
| memory.alloc<PADnote>(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<ADnote>(kit[item].adpars, pars); | |||
| if(kit[item].subpars && kit[item].Psubenabled) | |||
| note1.subnote = | |||
| memory.alloc<SUBnote>(kit[item].subpars, pars); | |||
| if(kit[item].padpars && kit[item].Ppadenabled) | |||
| note1.padnote = | |||
| memory.alloc<PADnote>(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<ADnote>(kit[item].adpars, pars); | |||
| if(kit[item].subpars && kit[item].Psubenabled) | |||
| note2.subnote = | |||
| memory.alloc<SUBnote>(kit[item].subpars, pars); | |||
| if(kit[item].padpars && kit[item].Ppadenabled) | |||
| note2.padnote = | |||
| memory.alloc<PADnote>(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<ADnote>(kit[i].adpars, pars), 0, i}); | |||
| if(item.Psubenabled) | |||
| notePool.insertNote(note, sendto, | |||
| {memory.alloc<SUBnote>(kit[i].subpars, pars), 1, i}); | |||
| if(item.Ppadenabled) | |||
| notePool.insertNote(note, sendto, | |||
| {memory.alloc<PADnote>(kit[i].padpars, pars, interpolation), 2, i}); | |||
| //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; i<NUM_PART_EFX; ++i) | |||
| partefx[i]->kill(); | |||
| 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); | |||
| } | |||
| @@ -27,6 +27,7 @@ | |||
| #include "../globals.h" | |||
| #include "../Params/Controller.h" | |||
| #include "../Containers/NotePool.h" | |||
| #include <functional> | |||
| @@ -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; | |||
| }; | |||
| @@ -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 <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}, | |||
| {"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<string> translate_preset_types(std::string metatype) | |||
| { | |||
| std::vector<string> results; | |||
| return results; | |||
| } | |||
| /***************************************************************************** | |||
| * Implementation Methods * | |||
| *****************************************************************************/ | |||
| @@ -0,0 +1,42 @@ | |||
| #pragma once | |||
| #include <stdint.h> | |||
| #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; | |||
| }; | |||
| @@ -157,12 +157,12 @@ char *rtosc_splat(const char *path, std::set<std::string>); | |||
| {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);}},\ | |||
| @@ -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, | |||
| @@ -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, | |||
| @@ -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, | |||
| @@ -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, | |||
| @@ -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; | |||
| @@ -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); | |||
| @@ -22,6 +22,7 @@ | |||
| #include <cmath> | |||
| #include <cstdlib> | |||
| #include <cassert> | |||
| #include <rtosc/ports.h> | |||
| #include <rtosc/port-sugar.h> | |||
| @@ -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 | |||
| @@ -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()); | |||
| }}, | |||
| @@ -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(); | |||
| } | |||
| @@ -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; /**<how the LFO is "stretched" according the note frequency (64=no stretch)*/ | |||
| int fel; //what kind is the LFO (0 - frequency, 1 - amplitude, 2 - filter) | |||
| static int time; //is used by Pcontinous parameter | |||
| static const rtosc::Ports &ports; | |||
| private: | |||
| @@ -90,8 +90,6 @@ static const rtosc::Ports PADnotePorts = | |||
| PC(fixedfreq), | |||
| PC(fixedfreqET), | |||
| //TODO detune, coarse detune | |||
| PC(DetuneType), | |||
| PC(Stereo), | |||
| PC(Panning), | |||
| PC(AmpVelocityScaleFunction), | |||
| @@ -115,7 +113,7 @@ static const rtosc::Ports PADnotePorts = | |||
| d.reply(d.loc, "i", p->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)) { | |||
| @@ -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)) { | |||
| @@ -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<ADnote>(&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<LFO>(*param.AmpLfo, basefreq, synth.dt()); | |||
| vce.AmpLfo = memory.alloc<LFO>(*param.AmpLfo, basefreq, time); | |||
| newamplitude[nvoice] *= vce.AmpLfo->amplfoout(); | |||
| } | |||
| @@ -762,7 +776,7 @@ void ADnote::initparameters() | |||
| vce.FreqEnvelope = memory.alloc<Envelope>(*param.FreqEnvelope, basefreq, synth.dt()); | |||
| if(param.PFreqLfoEnabled) | |||
| vce.FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, synth.dt()); | |||
| vce.FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, time); | |||
| /* Voice Filter Parameters Init */ | |||
| if(param.PFilterEnabled != 0) { | |||
| @@ -776,7 +790,7 @@ void ADnote::initparameters() | |||
| vce.FilterEnvelope = memory.alloc<Envelope>(*param.FilterEnvelope, basefreq, synth.dt()); | |||
| if(param.PFilterLfoEnabled) | |||
| vce.FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, synth.dt()); | |||
| vce.FilterLfo = memory.alloc<LFO>(*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<Envelope>(*param.FreqEnvelope, basefreq, synth.dt()); | |||
| FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, synth.dt()); | |||
| FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, time); | |||
| AmpEnvelope = memory.alloc<Envelope>(*param.AmpEnvelope, basefreq, synth.dt()); | |||
| AmpLfo = memory.alloc<LFO>(*param.AmpLfo, basefreq, synth.dt()); | |||
| AmpLfo = memory.alloc<LFO>(*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<Envelope>(*param.FilterEnvelope, basefreq, synth.dt()); | |||
| FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, synth.dt()); | |||
| FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time); | |||
| FilterQ = param.GlobalFilter->getq(); | |||
| FilterFreqTracking = param.GlobalFilter->getfreqtracking(basefreq); | |||
| } | |||
| @@ -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]; | |||
| @@ -28,8 +28,10 @@ | |||
| #include <cstdio> | |||
| #include <cmath> | |||
| 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); | |||
| @@ -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 | |||
| @@ -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) { | |||
| @@ -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 | |||
| @@ -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<Envelope>(*pars.FreqEnvelope, basefreq, synth.dt()); | |||
| NoteGlobalPar.FreqLfo = memory.alloc<LFO>(*pars.FreqLfo, basefreq, synth.dt()); | |||
| NoteGlobalPar.FreqLfo = memory.alloc<LFO>(*pars.FreqLfo, basefreq, time); | |||
| NoteGlobalPar.AmpEnvelope = memory.alloc<Envelope>(*pars.AmpEnvelope, basefreq, synth.dt()); | |||
| NoteGlobalPar.AmpLfo = memory.alloc<LFO>(*pars.AmpLfo, basefreq, synth.dt()); | |||
| NoteGlobalPar.AmpLfo = memory.alloc<LFO>(*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<Envelope>(*pars.FilterEnvelope, basefreq, synth.dt()); | |||
| NoteGlobalPar.FilterLfo = memory.alloc<LFO>(*pars.FilterLfo, basefreq, synth.dt()); | |||
| NoteGlobalPar.FilterLfo = memory.alloc<LFO>(*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<PADnote>(&pars, sp, interpolation); | |||
| } | |||
| void PADnote::legatonote(LegatoParams pars) | |||
| { | |||
| // Manage legato stuff | |||
| @@ -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); | |||
| @@ -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, | |||
| @@ -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<SUBnote>(&pars, sp); | |||
| } | |||
| void SUBnote::legatonote(LegatoParams pars) | |||
| { | |||
| // Manage legato stuff | |||
| @@ -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 | |||
| @@ -3,9 +3,9 @@ | |||
| #include <cstring> | |||
| 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, | |||
| @@ -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 | |||
| @@ -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;i<nvoice;i++) {sprintf(tmp,"ExtM.%2d",i+1);o->add(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;i<nvoice;i++) {sprintf(tmp,"Ext.%2d",i+1);o->add(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;i<nvoice;i++) { | |||
| sprintf(tmp,"Ext.%2d",i+1); | |||
| extoscil->add(tmp); | |||
| }; | |||
| extFMoscil->clear(); | |||
| extFMoscil->add("Internal"); | |||
| for (int i=0;i<nvoice;i++) { | |||
| sprintf(tmp,"ExtM.%2d",i+1); | |||
| extFMoscil->add(tmp); | |||
| } | |||
| extMod->clear(); | |||
| extMod->add("OFF"); | |||
| for (int i=0;i<nvoice;i++) { | |||
| sprintf(tmp,"ExtMod.%2d",i+1); | |||
| extMod->add(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;i<NUM_VOICES;i++){voicelistitem[i]=new ADvoicelistitem(0,0,620,25,"");voicelistitem[i]->init(i,loc+"VoicePar"+to_s(i)+"/",osc);}o->end();} | |||
| xywh {0 20 670 210} | |||
| code0 {o->begin();for (int i=0;i<NUM_VOICES;i++){voicelistitem[i]=new ADvoicelistitem(0,0,670,25,"");voicelistitem[i]->init(i,loc+"VoicePar"+to_s(i)+"/",osc);}o->end();} | |||
| } {} | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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"); | |||
| } {} | |||
| } | |||
| @@ -345,14 +345,18 @@ class UI_Interface:public Fl_Osc_Interface | |||
| { | |||
| #ifndef NO_UI | |||
| printf("\n\nDamage(\"%s\")\n", path); | |||
| std::set<Fl_Osc_Widget*> to_update; | |||
| for(auto pair:map) { | |||
| if(strstr(pair.first.c_str(), path)) { | |||
| auto *tmp = dynamic_cast<Fl_Widget*>(pair.second); | |||
| if(!tmp || tmp->visible_r()) { | |||
| pair.second->update(); | |||
| to_update.insert(pair.second); | |||
| } | |||
| } | |||
| } | |||
| for(auto elm:to_update) | |||
| elm->update(); | |||
| #endif | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| #include "Connection.h" | |||
| #include <unistd.h> | |||
| namespace GUI { | |||
| ui_handle_t createUi(Fl_Osc_Interface*, void *exit) | |||
| ui_handle_t createUi(Fl_Osc_Interface*, void *) | |||
| { | |||
| return 0; | |||
| } | |||
| @@ -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 | |||
| } { | |||
| @@ -21,6 +21,12 @@ void EnvelopeFreeEdit::init(void) | |||
| oscRegister("Penvdt"); | |||
| oscRegister("Penvval"); | |||
| oscRegister("Penvsustain"); | |||
| //register for non-bulk types | |||
| for(int i=0; i<MAX_ENVELOPE_POINTS; ++i) { | |||
| osc->createLink(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<npoints; ++i){ | |||
| oldxx=xx;oldyy=yy; | |||
| xx=getpointx(i);yy=getpointy(i); | |||
| if (i==currentpoint) fl_color(FL_RED); | |||
| else fl_color(alb); | |||
| if (i==currentpoint || (ctrldown && i==lastpoint)) | |||
| fl_color(FL_RED); | |||
| else | |||
| fl_color(alb); | |||
| fl_line(ox+oldxx,oy+oldyy,ox+xx,oy+yy); | |||
| fl_rectf(ox+xx-3,oy+yy-3,6,6); | |||
| } | |||
| @@ -151,13 +171,13 @@ void EnvelopeFreeEdit::draw(void) | |||
| //Show the envelope duration and the current line duration | |||
| fl_font(FL_HELVETICA|FL_BOLD,10); | |||
| float time=0.0; | |||
| if (currentpoint<=0){ | |||
| if (currentpoint<=0 && (!ctrldown||lastpoint <= 0)){ | |||
| fl_color(alb); | |||
| for(int i=1; i<npoints; ++i) | |||
| time+=getdt(i); | |||
| } else { | |||
| fl_color(255,0,0); | |||
| time=getdt(currentpoint); | |||
| fl_color(FL_RED); | |||
| time=getdt(lastpoint); | |||
| } | |||
| char tmpstr[20]; | |||
| if (time<1000.0) | |||
| @@ -165,55 +185,99 @@ void EnvelopeFreeEdit::draw(void) | |||
| else | |||
| snprintf((char *)&tmpstr,20,"%.2fs",time/1000.0); | |||
| fl_draw(tmpstr,ox+lx-20,oy+ly-10,20,10,FL_ALIGN_RIGHT,NULL,0); | |||
| if (lastpoint>=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; i<MAX_ENVELOPE_POINTS; ++i) { | |||
| string dt = string("Penvdt") + to_s(i); | |||
| string val = string("Penvval") + to_s(i); | |||
| osc->renameLink(loc+dt, new_base+dt, this); | |||
| osc->renameLink(loc+val, new_base+val, this); | |||
| } | |||
| loc = new_base; | |||
| update(); | |||
| } | |||
| @@ -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 <stdio.h>} {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()); | |||
| @@ -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 <cmath>} {private local | |||
| } | |||
| @@ -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) | |||
| @@ -1,4 +1,5 @@ | |||
| #include "Fl_Osc_ListView.H" | |||
| #include "Fl_Osc_Pane.H" | |||
| #include <cstdio> | |||
| #include <rtosc/rtosc.h> | |||
| @@ -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) | |||
| @@ -1,3 +1,4 @@ | |||
| #include <stdlib.h> | |||
| #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) | |||
| @@ -3,6 +3,7 @@ | |||
| #include <FL/Fl_Group.H> | |||
| #include <FL/Fl_Double_Window.H> | |||
| #include <string> | |||
| #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; | |||
| @@ -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; | |||
| @@ -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<Fl_Callback*, void*> cb_data; | |||
| @@ -7,6 +7,7 @@ | |||
| #include <cmath> | |||
| #include <cassert> | |||
| #include <sstream> | |||
| #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) | |||
| @@ -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 | |||
| @@ -0,0 +1,65 @@ | |||
| #include <cmath> | |||
| #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; | |||
| } | |||
| @@ -1,29 +1,35 @@ | |||
| #pragma once | |||
| #include <FL/Fl_Value_Slider.H> | |||
| #include "Fl_Osc_Widget.H" | |||
| #include "Fl_Osc_Slider.H" | |||
| #include <string> | |||
| 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<Fl_Callback*, void*> cb_data; | |||
| @@ -1,4 +1,5 @@ | |||
| #include <FL/Fl.H> | |||
| #include <FL/fl_draw.H> | |||
| #include "Fl_Osc_VSlider.H" | |||
| #include "Fl_Osc_Interface.h" | |||
| #include "Fl_Osc_Pane.H" | |||
| @@ -9,82 +10,60 @@ | |||
| #include <sstream> | |||
| 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<Fl_Osc_VSlider*>(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())); | |||
| } | |||
| @@ -4,7 +4,6 @@ | |||
| #include <cassert> | |||
| #include <cmath> | |||
| #include "Fl_Osc_Interface.h" | |||
| #include "Fl_Osc_Pane.H" | |||
| #include <FL/Fl_Group.H> | |||
| 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); | |||
| }; | |||
| @@ -1,4 +1,5 @@ | |||
| #include "Fl_Osc_Widget.H" | |||
| #include "Fl_Osc_Pane.H" | |||
| #include <rtosc/rtosc.h> | |||
| Fl_Osc_Widget::Fl_Osc_Widget(void) //Deprecated | |||
| @@ -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 | |||
| } | |||
| @@ -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;i<NUM_SYS_EFX;i++) | |||
| for (int j=0;j<NUM_SYS_EFX;j++) | |||
| @@ -1556,6 +1592,7 @@ simplelistitemgroup->redraw();} {} | |||
| 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;} {} | |||
| @@ -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 | |||
| @@ -11,6 +11,7 @@ | |||
| #include <FL/Fl_Group.H> | |||
| #include <FL/Fl_Text_Display.H> | |||
| #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<Fl_Callback*, void*> 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<Fl_Choice *>(c)->text()); | |||
| //static_cast<Fl_Choice *>(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<Fl_Choice *>(c)->text()); | |||
| //static_cast<Fl_Choice *>(c)->textcolor(fl_rgb_color(255 * !good, 0, 0)); | |||
| @@ -17,7 +17,7 @@ class Osc_DataModel:public Fl_Osc_Widget | |||
| value_t value; | |||
| std::function<void(value_t)> 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) | |||
| { | |||
| @@ -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) | |||
| { | |||
| @@ -18,7 +18,7 @@ class Osc_ListModel:public Fl_Osc_Widget | |||
| std::function<void(list_t)> 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) | |||
| { | |||
| @@ -18,7 +18,7 @@ class Osc_SimpleListModel:public Fl_Osc_Widget | |||
| std::function<void(list_t)> 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) | |||
| { | |||
| @@ -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;i<MAX_AD_HARMONICS;i++){ | |||
| for (int i=0;i<(MAX_AD_HARMONICS - 1);i++){ | |||
| h[i]->mag->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;i<MAX_AD_HARMONICS;i++){ | |||
| for (int i=0;i<(MAX_AD_HARMONICS - 1);i++){ | |||
| h[i]->mag->value(64); | |||
| h[i]->mag->do_callback(); | |||
| h[i]->phase->value(64); | |||
| @@ -537,7 +537,7 @@ for (int i=0;i<MAX_AD_HARMONICS;i++){ | |||
| h[0]->mag->value(0); | |||
| h[0]->mag->do_callback(); | |||
| //for (int i=0;i<MAX_AD_HARMONICS;i++){ | |||
| //for (int i=0;i<(MAX_AD_HARMONICS - 1);i++){ | |||
| // if (oscil->Phmag[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;i<MAX_AD_HARMONICS;i++){h[i]=new Oscilharmonic(0,0,20,o->h(),"");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; i<MAX_AD_HARMONICS; ++i) | |||
| for (int i=0; i<(MAX_AD_HARMONICS - 1); ++i) | |||
| delete h[i]; | |||
| delete oscilo; | |||
| @@ -970,7 +970,7 @@ delete osceditUI;} {} | |||
| Function {refresh()} {} { | |||
| code {magtype->update(); | |||
| for (int i=0;i<MAX_AD_HARMONICS;i++) h[i]->refresh(); | |||
| 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 | |||
| } | |||
| @@ -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(); | |||
| @@ -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 | |||
| } | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -0,0 +1,75 @@ | |||
| #include <cstdio> | |||
| #include <cmath> | |||
| #include <FL/Fl_Tooltip.H> | |||
| #include <FL/fl_draw.H> | |||
| #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(); | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| #ifndef TIPWIN_H | |||
| #include <string> | |||
| #include <FL/Fl_Menu_Window.H> | |||
| #include <FL/Fl_Tooltip.H> | |||
| #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 | |||
| @@ -1,104 +1,19 @@ | |||
| // generated by Fast Light User Interface Designer (fluid) version 1.0107f | |||
| #include "WidgetPDial.h" | |||
| #include <cstdio> | |||
| #include <iostream> | |||
| #include <cmath> | |||
| #include <string> | |||
| #include <FL/Fl_Tooltip.H> | |||
| #include <FL/fl_draw.H> | |||
| #include <FL/Fl_Group.H> | |||
| #include <FL/Fl_Menu_Window.H> | |||
| #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; | |||
| } | |||
| @@ -3,6 +3,7 @@ | |||
| #ifndef WIDGETPDIAL_h | |||
| #define WIDGETPDIAL_h | |||
| #include <FL/Fl_Dial.H> | |||
| #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 | |||
| @@ -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); | |||
| @@ -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; | |||
| } | |||
| @@ -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<fftw_real> 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 | |||
| @@ -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); | |||
| @@ -1,5 +1,5 @@ | |||
| #include "ports.h" | |||
| #include <rtosc/ports.h> | |||
| #include <cstring> | |||
| #include <algorithm> | |||
| #include <map> | |||
| @@ -8,7 +8,7 @@ | |||
| #include <utility> | |||
| #include <cassert> | |||
| #include "miditable.h" | |||
| #include <rtosc/miditable.h> | |||
| using namespace rtosc; | |||
| using std::string; | |||
| @@ -1,5 +1,5 @@ | |||
| #include <math.h> | |||
| #include "miditable.h" | |||
| #include <rtosc/miditable.h> | |||
| using namespace rtosc; | |||
| @@ -1,4 +1,4 @@ | |||
| #include "ports.h" | |||
| #include "../ports.h" | |||
| #include <ostream> | |||
| #include <cassert> | |||
| #include <climits> | |||
| @@ -255,15 +255,15 @@ bool has(T &t, Z&z) | |||
| return false; | |||
| } | |||
| int rtosc_max(int a, int b) { return a<b?b:a;} | |||
| static int int_max(int a, int b) { return a<b?b:a;} | |||
| ivec_t find_pos(words_t &strs) | |||
| static ivec_t find_pos(words_t &strs) | |||
| { | |||
| ivec_t pos; | |||
| int current_dups = strs.size(); | |||
| int N = 0; | |||
| for(auto w:strs) | |||
| N = rtosc_max(N,w.length()); | |||
| N = int_max(N,w.length()); | |||
| int pos_best = -1; | |||
| int pos_best_val = INT_MAX; | |||
| @@ -294,7 +294,7 @@ ivec_t find_pos(words_t &strs) | |||
| return pos; | |||
| } | |||
| ivec_t do_hash(const words_t &strs, const ivec_t &pos, const ivec_t &assoc) | |||
| static ivec_t do_hash(const words_t &strs, const ivec_t &pos, const ivec_t &assoc) | |||
| { | |||
| ivec_t ivec; | |||
| ivec.reserve(strs.size()); | |||
| @@ -308,7 +308,7 @@ ivec_t do_hash(const words_t &strs, const ivec_t &pos, const ivec_t &assoc) | |||
| return ivec; | |||
| } | |||
| ivec_t find_assoc(const words_t &strs, const ivec_t &pos) | |||
| static ivec_t find_assoc(const words_t &strs, const ivec_t &pos) | |||
| { | |||
| ivec_t assoc; | |||
| int current_dups = strs.size(); | |||
| @@ -354,7 +354,7 @@ ivec_t find_assoc(const words_t &strs, const ivec_t &pos) | |||
| return assoc; | |||
| } | |||
| ivec_t find_remap(words_t &strs, ivec_t &pos, ivec_t &assoc) | |||
| static ivec_t find_remap(words_t &strs, ivec_t &pos, ivec_t &assoc) | |||
| { | |||
| ivec_t remap; | |||
| auto hashed = do_hash(strs, pos, assoc); | |||
| @@ -362,7 +362,7 @@ ivec_t find_remap(words_t &strs, ivec_t &pos, ivec_t &assoc) | |||
| // printf("%d) '%s'\n", hashed[i], strs[i].c_str()); | |||
| int N = 0; | |||
| for(auto h:hashed) | |||
| N = rtosc_max(N,h+1); | |||
| N = int_max(N,h+1); | |||
| for(int i=0; i<N; ++i) | |||
| remap.push_back(0); | |||
| for(int i=0; i<(int)hashed.size(); ++i) | |||
| @@ -371,7 +371,7 @@ ivec_t find_remap(words_t &strs, ivec_t &pos, ivec_t &assoc) | |||
| return remap; | |||
| } | |||
| void generate_minimal_hash(std::vector<std::string> str, Port_Matcher &pm) | |||
| static void generate_minimal_hash(std::vector<std::string> str, Port_Matcher &pm) | |||
| { | |||
| pm.pos = find_pos(str); | |||
| if(pm.pos.empty()) { | |||
| @@ -382,7 +382,7 @@ void generate_minimal_hash(std::vector<std::string> 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<start) | |||
| break; | |||
| bool doBreak = *r=='/'; | |||
| r--; | |||
| if(doBreak) | |||
| break; | |||
| } | |||
| } | |||
| static void move_path(char *&r, char *&w, char *start) | |||
| { | |||
| while(1) | |||
| { | |||
| if(r<start) | |||
| break; | |||
| bool doBreak = *r=='/'; | |||
| *w-- = *r--; | |||
| if(doBreak) | |||
| break; | |||
| } | |||
| } | |||
| char *Ports::collapsePath(char *p) | |||
| { | |||
| //obtain the pointer to the last non-null char | |||
| char *p_end = p; | |||
| while(*p_end) p_end++; | |||
| p_end--; | |||
| //number of subpaths to consume | |||
| int consuming = 0; | |||
| char *write_pos = p_end; | |||
| char *read_pos = p_end; | |||
| while(read_pos >= 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<max; ++i) | |||
| { | |||
| sprintf(pos,"[0,%d]",max); | |||
| sprintf(pos,"[0,%d]",max-1); | |||
| //Ensure the result is a path | |||
| if(strrchr(name_buffer, '/')[1] != '/') | |||
| @@ -697,7 +762,7 @@ void walk_ports2(const rtosc::Ports *base, | |||
| //for(unsigned i=0; i<max; ++i) | |||
| { | |||
| sprintf(pos,"[0,%d]",max); | |||
| sprintf(pos,"[0,%d]",max-1); | |||
| //Apply walker function | |||
| walker(&p, name_buffer, data); | |||
| @@ -724,54 +789,110 @@ static void units(std::ostream &o, const char *u) | |||
| o << " units=\"" << u << "\""; | |||
| } | |||
| using std::ostream; | |||
| using std::string; | |||
| static ostream &dump_t_f_port(ostream &o, string name, string doc) | |||
| { | |||
| o << " <message_in pattern=\"" << name << "\" typetag=\"T\">\n"; | |||
| o << " <desc>Enable " << doc << "</desc>\n"; | |||
| o << " <param_T symbol=\"x\"/>\n"; | |||
| o << " </message_in>\n"; | |||
| o << " <message_in pattern=\"" << name << "\" typetag=\"F\">\n"; | |||
| o << " <desc>Disable " << doc << "</desc>\n"; | |||
| o << " <param_F symbol=\"x\"/>\n"; | |||
| o << " </message_in>\n"; | |||
| o << " <message_in pattern=\"" << name << "\" typetag=\"\">\n"; | |||
| o << " <desc>Get state of " << doc << "</desc>\n"; | |||
| o << " </message_in>\n"; | |||
| o << " <message_out pattern=\"" << name << "\" typetag=\"T\">\n"; | |||
| o << " <desc>Value of " << doc << "</desc>\n"; | |||
| o << " <param_T symbol=\"x\"/>"; | |||
| o << " </message_out>\n"; | |||
| o << " <message_out pattern=\"" << name << "\" typetag=\"F\">\n"; | |||
| o << " <desc>Value of " << doc << "</desc>\n"; | |||
| o << " <param_F symbol=\"x\"/>"; | |||
| o << " </message_out>\n"; | |||
| return o; | |||
| } | |||
| static ostream &dump_any_port(ostream &o, string name, string doc) | |||
| { | |||
| o << " <message_in pattern=\"" << name << "\" typetag=\"*\">\n"; | |||
| o << " <desc>" << doc << "</desc>\n"; | |||
| o << " </message_in>\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 << " <message_in pattern=\"" << name << "\" typetag=\"" << args << "\">\n"; | |||
| o << " <desc>" << doc << "</desc>\n"; | |||
| assert(args.length()<arg_names.length()); | |||
| for(unsigned i=0; i<args.length(); ++i) | |||
| o << " <param_" << args[i] << " symbol=\"" << arg_names[i] << "\"/>\n"; | |||
| o << " </message_in>\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 << " <message_in pattern=\"" << name << "\" typetag=\"T\">\n"; | |||
| o << " <desc>Enable " << p->meta()["documentation"] << "</desc>\n"; | |||
| o << " <param_T symbol=\"x\"/>\n"; | |||
| o << " </message_in>\n"; | |||
| o << " <message_in pattern=\"" << name << "\" typetag=\"F\">\n"; | |||
| o << " <desc>Disable " << p->meta()["documentation"] << "</desc>\n"; | |||
| o << " <param_F symbol=\"x\"/>\n"; | |||
| o << " </message_in>\n"; | |||
| o << " <message_in pattern=\"" << name << "\" typetag=\"\">\n"; | |||
| o << " <desc>Get state of " << p->meta()["documentation"] << "</desc>\n"; | |||
| o << " </message_in>\n"; | |||
| o << " <message_out pattern=\"" << name << "\" typetag=\"T\">\n"; | |||
| o << " <desc>Value of " << p->meta()["documentation"] << "</desc>\n"; | |||
| o << " <param_T symbol=\"x\"/>"; | |||
| o << " </message_out>\n"; | |||
| o << " <message_out pattern=\"" << name << "\" typetag=\"F\">\n"; | |||
| o << " <desc>Value of %s</desc>\n", p->meta()["documentation"]; | |||
| o << " <param_F symbol=\"x\"/>"; | |||
| o << " </message_out>\n"; | |||
| if(type == 't') { | |||
| dump_t_f_port(o, name, doc); | |||
| return; | |||
| } | |||
| o << " <message_in pattern=\"" << name << "\" typetag=\"" << type << "\">\n"; | |||
| o << " <desc>Set Value of " << p->meta()["documentation"] << "</desc>\n"; | |||
| if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') | |||
| { | |||
| o << " <desc>Set Value of " << doc << "</desc>\n"; | |||
| if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') { | |||
| o << " <param_" << type << " symbol=\"x\""; | |||
| units(o, meta["unit"]); | |||
| o << ">\n"; | |||
| @@ -785,12 +906,11 @@ void dump_ports_cb(const rtosc::Port *p, const char *name, void *v) | |||
| } | |||
| o << " </message_in>\n"; | |||
| o << " <message_in pattern=\"" << name << "\" typetag=\"\">\n"; | |||
| o << " <desc>Get Value of " << p->meta()["documentation"] << "</desc>\n"; | |||
| o << " <desc>Get Value of " << doc << "</desc>\n"; | |||
| o << " </message_in>\n"; | |||
| o << " <message_out pattern=\"" << name << "\" typetag=\"" << type << "\">\n"; | |||
| o << " <desc>Value of " << p->meta()["documentation"] << "</desc>\n"; | |||
| if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') | |||
| { | |||
| o << " <desc>Value of " << doc << "</desc>\n"; | |||
| if(meta.find("min") != meta.end() && meta.find("max") != meta.end() && type != 'c') { | |||
| o << " <param_" << type << " symbol=\"x\""; | |||
| units(o, meta["unit"]); | |||
| o << ">\n"; | |||
| @@ -803,10 +923,17 @@ void dump_ports_cb(const rtosc::Port *p, const char *name, void *v) | |||
| o << "/>\n"; | |||
| } | |||
| o << " </message_out>\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) | |||
| @@ -1,12 +1,20 @@ | |||
| #include "subtree-serialize.h" | |||
| #include "ports.h" | |||
| #include "rtosc.h" | |||
| #include <rtosc/subtree-serialize.h> | |||
| #include <rtosc/ports.h> | |||
| #include <rtosc/rtosc.h> | |||
| #include <cstring> | |||
| #include <cassert> | |||
| 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); | |||
| @@ -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 | |||
| @@ -3,8 +3,8 @@ | |||
| #include <cstdio> | |||
| #include <cassert> | |||
| #include <ctime> | |||
| #include "rtosc.h" | |||
| #include "undo-history.h" | |||
| #include <rtosc/rtosc.h> | |||
| #include <rtosc/undo-history.h> | |||
| using std::pair; | |||
| using std::make_pair; | |||
| @@ -1,7 +1,13 @@ | |||
| #include "rtosc.h" | |||
| #include <rtosc/rtosc.h> | |||
| #include <ctype.h> | |||
| #include <assert.h> | |||
| #include <string.h> | |||
| #include <strings.h> | |||
| #include <stdlib.h> | |||
| #include <stdio.h> | |||
| 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; | |||
| } | |||
| @@ -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 <rtosc/ports.h> | |||
| #include <string.h> | |||
| #include <algorithm> | |||
| #include <map> | |||
| @@ -276,3 +277,4 @@ class MidiTable | |||
| }; | |||
| }; | |||
| #endif | |||
| @@ -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<class T> constexpr T spice(T*t) {return *t;} | |||
| @@ -145,7 +169,7 @@ template<class T> 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<class T> 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" | |||
| @@ -28,7 +28,7 @@ | |||
| #include <vector> | |||
| #include <functional> | |||
| #include <initializer_list> | |||
| #include "rtosc.h" | |||
| #include <rtosc/rtosc.h> | |||
| #include <cstring> | |||
| #include <cctype> | |||
| #include <cstdlib> | |||
| @@ -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; | |||
| @@ -6,7 +6,7 @@ | |||
| #include <ctype.h> | |||
| #include <assert.h> | |||
| #include "rtosc.h" | |||
| #include <rtosc/rtosc.h> | |||
| 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<ring[0].len ? ring[0].data[pos] : | |||
| @@ -461,10 +523,10 @@ static size_t bundle_ring_length(ring_t *ring) | |||
| unsigned pos = 8+8;//goto first length field | |||
| uint32_t advance = 0; | |||
| do { | |||
| advance = deref(pos+0, ring) << (8*0) | | |||
| deref(pos+1, ring) << (8*1) | | |||
| deref(pos+2, ring) << (8*2) | | |||
| deref(pos+3, ring) << (8*3); | |||
| advance = deref(pos+0, ring) << (8*3) | | |||
| deref(pos+1, ring) << (8*2) | | |||
| deref(pos+2, ring) << (8*1) | | |||
| deref(pos+3, ring) << (8*0); | |||
| if(advance) | |||
| pos += 4+advance; | |||
| } while(advance); | |||
| @@ -598,6 +660,49 @@ bool rtosc_valid_message_p(const char *msg, size_t len) | |||
| size_t observed_length = rtosc_message_length(msg, len); | |||
| return observed_length == len; | |||
| } | |||
| static uint64_t extract_uint64(const uint8_t *arg_pos) | |||
| { | |||
| uint64_t arg = 0; | |||
| arg |= (((uint64_t)*arg_pos++) << 56); | |||
| arg |= (((uint64_t)*arg_pos++) << 48); | |||
| arg |= (((uint64_t)*arg_pos++) << 40); | |||
| arg |= (((uint64_t)*arg_pos++) << 32); | |||
| arg |= (((uint64_t)*arg_pos++) << 24); | |||
| arg |= (((uint64_t)*arg_pos++) << 16); | |||
| arg |= (((uint64_t)*arg_pos++) << 8); | |||
| arg |= (((uint64_t)*arg_pos++)); | |||
| return arg; | |||
| } | |||
| static uint32_t extract_uint32(const uint8_t *arg_pos) | |||
| { | |||
| uint32_t arg = 0; | |||
| arg |= (((uint32_t)*arg_pos++) << 24); | |||
| arg |= (((uint32_t)*arg_pos++) << 16); | |||
| arg |= (((uint32_t)*arg_pos++) << 8); | |||
| arg |= (((uint32_t)*arg_pos++)); | |||
| return arg; | |||
| } | |||
| static void emplace_uint64(uint8_t *buffer, uint64_t d) | |||
| { | |||
| buffer[0] = ((d>>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<elms; ++i) { | |||
| const char *msg = va_arg(va, const char*); | |||
| //It is assumed that any passed message/bundle is valid | |||
| size_t size = rtosc_message_length(msg, -1); | |||
| *(uint32_t*)buffer = size; | |||
| emplace_uint32((uint8_t*)buffer, size); | |||
| buffer += 4; | |||
| memcpy(buffer, msg, size); | |||
| buffer+=size; | |||
| @@ -623,14 +728,14 @@ size_t rtosc_bundle(char *buffer, size_t len, uint64_t tt, int elms, ...) | |||
| return buffer-_buffer; | |||
| } | |||
| #define POS ((size_t)(((const char *)lengths) - buffer)) | |||
| size_t rtosc_bundle_elements(const char *buffer, size_t len) | |||
| { | |||
| const uint32_t *lengths = (const uint32_t*) (buffer+16); | |||
| size_t elms = 0; | |||
| //TODO | |||
| while(POS < len && *lengths) { | |||
| lengths += *lengths/4+1; | |||
| while(POS < len && extract_uint32((const uint8_t*)lengths)) { | |||
| lengths += extract_uint32((const uint8_t*)lengths)/4+1; | |||
| if(POS > 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); | |||
| } | |||
| @@ -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 | |||