| @@ -68,6 +68,7 @@ extern "C" { | |||||
| #include "zynaddsubfx/rtosc/cpp/undo-history.cpp" | #include "zynaddsubfx/rtosc/cpp/undo-history.cpp" | ||||
| // zynaddsubfx includes | // zynaddsubfx includes | ||||
| #include "zynaddsubfx/Containers/NotePool.cpp" | |||||
| #include "zynaddsubfx/DSP/AnalogFilter.cpp" | #include "zynaddsubfx/DSP/AnalogFilter.cpp" | ||||
| #include "zynaddsubfx/DSP/FFTwrapper.cpp" | #include "zynaddsubfx/DSP/FFTwrapper.cpp" | ||||
| #include "zynaddsubfx/DSP/Filter.cpp" | #include "zynaddsubfx/DSP/Filter.cpp" | ||||
| @@ -91,6 +91,7 @@ extern "C" { | |||||
| #include "zynaddsubfx/UI/Fl_Osc_Pane.cpp" | #include "zynaddsubfx/UI/Fl_Osc_Pane.cpp" | ||||
| #include "zynaddsubfx/UI/Fl_Osc_Roller.cpp" | #include "zynaddsubfx/UI/Fl_Osc_Roller.cpp" | ||||
| #include "zynaddsubfx/UI/Fl_Osc_Slider.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_Value.cpp" | ||||
| #include "zynaddsubfx/UI/Fl_Osc_VSlider.cpp" | #include "zynaddsubfx/UI/Fl_Osc_VSlider.cpp" | ||||
| #include "zynaddsubfx/UI/Fl_Osc_Widget.cpp" | #include "zynaddsubfx/UI/Fl_Osc_Widget.cpp" | ||||
| @@ -106,6 +107,7 @@ extern "C" { | |||||
| #include "zynaddsubfx/UI/PresetsUI.cpp" | #include "zynaddsubfx/UI/PresetsUI.cpp" | ||||
| #include "zynaddsubfx/UI/ResonanceUI.cpp" | #include "zynaddsubfx/UI/ResonanceUI.cpp" | ||||
| #include "zynaddsubfx/UI/SUBnoteUI.cpp" | #include "zynaddsubfx/UI/SUBnoteUI.cpp" | ||||
| #include "zynaddsubfx/UI/TipWin.cpp" | |||||
| #include "zynaddsubfx/UI/VirKeyboard.cpp" | #include "zynaddsubfx/UI/VirKeyboard.cpp" | ||||
| #include "zynaddsubfx/UI/guimain.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()); | outgain = dB2rap(pars->getgain()); | ||||
| oldinput = -1.0f; | oldinput = -1.0f; | ||||
| Qfactor = 1.0f; | |||||
| Qfactor = pars->getq(); | |||||
| oldQfactor = Qfactor; | oldQfactor = Qfactor; | ||||
| firsttime = 1; | firsttime = 1; | ||||
| } | } | ||||
| @@ -45,7 +45,8 @@ static const rtosc::Ports local_ports = { | |||||
| rSelf(EffectMgr), | rSelf(EffectMgr), | ||||
| rPaste, | rPaste, | ||||
| rRecurp(filterpars, "Filter Parameter for Dynamic Filter"), | 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) | [](const char *msg, rtosc::RtData &d) | ||||
| { | { | ||||
| EffectMgr *eff = (EffectMgr*)d.obj; | EffectMgr *eff = (EffectMgr*)d.obj; | ||||
| @@ -54,20 +55,37 @@ static const rtosc::Ports local_ports = { | |||||
| if(!rtosc_narguments(msg)) | if(!rtosc_narguments(msg)) | ||||
| d.reply(d.loc, "i", eff->geteffectparrt(atoi(mm))); | 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); | eff->seteffectparrt(atoi(mm), rtosc_argument(msg, 0).i); | ||||
| d.broadcast(d.loc, "i", eff->geteffectparrt(atoi(mm))); | 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) | [](const char *msg, rtosc::RtData &d) | ||||
| { | { | ||||
| char loc[1024]; | |||||
| EffectMgr *eff = (EffectMgr*)d.obj; | EffectMgr *eff = (EffectMgr*)d.obj; | ||||
| if(!rtosc_narguments(msg)) | if(!rtosc_narguments(msg)) | ||||
| d.reply(d.loc, "i", eff->getpreset()); | d.reply(d.loc, "i", eff->getpreset()); | ||||
| else { | else { | ||||
| eff->changepresetrt(rtosc_argument(msg, 0).i); | eff->changepresetrt(rtosc_argument(msg, 0).i); | ||||
| d.broadcast(d.loc, "i", eff->getpreset()); | 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, | {"eq-coeffs:", rProp(internal) rDoc("Get equalizer Coefficients"), NULL, | ||||
| @@ -84,7 +102,8 @@ static const rtosc::Ports local_ports = { | |||||
| eq->getFilter(a,b); | eq->getFilter(a,b); | ||||
| d.reply(d.loc, "bb", sizeof(a), a, sizeof(b), 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; | EffectMgr *eff = (EffectMgr*)d.obj; | ||||
| if(rtosc_narguments(m)) { | if(rtosc_narguments(m)) { | ||||
| @@ -385,7 +404,7 @@ void EffectMgr::paste(EffectMgr &e) | |||||
| changeeffectrt(e.nefx, true); | changeeffectrt(e.nefx, true); | ||||
| changepresetrt(e.preset, true); | changepresetrt(e.preset, true); | ||||
| for(int i=0;i<128;++i) | for(int i=0;i<128;++i) | ||||
| seteffectparrt(e.settings[i], i); | |||||
| seteffectparrt(i, e.settings[i]); | |||||
| } | } | ||||
| void EffectMgr::add2XML(XMLwrapper *xml) | void EffectMgr::add2XML(XMLwrapper *xml) | ||||
| @@ -80,7 +80,6 @@ class EffectMgr:public Presets | |||||
| private: | private: | ||||
| //Parameters Prior to initialization | //Parameters Prior to initialization | ||||
| char effect_id; | |||||
| char preset; | char preset; | ||||
| char settings[128]; | char settings[128]; | ||||
| @@ -86,12 +86,14 @@ class Allocator | |||||
| //! the allocator for normal use | //! the allocator for normal use | ||||
| class AllocatorClass : public Allocator | 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 | //! the dummy allocator, which does not allow any allocation | ||||
| class DummyAllocator : public Allocator | class DummyAllocator : public Allocator | ||||
| @@ -32,7 +32,7 @@ | |||||
| #include "XMLwrapper.h" | #include "XMLwrapper.h" | ||||
| #define rStdString(name, len, ...) \ | #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 \ | #define rStdStringCb(name, length) rBOIL_BEGIN \ | ||||
| if(!strcmp("", args)) {\ | if(!strcmp("", args)) {\ | ||||
| data.reply(loc, "s", obj->name); \ | 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"), | rToggle(cfg.BankUIAutoClose, "Automatic Closing of BackUI After Patch Selection"), | ||||
| rParamI(cfg.GzipCompression, "Level of Gzip Compression For Save Files"), | rParamI(cfg.GzipCompression, "Level of Gzip Compression For Save Files"), | ||||
| rParamI(cfg.Interpolation, "Level of Interpolation, Linear/Cubic"), | 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) | [](const char *msg, rtosc::RtData &d) | ||||
| { | { | ||||
| Config &c = *(Config*)d.obj; | Config &c = *(Config*)d.obj; | ||||
| @@ -90,7 +90,7 @@ static const rtosc::Ports ports = { | |||||
| rtosc_amessage(buffer, sizeof(buffer), d.loc, types, args); | rtosc_amessage(buffer, sizeof(buffer), d.loc, types, args); | ||||
| d.reply(buffer); | 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) | [](const char *msg, rtosc::RtData &d) | ||||
| { | { | ||||
| Config &c = *(Config*)d.obj; | Config &c = *(Config*)d.obj; | ||||
| @@ -133,7 +133,7 @@ static const rtosc::Ports ports = { | |||||
| rParamI(cfg.VirKeybLayout, "Keyboard Layout For Virtual Piano Keyboard"), | rParamI(cfg.VirKeybLayout, "Keyboard Layout For Virtual Piano Keyboard"), | ||||
| //rParamS(cfg.LinuxALSAaudioDev), | //rParamS(cfg.LinuxALSAaudioDev), | ||||
| //rParamS(cfg.nameTag) | //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) | [](const char *msg, rtosc::RtData &d) | ||||
| { | { | ||||
| Config &c = *(Config*)d.obj; | Config &c = *(Config*)d.obj; | ||||
| @@ -53,7 +53,9 @@ using namespace rtosc; | |||||
| static const Ports sysefxPort = | 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 | //ok, this is going to be an ugly workaround | ||||
| //we know that if we are here the message previously MUST have | //we know that if we are here the message previously MUST have | ||||
| @@ -80,7 +82,8 @@ static const Ports sysefxPort = | |||||
| static const Ports sysefsendto = | 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 | //same ugly workaround as before | ||||
| const char *index_1 = m; | const char *index_1 = m; | ||||
| @@ -103,6 +106,7 @@ static const Ports sysefsendto = | |||||
| }; | }; | ||||
| static const Ports master_ports = { | 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(part, 16, "Part"),//NUM_MIDI_PARTS | ||||
| rRecursp(sysefx, 4, "System Effect"),//NUM_SYS_EFX | rRecursp(sysefx, 4, "System Effect"),//NUM_SYS_EFX | ||||
| rRecursp(insefx, 8, "Insertion Effect"),//NUM_INS_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"), | rArrayI(Pinsparts, NUM_INS_EFX, "Part to insert part onto"), | ||||
| {"echo", rDoc("Hidden port to echo messages"), 0, [](const char *m, RtData&d) { | {"echo", rDoc("Hidden port to echo messages"), 0, [](const char *m, RtData&d) { | ||||
| d.reply(m-1);}}, | 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; | Master *m = (Master*)d.obj; | ||||
| d.reply("/vu-meter", "bb", sizeof(m->vu), &m->vu, sizeof(float)*NUM_MIDI_PARTS, m->vuoutpeakpart);}}, | 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; | Master *m = (Master*)d.obj; | ||||
| m->vuresetpeaks();}}, | m->vuresetpeaks();}}, | ||||
| {"load-part:ib", rProp(internal) rDoc("Load Part From Middleware"), 0, [](const char *msg, RtData &d) { | {"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(); | p->initialize_rt(); | ||||
| //printf("part %d is now pointer %p\n", i, p); | //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) { | [](const char *m, rtosc::RtData &d) { | ||||
| if(rtosc_narguments(m)==0) { | if(rtosc_narguments(m)==0) { | ||||
| d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume); | d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume); | ||||
| } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') { | } else if(rtosc_narguments(m)==1 && rtosc_type(m,0)=='i') { | ||||
| ((Master*)d.obj)->setPvolume(limit<char>(rtosc_argument(m,0).i,0,127)); | ((Master*)d.obj)->setPvolume(limit<char>(rtosc_argument(m,0).i,0,127)); | ||||
| d.broadcast(d.loc, "i", ((Master*)d.obj)->Pvolume);}}}, | 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) { | [](const char *m, rtosc::RtData &d) { | ||||
| if(rtosc_narguments(m)==0) { | if(rtosc_narguments(m)==0) { | ||||
| d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume); | d.reply(d.loc, "i", ((Master*)d.obj)->Pvolume); | ||||
| @@ -200,7 +204,8 @@ static const Ports master_ports = { | |||||
| [](const char *m, RtData &d){ | [](const char *m, RtData &d){ | ||||
| Master *M = (Master*)d.obj; | Master *M = (Master*)d.obj; | ||||
| M->midi.clear_entry(rtosc_argument(m,0).s);}}, | 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", "");}}, | d.reply("/close-ui", "");}}, | ||||
| {"add-rt-memory:bi", rProp(internal) rDoc("Add Additional Memory To RT MemPool"), 0, | {"add-rt-memory:bi", rProp(internal) rDoc("Add Additional Memory To RT MemPool"), 0, | ||||
| [](const char *msg, RtData &d) | [](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", "f", m.synth.oscilsize_f); | ||||
| d.reply("/oscilsize", "i", m.synth.oscilsize); | 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 | SNIP | ||||
| preset_ports.dispatch(msg, data); | preset_ports.dispatch(msg, data); | ||||
| rBOIL_END}, | rBOIL_END}, | ||||
| @@ -292,7 +298,7 @@ Master::Master(const SYNTH_T &synth_, Config* config) | |||||
| :HDDRecorder(synth_), ctl(synth_), | :HDDRecorder(synth_), ctl(synth_), | ||||
| microtonal(config->cfg.GzipCompression), bank(config), | microtonal(config->cfg.GzipCompression), bank(config), | ||||
| midi(Master::ports), frozenState(false), pendingMemory(false), | 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; | bToU = NULL; | ||||
| uToB = NULL; | uToB = NULL; | ||||
| @@ -306,6 +312,7 @@ Master::Master(const SYNTH_T &synth_, Config* config) | |||||
| the_master = this; | the_master = this; | ||||
| #endif | #endif | ||||
| last_xmz[0] = 0; | |||||
| fft = new FFTwrapper(synth.oscilsize); | fft = new FFTwrapper(synth.oscilsize); | ||||
| shutup = 0; | shutup = 0; | ||||
| @@ -315,7 +322,7 @@ Master::Master(const SYNTH_T &synth_, Config* config) | |||||
| } | } | ||||
| for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) | 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); | config->cfg.Interpolation, µtonal, fft); | ||||
| //Insertion Effects init | //Insertion Effects init | ||||
| @@ -819,8 +826,8 @@ void Master::AudioOut(float *outl, float *outr) | |||||
| ShutUp(); | ShutUp(); | ||||
| } | } | ||||
| //update the LFO's time | |||||
| LFOParams::time++; | |||||
| //update the global frame timer | |||||
| time++; | |||||
| } | } | ||||
| //TODO review the respective code from yoshimi for this | //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) | int Master::loadXML(const char *filename) | ||||
| { | { | ||||
| XMLwrapper *xml = new XMLwrapper(); | XMLwrapper *xml = new XMLwrapper(); | ||||
| @@ -28,6 +28,7 @@ | |||||
| #include <rtosc/miditable.h> | #include <rtosc/miditable.h> | ||||
| #include <rtosc/ports.h> | #include <rtosc/ports.h> | ||||
| #include "Time.h" | |||||
| #include "Bank.h" | #include "Bank.h" | ||||
| #include "Recorder.h" | #include "Recorder.h" | ||||
| @@ -53,6 +54,8 @@ class Master | |||||
| /** Destructor*/ | /** Destructor*/ | ||||
| ~Master(); | ~Master(); | ||||
| char last_xmz[XMZ_PATH_MAX]; | |||||
| void applyOscEvent(const char *event); | void applyOscEvent(const char *event); | ||||
| /**Saves all settings to a XML file | /**Saves all settings to a XML file | ||||
| @@ -64,7 +67,6 @@ class Master | |||||
| void defaults(); | void defaults(); | ||||
| /**loads all settings from a XML file | /**loads all settings from a XML file | ||||
| * @return 0 for ok or -1 if there is an error*/ | * @return 0 for ok or -1 if there is an error*/ | ||||
| int loadXML(const char *filename); | int loadXML(const char *filename); | ||||
| @@ -99,7 +101,8 @@ class Master | |||||
| /**Audio Output*/ | /**Audio Output*/ | ||||
| void AudioOut(float *outl, float *outr) REALTIME; | 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, | void GetAudioOutSamples(size_t nsamples, | ||||
| unsigned samplerate, | unsigned samplerate, | ||||
| float *outl, | float *outl, | ||||
| @@ -171,6 +174,7 @@ class Master | |||||
| rtosc::ThreadLink *uToB; | rtosc::ThreadLink *uToB; | ||||
| bool pendingMemory; | bool pendingMemory; | ||||
| const SYNTH_T &synth; | const SYNTH_T &synth; | ||||
| AbsTime time; | |||||
| const int& gzip_compression; //!< value from config | const int& gzip_compression; //!< value from config | ||||
| private: | private: | ||||
| float sysefxvol[NUM_SYS_EFX][NUM_MIDI_PARTS]; | 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(Pname, MICROTONAL_MAX_NAME_LEN, "Microtonal Name"), | ||||
| rString(Pcomment, 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; | Microtonal &m = *(Microtonal*)d.obj; | ||||
| d.reply(d.loc, "i", m.getoctavesize()); | 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) | 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) { | if(bank.bankpos != pos) { | ||||
| bank.bankpos = pos; | bank.bankpos = pos; | ||||
| bank.loadbank(bank.banks[pos].dir); | bank.loadbank(bank.banks[pos].dir); | ||||
| @@ -433,13 +438,13 @@ namespace Nio | |||||
| using std::get; | using std::get; | ||||
| using rtosc::rtMsg; | using rtosc::rtMsg; | ||||
| rtosc::Ports ports = { | 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(); | auto list = Nio::getSinks(); | ||||
| char *ret = rtosc_splat(d.loc, list); | char *ret = rtosc_splat(d.loc, list); | ||||
| d.reply(ret); | d.reply(ret); | ||||
| delete [] 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(); | auto list = Nio::getSources(); | ||||
| char *ret = rtosc_splat(d.loc, list); | char *ret = rtosc_splat(d.loc, list); | ||||
| d.reply(ret); | d.reply(ret); | ||||
| @@ -489,7 +494,7 @@ public: | |||||
| void doReadOnlyOp(std::function<void()> read_only_fn); | 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; | int err = 0; | ||||
| doReadOnlyOp([master,nslot,npart,&err](){ | 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); | int err = master->bank.setname(slot, name, -1); | ||||
| if(err) { | 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); | int err = master->bank.swapslot(slota, slotb); | ||||
| if(err) { | 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); | int err = master->bank.clearslot(slot); | ||||
| if(err) { | if(err) { | ||||
| @@ -557,7 +562,14 @@ public: | |||||
| /*printf("results: '%s' '%d'\n",fname.c_str(), res);*/}); | /*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]++; | actual_load[npart]++; | ||||
| @@ -565,9 +577,12 @@ public: | |||||
| return; | return; | ||||
| assert(actual_load[npart] <= pending_load[npart]); | assert(actual_load[npart] <= pending_load[npart]); | ||||
| //load part in async fashion when possible | |||||
| #if HAVE_ASYNC | |||||
| auto alloc = std::async(std::launch::async, | auto alloc = std::async(std::launch::async, | ||||
| [master,filename,this,npart](){ | [master,filename,this,npart](){ | ||||
| Part *p = new Part(*master->memory, synth, | Part *p = new Part(*master->memory, synth, | ||||
| master->time, | |||||
| config->cfg.GzipCompression, | config->cfg.GzipCompression, | ||||
| config->cfg.Interpolation, | config->cfg.Interpolation, | ||||
| &master->microtonal, master->fft); | &master->microtonal, master->fft); | ||||
| @@ -589,6 +604,20 @@ public: | |||||
| } | } | ||||
| Part *p = alloc.get(); | 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); | obj_store.extractPart(p, npart); | ||||
| kits.extractPart(p, npart); | kits.extractPart(p, npart); | ||||
| @@ -605,6 +634,7 @@ public: | |||||
| if(npart == -1) | if(npart == -1) | ||||
| return; | return; | ||||
| Part *p = new Part(*master->memory, synth, | Part *p = new Part(*master->memory, synth, | ||||
| master->time, | |||||
| config->cfg.GzipCompression, | config->cfg.GzipCompression, | ||||
| config->cfg.Interpolation, | config->cfg.Interpolation, | ||||
| &master->microtonal, master->fft); | &master->microtonal, master->fft); | ||||
| @@ -628,7 +658,10 @@ public: | |||||
| m->uToB = uToB; | m->uToB = uToB; | ||||
| m->bToU = bToU; | m->bToU = bToU; | ||||
| if(filename) { | if(filename) { | ||||
| m->loadXML(filename); | |||||
| if ( m->loadXML(filename) ) { | |||||
| delete m; | |||||
| return; | |||||
| } | |||||
| m->applyparameters(); | m->applyparameters(); | ||||
| } | } | ||||
| @@ -658,7 +691,8 @@ public: | |||||
| void tick(void) | void tick(void) | ||||
| { | { | ||||
| while(lo_server_recv_noblock(server, 0)); | |||||
| if(server) | |||||
| while(lo_server_recv_noblock(server, 0)); | |||||
| while(bToU->hasNext()) { | while(bToU->hasNext()) { | ||||
| const char *rtmsg = bToU->read(); | const char *rtmsg = bToU->read(); | ||||
| bToUhandle(rtmsg); | bToUhandle(rtmsg); | ||||
| @@ -815,13 +849,19 @@ MiddleWareImpl::MiddleWareImpl(MiddleWare *mw, SYNTH_T synth_, | |||||
| LO_UDP, liblo_error_cb); | LO_UDP, liblo_error_cb); | ||||
| else | else | ||||
| server = lo_server_new_with_proto(NULL, LO_UDP, liblo_error_cb); | 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 | #ifndef PLUGINVERSION | ||||
| if(!isPlugin()) { | if(!isPlugin()) { | ||||
| clean_up_tmp_nams(); | 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 | #endif | ||||
| @@ -869,7 +909,8 @@ MiddleWareImpl::~MiddleWareImpl(void) | |||||
| warnMemoryLeaks(); | warnMemoryLeaks(); | ||||
| lo_server_free(server); | |||||
| if(server) | |||||
| lo_server_free(server); | |||||
| delete master; | delete master; | ||||
| delete osc; | delete osc; | ||||
| @@ -966,7 +1007,10 @@ void MiddleWareImpl::bToUhandle(const char *rtmsg, bool dummy) | |||||
| uToB->write("/add-rt-memory", "bi", sizeof(void*), &mem, N); | uToB->write("/add-rt-memory", "bi", sizeof(void*), &mem, N); | ||||
| } else if(!strcmp(rtmsg, "/setprogram") | } else if(!strcmp(rtmsg, "/setprogram") | ||||
| && !strcmp(rtosc_argument_string(rtmsg),"cc")) { | && !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)) { | } else if(!strcmp("/undo_pause", rtmsg)) { | ||||
| recording_undo = false; | recording_undo = false; | ||||
| } else if(!strcmp("/undo_resume", rtmsg)) { | } else if(!strcmp("/undo_resume", rtmsg)) { | ||||
| @@ -1148,7 +1192,7 @@ void MiddleWareImpl::handleMsg(const char *msg) | |||||
| if(last_url == "GUI") | if(last_url == "GUI") | ||||
| bank_cb = [this](const char *msg){if(osc)osc->tryLink(msg);}; | bank_cb = [this](const char *msg){if(osc)osc->tryLink(msg);}; | ||||
| else | 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")) { | if(!strcmp(msg, "/refresh_bank") && !strcmp(rtosc_argument_string(msg), "i")) { | ||||
| refreshBankView(master->bank, rtosc_argument(msg,0).i, bank_cb); | refreshBankView(master->bank, rtosc_argument(msg,0).i, bank_cb); | ||||
| @@ -1184,22 +1228,22 @@ void MiddleWareImpl::handleMsg(const char *msg) | |||||
| loadMaster(NULL); | loadMaster(NULL); | ||||
| } else if(!strcmp(msg, "/load_xiz") && !strcmp(rtosc_argument_string(msg), "is")) { | } else if(!strcmp(msg, "/load_xiz") && !strcmp(rtosc_argument_string(msg), "is")) { | ||||
| pending_load[rtosc_argument(msg,0).i]++; | 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")) { | } else if(strstr(msg, "load-part") && !strcmp(rtosc_argument_string(msg), "is")) { | ||||
| pending_load[rtosc_argument(msg,0).i]++; | 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") | } else if(!strcmp(msg, "/setprogram") | ||||
| && !strcmp(rtosc_argument_string(msg),"c")) { | && !strcmp(rtosc_argument_string(msg),"c")) { | ||||
| pending_load[0]++; | 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")) { | } 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")) { | } 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")) { | } 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")) { | } 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/")) { | } else if(strstr(msg, "/config/")) { | ||||
| handleConfig(msg); | handleConfig(msg); | ||||
| } else if(strstr(msg, "/presets/")) { | } else if(strstr(msg, "/presets/")) { | ||||
| @@ -1215,6 +1259,8 @@ void MiddleWareImpl::handleMsg(const char *msg) | |||||
| undo.seekHistory(-1); | undo.seekHistory(-1); | ||||
| } else if(!strcmp(msg, "/redo")) { | } else if(!strcmp(msg, "/redo")) { | ||||
| undo.seekHistory(+1); | undo.seekHistory(+1); | ||||
| } else if(!strcmp(msg, "/ui/title")) { | |||||
| ;//drop the message into the abyss | |||||
| } else | } else | ||||
| uToB->raw_write(msg); | 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"); | 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) | void MiddleWare::pendingSetProgram(int part, int program) | ||||
| { | { | ||||
| impl->pending_load[part]++; | impl->pending_load[part]++; | ||||
| @@ -1339,7 +1389,10 @@ const SYNTH_T &MiddleWare::getSynth(void) const | |||||
| const char* MiddleWare::getServerAddress(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 | const PresetsStore& MiddleWare::getPresetsStore() const | ||||
| @@ -4,15 +4,14 @@ | |||||
| #include <string> | #include <string> | ||||
| struct SYNTH_T; | struct SYNTH_T; | ||||
| class Config; | |||||
| class Master; | |||||
| class Master; | |||||
| class PresetsStore; | class PresetsStore; | ||||
| //Link between realtime and non-realtime layers | //Link between realtime and non-realtime layers | ||||
| class MiddleWare | class MiddleWare | ||||
| { | { | ||||
| public: | public: | ||||
| MiddleWare(SYNTH_T synth, Config *config, | |||||
| MiddleWare(SYNTH_T synth, class Config *config, | |||||
| int preferred_port = -1); | int preferred_port = -1); | ||||
| ~MiddleWare(void); | ~MiddleWare(void); | ||||
| void updateResources(Master *m); | void updateResources(Master *m); | ||||
| @@ -34,6 +33,8 @@ class MiddleWare | |||||
| void transmitMsg(const char *, const char *args, ...); | void transmitMsg(const char *, const char *args, ...); | ||||
| //Handle a rtosc Message uToB | //Handle a rtosc Message uToB | ||||
| void transmitMsg(const char *, const char *args, va_list va); | 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 | //Indicate that a program will be loaded on a known part | ||||
| void pendingSetProgram(int part, int program); | void pendingSetProgram(int part, int program); | ||||
| //Get/Set the active bToU url | //Get/Set the active bToU url | ||||
| @@ -73,7 +73,8 @@ static const Ports partPorts = { | |||||
| rToggle(Pnoteon, "If the channel accepts note on events"), | rToggle(Pnoteon, "If the channel accepts note on events"), | ||||
| //TODO FIXME Change to 0=OFF 1=MULTI 2=SINGLE | //TODO FIXME Change to 0=OFF 1=MULTI 2=SINGLE | ||||
| rParamI(Pkitmode, "Kit mode enable"), | 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(Ppolymode, "Polyphoney mode"), | ||||
| rToggle(Plegatomode, "Legato enable"), | rToggle(Plegatomode, "Legato enable"), | ||||
| rParamZyn(info.Ptype, "Class of Instrument"), | rParamZyn(info.Ptype, "Class of Instrument"), | ||||
| @@ -82,11 +83,15 @@ static const Ports partPorts = { | |||||
| rString(Pname, PART_MAX_NAME_LEN, "Kit User Specified Label"), | rString(Pname, PART_MAX_NAME_LEN, "Kit User Specified Label"), | ||||
| rArray(Pefxroute, NUM_PART_EFX, "Effect Routing"), | rArray(Pefxroute, NUM_PART_EFX, "Effect Routing"), | ||||
| rArrayT(Pefxbypass, NUM_PART_EFX, "If an effect is bypassed"), | 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;}}, | {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;}}, | {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; | Part *p = (Part*)d.obj; | ||||
| if(!rtosc_narguments(msg)) { | if(!rtosc_narguments(msg)) { | ||||
| @@ -108,7 +113,8 @@ static const Ports partPorts = { | |||||
| p->Ppolymode = 0; | p->Ppolymode = 0; | ||||
| p->Plegatomode = 1; | 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 | //XXX todo forward this event for middleware to handle | ||||
| //Part *p = (Part*)d.obj; | //Part *p = (Part*)d.obj; | ||||
| @@ -159,23 +165,24 @@ static const Ports kitPorts = { | |||||
| rToggle(Ppadenabled, "PADsynth enable"), | rToggle(Ppadenabled, "PADsynth enable"), | ||||
| rParamZyn(Psendtoparteffect, "Effect Levels"), | rParamZyn(Psendtoparteffect, "Effect Levels"), | ||||
| rString(Pname, PART_MAX_NAME_LEN, "Kit User Specified Label"), | 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;}}, | {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;}}, | {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) { | [](const char *msg, RtData &d) { | ||||
| rObject &o = *(rObject*)d.obj; | rObject &o = *(rObject*)d.obj; | ||||
| assert(o.padpars == NULL); | assert(o.padpars == NULL); | ||||
| o.padpars = *(decltype(o.padpars)*)rtosc_argument(msg, 0).b.data; | 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) { | [](const char *msg, RtData &d) { | ||||
| rObject &o = *(rObject*)d.obj; | rObject &o = *(rObject*)d.obj; | ||||
| assert(o.adpars == NULL); | assert(o.adpars == NULL); | ||||
| o.adpars = *(decltype(o.adpars)*)rtosc_argument(msg, 0).b.data; | 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) { | [](const char *msg, RtData &d) { | ||||
| rObject &o = *(rObject*)d.obj; | rObject &o = *(rObject*)d.obj; | ||||
| assert(o.subpars == NULL); | assert(o.subpars == NULL); | ||||
| @@ -188,18 +195,23 @@ static const Ports kitPorts = { | |||||
| const Ports &Part::Kit::ports = kitPorts; | const Ports &Part::Kit::ports = kitPorts; | ||||
| const Ports &Part::ports = partPorts; | 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, | const int &gzip_compression, const int &interpolation, | ||||
| Microtonal *microtonal_, FFTwrapper *fft_) | 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), | gzip_compression(gzip_compression), | ||||
| interpolation(interpolation) | interpolation(interpolation) | ||||
| { | { | ||||
| microtonal = microtonal_; | |||||
| fft = fft_; | |||||
| partoutl = new float [synth.buffersize]; | |||||
| partoutr = new float [synth.buffersize]; | |||||
| monomemClear(); | monomemClear(); | ||||
| for(int n = 0; n < NUM_KIT_ITEMS; ++n) { | 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); | partefx[nefx] = new EffectMgr(memory, synth, 1); | ||||
| Pefxbypass[nefx] = false; | Pefxbypass[nefx] = false; | ||||
| } | } | ||||
| assert(partefx[0]); | |||||
| for(int n = 0; n < NUM_PART_EFX + 1; ++n) { | for(int n = 0; n < NUM_PART_EFX + 1; ++n) { | ||||
| partfxinputl[n] = new float [synth.buffersize]; | partfxinputl[n] = new float [synth.buffersize]; | ||||
| @@ -226,28 +239,15 @@ Part::Part(Allocator &alloc, const SYNTH_T &synth_, | |||||
| killallnotes = false; | killallnotes = false; | ||||
| oldfreq = -1.0f; | 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(); | cleanup(); | ||||
| Pname = new char[PART_MAX_NAME_LEN]; | Pname = new char[PART_MAX_NAME_LEN]; | ||||
| oldvolumel = oldvolumer = 0.5f; | oldvolumel = oldvolumer = 0.5f; | ||||
| lastnote = -1; | lastnote = -1; | ||||
| lastpos = 0; // lastpos will store previously used NoteOn(...)'s pos. | |||||
| lastlegatomodevalid = false; // To store previous legatomodevalid value. | |||||
| defaults(); | defaults(); | ||||
| assert(partefx[0]); | |||||
| } | } | ||||
| void Part::cloneTraits(Part &p) const | void Part::cloneTraits(Part &p) const | ||||
| @@ -334,8 +334,7 @@ void Part::defaultsinstrument() | |||||
| */ | */ | ||||
| void Part::cleanup(bool final_) | void Part::cleanup(bool final_) | ||||
| { | { | ||||
| for(int k = 0; k < POLYPHONY; ++k) | |||||
| KillNotePos(k); | |||||
| notePool.killAllNotes(); | |||||
| for(int i = 0; i < synth.buffersize; ++i) { | for(int i = 0; i < synth.buffersize; ++i) { | ||||
| partoutl[i] = final_ ? 0.0f : synth.denormalkillbuf[i]; | partoutl[i] = final_ ? 0.0f : synth.denormalkillbuf[i]; | ||||
| partoutr[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 | * Note On Messages | ||||
| */ | */ | ||||
| @@ -378,299 +387,92 @@ void Part::NoteOn(unsigned char note, | |||||
| unsigned char velocity, | unsigned char velocity, | ||||
| int masterkeyshift) | 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)) | if(!Pnoteon || !inRange(note, Pminkey, Pmaxkey)) | ||||
| return; | 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); | setkeylimit(Pkeylimit); | ||||
| } | } | ||||
| @@ -683,17 +485,18 @@ void Part::NoteOff(unsigned char note) //release the key | |||||
| if(!monomemEmpty()) | if(!monomemEmpty()) | ||||
| monomemPop(note); | 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, | void Part::PolyphonicAftertouch(unsigned char note, | ||||
| @@ -709,36 +512,12 @@ void Part::PolyphonicAftertouch(unsigned char note, | |||||
| if(!Ppolymode) // if Poly is off | if(!Ppolymode) // if Poly is off | ||||
| monomem[note].velocity = velocity; // Store this note's velocity. | 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() | void Part::ReleaseSustainedKeys() | ||||
| { | { | ||||
| // Let's call MonoMemRenote() on some conditions: | // 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. | if(monomemBack() != lastnote) // Sustain controller manipulation would cause repeated same note respawn without this check. | ||||
| MonoMemRenote(); // To play most recent still held note. | 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() | 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 | // 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. | unsigned char mmrtempnote = monomemBack(); // Last list element. | ||||
| monomemPop(mmrtempnote); // We remove it, will be added again in NoteOn(...). | 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 | * 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; | int keylimit = Pkeylimit; | ||||
| if(keylimit == 0) | if(keylimit == 0) | ||||
| keylimit = POLYPHONY - 5; | 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; | 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 tmpoutr[synth.buffersize]; | ||||
| float tmpoutl[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) | 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 | //Apply part's effects and mix them | ||||
| for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) { | for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) { | ||||
| if(!Pefxbypass[nefx]) { | if(!Pefxbypass[nefx]) { | ||||
| @@ -1040,8 +759,8 @@ void Part::ComputePartSmps() | |||||
| partoutl[i] *= tmp; | partoutl[i] *= tmp; | ||||
| partoutr[i] *= tmp; | partoutr[i] *= tmp; | ||||
| } | } | ||||
| for(int k = 0; k < POLYPHONY; ++k) | |||||
| KillNotePos(k); | |||||
| notePool.killAllNotes(); | |||||
| monomemClear(); | |||||
| killallnotes = false; | killallnotes = false; | ||||
| for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) | for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) | ||||
| partefx[nefx]->cleanup(); | partefx[nefx]->cleanup(); | ||||
| @@ -1087,9 +806,7 @@ void Part::setkititemstatus(unsigned kititem, bool Penabled_) | |||||
| delete kkit.padpars; | delete kkit.padpars; | ||||
| kkit.Pname[0] = '\0'; | 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 { | else { | ||||
| //All parameters must be NULL in this case | //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) | for(int i=0; i<NUM_PART_EFX; ++i) | ||||
| partefx[i]->kill(); | partefx[i]->kill(); | ||||
| for(int k = 0; k < POLYPHONY; ++k) | |||||
| KillNotePos(k); | |||||
| notePool.killAllNotes(); | |||||
| } | } | ||||
| void Part::monomemPush(char note) | void Part::monomemPush(char note) | ||||
| @@ -1414,3 +1130,19 @@ void Part::getfromXML(XMLwrapper *xml) | |||||
| xml->exitbranch(); | 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 "../globals.h" | ||||
| #include "../Params/Controller.h" | #include "../Params/Controller.h" | ||||
| #include "../Containers/NotePool.h" | |||||
| #include <functional> | #include <functional> | ||||
| @@ -37,7 +38,7 @@ class Part | |||||
| /**Constructor | /**Constructor | ||||
| * @param microtonal_ Pointer to the microtonal object | * @param microtonal_ Pointer to the microtonal object | ||||
| * @param fft_ Pointer to the FFTwrapper*/ | * @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, | const int& gzip_compression, const int& interpolation, | ||||
| Microtonal *microtonal_, FFTwrapper *fft_); | Microtonal *microtonal_, FFTwrapper *fft_); | ||||
| /**Destructor*/ | /**Destructor*/ | ||||
| @@ -97,6 +98,10 @@ class Part | |||||
| SUBnoteParameters *subpars; | SUBnoteParameters *subpars; | ||||
| PADnoteParameters *padpars; | PADnoteParameters *padpars; | ||||
| bool active(void) const; | |||||
| uint8_t sendto(void) const; | |||||
| bool validNote(char note) const; | |||||
| const static rtosc::Ports &ports; | const static rtosc::Ports &ports; | ||||
| } kit[NUM_KIT_ITEMS]; | } kit[NUM_KIT_ITEMS]; | ||||
| @@ -118,6 +123,8 @@ class Part | |||||
| unsigned char Pveloffs; //velocity offset | unsigned char Pveloffs; //velocity offset | ||||
| bool Pnoteon; //if the part receives NoteOn messages | bool Pnoteon; //if the part receives NoteOn messages | ||||
| int Pkitmode; //if the kitmode is enabled | 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 Pdrummode; //if all keys are mapped and the system is 12tET (used for drums) | ||||
| bool Ppolymode; //Part mode - 0=monophonic , 1=polyphonic | bool Ppolymode; //Part mode - 0=monophonic , 1=polyphonic | ||||
| @@ -156,25 +163,22 @@ class Part | |||||
| const static rtosc::Ports &ports; | const static rtosc::Ports &ports; | ||||
| private: | private: | ||||
| void RunNote(unsigned k); | |||||
| void KillNotePos(int pos); | |||||
| void ReleaseNotePos(int pos); | |||||
| void MonoMemRenote(); // MonoMem stuff. | 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. | bool lastlegatomodevalid; // To keep track of previous legatomodevalid. | ||||
| // MonoMem stuff | // MonoMem stuff | ||||
| @@ -194,13 +198,12 @@ class Part | |||||
| store the velocity and masterkeyshift values of a given note (the list only store note values). | 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'.*/ | For example 'monomem[note].velocity' would be the velocity value of the note 'note'.*/ | ||||
| PartNotes partnote[POLYPHONY]; | |||||
| float oldfreq; //this is used for portamento | float oldfreq; //this is used for portamento | ||||
| Microtonal *microtonal; | Microtonal *microtonal; | ||||
| FFTwrapper *fft; | FFTwrapper *fft; | ||||
| Allocator &memory; | Allocator &memory; | ||||
| const SYNTH_T &synth; | const SYNTH_T &synth; | ||||
| const AbsTime &time; | |||||
| const int &gzip_compression, &interpolation; | const int &gzip_compression, &interpolation; | ||||
| }; | }; | ||||
| @@ -30,7 +30,7 @@ static void dummy(const char *, rtosc::RtData&) {} | |||||
| const rtosc::Ports real_preset_ports = | const rtosc::Ports real_preset_ports = | ||||
| { | { | ||||
| {"scan-for-presets:", 0, 0, | {"scan-for-presets:", 0, 0, | ||||
| [](const char *msg, rtosc::RtData &d) { | |||||
| [](const char *, rtosc::RtData &d) { | |||||
| MiddleWare &mw = *(MiddleWare*)d.obj; | MiddleWare &mw = *(MiddleWare*)d.obj; | ||||
| mw.getPresetsStore().scanforpresets(); | mw.getPresetsStore().scanforpresets(); | ||||
| auto &pre = mw.getPresetsStore().presets; | auto &pre = mw.getPresetsStore().presets; | ||||
| @@ -83,7 +83,7 @@ const rtosc::Ports real_preset_ports = | |||||
| assert(false && "bad arguments"); | assert(false && "bad arguments"); | ||||
| }}, | }}, | ||||
| {"clipboard-type:", 0, 0, | {"clipboard-type:", 0, 0, | ||||
| [](const char *msg, rtosc::RtData &d) { | |||||
| [](const char *, rtosc::RtData &d) { | |||||
| const MiddleWare &mw = *(MiddleWare*)d.obj; | const MiddleWare &mw = *(MiddleWare*)d.obj; | ||||
| d.reply(d.loc, "s", mw.getPresetsStore().clipboard.type.c_str()); | 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 | const rtosc::Ports preset_ports | ||||
| { | { | ||||
| {"scan-for-presets:", rDoc("Scan For Presets"), 0, dummy}, | {"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}, | {"clipboard-type:", rDoc("Type Stored In Clipboard"), 0, dummy}, | ||||
| {"delete:s", rDoc("Delete the given preset file"), 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"); | //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 * | * 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)} | {STRINGIFY(name) "::i", rProp(parameter) rMap(min, 0) rMap(max, 127) DOC(__VA_ARGS__), NULL, rParamICb(name)} | ||||
| #define rSelf(type) \ | #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){ \ | [](const char *, rtosc::RtData &d){ \ | ||||
| d.reply(d.loc, "b", sizeof(d.obj), &d.obj);}}\ | d.reply(d.loc, "b", sizeof(d.obj), &d.obj);}}\ | ||||
| #define rPaste \ | #define rPaste \ | ||||
| {"preset-type", rProp(internal), 0, \ | |||||
| {"preset-type:", rProp(internal) rDoc("clipboard type of object"), 0, \ | |||||
| [](const char *, rtosc::RtData &d){ \ | [](const char *, rtosc::RtData &d){ \ | ||||
| rObject *obj = (rObject*)d.obj; \ | rObject *obj = (rObject*)d.obj; \ | ||||
| d.reply(d.loc, "s", obj->type);}},\ | d.reply(d.loc, "s", obj->type);}},\ | ||||
| @@ -10,7 +10,7 @@ | |||||
| This program is distributed in the hope that it will be useful, | This program is distributed in the hope that it will be useful, | ||||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 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) | 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, | 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, | This program is distributed in the hope that it will be useful, | ||||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 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) | 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, | 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, | This program is distributed in the hope that it will be useful, | ||||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 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) | 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, | 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, | This program is distributed in the hope that it will be useful, | ||||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 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) | 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, | 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, "Subvoice vibratto"), | ||||
| rParamZyn(Unison_vibratto_speed, "Subvoice vibratto speed"), | rParamZyn(Unison_vibratto_speed, "Subvoice vibratto speed"), | ||||
| rOption(Unison_invert_phase, rOptions(none, random, 50%, 33%, 25%), "Subvoice Phases"), | 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"), | rParamZyn(PDelay, "Voice Startup Delay"), | ||||
| rToggle(Presonance, "Resonance Enable"), | 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(Poscilphase, "Oscillator Phase"), | ||||
| rParamZyn(PFMoscilphase, "FM Oscillator Phase"), | rParamZyn(PFMoscilphase, "FM Oscillator Phase"), | ||||
| rToggle(Pfilterbypass, "Filter Bypass"), | rToggle(Pfilterbypass, "Filter Bypass"), | ||||
| @@ -108,14 +108,19 @@ static const Ports voicePorts = { | |||||
| //weird stuff for PCoarseDetune | //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; | 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 check if this is accurate or if PCoarseDetune is utilized | ||||
| //TODO do the same for the other engines | //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; | rObject *obj = (rObject *)d.obj; | ||||
| if(!rtosc_narguments(msg)) { | if(!rtosc_narguments(msg)) { | ||||
| @@ -128,7 +133,8 @@ static const Ports voicePorts = { | |||||
| obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; | 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; | rObject *obj = (rObject *)d.obj; | ||||
| if(!rtosc_narguments(msg)) { | if(!rtosc_narguments(msg)) { | ||||
| @@ -143,14 +149,18 @@ static const Ports voicePorts = { | |||||
| }}, | }}, | ||||
| //weird stuff for PCoarseDetune | //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; | 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 check if this is accurate or if PCoarseDetune is utilized | ||||
| //TODO do the same for the other engines | //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; | rObject *obj = (rObject *)d.obj; | ||||
| if(!rtosc_narguments(msg)) { | if(!rtosc_narguments(msg)) { | ||||
| @@ -163,7 +173,8 @@ static const Ports voicePorts = { | |||||
| obj->PFMCoarseDetune = k*1024 + obj->PFMCoarseDetune%1024; | 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; | rObject *obj = (rObject *)d.obj; | ||||
| if(!rtosc_narguments(msg)) { | if(!rtosc_narguments(msg)) { | ||||
| @@ -178,7 +189,8 @@ static const Ports voicePorts = { | |||||
| }}, | }}, | ||||
| //Reader | //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; | rObject *obj = (rObject *)d.obj; | ||||
| d.reply(d.loc, "f", obj->getUnisonFrequencySpreadCents()); | 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"), | rParamZyn(Hrandgrouping, "How randomness is applied to multiple voices using the same oscil"), | ||||
| //weird stuff for PCoarseDetune | //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; | rObject *obj = (rObject *)d.obj; | ||||
| d.reply(d.loc, "f", getdetune(obj->PDetuneType, 0, obj->PDetune)); | 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; | rObject *obj = (rObject *)d.obj; | ||||
| if(!rtosc_narguments(msg)) { | if(!rtosc_narguments(msg)) { | ||||
| @@ -242,7 +256,8 @@ static const Ports globalPorts = { | |||||
| obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; | 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; | rObject *obj = (rObject *)d.obj; | ||||
| if(!rtosc_narguments(msg)) { | if(!rtosc_narguments(msg)) { | ||||
| @@ -279,8 +294,10 @@ ADnoteParameters::ADnoteParameters(const SYNTH_T &synth, FFTwrapper *fft_) | |||||
| fft = 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); | EnableVoice(synth, nvoice); | ||||
| } | |||||
| defaults(); | defaults(); | ||||
| } | } | ||||
| @@ -524,40 +541,6 @@ ADnoteParameters::~ADnoteParameters() | |||||
| KillVoice(nvoice); | 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) | void ADnoteParameters::add2XMLsection(XMLwrapper *xml, int n) | ||||
| { | { | ||||
| int nvoice = n; | int nvoice = n; | ||||
| @@ -29,8 +29,6 @@ | |||||
| enum FMTYPE { | enum FMTYPE { | ||||
| NONE, MORPH, RING_MOD, PHASE_MOD, FREQ_MOD, PITCH_MOD | 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 */ | /* GLOBAL PARAMETERS */ | ||||
| @@ -284,6 +282,8 @@ struct ADnoteVoiceParam { | |||||
| unsigned char PFMAmpEnvelopeEnabled; | unsigned char PFMAmpEnvelopeEnabled; | ||||
| EnvelopeParams *FMAmpEnvelope; | EnvelopeParams *FMAmpEnvelope; | ||||
| unsigned char *GlobalPDetuneType; | |||||
| static const rtosc::Ports &ports; | static const rtosc::Ports &ports; | ||||
| }; | }; | ||||
| @@ -306,8 +306,6 @@ class ADnoteParameters:public PresetsArray | |||||
| float getBandwidthDetuneMultiplier() const; | float getBandwidthDetuneMultiplier() const; | ||||
| float getUnisonFrequencySpreadCents(int nvoice) 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; | static const rtosc::Ports &ports; | ||||
| void defaults(int n); //n is the nvoice | void defaults(int n); //n is the nvoice | ||||
| void add2XMLsection(XMLwrapper *xml, int n); | void add2XMLsection(XMLwrapper *xml, int n); | ||||
| @@ -22,6 +22,7 @@ | |||||
| #include <cmath> | #include <cmath> | ||||
| #include <cstdlib> | #include <cstdlib> | ||||
| #include <cassert> | |||||
| #include <rtosc/ports.h> | #include <rtosc/ports.h> | ||||
| #include <rtosc/port-sugar.h> | #include <rtosc/port-sugar.h> | ||||
| @@ -131,7 +132,13 @@ void EnvelopeParams::paste(const EnvelopeParams &ep) | |||||
| //Avoid undefined behavior | //Avoid undefined behavior | ||||
| if(&ep == this) | if(&ep == this) | ||||
| return; | 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 | float EnvelopeParams::getdt(char i) const | ||||
| @@ -105,13 +105,14 @@ const rtosc::Ports FilterParams::ports = { | |||||
| if(rtosc_narguments(msg)) | if(rtosc_narguments(msg)) | ||||
| rChangeCb; | 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; | FilterParams *obj = (FilterParams *) d.obj; | ||||
| d.reply(d.loc, "f", obj->getcenterfreq()); | 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; | FilterParams *obj = (FilterParams *) d.obj; | ||||
| d.reply(d.loc, "f", obj->getoctavesfreq()); | d.reply(d.loc, "f", obj->getoctavesfreq()); | ||||
| }}, | }}, | ||||
| @@ -49,8 +49,6 @@ static const rtosc::Ports _ports = { | |||||
| const rtosc::Ports &LFOParams::ports = _ports; | const rtosc::Ports &LFOParams::ports = _ports; | ||||
| int LFOParams::time; | |||||
| LFOParams::LFOParams() | LFOParams::LFOParams() | ||||
| { | { | ||||
| Dfreq = 64; | Dfreq = 64; | ||||
| @@ -61,7 +59,6 @@ LFOParams::LFOParams() | |||||
| Ddelay = 0; | Ddelay = 0; | ||||
| Dcontinous = 0; | Dcontinous = 0; | ||||
| fel = 0; | fel = 0; | ||||
| time = 0; | |||||
| defaults(); | defaults(); | ||||
| } | } | ||||
| @@ -94,7 +91,6 @@ LFOParams::LFOParams(char Pfreq_, | |||||
| Ddelay = Pdelay_; | Ddelay = Pdelay_; | ||||
| Dcontinous = Pcontinous_; | Dcontinous = Pcontinous_; | ||||
| fel = fel_; | fel = fel_; | ||||
| time = 0; | |||||
| defaults(); | defaults(); | ||||
| } | } | ||||
| @@ -28,6 +28,14 @@ | |||||
| class XMLwrapper; | 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 | class LFOParams:public Presets | ||||
| { | { | ||||
| public: | public: | ||||
| @@ -60,7 +68,6 @@ class LFOParams:public Presets | |||||
| unsigned char Pstretch; /**<how the LFO is "stretched" according the note frequency (64=no stretch)*/ | 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) | 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; | static const rtosc::Ports &ports; | ||||
| private: | private: | ||||
| @@ -90,8 +90,6 @@ static const rtosc::Ports PADnotePorts = | |||||
| PC(fixedfreq), | PC(fixedfreq), | ||||
| PC(fixedfreqET), | PC(fixedfreqET), | ||||
| //TODO detune, coarse detune | |||||
| PC(DetuneType), | |||||
| PC(Stereo), | PC(Stereo), | ||||
| PC(Panning), | PC(Panning), | ||||
| PC(AmpVelocityScaleFunction), | PC(AmpVelocityScaleFunction), | ||||
| @@ -115,7 +113,7 @@ static const rtosc::Ports PADnotePorts = | |||||
| d.reply(d.loc, "i", p->Pbandwidth); | d.reply(d.loc, "i", p->Pbandwidth); | ||||
| }}}, | }}}, | ||||
| {"bandwidthvalue:", NULL, NULL, | |||||
| {"bandwidthvalue:", rMap(unit, cents) rDoc("Get Bandwidth"), NULL, | |||||
| [](const char *, rtosc::RtData &d) { | [](const char *, rtosc::RtData &d) { | ||||
| PADnoteParameters *p = ((PADnoteParameters*)d.obj); | PADnoteParameters *p = ((PADnoteParameters*)d.obj); | ||||
| d.reply(d.loc, "f", p->setPbandwidth(p->Pbandwidth)); | 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, "b", n*sizeof(float), tmp); | ||||
| d.reply(d.loc, "i", realbw); | d.reply(d.loc, "i", realbw); | ||||
| delete[] tmp;}}, | 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) | [](const char *m, rtosc::RtData &d) | ||||
| { | { | ||||
| PADnoteParameters *p = (PADnoteParameters*)d.obj; | PADnoteParameters *p = (PADnoteParameters*)d.obj; | ||||
| @@ -157,12 +155,14 @@ static const rtosc::Ports PADnotePorts = | |||||
| //XXX TODO memory managment (deallocation of smp buffer) | //XXX TODO memory managment (deallocation of smp buffer) | ||||
| }}, | }}, | ||||
| //weird stuff for PCoarseDetune | //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; | PADnoteParameters *obj = (PADnoteParameters *)d.obj; | ||||
| d.reply(d.loc, "f", getdetune(obj->PDetuneType, 0, obj->PDetune)); | 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; | PADnoteParameters *obj = (PADnoteParameters *)d.obj; | ||||
| if(!rtosc_narguments(msg)) { | if(!rtosc_narguments(msg)) { | ||||
| @@ -175,7 +175,8 @@ static const rtosc::Ports PADnotePorts = | |||||
| obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; | 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; | PADnoteParameters *obj = (PADnoteParameters *)d.obj; | ||||
| if(!rtosc_narguments(msg)) { | if(!rtosc_narguments(msg)) { | ||||
| @@ -93,7 +93,8 @@ static const rtosc::Ports SUBnotePorts = { | |||||
| d.reply(d.loc, "f", getdetune(obj->PDetuneType, 0, obj->PDetune)); | d.reply(d.loc, "f", getdetune(obj->PDetuneType, 0, obj->PDetune)); | ||||
| }}, | }}, | ||||
| //weird stuff for PCoarseDetune | //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; | SUBnoteParameters *obj = (SUBnoteParameters *)d.obj; | ||||
| if(!rtosc_narguments(msg)) { | if(!rtosc_narguments(msg)) { | ||||
| @@ -106,7 +107,8 @@ static const rtosc::Ports SUBnotePorts = { | |||||
| obj->PCoarseDetune = k*1024 + obj->PCoarseDetune%1024; | 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; | SUBnoteParameters *obj = (SUBnoteParameters *)d.obj; | ||||
| if(!rtosc_narguments(msg)) { | if(!rtosc_narguments(msg)) { | ||||
| @@ -49,7 +49,6 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) | |||||
| NoteEnabled = ON; | NoteEnabled = ON; | ||||
| basefreq = spars.frequency; | basefreq = spars.frequency; | ||||
| velocity = spars.velocity; | velocity = spars.velocity; | ||||
| time = 0.0f; | |||||
| stereo = pars.GlobalPar.PStereo; | stereo = pars.GlobalPar.PStereo; | ||||
| NoteGlobalPar.Detune = getdetune(pars.GlobalPar.PDetuneType, | NoteGlobalPar.Detune = getdetune(pars.GlobalPar.PDetuneType, | ||||
| @@ -87,6 +86,9 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) | |||||
| NoteGlobalPar.Punch.Enabled = 0; | NoteGlobalPar.Punch.Enabled = 0; | ||||
| for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { | 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()); | pars.VoicePar[nvoice].OscilSmp->newrandseed(prng()); | ||||
| NoteVoicePar[nvoice].OscilSmp = NULL; | NoteVoicePar[nvoice].OscilSmp = NULL; | ||||
| NoteVoicePar[nvoice].FMSmp = NULL; | NoteVoicePar[nvoice].FMSmp = NULL; | ||||
| @@ -106,6 +108,10 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) | |||||
| if(unison < 1) | if(unison < 1) | ||||
| 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 | //compute unison | ||||
| unison_size[nvoice] = unison; | unison_size[nvoice] = unison; | ||||
| @@ -397,6 +403,13 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) | |||||
| initparameters(); | 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 | // ADlegatonote: This function is (mostly) a copy of ADnote(...) and | ||||
| // initparameters() stuck together with some lines removed so that it | // initparameters() stuck together with some lines removed so that it | ||||
| // only alter the already playing note (to perform legato). It is | // only alter the already playing note (to perform legato). It is | ||||
| @@ -711,6 +724,7 @@ void ADnote::initparameters() | |||||
| // Global Parameters | // Global Parameters | ||||
| NoteGlobalPar.initparameters(pars.GlobalPar, synth, | NoteGlobalPar.initparameters(pars.GlobalPar, synth, | ||||
| time, | |||||
| memory, basefreq, velocity, | memory, basefreq, velocity, | ||||
| stereo); | stereo); | ||||
| @@ -753,7 +767,7 @@ void ADnote::initparameters() | |||||
| } | } | ||||
| if(param.PAmpLfoEnabled) { | 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(); | newamplitude[nvoice] *= vce.AmpLfo->amplfoout(); | ||||
| } | } | ||||
| @@ -762,7 +776,7 @@ void ADnote::initparameters() | |||||
| vce.FreqEnvelope = memory.alloc<Envelope>(*param.FreqEnvelope, basefreq, synth.dt()); | vce.FreqEnvelope = memory.alloc<Envelope>(*param.FreqEnvelope, basefreq, synth.dt()); | ||||
| if(param.PFreqLfoEnabled) | 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 */ | /* Voice Filter Parameters Init */ | ||||
| if(param.PFilterEnabled != 0) { | if(param.PFilterEnabled != 0) { | ||||
| @@ -776,7 +790,7 @@ void ADnote::initparameters() | |||||
| vce.FilterEnvelope = memory.alloc<Envelope>(*param.FilterEnvelope, basefreq, synth.dt()); | vce.FilterEnvelope = memory.alloc<Envelope>(*param.FilterEnvelope, basefreq, synth.dt()); | ||||
| if(param.PFilterLfoEnabled) | if(param.PFilterLfoEnabled) | ||||
| vce.FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, synth.dt()); | |||||
| vce.FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time); | |||||
| vce.FilterFreqTracking = | vce.FilterFreqTracking = | ||||
| param.VoiceFilter->getfreqtracking(basefreq); | 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 | * Computes the Noise | ||||
| */ | */ | ||||
| inline void ADnote::ComputeVoiceNoise(int nvoice) | |||||
| inline void ADnote::ComputeVoiceWhiteNoise(int nvoice) | |||||
| { | { | ||||
| for(int k = 0; k < unison_size[nvoice]; ++k) { | for(int k = 0; k < unison_size[nvoice]; ++k) { | ||||
| float *tw = tmpwave_unison[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) | if((NoteVoicePar[nvoice].Enabled != ON) | ||||
| || (NoteVoicePar[nvoice].DelayTicks > 0)) | || (NoteVoicePar[nvoice].DelayTicks > 0)) | ||||
| continue; | 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 | // Voice Processing | ||||
| @@ -1772,15 +1811,16 @@ void ADnote::Global::kill(Allocator &memory) | |||||
| void ADnote::Global::initparameters(const ADnoteGlobalParam ¶m, | void ADnote::Global::initparameters(const ADnoteGlobalParam ¶m, | ||||
| const SYNTH_T &synth, | const SYNTH_T &synth, | ||||
| const AbsTime &time, | |||||
| class Allocator &memory, | class Allocator &memory, | ||||
| float basefreq, float velocity, | float basefreq, float velocity, | ||||
| bool stereo) | bool stereo) | ||||
| { | { | ||||
| FreqEnvelope = memory.alloc<Envelope>(*param.FreqEnvelope, basefreq, synth.dt()); | 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()); | 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 | Volume = 4.0f * powf(0.1f, 3.0f * (1.0f - param.PVolume / 96.0f)) //-60 dB .. 0 dB | ||||
| * VelF(velocity, param.PAmpVelocityScaleFunction); //sensing | * VelF(velocity, param.PAmpVelocityScaleFunction); //sensing | ||||
| @@ -1794,7 +1834,7 @@ void ADnote::Global::initparameters(const ADnoteGlobalParam ¶m, | |||||
| GlobalFilterR = NULL; | GlobalFilterR = NULL; | ||||
| FilterEnvelope = memory.alloc<Envelope>(*param.FilterEnvelope, basefreq, synth.dt()); | 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(); | FilterQ = param.GlobalFilter->getq(); | ||||
| FilterFreqTracking = param.GlobalFilter->getfreqtracking(basefreq); | FilterFreqTracking = param.GlobalFilter->getfreqtracking(basefreq); | ||||
| } | } | ||||
| @@ -53,6 +53,8 @@ class ADnote:public SynthNote | |||||
| int noteout(float *outl, float *outr); | int noteout(float *outl, float *outr); | ||||
| void releasekey(); | void releasekey(); | ||||
| int finished() const; | int finished() const; | ||||
| virtual SynthNote *cloneLegato(void) override; | |||||
| private: | private: | ||||
| /**Changes the frequency of an oscillator. | /**Changes the frequency of an oscillator. | ||||
| @@ -97,7 +99,8 @@ class ADnote:public SynthNote | |||||
| inline void ComputeVoiceOscillatorPitchModulation(int nvoice); | inline void ComputeVoiceOscillatorPitchModulation(int nvoice); | ||||
| /**Generate Noise Samples for Voice*/ | /**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"*/ | /**Fadein in a way that removes clicks but keep sound "punchy"*/ | ||||
| inline void fadein(float *smps) const; | inline void fadein(float *smps) const; | ||||
| @@ -119,6 +122,7 @@ class ADnote:public SynthNote | |||||
| void kill(Allocator &memory); | void kill(Allocator &memory); | ||||
| void initparameters(const ADnoteGlobalParam ¶m, | void initparameters(const ADnoteGlobalParam ¶m, | ||||
| const SYNTH_T &synth, | const SYNTH_T &synth, | ||||
| const AbsTime &time, | |||||
| class Allocator &memory, | class Allocator &memory, | ||||
| float basefreq, float velocity, | float basefreq, float velocity, | ||||
| bool stereo); | bool stereo); | ||||
| @@ -246,8 +250,8 @@ class ADnote:public SynthNote | |||||
| /* INTERNAL VALUES OF THE NOTE AND OF THE VOICES */ | /* 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 | //the size of unison for a single voice | ||||
| int unison_size[NUM_VOICES]; | int unison_size[NUM_VOICES]; | ||||
| @@ -28,8 +28,10 @@ | |||||
| #include <cstdio> | #include <cstdio> | ||||
| #include <cmath> | #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; | int stretch = lfopars.Pstretch; | ||||
| if(stretch == 0) | if(stretch == 0) | ||||
| @@ -38,24 +40,24 @@ LFO::LFO(const LFOParams &lfopars, float basefreq, float dt_) | |||||
| //max 2x/octave | //max 2x/octave | ||||
| const float lfostretch = powf(basefreq / 440.0f, (stretch - 64.0f) / 63.0f); | 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; | (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.Pcontinous) { | ||||
| if(lfopars.Pstartphase == 0) | if(lfopars.Pstartphase == 0) | ||||
| x = RND; | |||||
| phase = RND; | |||||
| else | 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 { | 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...) | //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); | 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 | break; //in octave | ||||
| default: | default: | ||||
| lfointensity = powf(2, lfopars.Pintensity / 127.0f * 11.0f) - 1.0f; //in centi | 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; | break; | ||||
| } | } | ||||
| amp1 = (1 - lfornd) + lfornd * RND; | amp1 = (1 - lfornd) + lfornd * RND; | ||||
| amp2 = (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; | 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::~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 | 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; | break; | ||||
| case 2: //LFO_SQUARE | |||||
| if(x < 0.5f) | |||||
| out = -1; | |||||
| case LFO_SQUARE: | |||||
| if(phase < 0.5f) | |||||
| return -1; | |||||
| else | else | ||||
| out = 1; | |||||
| break; | |||||
| case 3: //LFO_RAMPUP | |||||
| out = (x - 0.5f) * 2.0f; | |||||
| return 1; | |||||
| break; | 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 | else | ||||
| out *= lfointensity * amp2; | 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; | return out; | ||||
| } | } | ||||
| @@ -164,9 +150,9 @@ float LFO::amplfoout() | |||||
| } | } | ||||
| void LFO::computenextincrnd() | |||||
| void LFO::computeNextFreqRnd() | |||||
| { | { | ||||
| if(freqrndenabled == 0) | |||||
| if(deterministic) | |||||
| return; | return; | ||||
| incrnd = nextincrnd; | incrnd = nextincrnd; | ||||
| nextincrnd = powf(0.5f, lfofreqrnd) + RND * (powf(2.0f, lfofreqrnd) - 1.0f); | nextincrnd = powf(0.5f, lfofreqrnd) + RND * (powf(2.0f, lfofreqrnd) - 1.0f); | ||||
| @@ -24,8 +24,9 @@ | |||||
| #define LFO_H | #define LFO_H | ||||
| #include "../globals.h" | #include "../globals.h" | ||||
| #include "../Misc/Time.h" | |||||
| /**Class for creating Low Frequency Ocillators*/ | |||||
| /**Class for creating Low Frequency Oscillators*/ | |||||
| class LFO | class LFO | ||||
| { | { | ||||
| public: | public: | ||||
| @@ -34,24 +35,36 @@ class LFO | |||||
| * @param lfopars pointer to a LFOParams object | * @param lfopars pointer to a LFOParams object | ||||
| * @param basefreq base frequency of LFO | * @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(); | ~LFO(); | ||||
| float lfoout(); | float lfoout(); | ||||
| float amplfoout(); | float amplfoout(); | ||||
| private: | 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; | float lfointensity; | ||||
| //Amount Randomness | |||||
| float lfornd, lfofreqrnd; | 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 | #endif | ||||
| @@ -44,38 +44,96 @@ pthread_t main_thread; | |||||
| const rtosc::Ports OscilGen::ports = { | const rtosc::Ports OscilGen::ports = { | ||||
| rSelf(OscilGen), | rSelf(OscilGen), | ||||
| rPaste, | 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(filterpar1), | ||||
| PC(filterpar2), | 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(satype), | ||||
| PC(sapar), | |||||
| rParamZyn(Psapar, "Spectral Adjustment Parameter"), | |||||
| rParamI(Pharmonicshift, "Amount of shift on harmonics"), | rParamI(Pharmonicshift, "Amount of shift on harmonics"), | ||||
| rToggle(Pharmonicshiftfirst, "If harmonics are shifted before waveshaping/filtering"), | 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 | //FIXME realtime parameters lurking below | ||||
| PC(rand), | 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 | //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) { | NULL, [](const char *m, rtosc::RtData &d) { | ||||
| const char *mm = m; | const char *mm = m; | ||||
| while(*mm && !isdigit(*mm)) ++mm; | while(*mm && !isdigit(*mm)) ++mm; | ||||
| @@ -86,7 +144,7 @@ const rtosc::Ports OscilGen::ports = { | |||||
| phase = rtosc_argument(m,0).i; | phase = rtosc_argument(m,0).i; | ||||
| }}, | }}, | ||||
| //TODO update to rArray and test | //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) { | NULL, [](const char *m, rtosc::RtData &d) { | ||||
| //printf("I'm at '%s'\n", d.loc); | //printf("I'm at '%s'\n", d.loc); | ||||
| const char *mm = m; | const char *mm = m; | ||||
| @@ -153,6 +211,10 @@ const rtosc::Ports OscilGen::ports = { | |||||
| NULL, [](const char *, rtosc::RtData &d) { | NULL, [](const char *, rtosc::RtData &d) { | ||||
| ((OscilGen*)d.obj)->convert2sine(); | ((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"), | {"prepare:b", rProp(internal) rProp(non-realtime) rProp(pointer) rDoc("Sets prepared fft data"), | ||||
| NULL, [](const char *m, rtosc::RtData &d) { | NULL, [](const char *m, rtosc::RtData &d) { | ||||
| //fprintf(stderr, "prepare:b got a message from '%s'\n", m); | //fprintf(stderr, "prepare:b got a message from '%s'\n", m); | ||||
| @@ -385,64 +447,45 @@ void OscilGen::convert2sine() | |||||
| */ | */ | ||||
| void OscilGen::getbasefunction(float *smps) | void OscilGen::getbasefunction(float *smps) | ||||
| { | { | ||||
| int i; | |||||
| float par = (Pbasefuncpar + 0.5f) / 128.0f; | float par = (Pbasefuncpar + 0.5f) / 128.0f; | ||||
| if(Pbasefuncpar == 64) | if(Pbasefuncpar == 64) | ||||
| par = 0.5f; | 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) { | switch(Pbasefuncmodulation) { | ||||
| case 1: | 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; | break; | ||||
| case 2: | 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; | break; | ||||
| case 3: | 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; | break; | ||||
| } | } | ||||
| base_func func = getBaseFunction(Pcurrentbasefunc); | 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; | float t = i * 1.0f / synth.oscilsize; | ||||
| switch(Pbasefuncmodulation) { | 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; | 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; | 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; | break; | ||||
| } | } | ||||
| @@ -652,12 +695,11 @@ void OscilGen::spectrumadjust(fft_t *freqs) | |||||
| break; | break; | ||||
| } | } | ||||
| normalize(freqs, synth.oscilsize); | normalize(freqs, synth.oscilsize); | ||||
| for(int i = 0; i < synth.oscilsize / 2; ++i) { | 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) { | switch(Psatype) { | ||||
| case 1: | case 1: | ||||
| @@ -783,13 +825,12 @@ void OscilGen::prepare(fft_t *freqs) | |||||
| if(Pharmonicshiftfirst != 0) | if(Pharmonicshiftfirst != 0) | ||||
| shiftharmonics(freqs); | shiftharmonics(freqs); | ||||
| if(Pfilterbeforews == 0) { | |||||
| waveshape(freqs); | |||||
| oscilfilter(freqs); | |||||
| } | |||||
| else { | |||||
| if(Pfilterbeforews) { | |||||
| oscilfilter(freqs); | oscilfilter(freqs); | ||||
| waveshape(freqs); | waveshape(freqs); | ||||
| } else { | |||||
| waveshape(freqs); | |||||
| oscilfilter(freqs); | |||||
| } | } | ||||
| modulation(freqs); | modulation(freqs); | ||||
| @@ -876,9 +917,9 @@ void OscilGen::adaptiveharmonicpostprocess(fft_t *f, int size) | |||||
| if(Padaptiveharmonics == 2) { //2n+1 | if(Padaptiveharmonics == 2) { //2n+1 | ||||
| for(int i = 0; i < size; ++i) | for(int i = 0; i < size; ++i) | ||||
| if((i % 2) == 0) | 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 nh = (Padaptiveharmonics - 3) / 2 + 2; | ||||
| int sub_vs_add = (Padaptiveharmonics - 3) % 2; | int sub_vs_add = (Padaptiveharmonics - 3) % 2; | ||||
| if(sub_vs_add == 0) { | if(sub_vs_add == 0) { | ||||
| @@ -84,7 +84,7 @@ class OscilGen:public Presets | |||||
| unsigned char Pwaveshaping, Pwaveshapingfunction; | unsigned char Pwaveshaping, Pwaveshapingfunction; | ||||
| unsigned char Pfiltertype, Pfilterpar1, Pfilterpar2; | unsigned char Pfiltertype, Pfilterpar1, Pfilterpar2; | ||||
| unsigned char Pfilterbeforews; | |||||
| bool Pfilterbeforews; | |||||
| unsigned char Psatype, Psapar; //spectrum adjust | unsigned char Psatype, Psapar; //spectrum adjust | ||||
| int Pharmonicshift; //how the harmonics are shifted | int Pharmonicshift; //how the harmonics are shifted | ||||
| @@ -28,7 +28,7 @@ | |||||
| #include "../Params/FilterParams.h" | #include "../Params/FilterParams.h" | ||||
| #include "../Misc/Util.h" | #include "../Misc/Util.h" | ||||
| PADnote::PADnote(PADnoteParameters *parameters, | |||||
| PADnote::PADnote(const PADnoteParameters *parameters, | |||||
| SynthParams pars, const int& interpolation) | SynthParams pars, const int& interpolation) | ||||
| :SynthNote(pars), pars(*parameters), interpolation(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) | ((powf(10, 1.5f * pars.PPunchStrength / 127.0f) - 1.0f) | ||||
| * VelF(velocity, | * VelF(velocity, | ||||
| pars.PPunchVelocitySensing)); | pars.PPunchVelocitySensing)); | ||||
| float time = | |||||
| const float time = | |||||
| powf(10, 3.0f * pars.PPunchTime / 127.0f) / 10000.0f; //0.1f .. 100 ms | 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 | NoteGlobalPar.Punch.dt = 1.0f | ||||
| / (time * synth.samplerate_f * stretch); | / (time * synth.samplerate_f * stretch); | ||||
| } | } | ||||
| @@ -131,10 +131,10 @@ void PADnote::setup(float freq, | |||||
| NoteGlobalPar.Punch.Enabled = 0; | NoteGlobalPar.Punch.Enabled = 0; | ||||
| NoteGlobalPar.FreqEnvelope = memory.alloc<Envelope>(*pars.FreqEnvelope, basefreq, synth.dt()); | 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.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 | NoteGlobalPar.Volume = 4.0f | ||||
| @@ -154,7 +154,7 @@ void PADnote::setup(float freq, | |||||
| synth.samplerate, synth.buffersize); | synth.samplerate, synth.buffersize); | ||||
| NoteGlobalPar.FilterEnvelope = memory.alloc<Envelope>(*pars.FilterEnvelope, basefreq, synth.dt()); | 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.FilterQ = pars.GlobalFilter->getq(); | ||||
| NoteGlobalPar.FilterFreqTracking = pars.GlobalFilter->getfreqtracking( | 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) | void PADnote::legatonote(LegatoParams pars) | ||||
| { | { | ||||
| // Manage legato stuff | // Manage legato stuff | ||||
| @@ -30,10 +30,11 @@ | |||||
| class PADnote:public SynthNote | class PADnote:public SynthNote | ||||
| { | { | ||||
| public: | public: | ||||
| PADnote(PADnoteParameters *parameters, SynthParams pars, | |||||
| PADnote(const PADnoteParameters *parameters, SynthParams pars, | |||||
| const int &interpolation); | const int &interpolation); | ||||
| ~PADnote(); | ~PADnote(); | ||||
| SynthNote *cloneLegato(void); | |||||
| void legatonote(LegatoParams pars); | void legatonote(LegatoParams pars); | ||||
| int noteout(float *outl, float *outr); | int noteout(float *outl, float *outr); | ||||
| @@ -44,7 +44,7 @@ const rtosc::Ports Resonance::ports = { | |||||
| rAction(smooth, "Smooth out frequency response"), | rAction(smooth, "Smooth out frequency response"), | ||||
| rAction(zero, "Reset frequency response"), | rAction(zero, "Reset frequency response"), | ||||
| //UI Value listeners | //UI Value listeners | ||||
| {"centerfreq:", rDoc("Get center frequency"), NULL, | |||||
| {"centerfreq:", rDoc("Get center frequency") rMap(unit, Hz), NULL, | |||||
| [](const char *, RtData &d) | [](const char *, RtData &d) | ||||
| {d.reply(d.loc, "f", ((rObject*)d.obj)->getcenterfreq());}}, | {d.reply(d.loc, "f", ((rObject*)d.obj)->getcenterfreq());}}, | ||||
| {"octavesfreq:", rDoc("Get center freq of graph"), NULL, | {"octavesfreq:", rDoc("Get center freq of graph"), NULL, | ||||
| @@ -33,7 +33,7 @@ | |||||
| #include "../Misc/Util.h" | #include "../Misc/Util.h" | ||||
| #include "../Misc/Allocator.h" | #include "../Misc/Allocator.h" | ||||
| SUBnote::SUBnote(SUBnoteParameters *parameters, SynthParams &spars) | |||||
| SUBnote::SUBnote(const SUBnoteParameters *parameters, SynthParams &spars) | |||||
| :SynthNote(spars), pars(*parameters) | :SynthNote(spars), pars(*parameters) | ||||
| { | { | ||||
| NoteEnabled = ON; | NoteEnabled = ON; | ||||
| @@ -46,6 +46,7 @@ void SUBnote::setup(float freq, | |||||
| int midinote, | int midinote, | ||||
| bool legato) | bool legato) | ||||
| { | { | ||||
| this->velocity = velocity; | |||||
| portamento = portamento_; | portamento = portamento_; | ||||
| NoteEnabled = ON; | NoteEnabled = ON; | ||||
| volume = powf(0.1f, 3.0f * (1.0f - pars.PVolume / 96.0f)); //-60 dB .. 0 dB | 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; | 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) | void SUBnote::legatonote(LegatoParams pars) | ||||
| { | { | ||||
| // Manage legato stuff | // Manage legato stuff | ||||
| @@ -30,9 +30,10 @@ | |||||
| class SUBnote:public SynthNote | class SUBnote:public SynthNote | ||||
| { | { | ||||
| public: | public: | ||||
| SUBnote(SUBnoteParameters *parameters, SynthParams &pars); | |||||
| SUBnote(const SUBnoteParameters *parameters, SynthParams &pars); | |||||
| ~SUBnote(); | ~SUBnote(); | ||||
| SynthNote *cloneLegato(void); | |||||
| void legatonote(LegatoParams pars); | void legatonote(LegatoParams pars); | ||||
| int noteout(float *outl, float *outr); //note output,return 0 if the note is finished | 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; | int oldpitchwheel, oldbandwidth; | ||||
| float globalfiltercenterq; | float globalfiltercenterq; | ||||
| float velocity; | |||||
| }; | }; | ||||
| #endif | #endif | ||||
| @@ -3,9 +3,9 @@ | |||||
| #include <cstring> | #include <cstring> | ||||
| SynthNote::SynthNote(SynthParams &pars) | 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, | SynthNote::Legato::Legato(const SYNTH_T &synth_, float freq, float vel, int port, | ||||
| @@ -28,8 +28,9 @@ class Controller; | |||||
| struct SynthParams | struct SynthParams | ||||
| { | { | ||||
| Allocator &memory; //Memory Allocator for the Note to use | Allocator &memory; //Memory Allocator for the Note to use | ||||
| Controller &ctl; | |||||
| const Controller &ctl; | |||||
| const SYNTH_T &synth; | const SYNTH_T &synth; | ||||
| const AbsTime &time; | |||||
| float frequency; //Note base frequency | float frequency; //Note base frequency | ||||
| float velocity; //Velocity of the Note | float velocity; //Velocity of the Note | ||||
| bool portamento;//True if portamento is used for this note | bool portamento;//True if portamento is used for this note | ||||
| @@ -65,8 +66,14 @@ class SynthNote | |||||
| virtual int finished() const = 0; | virtual int finished() const = 0; | ||||
| virtual void legatonote(LegatoParams pars) = 0; | virtual void legatonote(LegatoParams pars) = 0; | ||||
| virtual SynthNote *cloneLegato(void) = 0; | |||||
| /* For polyphonic aftertouch needed */ | /* For polyphonic aftertouch needed */ | ||||
| void setVelocity(float velocity_); | void setVelocity(float velocity_); | ||||
| //Realtime Safe Memory Allocator For notes | |||||
| class Allocator &memory; | |||||
| protected: | protected: | ||||
| // Legato transitions | // Legato transitions | ||||
| class Legato | class Legato | ||||
| @@ -87,6 +94,7 @@ class SynthNote | |||||
| int length; | int length; | ||||
| float m, step; | float m, step; | ||||
| } fade; | } fade; | ||||
| public: | |||||
| struct { // Note parameters | struct { // Note parameters | ||||
| float freq, vel; | float freq, vel; | ||||
| bool portamento; | bool portamento; | ||||
| @@ -103,10 +111,9 @@ class SynthNote | |||||
| void setDecounter(int decounter_) {decounter = decounter_; } | void setDecounter(int decounter_) {decounter = decounter_; } | ||||
| } legato; | } legato; | ||||
| //Realtime Safe Memory Allocator For notes | |||||
| class Allocator &memory; | |||||
| const Controller &ctl; | const Controller &ctl; | ||||
| const SYNTH_T &synth; | const SYNTH_T &synth; | ||||
| const AbsTime &time; | |||||
| }; | }; | ||||
| #endif | #endif | ||||
| @@ -70,12 +70,21 @@ decl {\#include "OscilGenUI.h"} {public local | |||||
| decl {\#include "PresetsUI.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} | class ADvoicelistitem {open : {public Fl_Osc_Group} | ||||
| } { | } { | ||||
| Function {make_window()} {open private | Function {make_window()} {open private | ||||
| } { | } { | ||||
| Fl_Window ADnoteVoiceListItem {open | 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 | class Fl_Osc_Group visible | ||||
| } { | } { | ||||
| Fl_Box {} { | Fl_Box {} { | ||||
| @@ -83,61 +92,61 @@ class ADvoicelistitem {open : {public Fl_Osc_Group} | |||||
| code0 {ADnoteVoiceListItem->base = loc;} | code0 {ADnoteVoiceListItem->base = loc;} | ||||
| } | } | ||||
| Fl_Group voicelistitemgroup {open | Fl_Group voicelistitemgroup {open | ||||
| private xywh {50 0 570 25} | |||||
| private xywh {0 0 670 25} | |||||
| class Fl_Osc_Group | class Fl_Osc_Group | ||||
| } { | } { | ||||
| Fl_Value_Slider voicevolume { | 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");} | code1 {o->init("PVolume");} | ||||
| class Fl_Osc_VSlider | class Fl_Osc_VSlider | ||||
| } | } | ||||
| Fl_Check_Button voiceresonanceenabled { | 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");} | code0 {o->init("Presonance");} | ||||
| class Fl_Osc_Check | class Fl_Osc_Check | ||||
| } | } | ||||
| Fl_Value_Slider voicelfofreq { | 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');} | code0 {o->init("FreqLfo/Pintensity", 'i');} | ||||
| class Fl_Osc_Slider | |||||
| class Fl_Osc_VSlider | |||||
| } | } | ||||
| Fl_Dial voicepanning { | 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");} | code0 {o->init("PPanning");} | ||||
| class Fl_Osc_Dial | class Fl_Osc_Dial | ||||
| } | } | ||||
| Fl_Group voiceoscil {open | 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/";} | code0 {voiceoscil->ext = "OscilSmp/";} | ||||
| code1 {oscil=new Fl_Oscilloscope(o->x(),o->y(),o->w(),o->h(),"");} | code1 {oscil=new Fl_Oscilloscope(o->x(),o->y(),o->w(),o->h(),"");} | ||||
| code2 {oscil->init(false);} | code2 {oscil->init(false);} | ||||
| class Fl_Osc_Group | class Fl_Osc_Group | ||||
| } {} | } {} | ||||
| Fl_Value_Output detunevalueoutput { | 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");} | code0 {o->init("detunevalue");} | ||||
| class Fl_Osc_Output | class Fl_Osc_Output | ||||
| } | } | ||||
| Fl_Slider voicedetune { | Fl_Slider voicedetune { | ||||
| callback {detunevalueoutput->update();} | 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');} | code0 {o->init("PDetune",'i');} | ||||
| class Fl_Osc_Slider | class Fl_Osc_Slider | ||||
| } | } | ||||
| Fl_Box noiselabel { | |||||
| Fl_Box whitenoiselabel { | |||||
| label N | 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 { | Fl_Check_Button noisehack { | ||||
| callback {if (o->value()==0) { | callback {if (o->value()==0) { | ||||
| noiselabel->hide(); | |||||
| whitenoiselabel->hide(); | |||||
| voiceresonanceenabled->activate(); | voiceresonanceenabled->activate(); | ||||
| detunevalueoutput->activate(); | detunevalueoutput->activate(); | ||||
| voicedetune->activate(); | voicedetune->activate(); | ||||
| voicelfofreq->activate(); | voicelfofreq->activate(); | ||||
| voiceoscil->activate(); | voiceoscil->activate(); | ||||
| } else { | } else { | ||||
| noiselabel->show(); | |||||
| whitenoiselabel->show(); | |||||
| voiceresonanceenabled->deactivate(); | voiceresonanceenabled->deactivate(); | ||||
| detunevalueoutput->deactivate(); | detunevalueoutput->deactivate(); | ||||
| voicedetune->deactivate(); | voicedetune->deactivate(); | ||||
| @@ -159,6 +168,21 @@ o->redraw();} | |||||
| code1 {o->init("Enabled");} | code1 {o->init("Enabled");} | ||||
| class Fl_Osc_Check | 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 | 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 | label Vol | ||||
| tooltip Volume xywh {540 80 160 15} type {Horz Knob} box NO_BOX labelsize 11 align 8 maximum 127 step 1 | 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');} | code0 {o->init("PFMVolume", 'i');} | ||||
| class Fl_Osc_Slider | |||||
| class Fl_Osc_VSlider | |||||
| } | } | ||||
| Fl_Value_Slider {} { | Fl_Value_Slider {} { | ||||
| label {V.Sns} | label {V.Sns} | ||||
| @@ -309,7 +333,7 @@ o->redraw();} | |||||
| label {F.Damp} | 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 | 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');} | code0 {o->init("PFMVolumeDamp",'i');} | ||||
| class Fl_Osc_Slider | |||||
| class Fl_Osc_VSlider | |||||
| } | } | ||||
| } | } | ||||
| Fl_Group modoscil {open | 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 | xywh {560 410 75 20} down_box BORDER_BOX labelsize 10 textfont 1 textsize 10 | ||||
| code0 {o->add("Internal");} | code0 {o->add("Internal");} | ||||
| code1 {char tmp[50]; for (int i=0;i<nvoice;i++) {sprintf(tmp,"ExtM.%2d",i+1);o->add(tmp);};} | 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 | class Fl_Osc_Choice | ||||
| } {} | } {} | ||||
| } | } | ||||
| Fl_Choice {} { | |||||
| Fl_Choice extMod { | |||||
| label {External Mod.} | label {External Mod.} | ||||
| callback {if ((int) o->value() != 0) { | callback {if ((int) o->value() != 0) { | ||||
| modoscil->deactivate(); | modoscil->deactivate(); | ||||
| @@ -383,7 +407,7 @@ voiceFMparametersgroup->redraw();} open | |||||
| class Fl_Osc_Choice | class Fl_Osc_Choice | ||||
| } {} | } {} | ||||
| } | } | ||||
| Fl_Choice {} { | |||||
| Fl_Choice mod_type { | |||||
| label {Type:} | label {Type:} | ||||
| callback {if (o->value()==0) voiceFMparametersgroup->deactivate(); | callback {if (o->value()==0) voiceFMparametersgroup->deactivate(); | ||||
| else voiceFMparametersgroup->activate(); | 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 | xywh {5 470 65 15} down_box BORDER_BOX labelsize 10 align 5 textfont 1 textsize 10 | ||||
| code0 {o->add("Internal");} | code0 {o->add("Internal");} | ||||
| code1 {char tmp[50]; for (int i=0;i<nvoice;i++) {sprintf(tmp,"Ext.%2d",i+1);o->add(tmp);};} | 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 | class Fl_Osc_Choice | ||||
| } {} | } {} | ||||
| Fl_Group {} {open | Fl_Group {} {open | ||||
| xywh {5 540 520 50} box UP_FRAME | 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 {} { | Fl_Dial {} { | ||||
| label Vibrato | label Vibrato | ||||
| tooltip Vibrato xywh {364 555 25 25} box ROUND_UP_BOX labelsize 10 align 1 maximum 127 step 1 | 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));} | code0 {char tmp[10];snprintf(tmp,10,"%d",nvoice+1);o->label(strdup(tmp));} | ||||
| } {} | } {} | ||||
| Fl_Choice {} { | 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 | tooltip {Oscillator Type (sound/noise)} xywh {5 515 65 20} down_box BORDER_BOX labelsize 10 textfont 1 textsize 10 | ||||
| code0 {o->init("Type");} | code0 {o->init("Type");} | ||||
| class Fl_Osc_Choice | class Fl_Osc_Choice | ||||
| @@ -729,8 +743,12 @@ o->redraw();} | |||||
| xywh {5 5 100 20} labelfont 1 labelsize 11 | xywh {5 5 100 20} labelfont 1 labelsize 11 | ||||
| } | } | ||||
| MenuItem {} { | 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 { | Fl_Check_Button bypassfiltercheckbutton { | ||||
| @@ -759,10 +777,27 @@ bypassfiltercheckbutton->redraw();} | |||||
| code0 {o->init("PFilterEnabled");} | code0 {o->init("PFilterEnabled");} | ||||
| class Fl_Osc_Check | class Fl_Osc_Check | ||||
| } | } | ||||
| Fl_Box noiselabel { | |||||
| Fl_Box whitenoiselabel { | |||||
| label {White Noise} | label {White Noise} | ||||
| xywh {150 430 300 65} labelfont 1 labelsize 50 labelcolor 53 hide | 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 { | Fl_Check_Button voiceonbutton { | ||||
| label On | label On | ||||
| @@ -802,9 +837,29 @@ delete(oscedit); | |||||
| } { | } { | ||||
| code {nvoice = nvoice_; | code {nvoice = nvoice_; | ||||
| loc = base+"VoicePar"+to_s(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)); | 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 | decl {int nvoice;} {private local | ||||
| @@ -834,7 +889,7 @@ class ADnoteUI {open : {public PresetsUI_} | |||||
| } { | } { | ||||
| Fl_Box {} { | Fl_Box {} { | ||||
| xywh {0 0 0 0} | xywh {0 0 0 0} | ||||
| code0 {ADnoteGlobalParameters->base = loc + "GlobalPar/"; ADnoteGlobalParameters->osc = osc;} | |||||
| code0 {ADnoteGlobalParameters->init(osc, loc + "GlobalPar/");} | |||||
| } | } | ||||
| Fl_Group {} { | Fl_Group {} { | ||||
| label FREQUENCY open | label FREQUENCY open | ||||
| @@ -879,7 +934,10 @@ class ADnoteUI {open : {public PresetsUI_} | |||||
| } | } | ||||
| Fl_Choice detunetype { | Fl_Choice detunetype { | ||||
| label {Detune Type} | 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 | 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");} | code0 {o->add("L35cents");o->add("L10cents");o->add("E100cents");o->add("E1200cents");} | ||||
| code1 {o->init("PDetuneType",1);} | code1 {o->init("PDetuneType",1);} | ||||
| @@ -1000,7 +1058,12 @@ ADnoteVoiceList->show();} | |||||
| } | } | ||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label {Show Voice Parameters} | 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 | xywh {5 400 170 25} labelsize 12 | ||||
| } | } | ||||
| Fl_Button {} { | Fl_Button {} { | ||||
| @@ -1032,8 +1095,7 @@ resui->resonancewindow->show();} | |||||
| } { | } { | ||||
| Fl_Box {} { | Fl_Box {} { | ||||
| xywh {0 0 0 0} | xywh {0 0 0 0} | ||||
| code0 {ADnoteVoice->base = loc;} | |||||
| code1 {ADnoteVoice->osc = osc;} | |||||
| code0 {ADnoteVoice->init(osc,loc);} | |||||
| } | } | ||||
| Fl_Group advoice {open | Fl_Group advoice {open | ||||
| xywh {0 0 765 595} | xywh {0 0 765 595} | ||||
| @@ -1066,49 +1128,48 @@ advoice->change_voice(nvoice);} | |||||
| } | } | ||||
| Fl_Window ADnoteVoiceList { | Fl_Window ADnoteVoiceList { | ||||
| label {ADsynth Voices list} open | 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 | class Fl_Osc_Window | ||||
| } { | } { | ||||
| Fl_Box {} { | Fl_Box {} { | ||||
| xywh {0 0 0 0} | xywh {0 0 0 0} | ||||
| code0 {ADnoteVoiceList->base = loc;} | |||||
| code1 {ADnoteVoiceList->osc = osc;} | |||||
| code0 {ADnoteVoiceList->init(osc, loc);} | |||||
| } | } | ||||
| Fl_Text_Display {} { | Fl_Text_Display {} { | ||||
| label {No.} | 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 {} { | Fl_Text_Display {} { | ||||
| label Vol | 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 {} { | Fl_Text_Display {} { | ||||
| label Detune | 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 {} { | Fl_Text_Display {} { | ||||
| label Pan | 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 {} { | 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 {} { | Fl_Text_Display {} { | ||||
| label {R.} | 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 {} { | Fl_Button {} { | ||||
| label {Hide Voice List} | label {Hide Voice List} | ||||
| callback {ADnoteVoiceList->hide();} | callback {ADnoteVoiceList->hide();} | ||||
| xywh {255 237 125 20} | |||||
| xywh {271 237 125 20} | |||||
| } | } | ||||
| Fl_Scroll {} {open | 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 | 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_Check.H"} {public local | ||||
| } | } | ||||
| decl {\#include "Fl_Osc_Pane.H"} {public local | |||||
| } | |||||
| decl {\#include "../Misc/Util.h"} {public local | decl {\#include "../Misc/Util.h"} {public local | ||||
| } | } | ||||
| @@ -46,8 +49,7 @@ class BankUI {open | |||||
| } { | } { | ||||
| Fl_Box {} { | Fl_Box {} { | ||||
| xywh {0 0 0 0} | xywh {0 0 0 0} | ||||
| code0 {bankuiwindow->osc = osc;} | |||||
| code1 {bankuiwindow->base = "/";} | |||||
| code0 {bankuiwindow->init(osc, "/");} | |||||
| } | } | ||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label Close | label Close | ||||
| @@ -21,7 +21,7 @@ void BankList::init(std::string path) | |||||
| void BankList::OSC_raw(const char *msg) | 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 int pos = rtosc_argument(msg, 0).i; | ||||
| const char *path = rtosc_argument(msg, 1).s; | const char *path = rtosc_argument(msg, 1).s; | ||||
| @@ -33,7 +33,7 @@ void BankList::OSC_raw(const char *msg) | |||||
| this->add(path); | this->add(path); | ||||
| osc->write("/loadbank"); | osc->write("/loadbank"); | ||||
| } | } | ||||
| if(!strcmp(msg, "/loadbank")) { | |||||
| if(!strcmp(msg, "/loadbank")&& !strcmp(rtosc_argument_string(msg),"i")) { | |||||
| value(rtosc_argument(msg, 0).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_ListView.H"} {public local | ||||
| } | } | ||||
| decl {\#include "Fl_Osc_Pane.H"} {public local | |||||
| } | |||||
| decl {\#include "../globals.h"} {public local | decl {\#include "../globals.h"} {public local | ||||
| } | } | ||||
| @@ -60,8 +63,7 @@ class ConfigUI {} { | |||||
| } { | } { | ||||
| Fl_Box dummy { | Fl_Box dummy { | ||||
| xywh {25 25 25 25} | xywh {25 25 25 25} | ||||
| code0 {configwindow->osc = osc;} | |||||
| code1 {configwindow->base = "/config/";} | |||||
| code0 {configwindow->init(osc, "/config/");} | |||||
| } | } | ||||
| Fl_Tabs {} { | Fl_Tabs {} { | ||||
| xywh {5 5 500 330} | xywh {5 5 500 330} | ||||
| @@ -291,7 +293,7 @@ activatebutton_presetdir(true);} | |||||
| oscilsize->callback = [this](int i){ | oscilsize->callback = [this](int i){ | ||||
| oscilsize_widget->value(i-7); | 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 | #ifndef NO_UI | ||||
| printf("\n\nDamage(\"%s\")\n", path); | printf("\n\nDamage(\"%s\")\n", path); | ||||
| std::set<Fl_Osc_Widget*> to_update; | |||||
| for(auto pair:map) { | for(auto pair:map) { | ||||
| if(strstr(pair.first.c_str(), path)) { | if(strstr(pair.first.c_str(), path)) { | ||||
| auto *tmp = dynamic_cast<Fl_Widget*>(pair.second); | auto *tmp = dynamic_cast<Fl_Widget*>(pair.second); | ||||
| if(!tmp || tmp->visible_r()) { | if(!tmp || tmp->visible_r()) { | ||||
| pair.second->update(); | |||||
| to_update.insert(pair.second); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| for(auto elm:to_update) | |||||
| elm->update(); | |||||
| #endif | #endif | ||||
| } | } | ||||
| @@ -1,7 +1,7 @@ | |||||
| #include "Connection.h" | #include "Connection.h" | ||||
| #include <unistd.h> | #include <unistd.h> | ||||
| namespace GUI { | namespace GUI { | ||||
| ui_handle_t createUi(Fl_Osc_Interface*, void *exit) | |||||
| ui_handle_t createUi(Fl_Osc_Interface*, void *) | |||||
| { | { | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -29,6 +29,9 @@ decl {\#include "Fl_Osc_Check.H"} {public local | |||||
| decl {\#include "Fl_EQGraph.H"} {public local | decl {\#include "Fl_EQGraph.H"} {public local | ||||
| } | } | ||||
| decl {\#include "Fl_Osc_Pane.H"} {public local | |||||
| } | |||||
| decl {\#include "EnvelopeUI.h"} {public local | decl {\#include "EnvelopeUI.h"} {public local | ||||
| } | } | ||||
| @@ -150,7 +153,7 @@ if (filterwindow!=NULL){ | |||||
| label Type | label Type | ||||
| callback {if(o->value()==2) revp12->activate(); | callback {if(o->value()==2) revp12->activate(); | ||||
| else revp12->deactivate();} | 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");} | code0 {o->init("parameter10");} | ||||
| class Fl_Osc_Choice | class Fl_Osc_Choice | ||||
| } { | } { | ||||
| @@ -21,6 +21,12 @@ void EnvelopeFreeEdit::init(void) | |||||
| oscRegister("Penvdt"); | oscRegister("Penvdt"); | ||||
| oscRegister("Penvval"); | oscRegister("Penvval"); | ||||
| oscRegister("Penvsustain"); | 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) | 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; | rtosc_blob_t b = rtosc_argument(msg, 0).b; | ||||
| assert(b.len == MAX_ENVELOPE_POINTS); | assert(b.len == MAX_ENVELOPE_POINTS); | ||||
| memcpy(Penvval, b.data, 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")) { | } else if(strstr(msg,"Penvsustain") && !strcmp(args, "i")) { | ||||
| Penvsustain = rtosc_argument(msg, 0).i; | Penvsustain = rtosc_argument(msg, 0).i; | ||||
| } | } | ||||
| @@ -98,6 +114,8 @@ float EnvelopeFreeEdit::getdt(int i) const | |||||
| return dt(Penvdt[i]); | return dt(Penvdt[i]); | ||||
| } | } | ||||
| static bool ctrldown; | |||||
| void EnvelopeFreeEdit::draw(void) | void EnvelopeFreeEdit::draw(void) | ||||
| { | { | ||||
| int ox=x(),oy=y(),lx=w(),ly=h(); | int ox=x(),oy=y(),lx=w(),ly=h(); | ||||
| @@ -129,8 +147,10 @@ void EnvelopeFreeEdit::draw(void) | |||||
| for (int i=1; i<npoints; ++i){ | for (int i=1; i<npoints; ++i){ | ||||
| oldxx=xx;oldyy=yy; | oldxx=xx;oldyy=yy; | ||||
| xx=getpointx(i);yy=getpointy(i); | 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_line(ox+oldxx,oy+oldyy,ox+xx,oy+yy); | ||||
| fl_rectf(ox+xx-3,oy+yy-3,6,6); | 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 | //Show the envelope duration and the current line duration | ||||
| fl_font(FL_HELVETICA|FL_BOLD,10); | fl_font(FL_HELVETICA|FL_BOLD,10); | ||||
| float time=0.0; | float time=0.0; | ||||
| if (currentpoint<=0){ | |||||
| if (currentpoint<=0 && (!ctrldown||lastpoint <= 0)){ | |||||
| fl_color(alb); | fl_color(alb); | ||||
| for(int i=1; i<npoints; ++i) | for(int i=1; i<npoints; ++i) | ||||
| time+=getdt(i); | time+=getdt(i); | ||||
| } else { | } else { | ||||
| fl_color(255,0,0); | |||||
| time=getdt(currentpoint); | |||||
| fl_color(FL_RED); | |||||
| time=getdt(lastpoint); | |||||
| } | } | ||||
| char tmpstr[20]; | char tmpstr[20]; | ||||
| if (time<1000.0) | if (time<1000.0) | ||||
| @@ -165,55 +185,99 @@ void EnvelopeFreeEdit::draw(void) | |||||
| else | else | ||||
| snprintf((char *)&tmpstr,20,"%.2fs",time/1000.0); | snprintf((char *)&tmpstr,20,"%.2fs",time/1000.0); | ||||
| fl_draw(tmpstr,ox+lx-20,oy+ly-10,20,10,FL_ALIGN_RIGHT,NULL,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) | int EnvelopeFreeEdit::handle(int event) | ||||
| { | { | ||||
| const int x_=Fl::event_x()-x(); | const int x_=Fl::event_x()-x(); | ||||
| const int y_=Fl::event_y()-y(); | 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) | 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+"Penvdt", new_base+"Penvdt", this); | ||||
| osc->renameLink(loc+"Penvval", new_base+"Penvval", this); | osc->renameLink(loc+"Penvval", new_base+"Penvval", this); | ||||
| osc->renameLink(loc+"Penvsustain", new_base+"Penvsustain", 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; | loc = new_base; | ||||
| update(); | 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_Counter.H"} {public local | ||||
| } | } | ||||
| decl {\#include "Fl_Osc_Pane.H"} {public local | |||||
| } | |||||
| decl {\#include <stdio.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 | 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_} | 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)} {} { | 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 {} { | Fl_Button {} { | ||||
| label C | label C | ||||
| callback {presetsui->copy(freemodeeditwindow->loc());} | 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 {} { | Fl_Button {} { | ||||
| label P | label P | ||||
| callback {presetsui->paste(freemodeeditwindow->loc(),this);} | 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 { | Fl_Button addpoint { | ||||
| label {Add point} | label {Add point} | ||||
| @@ -101,8 +119,8 @@ sustaincounter->update(); | |||||
| //sustaincounter->value(Penvsustain); | //sustaincounter->value(Penvsustain); | ||||
| //sustaincounter->maximum(Penvpoints-2);} | //sustaincounter->maximum(Penvpoints-2);} | ||||
| xywh {115 155 80 20} box THIN_UP_BOX labelsize 11 | 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 { | Fl_Box freeedit { | ||||
| label Envelope | label Envelope | ||||
| @@ -121,20 +139,8 @@ sustaincounter->update(); | |||||
| //sustaincounter->value(Penvsustain); | //sustaincounter->value(Penvsustain); | ||||
| //sustaincounter->maximum(Penvpoints-2);} | //sustaincounter->maximum(Penvpoints-2);} | ||||
| xywh {200 155 80 20} box THIN_UP_BOX labelsize 11 | 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 { | Fl_Check_Button forcedreleasecheck { | ||||
| label frcR | label frcR | ||||
| @@ -170,6 +176,18 @@ envfree->redraw();} | |||||
| code3 {o->init("Penvsustain");} | code3 {o->init("Penvsustain");} | ||||
| class Fl_Osc_Counter | 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()} {} { | Function {make_ADSR_window()} {} { | ||||
| @@ -185,12 +203,12 @@ envfree->redraw();} | |||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label C | label C | ||||
| callback {presetsui->copy(envADSR->loc());} | 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 {} { | Fl_Button {} { | ||||
| label P | label P | ||||
| callback {presetsui->paste(envADSR->loc(),this);} | 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 { | Fl_Dial e1adt { | ||||
| label {A.dt} | label {A.dt} | ||||
| @@ -234,8 +252,9 @@ envfree->redraw();} | |||||
| } | } | ||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label E | label E | ||||
| callback {freemodeeditwindow->show();} | |||||
| callback {open_as_freemode();} | |||||
| tooltip {Envelope window} xywh {185 5 15 15} labelfont 1 labelsize 10 | tooltip {Envelope window} xywh {185 5 15 15} labelfont 1 labelsize 10 | ||||
| class Fl_Osc_Button | |||||
| } | } | ||||
| Fl_Check_Button e1linearenvelope { | Fl_Check_Button e1linearenvelope { | ||||
| label L | label L | ||||
| @@ -259,12 +278,12 @@ envfree->redraw();} | |||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label C | label C | ||||
| callback {presetsui->copy(envASR->loc());} | 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 {} { | Fl_Button {} { | ||||
| label P | label P | ||||
| callback {presetsui->paste(envASR->loc(),this);} | 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 { | Fl_Dial e2aval { | ||||
| label {A.val} | label {A.val} | ||||
| @@ -309,8 +328,9 @@ envfree->redraw();} | |||||
| } | } | ||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label E | label E | ||||
| callback {freemodeeditwindow->show();} | |||||
| callback {open_as_freemode();} | |||||
| tooltip {Envelope window} xywh {190 5 15 15} labelfont 1 labelsize 10 | tooltip {Envelope window} xywh {190 5 15 15} labelfont 1 labelsize 10 | ||||
| class Fl_Osc_Button | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -327,12 +347,12 @@ envfree->redraw();} | |||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label C | label C | ||||
| callback {presetsui->copy(envADSRfilter->loc());} | 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 {} { | Fl_Button {} { | ||||
| label P | label P | ||||
| callback {presetsui->paste(envADSRfilter->loc(),this);} | 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 { | Fl_Dial e3aval { | ||||
| label {A.val} | label {A.val} | ||||
| @@ -390,8 +410,9 @@ envfree->redraw();} | |||||
| } | } | ||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label E | label E | ||||
| callback {freemodeeditwindow->show();} | |||||
| callback {open_as_freemode();} | |||||
| xywh {255 5 15 15} labelfont 1 labelsize 10 | xywh {255 5 15 15} labelfont 1 labelsize 10 | ||||
| class Fl_Osc_Button | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -410,12 +431,12 @@ envfree->redraw();} | |||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label C | label C | ||||
| callback {presetsui->copy(envASRbw->loc());} | 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 {} { | Fl_Button {} { | ||||
| label P | label P | ||||
| callback {presetsui->paste(envASRbw->loc(),this);} | 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 { | Fl_Dial e4aval { | ||||
| label {A.val} | label {A.val} | ||||
| @@ -460,8 +481,9 @@ envfree->redraw();} | |||||
| } | } | ||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label E | label E | ||||
| callback {freemodeeditwindow->show();} | |||||
| callback {open_as_freemode();} | |||||
| xywh {190 5 15 15} labelfont 1 labelsize 10 | xywh {190 5 15 15} labelfont 1 labelsize 10 | ||||
| class Fl_Osc_Button | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -478,8 +500,9 @@ envfree->redraw();} | |||||
| } { | } { | ||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label E | label E | ||||
| callback {freemodeeditwindow->show();} | |||||
| callback {open_as_freemode();} | |||||
| xywh {185 5 15 15} labelfont 1 labelsize 10 | xywh {185 5 15 15} labelfont 1 labelsize 10 | ||||
| class Fl_Osc_Button | |||||
| } | } | ||||
| Fl_Box freeeditsmall { | Fl_Box freeeditsmall { | ||||
| label Envelope | 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 | Function {init(int env_type, Fl_Osc_Interface *osc_, std::string base_, std::string ext_)} {open | ||||
| } { | } { | ||||
| code {osc = osc_; | code {osc = osc_; | ||||
| @@ -533,113 +568,68 @@ freeeditsmall->setpair(freeedit); | |||||
| freeedit->setpair(freeeditsmall); | freeedit->setpair(freeeditsmall); | ||||
| refresh();} {} | |||||
| refresh_display();} {} | |||||
| } | } | ||||
| Function {rebase(std::string new_base)} {open | Function {rebase(std::string new_base)} {open | ||||
| } { | } { | ||||
| code {Fl_Osc_Group::rebase(new_base); | code {Fl_Osc_Group::rebase(new_base); | ||||
| freemodeeditwindow->rebase(new_base+ext);} {} | 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(); | 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(); | freemodeeditwindow->hide(); | ||||
| envwindow->hide(); | envwindow->hide(); | ||||
| Fl_Group *par=envwindow->parent(); | Fl_Group *par=envwindow->parent(); | ||||
| par->hide(); | par->hide(); | ||||
| refresh(); | |||||
| freemodehack->oscWrite("Pfreemode", "F"); | |||||
| freeedit->update(); | |||||
| envwindow->show(); | envwindow->show(); | ||||
| par->redraw(); | par->redraw(); | ||||
| par->show(); | par->show(); | ||||
| 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 { | code { | ||||
| sustaincounter->value(Penvsustain); | sustaincounter->value(Penvsustain); | ||||
| sustaincounter->maximum(Penvpoints-2); | sustaincounter->maximum(Penvpoints-2); | ||||
| envstretchdial->value(Penvstretch); | 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); | forcedreleasecheck->value(Pforcedrelease); | ||||
| if (freemodebutton->value()==0){ | |||||
| addpoint->hide(); | |||||
| deletepoint->hide(); | |||||
| } else { | |||||
| addpoint->show(); | |||||
| deletepoint->show(); | |||||
| } | |||||
| envADSR->hide(); | envADSR->hide(); | ||||
| envASR->hide(); | envASR->hide(); | ||||
| envADSRfilter->hide(); | envADSRfilter->hide(); | ||||
| envASRbw->hide(); | envASRbw->hide(); | ||||
| envfree->hide(); | envfree->hide(); | ||||
| if (freemodebutton->value()==0){ | |||||
| if (freemodehack->value()) { | |||||
| envwindow=envfree; | |||||
| freeedit->update(); | |||||
| } else { | |||||
| freemodeeditwindow->hide(); | |||||
| switch(Envmode){ | switch(Envmode){ | ||||
| case 1: | case 1: | ||||
| case 2: | case 2: | ||||
| @@ -656,10 +646,8 @@ if (freemodebutton->value()==0){ | |||||
| break; | break; | ||||
| default: | default: | ||||
| break; | break; | ||||
| }; | |||||
| }else{ | |||||
| envwindow=envfree; | |||||
| }; | |||||
| } | |||||
| } | |||||
| assert(envwindow); | assert(envwindow); | ||||
| envwindow->resize(this->x(),this->y(),this->w(),this->h()); | 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_Slider.H"} {public local | ||||
| } | } | ||||
| decl {\#include "Fl_Osc_Pane.H"} {public local | |||||
| } | |||||
| decl {\#include <cmath>} {private local | decl {\#include <cmath>} {private local | ||||
| } | } | ||||
| @@ -39,6 +39,7 @@ void Fl_Osc_DialF::init(const char *path) | |||||
| ext = path; | ext = path; | ||||
| loc = pane->base; | loc = pane->base; | ||||
| oscRegister(path); | oscRegister(path); | ||||
| integer_step = false; | |||||
| }; | }; | ||||
| Fl_Osc_DialF::~Fl_Osc_DialF(void) | Fl_Osc_DialF::~Fl_Osc_DialF(void) | ||||
| @@ -1,4 +1,5 @@ | |||||
| #include "Fl_Osc_ListView.H" | #include "Fl_Osc_ListView.H" | ||||
| #include "Fl_Osc_Pane.H" | |||||
| #include <cstdio> | #include <cstdio> | ||||
| #include <rtosc/rtosc.h> | #include <rtosc/rtosc.h> | ||||
| @@ -21,7 +22,7 @@ void Fl_Osc_ListView::init(const char *path_) | |||||
| path = path_; | path = path_; | ||||
| data = new Osc_SimpleListModel(osc); | data = new Osc_SimpleListModel(osc); | ||||
| data->callback = [this](Osc_SimpleListModel::list_t l){this->doUpdate(l);}; | 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) | 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) | void Fl_Osc_ListView::update(void) | ||||
| { | { | ||||
| data->update(loc+path); | |||||
| data->doUpdate(loc+path); | |||||
| } | } | ||||
| void Fl_Osc_ListView::insert(std::string s, int offset) | void Fl_Osc_ListView::insert(std::string s, int offset) | ||||
| @@ -1,3 +1,4 @@ | |||||
| #include <stdlib.h> | |||||
| #include "Fl_Osc_Numeric_Input.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) | 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_Group.H> | ||||
| #include <FL/Fl_Double_Window.H> | #include <FL/Fl_Double_Window.H> | ||||
| #include <string> | #include <string> | ||||
| #include "Osc_DataModel.h" | |||||
| class Fl_Osc_Pane | class Fl_Osc_Pane | ||||
| { | { | ||||
| @@ -16,8 +17,12 @@ class Fl_Osc_Pane | |||||
| class Fl_Osc_Window:public Fl_Double_Window, public 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: | public: | ||||
| Fl_Osc_Window(int w, int h, const char *L=0); | Fl_Osc_Window(int w, int h, const char *L=0); | ||||
| ~Fl_Osc_Window(void); | |||||
| void init(Fl_Osc_Interface *osc_, std::string loc_); | void init(Fl_Osc_Interface *osc_, std::string loc_); | ||||
| virtual std::string loc(void) const; | 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_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_) | 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_; | osc = osc_; | ||||
| base = loc_; | base = loc_; | ||||
| } | } | ||||
| Fl_Osc_Window::~Fl_Osc_Window(void) | |||||
| { | |||||
| delete title_ext; | |||||
| } | |||||
| std::string Fl_Osc_Window::loc(void) const | std::string Fl_Osc_Window::loc(void) const | ||||
| { | { | ||||
| return base; | return base; | ||||
| @@ -5,7 +5,6 @@ | |||||
| class Fl_Osc_Slider:public Fl_Slider, public Fl_Osc_Widget | class Fl_Osc_Slider:public Fl_Slider, public Fl_Osc_Widget | ||||
| { | { | ||||
| public: | public: | ||||
| Fl_Osc_Slider(int X, int Y, int W, int H, const char *label = NULL); | Fl_Osc_Slider(int X, int Y, int W, int H, const char *label = NULL); | ||||
| // string name, | // 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); | void callback(Fl_Callback *cb, void *p = NULL); | ||||
| //MIDI Learn | //MIDI Learn | ||||
| int handle(int); | |||||
| int handle(int ev, int X, int Y, int W, int H); | |||||
| int handle(int ev); | |||||
| void cb(void); | void cb(void); | ||||
| static void _cb(Fl_Widget *w, void *); | static void _cb(Fl_Widget *w, void *); | ||||
| float reset_value; | |||||
| private: | private: | ||||
| char osc_type; | char osc_type; | ||||
| std::pair<Fl_Callback*, void*> cb_data; | std::pair<Fl_Callback*, void*> cb_data; | ||||
| @@ -7,6 +7,7 @@ | |||||
| #include <cmath> | #include <cmath> | ||||
| #include <cassert> | #include <cassert> | ||||
| #include <sstream> | #include <sstream> | ||||
| #include "../Misc/Util.h" | |||||
| static double min__(double a, double b) | 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_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); | //bounds(0.0f,1.0f); | ||||
| Fl_Slider::callback(Fl_Osc_Slider::_cb); | 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; | 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 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 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()); | osc->write("/unlearn", "s", (loc+ext).c_str()); | ||||
| return 1; | 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) | 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 | #pragma once | ||||
| #include <FL/Fl_Value_Slider.H> | #include <FL/Fl_Value_Slider.H> | ||||
| #include "Fl_Osc_Widget.H" | |||||
| #include "Fl_Osc_Slider.H" | |||||
| #include <string> | #include <string> | ||||
| class Fl_Osc_VSlider:public Fl_Value_Slider, public Fl_Osc_Widget | |||||
| class Fl_Osc_VSlider:public Fl_Osc_Slider | |||||
| { | { | ||||
| public: | public: | ||||
| Fl_Osc_VSlider(int X, int Y, int W, int H, const char *label = NULL); | Fl_Osc_VSlider(int X, int Y, int W, int H, const char *label = NULL); | ||||
| virtual ~Fl_Osc_VSlider(void); | 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 | //Refetch parameter information | ||||
| void update(void); | |||||
| void callback(Fl_Callback *cb, void *p = NULL); | |||||
| //MIDI Learn | //MIDI Learn | ||||
| int handle(int); | 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: | private: | ||||
| char osc_type; | char osc_type; | ||||
| std::pair<Fl_Callback*, void*> cb_data; | std::pair<Fl_Callback*, void*> cb_data; | ||||
| @@ -1,4 +1,5 @@ | |||||
| #include <FL/Fl.H> | #include <FL/Fl.H> | ||||
| #include <FL/fl_draw.H> | |||||
| #include "Fl_Osc_VSlider.H" | #include "Fl_Osc_VSlider.H" | ||||
| #include "Fl_Osc_Interface.h" | #include "Fl_Osc_Interface.h" | ||||
| #include "Fl_Osc_Pane.H" | #include "Fl_Osc_Pane.H" | ||||
| @@ -9,82 +10,60 @@ | |||||
| #include <sstream> | #include <sstream> | ||||
| Fl_Osc_VSlider::Fl_Osc_VSlider(int X, int Y, int W, int H, const char *label) | 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); | //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) | 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) | 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 <cassert> | ||||
| #include <cmath> | #include <cmath> | ||||
| #include "Fl_Osc_Interface.h" | #include "Fl_Osc_Interface.h" | ||||
| #include "Fl_Osc_Pane.H" | |||||
| #include <FL/Fl_Group.H> | #include <FL/Fl_Group.H> | ||||
| class Fl_Osc_Widget | class Fl_Osc_Widget | ||||
| @@ -54,5 +53,5 @@ class Fl_Osc_Widget | |||||
| std::string ext; | std::string ext; | ||||
| Fl_Osc_Interface *osc; | Fl_Osc_Interface *osc; | ||||
| protected: | 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_Widget.H" | ||||
| #include "Fl_Osc_Pane.H" | |||||
| #include <rtosc/rtosc.h> | #include <rtosc/rtosc.h> | ||||
| Fl_Osc_Widget::Fl_Osc_Widget(void) //Deprecated | 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_Check.H"} {public local | ||||
| } | } | ||||
| decl {\#include "Fl_Osc_Pane.H"} {public local | |||||
| } | |||||
| decl {\#include "../globals.h"} {private global | 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 "Fl_Osc_Dial.H"} {private local | ||||
| } | } | ||||
| decl {\#include "Osc_DataModel.h"} {private local | |||||
| } | |||||
| decl {\#include "VuMasterMeter.h"} {public local | decl {\#include "VuMasterMeter.h"} {public local | ||||
| } | } | ||||
| @@ -241,7 +244,13 @@ class MasterUI {open | |||||
| close(); | close(); | ||||
| };} open | };} open | ||||
| xywh {330 365 390 525} type Double xclass zynaddsubfx visible | 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 | Fl_Group win_root {open | ||||
| xywh {0 0 390 525} | xywh {0 0 390 525} | ||||
| class Fl_Osc_Group | class Fl_Osc_Group | ||||
| @@ -258,6 +267,11 @@ class MasterUI {open | |||||
| callback {do_new_master();} | callback {do_new_master();} | ||||
| xywh {25 25 100 20} | xywh {25 25 100 20} | ||||
| } | } | ||||
| MenuItem {} { | |||||
| label {&Revert changes...} | |||||
| callback {do_revert_changes();} | |||||
| xywh {25 25 100 20} | |||||
| } | |||||
| MenuItem {} { | MenuItem {} { | ||||
| label {&Open Parameters...} | label {&Open Parameters...} | ||||
| callback {\#if USE_NSM | callback {\#if USE_NSM | ||||
| @@ -568,7 +582,7 @@ syseffectui->refresh();} | |||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label C | label C | ||||
| callback {presetsui->copy("/sysefx"+to_s(nsyseff)+"/");} | 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 {} { | Fl_Button {} { | ||||
| label P | label P | ||||
| @@ -855,8 +869,7 @@ GNU General Public License for details.} | |||||
| } { | } { | ||||
| Fl_Box {} { | Fl_Box {} { | ||||
| xywh {0 0 0 0} | xywh {0 0 0 0} | ||||
| code0 {syseffsendwindow->osc = osc; assert(osc);} | |||||
| code1 {syseffsendwindow->base = "";} | |||||
| code0 {syseffsendwindow->init(osc,"");} | |||||
| } | } | ||||
| Fl_Scroll syseffscroll {open | Fl_Scroll syseffscroll {open | ||||
| xywh {0 45 120 170} box FLAT_BOX resizable | xywh {0 45 120 170} box FLAT_BOX resizable | ||||
| @@ -880,8 +893,7 @@ GNU General Public License for details.} | |||||
| } { | } { | ||||
| Fl_Box {} { | Fl_Box {} { | ||||
| xywh {0 0 0 0} | xywh {0 0 0 0} | ||||
| code0 {panelwindow->osc = osc;} | |||||
| code1 {panelwindow->base = "/";} | |||||
| code0 {panelwindow->init(osc,"/");} | |||||
| } | } | ||||
| Fl_Scroll {} {open | Fl_Scroll {} {open | ||||
| xywh {0 5 570 310} type HORIZONTAL box THIN_UP_BOX | 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 {} { | Fl_Box {} { | ||||
| xywh {0 0 0 0} | xywh {0 0 0 0} | ||||
| code0 {simplemasterwindow->osc = osc;} | |||||
| code1 {simplemasterwindow->base = "/";} | |||||
| code0 {simplemasterwindow->init(osc, "/");} | |||||
| } | } | ||||
| Fl_Menu_Bar simplemastermenu { | Fl_Menu_Bar simplemastermenu { | ||||
| xywh {0 0 600 25} | 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();} | callback {do_new_master();} | ||||
| xywh {30 30 100 20} | xywh {30 30 100 20} | ||||
| } | } | ||||
| MenuItem {} { | |||||
| label {&Revert changes...} | |||||
| callback {do_revert_changes();} | |||||
| xywh {30 30 100 20} | |||||
| } | |||||
| MenuItem {} { | MenuItem {} { | ||||
| label {&Open Parameters...} | label {&Open Parameters...} | ||||
| callback {do_load_master();} | callback {do_load_master();} | ||||
| @@ -1427,6 +1443,7 @@ virkeys->take_focus();} | |||||
| label {User Interface mode} | label {User Interface mode} | ||||
| callback {*exitprogram=1;} | callback {*exitprogram=1;} | ||||
| xywh {342 246 430 250} type Double hide non_modal | xywh {342 246 430 250} type Double hide non_modal | ||||
| class Fl_Osc_Window | |||||
| } { | } { | ||||
| Fl_Box {} { | Fl_Box {} { | ||||
| label {Welcome to ZynAddSubFX} | label {Welcome to ZynAddSubFX} | ||||
| @@ -1489,6 +1506,25 @@ osc=osc_; | |||||
| ninseff=0; | ninseff=0; | ||||
| nsyseff=0; | nsyseff=0; | ||||
| npart=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 i=0;i<NUM_SYS_EFX;i++) | ||||
| for (int j=0;j<NUM_SYS_EFX;j++) | for (int j=0;j<NUM_SYS_EFX;j++) | ||||
| @@ -1556,6 +1592,7 @@ simplelistitemgroup->redraw();} {} | |||||
| microtonalui=new MicrotonalUI(osc, "/microtonal/"); | microtonalui=new MicrotonalUI(osc, "/microtonal/"); | ||||
| osc->write("/reset_master"); | osc->write("/reset_master"); | ||||
| osc->write("/last_xmz"); | |||||
| npartcounter->value(1); | npartcounter->value(1); | ||||
| refresh_master_ui(); | refresh_master_ui(); | ||||
| updatepanel();} {} | updatepanel();} {} | ||||
| @@ -1565,9 +1602,29 @@ microtonalui=new MicrotonalUI(osc, "/microtonal/"); | |||||
| do_new_master_unconditional(); | 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 | 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(); | refresh_master_ui(); | ||||
| updatepanel(); | updatepanel(); | ||||
| @@ -1648,6 +1705,8 @@ simpleinseffnocounter->do_callback(); | |||||
| simplerefresh(); | simplerefresh(); | ||||
| bankui->hide();} {} | bankui->hide();} {} | ||||
| } | |||||
| decl {class Osc_DataModel *last_xmz;} {public local | |||||
| } | } | ||||
| decl {MicrotonalUI *microtonalui;} {private local | decl {MicrotonalUI *microtonalui;} {private local | ||||
| } | } | ||||
| @@ -1675,6 +1734,10 @@ bankui->hide();} {} | |||||
| } | } | ||||
| decl {class Fl_Osc_Interface *osc;} {public local | 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 | Function {close()} {open return_type void | ||||
| } { | } { | ||||
| code {*exitprogram=1;} {} | code {*exitprogram=1;} {} | ||||
| @@ -51,8 +51,7 @@ class MicrotonalUI {} { | |||||
| } { | } { | ||||
| Fl_Box {} { | Fl_Box {} { | ||||
| xywh {0 0 0 0} | xywh {0 0 0 0} | ||||
| code0 {microtonaluiwindow->osc = osc;} | |||||
| code1 {microtonaluiwindow->base = base;} | |||||
| code0 {microtonaluiwindow->init(osc, base);} | |||||
| } | } | ||||
| Fl_Group {} { | Fl_Group {} { | ||||
| tooltip {Center where the note's freqs. are turned upside-down} xywh {249 2 155 45} box ENGRAVED_FRAME | 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_Group.H> | ||||
| #include <FL/Fl_Text_Display.H> | #include <FL/Fl_Text_Display.H> | ||||
| #include "Osc_SimpleListModel.h" | #include "Osc_SimpleListModel.h" | ||||
| #include "Fl_Osc_Pane.H" | |||||
| using namespace std; | using namespace std; | ||||
| @@ -56,7 +57,7 @@ class Fl_Osc_StrChoice:public Fl_Choice, public Fl_Osc_Widget | |||||
| cb_data.first = cb; | cb_data.first = cb; | ||||
| cb_data.second = p; | cb_data.second = p; | ||||
| } | } | ||||
| void cb(void) | void cb(void) | ||||
| { | { | ||||
| assert(osc); | assert(osc); | ||||
| @@ -66,7 +67,6 @@ class Fl_Osc_StrChoice:public Fl_Choice, public Fl_Osc_Widget | |||||
| cb_data.first(this, cb_data.second); | cb_data.first(this, cb_data.second); | ||||
| } | } | ||||
| private: | private: | ||||
| int min; | |||||
| std::pair<Fl_Callback*, void*> cb_data; | std::pair<Fl_Callback*, void*> cb_data; | ||||
| }; | }; | ||||
| static void callback_fn_choice_nio(Fl_Widget *w, void *) | static void callback_fn_choice_nio(Fl_Widget *w, void *) | ||||
| @@ -110,9 +110,9 @@ NioUI::NioUI(Fl_Osc_Interface *osc) | |||||
| for(auto io:list) | for(auto io:list) | ||||
| audio->add(io.c_str()); | 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); | resizable(this); | ||||
| size_range(400, 300); | size_range(400, 300); | ||||
| @@ -123,19 +123,19 @@ NioUI::~NioUI() | |||||
| void NioUI::refresh() | 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(); | midi->update(); | ||||
| audio->update(); | audio->update(); | ||||
| } | } | ||||
| void NioUI::midiCallback(Fl_Widget *c) | |||||
| void NioUI::midiCallback(Fl_Widget *) | |||||
| { | { | ||||
| //bool good = Nio::setSource(static_cast<Fl_Choice *>(c)->text()); | //bool good = Nio::setSource(static_cast<Fl_Choice *>(c)->text()); | ||||
| //static_cast<Fl_Choice *>(c)->textcolor(fl_rgb_color(255 * !good, 0, 0)); | //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()); | //bool good = Nio::setSink(static_cast<Fl_Choice *>(c)->text()); | ||||
| //static_cast<Fl_Choice *>(c)->textcolor(fl_rgb_color(255 * !good, 0, 0)); | //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; | value_t value; | ||||
| std::function<void(value_t)> callback; | std::function<void(value_t)> callback; | ||||
| void update(std::string url) | |||||
| void doUpdate(std::string url) | |||||
| { | { | ||||
| if(!ext.empty()) | if(!ext.empty()) | ||||
| osc->removeLink(this); | osc->removeLink(this); | ||||
| @@ -26,7 +26,7 @@ class Osc_DataModel:public Fl_Osc_Widget | |||||
| oscRegister(ext.c_str()); | oscRegister(ext.c_str()); | ||||
| } | } | ||||
| //Raw messages | //Raw messages | ||||
| virtual void OSC_raw(const char *msg) | virtual void OSC_raw(const char *msg) | ||||
| { | { | ||||
| @@ -23,7 +23,7 @@ class Osc_IntModel:public Fl_Osc_Widget | |||||
| oscWrite(ext, "i", v); | oscWrite(ext, "i", v); | ||||
| } | } | ||||
| void update(std::string url) | |||||
| void doUpdate(std::string url) | |||||
| { | { | ||||
| if(!ext.empty()) | if(!ext.empty()) | ||||
| osc->removeLink(this); | osc->removeLink(this); | ||||
| @@ -31,7 +31,7 @@ class Osc_IntModel:public Fl_Osc_Widget | |||||
| oscRegister(ext.c_str()); | oscRegister(ext.c_str()); | ||||
| } | } | ||||
| //Raw messages | //Raw messages | ||||
| virtual void OSC_raw(const char *msg) | virtual void OSC_raw(const char *msg) | ||||
| { | { | ||||
| @@ -18,7 +18,7 @@ class Osc_ListModel:public Fl_Osc_Widget | |||||
| std::function<void(list_t)> callback; | std::function<void(list_t)> callback; | ||||
| unsigned list_size; | unsigned list_size; | ||||
| void update(std::string url) | |||||
| void doUpdate(std::string url) | |||||
| { | { | ||||
| if(!ext.empty()) | if(!ext.empty()) | ||||
| osc->removeLink(this); | osc->removeLink(this); | ||||
| @@ -26,7 +26,7 @@ class Osc_ListModel:public Fl_Osc_Widget | |||||
| oscRegister(ext.c_str()); | oscRegister(ext.c_str()); | ||||
| } | } | ||||
| //Raw messages | //Raw messages | ||||
| virtual void OSC_raw(const char *msg) | virtual void OSC_raw(const char *msg) | ||||
| { | { | ||||
| @@ -18,7 +18,7 @@ class Osc_SimpleListModel:public Fl_Osc_Widget | |||||
| std::function<void(list_t)> callback; | std::function<void(list_t)> callback; | ||||
| unsigned list_size; | unsigned list_size; | ||||
| void update(std::string url) | |||||
| void doUpdate(std::string url) | |||||
| { | { | ||||
| if(!ext.empty()) | if(!ext.empty()) | ||||
| osc->removeLink(this); | osc->removeLink(this); | ||||
| @@ -47,7 +47,7 @@ class Osc_SimpleListModel:public Fl_Osc_Widget | |||||
| rtosc_amessage(buffer, sizeof(buffer), ext.c_str(), types, args); | rtosc_amessage(buffer, sizeof(buffer), ext.c_str(), types, args); | ||||
| osc->writeRaw(buffer); | osc->writeRaw(buffer); | ||||
| } | } | ||||
| //Raw messages | //Raw messages | ||||
| virtual void OSC_raw(const char *msg) | virtual void OSC_raw(const char *msg) | ||||
| { | { | ||||
| @@ -346,7 +346,7 @@ oscildisplaygroup->redraw();} | |||||
| code1 {oscilo_base->parent(o);oscilo_base->init(true);} | code1 {oscilo_base->parent(o);oscilo_base->init(true);} | ||||
| } {} | } {} | ||||
| Fl_Dial bfslider { | 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 | tooltip {Base Function Parameter} xywh {525 285 20 20} minimum -64 maximum 63 step 1 | ||||
| code0 {o->init("Pbasefuncpar");} | code0 {o->init("Pbasefuncpar");} | ||||
| class Fl_Osc_Dial | class Fl_Osc_Dial | ||||
| @@ -492,9 +492,9 @@ redrawoscil();} | |||||
| } | } | ||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label {Use as base} | label {Use as base} | ||||
| callback {//oscil->useasbase(); | |||||
| callback {osc->requestValue(loc+"use-as-base"); | |||||
| if (autoclearbutton->value()){ | 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->value(64); | ||||
| h[i]->mag->do_callback(); | h[i]->mag->do_callback(); | ||||
| h[i]->phase->value(64); | h[i]->phase->value(64); | ||||
| @@ -528,7 +528,7 @@ redrawoscil();} | |||||
| label Clear | label Clear | ||||
| callback {if (!fl_choice("Clear the harmonics settings?","No","Yes",NULL)) return; | 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->value(64); | ||||
| h[i]->mag->do_callback(); | h[i]->mag->do_callback(); | ||||
| h[i]->phase->value(64); | 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->value(0); | ||||
| h[0]->mag->do_callback(); | 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); | // if (oscil->Phmag[i]==64) h[i]->mag->selection_color(0); | ||||
| // else h[i]->mag->selection_color(222); | // else h[i]->mag->selection_color(222); | ||||
| //}; | //}; | ||||
| @@ -620,7 +620,7 @@ redrawoscil();} | |||||
| } | } | ||||
| } | } | ||||
| Fl_Dial wshpar { | 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 | tooltip {Waveshaping Parameter} xywh {265 318 20 20} minimum -64 maximum 63 step 1 | ||||
| code0 {o->init("Pwaveshaping");} | code0 {o->init("Pwaveshaping");} | ||||
| class Fl_Osc_Dial | class Fl_Osc_Dial | ||||
| @@ -916,7 +916,7 @@ refresh();} | |||||
| } { | } { | ||||
| Fl_Pack harmonics {open | Fl_Pack harmonics {open | ||||
| xywh {15 350 650 225} type HORIZONTAL | 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 | Function {~OscilEditor()} {open | ||||
| } { | } { | ||||
| code {osceditUI->hide(); | 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 h[i]; | ||||
| delete oscilo; | delete oscilo; | ||||
| @@ -970,7 +970,7 @@ delete osceditUI;} {} | |||||
| Function {refresh()} {} { | Function {refresh()} {} { | ||||
| code {magtype->update(); | 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"); | osc->requestValue(loc+"prepare"); | ||||
| @@ -989,7 +989,7 @@ oscils->update(); | |||||
| oscilo_base->update(); | oscilo_base->update(); | ||||
| oscils_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 | decl {std::string loc;} {private local | ||||
| } | } | ||||
| @@ -84,8 +84,7 @@ initialized = true;} {selected | |||||
| } { | } { | ||||
| Fl_Box dummy { | Fl_Box dummy { | ||||
| code0 {padnotewindow->osc = osc_i; padnotewindow->base = location;} | |||||
| code1 {puts("dummy setup done...");} | |||||
| code0 {padnotewindow->init(osc_i, location);} | |||||
| } | } | ||||
| Fl_Tabs {} { | Fl_Tabs {} { | ||||
| callback {if (o->value()!=harmonicstructuregroup) applybutton->hide(); | callback {if (o->value()!=harmonicstructuregroup) applybutton->hide(); | ||||
| @@ -418,8 +418,7 @@ if (event==FL_RIGHT_MOUSE){ | |||||
| } { | } { | ||||
| Fl_Box {} { | Fl_Box {} { | ||||
| xywh {0 0 0 0} | 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 {} { | Fl_Check_Button {} { | ||||
| label Expr | label Expr | ||||
| @@ -610,8 +609,7 @@ else {propta->deactivate();proptb->deactivate();}} | |||||
| } { | } { | ||||
| Fl_Box {} { | Fl_Box {} { | ||||
| xywh {0 0 0 0} | xywh {0 0 0 0} | ||||
| code0 {partfx->osc = osc;} | |||||
| code1 {partfx->base = part_path;} | |||||
| code0 {partfx->init(osc, part_path);} | |||||
| } | } | ||||
| Fl_Counter inseffnocounter { | Fl_Counter inseffnocounter { | ||||
| label {FX No.} | label {FX No.} | ||||
| @@ -726,12 +724,12 @@ if (x==2) part->partefx[ninseff]->setdryonly(true); | |||||
| } | } | ||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label C | 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 | xywh {90 127 25 15} box THIN_UP_BOX color 179 labelfont 1 labelsize 11 labelcolor 55 | ||||
| } | } | ||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label P | 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 | 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 {} { | Fl_Box {} { | ||||
| xywh {0 0 0 0} | 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 {} { | Fl_Button {} { | ||||
| label {Close Window} | label {Close Window} | ||||
| @@ -824,8 +821,7 @@ if (x==2) part->partefx[ninseff]->setdryonly(true); | |||||
| } { | } { | ||||
| Fl_Box {} { | Fl_Box {} { | ||||
| xywh {0 0 0 0} | 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 { | Fl_Group editgroup { | ||||
| xywh {0 220 395 110} box UP_FRAME | xywh {0 220 395 110} box UP_FRAME | ||||
| @@ -1093,7 +1089,7 @@ delete(instrumenteditwindow);} {} | |||||
| } | } | ||||
| decl {BankUI *bankui;} {private local | decl {BankUI *bankui;} {private local | ||||
| } | } | ||||
| decl {ADnoteUI *adnoteui;} {private local | |||||
| decl {ADnoteUI *adnoteui;} {selected public local | |||||
| } | } | ||||
| decl {SUBnoteUI *subnoteui;} {private local | decl {SUBnoteUI *subnoteui;} {private local | ||||
| } | } | ||||
| @@ -60,7 +60,7 @@ class PresetsUI {} { | |||||
| printf("Value = %s\\n", val.c_str()); | printf("Value = %s\\n", val.c_str()); | ||||
| copytypetext->label(val.c_str()); | copytypetext->label(val.c_str()); | ||||
| pastetypetext->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(); | pastebrowse->clear(); | ||||
| printf("Datamodel Update..."); | printf("Datamodel Update..."); | ||||
| datamodel.update(url+"preset-type"); | |||||
| datamodel.doUpdate(url+"preset-type"); | |||||
| } {} | } {} | ||||
| } | } | ||||
| decl {std::string url;} {public local | decl {std::string url;} {public local | ||||
| @@ -184,7 +184,7 @@ class SUBnoteUI {open : {public PresetsUI_} | |||||
| } { | } { | ||||
| Fl_Box {} { | Fl_Box {} { | ||||
| xywh {0 0 0 0} box FLAT_BOX color 45 | xywh {0 0 0 0} box FLAT_BOX color 45 | ||||
| code0 {SUBparameters->osc = osc; SUBparameters->base = loc;} | |||||
| code0 {SUBparameters->init(osc, loc);} | |||||
| } | } | ||||
| Fl_Scroll {} { | Fl_Scroll {} { | ||||
| label scroll open | 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 | // generated by Fast Light User Interface Designer (fluid) version 1.0107f | ||||
| #include "WidgetPDial.h" | |||||
| #include <cstdio> | |||||
| #include <iostream> | |||||
| #include <cmath> | #include <cmath> | ||||
| #include <string> | |||||
| #include <FL/Fl_Tooltip.H> | |||||
| #include <FL/fl_draw.H> | #include <FL/fl_draw.H> | ||||
| #include <FL/Fl_Group.H> | |||||
| #include <FL/Fl_Menu_Window.H> | |||||
| #include "../Misc/Util.h" | #include "../Misc/Util.h" | ||||
| #include "WidgetPDial.h" | |||||
| //Copyright (c) 2003-2005 Nasca Octavian Paul | //Copyright (c) 2003-2005 Nasca Octavian Paul | ||||
| //License: GNU GPL version 2 or later | //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; | //static int numobj = 0; | ||||
| WidgetPDial::WidgetPDial(int x, int y, int w, int h, const char *label) | 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; | //cout << "[" << label << "] There are now " << ++numobj << endl; | ||||
| Fl_Group *save = Fl_Group::current(); | Fl_Group *save = Fl_Group::current(); | ||||
| @@ -113,27 +28,46 @@ WidgetPDial::~WidgetPDial() | |||||
| delete tipwin; | delete tipwin; | ||||
| } | } | ||||
| void WidgetPDial::setRounding(unsigned int digits) | |||||
| { | |||||
| tipwin->setRounding(digits); | |||||
| } | |||||
| int WidgetPDial::handle(int event) | 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) { | switch(event) { | ||||
| case FL_PUSH: | case FL_PUSH: | ||||
| if (integer_step) | |||||
| setRounding(0); | |||||
| else if (Fl::event_shift()) | |||||
| setRounding(4); | |||||
| else | |||||
| setRounding(Fl::event_button1() ? 2 : 3); | |||||
| oldvalue = value(); | oldvalue = value(); | ||||
| old_y = Fl::event_y(); | |||||
| case FL_DRAG: | case FL_DRAG: | ||||
| getPos(); | 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(); | value_damage(); | ||||
| if(this->when() != 0) | if(this->when() != 0) | ||||
| do_callback(); | do_callback(); | ||||
| @@ -141,14 +75,28 @@ int WidgetPDial::handle(int event) | |||||
| case FL_MOUSEWHEEL: | case FL_MOUSEWHEEL: | ||||
| if (Fl::belowmouse() != this) | if (Fl::belowmouse() != this) | ||||
| return 1; | 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(); | value_damage(); | ||||
| if(this->when() != 0) | if(this->when() != 0) | ||||
| do_callback(); | do_callback(); | ||||
| @@ -163,6 +111,10 @@ int WidgetPDial::handle(int event) | |||||
| resetPos(); | resetPos(); | ||||
| break; | break; | ||||
| case FL_RELEASE: | case FL_RELEASE: | ||||
| if (integer_step) { | |||||
| int rounded = value() + 0.5; | |||||
| value(clamp(rounded)); | |||||
| } | |||||
| tipwin->hide(); | tipwin->hide(); | ||||
| resetPos(); | resetPos(); | ||||
| if(this->when() == 0) | if(this->when() == 0) | ||||
| @@ -262,3 +214,14 @@ void WidgetPDial::resetPos() | |||||
| { | { | ||||
| pos = false; | 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 | #ifndef WIDGETPDIAL_h | ||||
| #define WIDGETPDIAL_h | #define WIDGETPDIAL_h | ||||
| #include <FL/Fl_Dial.H> | #include <FL/Fl_Dial.H> | ||||
| #include "TipWin.h" | |||||
| class WidgetPDial:public Fl_Dial | class WidgetPDial:public Fl_Dial | ||||
| @@ -14,12 +15,21 @@ class WidgetPDial:public Fl_Dial | |||||
| void draw(); | void draw(); | ||||
| void pdialcolor(int r, int g, int b); | void pdialcolor(int r, int g, int b); | ||||
| void tooltip(const char *c); | 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: | private: | ||||
| void getPos(); | void getPos(); | ||||
| void resetPos(); | void resetPos(); | ||||
| double oldvalue; | double oldvalue; | ||||
| int old_y; | |||||
| bool pos; | bool pos; | ||||
| bool textset; | bool textset; | ||||
| class TipWin * tipwin; | class TipWin * tipwin; | ||||
| float value_offset; | |||||
| float value_scale; | |||||
| }; | }; | ||||
| #endif | #endif | ||||
| @@ -541,6 +541,7 @@ int main(int argc, char *argv[]) | |||||
| lo_server_add_method(server, NULL, NULL, handler_function, 0); | lo_server_add_method(server, NULL, NULL, handler_function, 0); | ||||
| sendtourl = argv[1]; | sendtourl = argv[1]; | ||||
| } | } | ||||
| fprintf(stderr, "ext client running on %d\n", lo_server_get_port(server)); | |||||
| gui = GUI::createUi(new UI_Interface(), &Pexitprogram); | gui = GUI::createUi(new UI_Interface(), &Pexitprogram); | ||||
| @@ -24,7 +24,7 @@ | |||||
| #include "Misc/Util.h" | #include "Misc/Util.h" | ||||
| #include "globals.h" | #include "globals.h" | ||||
| void SYNTH_T::alias() | |||||
| void SYNTH_T::alias(bool randomize) | |||||
| { | { | ||||
| halfsamplerate_f = (samplerate_f = samplerate) / 2.0f; | halfsamplerate_f = (samplerate_f = samplerate) / 2.0f; | ||||
| buffersize_f = buffersize; | buffersize_f = buffersize; | ||||
| @@ -36,5 +36,8 @@ void SYNTH_T::alias() | |||||
| // for deleting the buffers and also call it in the dtor | // for deleting the buffers and also call it in the dtor | ||||
| denormalkillbuf.resize(buffersize); | denormalkillbuf.resize(buffersize); | ||||
| for(int i = 0; i < buffersize; ++i) | 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 SynthNote; | ||||
| class Allocator; | class Allocator; | ||||
| class AbsTime; | |||||
| class RelTime; | |||||
| class Microtonal; | class Microtonal; | ||||
| class XMLwrapper; | class XMLwrapper; | ||||
| @@ -142,6 +144,14 @@ typedef std::complex<fftw_real> fft_t; | |||||
| */ | */ | ||||
| #define PART_MAX_NAME_LEN 30 | #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 | * The maximum number of bands of the equaliser | ||||
| */ | */ | ||||
| @@ -270,8 +280,8 @@ public: | |||||
| operator T*() { return ptr; } | operator T*() { return ptr; } | ||||
| operator const T*() const { 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 | //temporary include for synth->{samplerate/buffersize} members | ||||
| @@ -280,7 +290,7 @@ struct SYNTH_T { | |||||
| SYNTH_T(void) | SYNTH_T(void) | ||||
| :samplerate(44100), buffersize(256), oscilsize(1024) | :samplerate(44100), buffersize(256), oscilsize(1024) | ||||
| { | { | ||||
| alias(); | |||||
| alias(false); | |||||
| } | } | ||||
| SYNTH_T(const SYNTH_T& ) = delete; | SYNTH_T(const SYNTH_T& ) = delete; | ||||
| @@ -320,7 +330,7 @@ struct SYNTH_T { | |||||
| { | { | ||||
| return buffersize_f / samplerate_f; | return buffersize_f / samplerate_f; | ||||
| } | } | ||||
| void alias(void); | |||||
| void alias(bool randomize=true); | |||||
| static float numRandom(void); //defined in Util.cpp for now | static float numRandom(void); //defined in Util.cpp for now | ||||
| }; | }; | ||||
| #endif | #endif | ||||
| @@ -138,7 +138,7 @@ int main(int argc, char *argv[]) | |||||
| << " Copyright (c) 2009-2014 Mark McCurry [active maintainer]" | << " Copyright (c) 2009-2014 Mark McCurry [active maintainer]" | ||||
| << endl; | << endl; | ||||
| cerr << "Compiled: " << __DATE__ << " " << __TIME__ << 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; | cerr << "it comes with ABSOLUTELY NO WARRANTY.\n" << endl; | ||||
| if(argc == 1) | if(argc == 1) | ||||
| cerr << "Try 'zynaddsubfx --help' for command-line options." << endl; | cerr << "Try 'zynaddsubfx --help' for command-line options." << endl; | ||||
| @@ -211,6 +211,9 @@ int main(int argc, char *argv[]) | |||||
| { | { | ||||
| "dump-oscdoc", 2, NULL, 'd' | "dump-oscdoc", 2, NULL, 'd' | ||||
| }, | }, | ||||
| { | |||||
| "ui-title", 1, NULL, 'u' | |||||
| }, | |||||
| { | { | ||||
| 0, 0, 0, 0 | 0, 0, 0, 0 | ||||
| } | } | ||||
| @@ -219,7 +222,7 @@ int main(int argc, char *argv[]) | |||||
| int option_index = 0, opt, exitwithhelp = 0, exitwithversion = 0; | int option_index = 0, opt, exitwithhelp = 0, exitwithversion = 0; | ||||
| int prefered_port = -1; | int prefered_port = -1; | ||||
| string loadfile, loadinstrument, execAfterInit; | |||||
| string loadfile, loadinstrument, execAfterInit, ui_title; | |||||
| while(1) { | while(1) { | ||||
| int tmp = 0; | int tmp = 0; | ||||
| @@ -227,7 +230,7 @@ int main(int argc, char *argv[]) | |||||
| /**\todo check this process for a small memory leak*/ | /**\todo check this process for a small memory leak*/ | ||||
| opt = getopt_long(argc, | opt = getopt_long(argc, | ||||
| argv, | 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, | opts, | ||||
| &option_index); | &option_index); | ||||
| char *optarguments = optarg; | char *optarguments = optarg; | ||||
| @@ -335,6 +338,10 @@ int main(int argc, char *argv[]) | |||||
| outfile << s; | outfile << s; | ||||
| } | } | ||||
| break; | break; | ||||
| case 'u': | |||||
| if(optarguments) | |||||
| ui_title = optarguments; | |||||
| break; | |||||
| case '?': | case '?': | ||||
| cerr << "ERROR:Bad option or parameter.\n" << endl; | cerr << "ERROR:Bad option or parameter.\n" << endl; | ||||
| exitwithhelp = 1; | exitwithhelp = 1; | ||||
| @@ -370,6 +377,7 @@ int main(int argc, char *argv[]) | |||||
| << " -I , --input\t\t\t\t Set Input Engine\n" | << " -I , --input\t\t\t\t Set Input Engine\n" | ||||
| << " -e , --exec-after-init\t\t Run post-initialization script\n" | << " -e , --exec-after-init\t\t Run post-initialization script\n" | ||||
| << " -d , --dump-oscdoc=FILE\t\t Dump oscdoc xml to file\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; | << endl; | ||||
| return 0; | return 0; | ||||
| @@ -384,20 +392,26 @@ int main(int argc, char *argv[]) | |||||
| initprogram(std::move(synth), &config, prefered_port); | initprogram(std::move(synth), &config, prefered_port); | ||||
| bool altered_master = false; | |||||
| if(!loadfile.empty()) { | 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) { | if(tmp < 0) { | ||||
| cerr << "ERROR: Could not load master file " << loadfile | cerr << "ERROR: Could not load master file " << loadfile | ||||
| << "." << endl; | << "." << endl; | ||||
| exit(1); | exit(1); | ||||
| } | } | ||||
| else { | else { | ||||
| strncpy(master->last_xmz, filename, XMZ_PATH_MAX); | |||||
| master->last_xmz[XMZ_PATH_MAX-1] = 0; | |||||
| master->applyparameters(); | master->applyparameters(); | ||||
| cout << "Master file loaded." << endl; | cout << "Master file loaded." << endl; | ||||
| } | } | ||||
| } | } | ||||
| if(!loadinstrument.empty()) { | if(!loadinstrument.empty()) { | ||||
| altered_master = true; | |||||
| int loadtopart = 0; | int loadtopart = 0; | ||||
| int tmp = master->part[loadtopart]->loadXMLinstrument( | int tmp = master->part[loadtopart]->loadXMLinstrument( | ||||
| loadinstrument.c_str()); | loadinstrument.c_str()); | ||||
| @@ -413,6 +427,9 @@ int main(int argc, char *argv[]) | |||||
| } | } | ||||
| } | } | ||||
| if(altered_master) | |||||
| middleware->updateResources(master); | |||||
| //Run the Nio system | //Run the Nio system | ||||
| bool ioGood = Nio::start(); | bool ioGood = Nio::start(); | ||||
| @@ -447,6 +464,10 @@ int main(int argc, char *argv[]) | |||||
| delete [] msg; | delete [] msg; | ||||
| } | } | ||||
| //set titles | |||||
| if(!ui_title.empty()) | |||||
| GUI::raiseUi(gui, "/ui/title", "s", ui_title.c_str()); | |||||
| if(!noui) | if(!noui) | ||||
| { | { | ||||
| GUI::raiseUi(gui, "/show", "i", config.cfg.UserInterfaceMode); | GUI::raiseUi(gui, "/show", "i", config.cfg.UserInterfaceMode); | ||||
| @@ -1,5 +1,5 @@ | |||||
| #include "ports.h" | |||||
| #include <rtosc/ports.h> | |||||
| #include <cstring> | #include <cstring> | ||||
| #include <algorithm> | #include <algorithm> | ||||
| #include <map> | #include <map> | ||||
| @@ -8,7 +8,7 @@ | |||||
| #include <utility> | #include <utility> | ||||
| #include <cassert> | #include <cassert> | ||||
| #include "miditable.h" | |||||
| #include <rtosc/miditable.h> | |||||
| using namespace rtosc; | using namespace rtosc; | ||||
| using std::string; | using std::string; | ||||
| @@ -1,5 +1,5 @@ | |||||
| #include <math.h> | #include <math.h> | ||||
| #include "miditable.h" | |||||
| #include <rtosc/miditable.h> | |||||
| using namespace rtosc; | using namespace rtosc; | ||||
| @@ -1,4 +1,4 @@ | |||||
| #include "ports.h" | |||||
| #include "../ports.h" | |||||
| #include <ostream> | #include <ostream> | ||||
| #include <cassert> | #include <cassert> | ||||
| #include <climits> | #include <climits> | ||||
| @@ -255,15 +255,15 @@ bool has(T &t, Z&z) | |||||
| return false; | 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; | ivec_t pos; | ||||
| int current_dups = strs.size(); | int current_dups = strs.size(); | ||||
| int N = 0; | int N = 0; | ||||
| for(auto w:strs) | for(auto w:strs) | ||||
| N = rtosc_max(N,w.length()); | |||||
| N = int_max(N,w.length()); | |||||
| int pos_best = -1; | int pos_best = -1; | ||||
| int pos_best_val = INT_MAX; | int pos_best_val = INT_MAX; | ||||
| @@ -294,7 +294,7 @@ ivec_t find_pos(words_t &strs) | |||||
| return pos; | 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_t ivec; | ||||
| ivec.reserve(strs.size()); | 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; | 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; | ivec_t assoc; | ||||
| int current_dups = strs.size(); | int current_dups = strs.size(); | ||||
| @@ -354,7 +354,7 @@ ivec_t find_assoc(const words_t &strs, const ivec_t &pos) | |||||
| return assoc; | 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; | ivec_t remap; | ||||
| auto hashed = do_hash(strs, pos, assoc); | 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()); | // printf("%d) '%s'\n", hashed[i], strs[i].c_str()); | ||||
| int N = 0; | int N = 0; | ||||
| for(auto h:hashed) | for(auto h:hashed) | ||||
| N = rtosc_max(N,h+1); | |||||
| N = int_max(N,h+1); | |||||
| for(int i=0; i<N; ++i) | for(int i=0; i<N; ++i) | ||||
| remap.push_back(0); | remap.push_back(0); | ||||
| for(int i=0; i<(int)hashed.size(); ++i) | 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; | 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); | pm.pos = find_pos(str); | ||||
| if(pm.pos.empty()) { | 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); | 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; | svec_t keys; | ||||
| cvec_t args; | cvec_t args; | ||||
| @@ -575,6 +575,71 @@ const Port *Ports::apropos(const char *path) const | |||||
| return NULL; | 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, | void rtosc::walk_ports(const Ports *base, | ||||
| char *name_buffer, | char *name_buffer, | ||||
| size_t buffer_size, | size_t buffer_size, | ||||
| @@ -670,7 +735,7 @@ void walk_ports2(const rtosc::Ports *base, | |||||
| //for(unsigned i=0; i<max; ++i) | //for(unsigned i=0; i<max; ++i) | ||||
| { | { | ||||
| sprintf(pos,"[0,%d]",max); | |||||
| sprintf(pos,"[0,%d]",max-1); | |||||
| //Ensure the result is a path | //Ensure the result is a path | ||||
| if(strrchr(name_buffer, '/')[1] != '/') | if(strrchr(name_buffer, '/')[1] != '/') | ||||
| @@ -697,7 +762,7 @@ void walk_ports2(const rtosc::Ports *base, | |||||
| //for(unsigned i=0; i<max; ++i) | //for(unsigned i=0; i<max; ++i) | ||||
| { | { | ||||
| sprintf(pos,"[0,%d]",max); | |||||
| sprintf(pos,"[0,%d]",max-1); | |||||
| //Apply walker function | //Apply walker function | ||||
| walker(&p, name_buffer, data); | walker(&p, name_buffer, data); | ||||
| @@ -724,54 +789,110 @@ static void units(std::ostream &o, const char *u) | |||||
| o << " units=\"" << 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) | 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; | 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) { | 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; | 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; | return; | ||||
| } | } | ||||
| o << " <message_in pattern=\"" << name << "\" typetag=\"" << type << "\">\n"; | 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\""; | o << " <param_" << type << " symbol=\"x\""; | ||||
| units(o, meta["unit"]); | units(o, meta["unit"]); | ||||
| o << ">\n"; | 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>\n"; | ||||
| o << " <message_in pattern=\"" << name << "\" typetag=\"\">\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_in>\n"; | ||||
| o << " <message_out pattern=\"" << name << "\" typetag=\"" << type << "\">\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\""; | o << " <param_" << type << " symbol=\"x\""; | ||||
| units(o, meta["unit"]); | units(o, meta["unit"]); | ||||
| o << ">\n"; | o << ">\n"; | ||||
| @@ -803,10 +923,17 @@ void dump_ports_cb(const rtosc::Port *p, const char *name, void *v) | |||||
| o << "/>\n"; | o << "/>\n"; | ||||
| } | } | ||||
| o << " </message_out>\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) | 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 <cstring> | ||||
| #include <cassert> | #include <cassert> | ||||
| using namespace rtosc; | 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. | * Append another message onto a bundle if the space permits it. | ||||
| * If insufficient space is available, then zero is returned and the buffer is | * 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) | if(max_len < dst_len + src_len + 4 || dst_len == 0 || src_len == 0) | ||||
| return 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); | memcpy(dst+dst_len+4, src, src_len); | ||||
| @@ -1,4 +1,4 @@ | |||||
| #include "thread-link.h" | |||||
| #include "../thread-link.h" | |||||
| namespace rtosc { | namespace rtosc { | ||||
| #ifdef off_t | #ifdef off_t | ||||
| @@ -17,7 +17,6 @@ struct internal_ringbuffer_t { | |||||
| }; | }; | ||||
| typedef internal_ringbuffer_t ringbuffer_t; | typedef internal_ringbuffer_t ringbuffer_t; | ||||
| #define static | |||||
| static size_t ring_read_size(ringbuffer_t *ring) | 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;} | size_t ThreadLink::buffer_size(void) const {return BufferSize;} | ||||
| }; | }; | ||||
| #undef static | |||||
| @@ -3,8 +3,8 @@ | |||||
| #include <cstdio> | #include <cstdio> | ||||
| #include <cassert> | #include <cassert> | ||||
| #include <ctime> | #include <ctime> | ||||
| #include "rtosc.h" | |||||
| #include "undo-history.h" | |||||
| #include <rtosc/rtosc.h> | |||||
| #include <rtosc/undo-history.h> | |||||
| using std::pair; | using std::pair; | ||||
| using std::make_pair; | using std::make_pair; | ||||
| @@ -1,7 +1,13 @@ | |||||
| #include "rtosc.h" | |||||
| #include <rtosc/rtosc.h> | |||||
| #include <ctype.h> | #include <ctype.h> | ||||
| #include <assert.h> | |||||
| #include <string.h> | |||||
| #include <strings.h> | |||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <stdio.h> | |||||
| static bool rtosc_match_number(const char **pattern, const char **msg) | static bool rtosc_match_number(const char **pattern, const char **msg) | ||||
| { | { | ||||
| //Verify both hold digits | //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 rtosc_match_args(arg_pattern, msg); | ||||
| return true; | 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 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||
| * DEALINGS IN THE SOFTWARE. | * 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 <string.h> | ||||
| #include <algorithm> | #include <algorithm> | ||||
| #include <map> | #include <map> | ||||
| @@ -276,3 +277,4 @@ class MidiTable | |||||
| }; | }; | ||||
| }; | }; | ||||
| #endif | |||||
| @@ -43,8 +43,8 @@ struct rtosc_hack_decltype_t | |||||
| #define STRINGIFY(a) STRINGIFY2(a) | #define STRINGIFY(a) STRINGIFY2(a) | ||||
| //Helper for documenting varargs | //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_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_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) | #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_I(mac, count, ...) MAC_EACH_IMP(mac, count, __VA_ARGS__) | ||||
| #define MAC_EACH(mac, ...) MAC_EACH_I(mac, LAST_IMP(__VA_ARGS__), __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) \ | #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(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(7,h) rOpt(8,i) | ||||
| @@ -137,7 +161,7 @@ struct rtosc_hack_decltype_t | |||||
| //Alias operators | //Alias operators | ||||
| #define rParams(name, length, ...) \ | #define rParams(name, length, ...) \ | ||||
| rArray(name, length, __VA_ARGS__), \ | 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;} | 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] | //Recursion [two ports in one for pointer manipulation] | ||||
| #define rRecur(name, ...) \ | #define rRecur(name, ...) \ | ||||
| {STRINGIFY(name) "/", DOC(__VA_ARGS__), &decltype(rObject::name)::ports, rRecurCb(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, ...) \ | #define rRecurp(name, ...) \ | ||||
| {STRINGIFY(name) "/", DOC(__VA_ARGS__), \ | {STRINGIFY(name) "/", DOC(__VA_ARGS__), \ | ||||
| @@ -168,7 +192,7 @@ template<class T> constexpr T spice(T*t) {return *t;} | |||||
| //Misc | //Misc | ||||
| #define rDummy(name, ...) {STRINIFY(name), rProp(dummy), NULL, [](msg_t, rtosc::RtData &){}} | #define rDummy(name, ...) {STRINIFY(name), rProp(dummy), NULL, [](msg_t, rtosc::RtData &){}} | ||||
| #define rString(name, len, ...) \ | #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 | //General property operators | ||||
| #define rMap(name, value) ":" STRINGIFY(name) "\0=" STRINGIFY(value) "\0" | #define rMap(name, value) ":" STRINGIFY(name) "\0=" STRINGIFY(value) "\0" | ||||
| @@ -28,7 +28,7 @@ | |||||
| #include <vector> | #include <vector> | ||||
| #include <functional> | #include <functional> | ||||
| #include <initializer_list> | #include <initializer_list> | ||||
| #include "rtosc.h" | |||||
| #include <rtosc/rtosc.h> | |||||
| #include <cstring> | #include <cstring> | ||||
| #include <cctype> | #include <cctype> | ||||
| #include <cstdlib> | #include <cstdlib> | ||||
| @@ -180,6 +180,13 @@ struct Ports | |||||
| */ | */ | ||||
| const Port *apropos(const char *path) const; | 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: | private: | ||||
| //Performance hacks | //Performance hacks | ||||
| class Port_Matcher *impl; | class Port_Matcher *impl; | ||||
| @@ -6,7 +6,7 @@ | |||||
| #include <ctype.h> | #include <ctype.h> | ||||
| #include <assert.h> | #include <assert.h> | ||||
| #include "rtosc.h" | |||||
| #include <rtosc/rtosc.h> | |||||
| const char *rtosc_argument_string(const char *msg) | 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) | static unsigned arg_off(const char *msg, unsigned idx) | ||||
| { | { | ||||
| if(!has_reserved(rtosc_type(msg,idx))) | if(!has_reserved(rtosc_type(msg,idx))) | ||||
| @@ -97,45 +149,11 @@ static unsigned arg_off(const char *msg, unsigned idx) | |||||
| ++args; | ++args; | ||||
| while(idx--) { | 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; | return arg_pos-(uint8_t*)msg; | ||||
| } | } | ||||
| @@ -385,10 +403,9 @@ size_t rtosc_amessage(char *buffer, | |||||
| return pos; | 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}; | rtosc_arg_t result = {0}; | ||||
| char type = rtosc_type(msg, idx); | |||||
| //trivial case | //trivial case | ||||
| if(!has_reserved(type)) { | if(!has_reserved(type)) { | ||||
| switch(type) | switch(type) | ||||
| @@ -403,7 +420,6 @@ rtosc_arg_t rtosc_argument(const char *msg, unsigned idx) | |||||
| ; | ; | ||||
| } | } | ||||
| } else { | } else { | ||||
| const unsigned char *arg_pos = (const unsigned char*)msg+arg_off(msg,idx); | |||||
| switch(type) | switch(type) | ||||
| { | { | ||||
| case 'h': | case 'h': | ||||
| @@ -450,6 +466,52 @@ rtosc_arg_t rtosc_argument(const char *msg, unsigned idx) | |||||
| return result; | 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) | static unsigned char deref(unsigned pos, ring_t *ring) | ||||
| { | { | ||||
| return pos<ring[0].len ? ring[0].data[pos] : | 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 | unsigned pos = 8+8;//goto first length field | ||||
| uint32_t advance = 0; | uint32_t advance = 0; | ||||
| do { | 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) | if(advance) | ||||
| pos += 4+advance; | pos += 4+advance; | ||||
| } while(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); | size_t observed_length = rtosc_message_length(msg, len); | ||||
| return observed_length == 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, ...) | 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); | memset(buffer, 0, len); | ||||
| strcpy(buffer, "#bundle"); | strcpy(buffer, "#bundle"); | ||||
| buffer += 8; | buffer += 8; | ||||
| (*(uint64_t*)buffer) = tt; | |||||
| buffer +=8; | |||||
| emplace_uint64((uint8_t*)buffer, tt); | |||||
| buffer += 8; | |||||
| va_list va; | va_list va; | ||||
| va_start(va, elms); | va_start(va, elms); | ||||
| for(int i=0; i<elms; ++i) { | for(int i=0; i<elms; ++i) { | ||||
| const char *msg = va_arg(va, const char*); | const char *msg = va_arg(va, const char*); | ||||
| //It is assumed that any passed message/bundle is valid | //It is assumed that any passed message/bundle is valid | ||||
| size_t size = rtosc_message_length(msg, -1); | size_t size = rtosc_message_length(msg, -1); | ||||
| *(uint32_t*)buffer = size; | |||||
| emplace_uint32((uint8_t*)buffer, size); | |||||
| buffer += 4; | buffer += 4; | ||||
| memcpy(buffer, msg, size); | memcpy(buffer, msg, size); | ||||
| buffer+=size; | buffer+=size; | ||||
| @@ -623,14 +728,14 @@ size_t rtosc_bundle(char *buffer, size_t len, uint64_t tt, int elms, ...) | |||||
| return buffer-_buffer; | return buffer-_buffer; | ||||
| } | } | ||||
| #define POS ((size_t)(((const char *)lengths) - buffer)) | #define POS ((size_t)(((const char *)lengths) - buffer)) | ||||
| size_t rtosc_bundle_elements(const char *buffer, size_t len) | size_t rtosc_bundle_elements(const char *buffer, size_t len) | ||||
| { | { | ||||
| const uint32_t *lengths = (const uint32_t*) (buffer+16); | const uint32_t *lengths = (const uint32_t*) (buffer+16); | ||||
| size_t elms = 0; | 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) | if(POS > len) | ||||
| break; | break; | ||||
| @@ -644,7 +749,10 @@ const char *rtosc_bundle_fetch(const char *buffer, unsigned elm) | |||||
| { | { | ||||
| const uint32_t *lengths = (const uint32_t*) (buffer+16); | const uint32_t *lengths = (const uint32_t*) (buffer+16); | ||||
| size_t elm_pos = 0; | 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); | 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); | const uint32_t *lengths = (const uint32_t*) (buffer+16); | ||||
| size_t elm_pos = 0; | size_t elm_pos = 0; | ||||
| size_t last_len = 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; | return last_len; | ||||
| @@ -669,5 +777,5 @@ int rtosc_bundle_p(const char *msg) | |||||
| uint64_t rtosc_bundle_timetag(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); | 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 | * Blob data may be safely written to | ||||
| * @param msg OSC message | * @param msg OSC message | ||||