diff --git a/.gitignore b/.gitignore index 32fcf0614..e198ae035 100644 --- a/.gitignore +++ b/.gitignore @@ -108,6 +108,7 @@ data/windows/Carla-*-win64/ source/bridges/jackplugin/libjack.so.0 source/frontend/Makefile source/tests/ansi-pedantic-test_* +source/tests/CachedPlugins source/tests/CarlaRingBuffer source/tests/CarlaPipeUtils source/tests/CarlaString diff --git a/source/native-plugins/Makefile b/source/native-plugins/Makefile index a65d96817..a5e59b220 100644 --- a/source/native-plugins/Makefile +++ b/source/native-plugins/Makefile @@ -331,17 +331,17 @@ $(OBJDIR)/zynaddsubfx-fx.cpp.o: zynaddsubfx-fx.cpp $(ZYN_UI_FILES_H) $(OBJDIR)/zynaddsubfx-synth.cpp.o: zynaddsubfx-synth.cpp $(ZYN_UI_FILES_H) -@mkdir -p $(OBJDIR) @echo "Compiling $<" - @$(CXX) $< $(ZYN_CXX_FLAGS) -c -o $@ + @$(CXX) $< $(ZYN_CXX_FLAGS) -Wno-unused-parameter -c -o $@ $(OBJDIR)/zynaddsubfx-src.cpp.o: zynaddsubfx-src.cpp $(ZYN_UI_FILES_H) -@mkdir -p $(OBJDIR) @echo "Compiling $<" - @$(CXX) $< $(ZYN_CXX_FLAGS) -Wno-unused-parameter -c -o $@ + @$(CXX) $< $(ZYN_CXX_FLAGS) -Wno-unused-parameter -Wno-unused-variable -c -o $@ $(OBJDIR)/zynaddsubfx-ui.cpp.o: zynaddsubfx-ui.cpp $(ZYN_UI_FILES_H) $(ZYN_UI_FILES_CPP) -@mkdir -p $(OBJDIR) @echo "Compiling $<" - $(CXX) $< $(ZYN_CXX_FLAGS) -Wno-unused-parameter -Wno-unused-variable -c -o $@ + @$(CXX) $< $(ZYN_CXX_FLAGS) -Wno-unused-parameter -Wno-unused-variable -c -o $@ # ---------------------------------------------------------------------------------------------------------------------------- diff --git a/source/native-plugins/zynaddsubfx-src.cpp b/source/native-plugins/zynaddsubfx-src.cpp index e1691b5e8..d76758378 100644 --- a/source/native-plugins/zynaddsubfx-src.cpp +++ b/source/native-plugins/zynaddsubfx-src.cpp @@ -220,6 +220,13 @@ extern "C" { #undef rChangeCb #define rChangeCb +#include "zynaddsubfx/Misc/CallbackRepeater.cpp" +#undef rObject +#undef rStdString +#undef rStdStringCb +#undef rChangeCb +#define rChangeCb + #include "zynaddsubfx/Misc/Config.cpp" #undef rObject #undef rStdString diff --git a/source/native-plugins/zynaddsubfx-synth.cpp b/source/native-plugins/zynaddsubfx-synth.cpp index a662418f3..4db327609 100644 --- a/source/native-plugins/zynaddsubfx-synth.cpp +++ b/source/native-plugins/zynaddsubfx-synth.cpp @@ -286,6 +286,7 @@ public: fMiddleWare(nullptr), fMaster(nullptr), fSynth(), + fDefaultState(nullptr), fMutex(), fMiddleWareThread(new MiddleWareThread()) { @@ -324,6 +325,8 @@ public: _initMaster(); _setMasterParameters(); + fMaster->getalldata(&fDefaultState); + fMiddleWareThread->start(fMiddleWare); } @@ -331,6 +334,7 @@ public: { fMiddleWareThread->stop(); _deleteMaster(); + std::free(fDefaultState); } protected: @@ -594,7 +598,7 @@ protected: if (bank == 0) { // reset part to default - // TODO + setState(fDefaultState); return; } @@ -828,6 +832,7 @@ private: Master* fMaster; SYNTH_T fSynth; Config fConfig; + char* fDefaultState; float fParameters[kParamCount]; diff --git a/source/native-plugins/zynaddsubfx/Containers/NotePool.cpp b/source/native-plugins/zynaddsubfx/Containers/NotePool.cpp index e1535fb6c..e493bfb7c 100644 --- a/source/native-plugins/zynaddsubfx/Containers/NotePool.cpp +++ b/source/native-plugins/zynaddsubfx/Containers/NotePool.cpp @@ -1,18 +1,69 @@ #include "NotePool.h" -//XXX eliminate dependence on Part.h -#include "../Misc/Part.h" #include "../Misc/Allocator.h" #include "../Synth/SynthNote.h" #include #include #include +#define SUSTAIN_BIT 0x04 +#define NOTE_MASK 0x03 + +enum NoteStatus { + KEY_OFF = 0x00, + KEY_PLAYING = 0x01, + KEY_RELEASED_AND_SUSTAINED = 0x02, + KEY_RELEASED = 0x03 +}; + + NotePool::NotePool(void) :needs_cleaning(0) { memset(ndesc, 0, sizeof(ndesc)); memset(sdesc, 0, sizeof(sdesc)); } + +bool NotePool::NoteDescriptor::playing(void) const +{ + return (status&NOTE_MASK) == KEY_PLAYING; +} + +bool NotePool::NoteDescriptor::sustained(void) const +{ + return (status&NOTE_MASK) == KEY_RELEASED_AND_SUSTAINED; +} + +bool NotePool::NoteDescriptor::released(void) const +{ + return (status&NOTE_MASK) == KEY_RELEASED; +} + +bool NotePool::NoteDescriptor::off(void) const +{ + return (status&NOTE_MASK) == KEY_OFF; +} + +void NotePool::NoteDescriptor::setStatus(uint8_t s) +{ + status &= ~NOTE_MASK; + status |= (NOTE_MASK&s); +} + +void NotePool::NoteDescriptor::doSustain(void) +{ + setStatus(KEY_RELEASED_AND_SUSTAINED); +} + +bool NotePool::NoteDescriptor::canSustain(void) const +{ + return !(status & SUSTAIN_BIT); +} + +void NotePool::NoteDescriptor::makeUnsustainable(void) +{ + status |= SUSTAIN_BIT; +} + NotePool::activeNotesIter NotePool::activeNotes(NoteDescriptor &n) { const int off_d1 = &n-ndesc; @@ -35,18 +86,18 @@ static int getMergeableDescriptor(uint8_t note, uint8_t sendto, bool legato, { int desc_id = 0; for(int i=0; i= POLYPHONY || ndesc[desc_id].status != Part::KEY_OFF) { + if(desc_id >= POLYPHONY || !ndesc[desc_id].off()) { return -1; } @@ -65,6 +116,28 @@ NotePool::constActiveDescIter NotePool::activeDesc(void) const return constActiveDescIter{*this}; } +int NotePool::usedNoteDesc(void) const +{ + if(needs_cleaning) + const_cast(this)->cleanup(); + + int cnt = 0; + for(int i=0; i(this)->cleanup(); + + int cnt = 0; + for(int i=0; i 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::enforceKeyLimit(int limit) +{ + int notes_to_kill = getRunningNotes() - limit; + if(notes_to_kill <= 0) + return; + + NoteDescriptor *to_kill = NULL; + unsigned oldest = 0; + for(auto &nd : activeDesc()) { + if(to_kill == NULL) { + //There must be something to kill + oldest = nd.age; + to_kill = &nd; + } else if(to_kill->released() && nd.playing()) { + //Prefer to kill off a running note + oldest = nd.age; + to_kill = &nd; + } else if(nd.age > oldest && !(to_kill->playing() && nd.released())) { + //Get an older note when it doesn't move from running to released + oldest = nd.age; + to_kill = &nd; + } + } + + if(to_kill) { + auto &tk = *to_kill; + if(tk.released() || tk.sustained()) + kill(*to_kill); + else + entomb(*to_kill); + } } void NotePool::releasePlayingNotes(void) { for(auto &d:activeDesc()) { - if(d.status == Part::KEY_PLAYING) { - d.status = Part::KEY_RELEASED; + if(d.playing()) { + d.setStatus(KEY_RELEASED); for(auto s:activeNotes(d)) s.note->releasekey(); } @@ -192,7 +289,7 @@ void NotePool::releasePlayingNotes(void) void NotePool::release(NoteDescriptor &d) { - d.status = Part::KEY_RELEASED; + d.setStatus(KEY_RELEASED); for(auto s:activeNotes(d)) s.note->releasekey(); } @@ -213,7 +310,7 @@ void NotePool::killNote(uint8_t note) void NotePool::kill(NoteDescriptor &d) { - d.status = Part::KEY_OFF; + d.setStatus(KEY_OFF); for(auto &s:activeNotes(d)) kill(s); } @@ -225,6 +322,13 @@ void NotePool::kill(SynthDescriptor &s) needs_cleaning = true; } +void NotePool::entomb(NoteDescriptor &d) +{ + d.setStatus(KEY_RELEASED); + for(auto &s:activeNotes(d)) + s.note->entomb(); +} + const char *getStatus(int status_bits) { switch(status_bits) @@ -252,7 +356,7 @@ void NotePool::cleanup(void) int last_valid_desc = 0; for(int i=0; i dt && dt >= 0) { + cb(); + last = now; + } +} diff --git a/source/native-plugins/zynaddsubfx/Misc/CallbackRepeater.h b/source/native-plugins/zynaddsubfx/Misc/CallbackRepeater.h new file mode 100644 index 000000000..e9330e9d0 --- /dev/null +++ b/source/native-plugins/zynaddsubfx/Misc/CallbackRepeater.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +struct CallbackRepeater +{ + typedef std::function cb_t ; + + //Call interval in seconds and callback + CallbackRepeater(int interval, cb_t cb_); + + //Time Check + void tick(void); + + std::time_t last; + std::time_t dt; + cb_t cb; +}; diff --git a/source/native-plugins/zynaddsubfx/Misc/Master.cpp b/source/native-plugins/zynaddsubfx/Misc/Master.cpp index 173950a11..4b1659bff 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Master.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Master.cpp @@ -237,6 +237,19 @@ static const Ports master_ports = { SNIP preset_ports.dispatch(msg, data); rBOIL_END}, + {"HDDRecorder/preparefile:s", rDoc("Init WAV file"), 0, [](const char *msg, RtData &d) { + Master *m = (Master*)d.obj; + m->HDDRecorder.preparefile(rtosc_argument(msg, 0).s, 1);}}, + {"HDDRecorder/start:", rDoc("Start recording"), 0, [](const char *, RtData &d) { + Master *m = (Master*)d.obj; + m->HDDRecorder.start();}}, + {"HDDRecorder/stop:", rDoc("Stop recording"), 0, [](const char *, RtData &d) { + Master *m = (Master*)d.obj; + m->HDDRecorder.stop();}}, + {"HDDRecorder/pause:", rDoc("Pause recording"), 0, [](const char *, RtData &d) { + Master *m = (Master*)d.obj; + m->HDDRecorder.pause();}}, + }; const Ports &Master::ports = master_ports; @@ -618,7 +631,7 @@ int msg_id=0; /* * Master audio out (the final sound) */ -bool Master::AudioOut(float *outl, float *outr) +bool Master::AudioOut(float *outr, float *outl) { //Danger Limits if(memory->lowMemory(2,1024*1024)) diff --git a/source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp b/source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp index df63a9061..d1bc7417d 100644 --- a/source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -19,6 +20,7 @@ #include #include "Util.h" +#include "CallbackRepeater.h" #include "Master.h" #include "Part.h" #include "PresetExtractor.h" @@ -576,14 +578,18 @@ public: { if(server) while(lo_server_recv_noblock(server, 0)); + while(bToU->hasNext()) { const char *rtmsg = bToU->read(); bToUhandle(rtmsg); } + while(auto *m = multi_thread_source.read()) { handleMsg(m->memory); multi_thread_source.free(m); } + + autoSave.tick(); } @@ -659,6 +665,8 @@ public: const SYNTH_T synth; PresetsStore presetsstore; + + CallbackRepeater autoSave; }; /***************************************************************************** @@ -965,6 +973,25 @@ static rtosc::Ports middwareSnoopPorts = { const char *file = rtosc_argument(msg,1).s; impl.savePart(part_id, file); rEnd}, + {"reload_auto_save:i", 0, 0, + rBegin + const int save_id = rtosc_argument(msg,0).i; + const string save_dir = string(getenv("HOME")) + "/.local"; + const string save_file = "zynaddsubfx-"+to_s(save_id)+"-autosave.xmz"; + const string save_loc = save_dir + "/" + save_file; + impl.loadMaster(save_loc.c_str()); + //XXX it would be better to remove the autosave after there is a new + //autosave, but this method should work for non-immediate crashes :-| + remove(save_loc.c_str()); + rEnd}, + {"delete_auto_save:i", 0, 0, + rBegin + const int save_id = rtosc_argument(msg,0).i; + const string save_dir = string(getenv("HOME")) + "/.local"; + const string save_file = "zynaddsubfx-"+to_s(save_id)+"-autosave.xmz"; + const string save_loc = save_dir + "/" + save_file; + remove(save_loc.c_str()); + rEnd}, {"load_xmz:s", 0, 0, rBegin; const char *file = rtosc_argument(msg, 0).s; @@ -1100,7 +1127,14 @@ static rtosc::Ports middlewareReplyPorts = { MiddleWareImpl::MiddleWareImpl(MiddleWare *mw, SYNTH_T synth_, Config* config, int preferrred_port) :parent(mw), config(config), ui(nullptr), synth(std::move(synth_)), - presetsstore(*config) + presetsstore(*config), autoSave(-1, [this]() { + auto master = this->master; + this->doReadOnlyOp([master](){ + std::string home = getenv("HOME"); + std::string save_file = home+"/.local/zynaddsubfx-"+to_s(getpid())+"-autosave.xmz"; + printf("doing an autosave <%s>...\n", save_file.c_str()); + int res = master->saveXML(save_file.c_str()); + (void)res;});}) { bToU = new rtosc::ThreadLink(4096*2,1024); uToB = new rtosc::ThreadLink(4096*2,1024); @@ -1425,24 +1459,86 @@ MiddleWare::MiddleWare(SYNTH_T synth, Config* config, int preferred_port) :impl(new MiddleWareImpl(this, std::move(synth), config, preferred_port)) {} + MiddleWare::~MiddleWare(void) { delete impl; } + void MiddleWare::updateResources(Master *m) { impl->updateResources(m); } + Master *MiddleWare::spawnMaster(void) { assert(impl->master); assert(impl->master->uToB); return impl->master; } + +void MiddleWare::enableAutoSave(int interval_sec) +{ + impl->autoSave.dt = interval_sec; +} + +int MiddleWare::checkAutoSave(void) +{ + //save spec zynaddsubfx-PID-autosave.xmz + const std::string home = getenv("HOME"); + const std::string save_dir = home+"/.local/"; + + DIR *dir = opendir(save_dir.c_str()); + + if(dir == NULL) + return -1; + + struct dirent *fn; + int reload_save = -1; + + while((fn = readdir(dir))) { + const char *filename = fn->d_name; + const char *prefix = "zynaddsubfx-"; + + //check for manditory prefix + if(strstr(filename, prefix) != filename) + continue; + + int id = atoi(filename+strlen(prefix)); + + bool in_use = false; + + std::string proc_file = "/proc/" + to_s(id) + "/comm"; + std::ifstream ifs(proc_file); + if(ifs.good()) { + std::string comm_name; + ifs >> comm_name; + in_use = (comm_name == "zynaddsubfx"); + } + + if(!in_use) { + reload_save = id; + break; + } + } + + closedir(dir); + + return reload_save; +} + +void MiddleWare::removeAutoSave(void) +{ + std::string home = getenv("HOME"); + std::string save_file = home+"/.local/zynaddsubfx-"+to_s(getpid())+"-autosave.xmz"; + remove(save_file.c_str()); +} + Fl_Osc_Interface *MiddleWare::spawnUiApi(void) { return impl->osc; } + void MiddleWare::tick(void) { impl->tick(); diff --git a/source/native-plugins/zynaddsubfx/Misc/MiddleWare.h b/source/native-plugins/zynaddsubfx/Misc/MiddleWare.h index 1fc1491c8..d62e035ad 100644 --- a/source/native-plugins/zynaddsubfx/Misc/MiddleWare.h +++ b/source/native-plugins/zynaddsubfx/Misc/MiddleWare.h @@ -17,6 +17,18 @@ class MiddleWare void updateResources(Master *m); //returns internal master pointer class Master *spawnMaster(void); + + //Enable AutoSave Functionality + void enableAutoSave(int interval_sec=60); + + //Check for old automatic saves which should only exist if multiple + //instances are in use OR when there was a crash + // + //When an old save is found return the id of the save file + int checkAutoSave(void); + + void removeAutoSave(void); + //return UI interface class Fl_Osc_Interface *spawnUiApi(void); //Set callback to push UI events to @@ -40,6 +52,7 @@ class MiddleWare //Indicate that a bank will be loaded //NOTE: Can only be called by realtime thread void pendingSetBank(int bank); + //Indicate that a program will be loaded on a known part //NOTE: Can only be called by realtime thread void pendingSetProgram(int part, int program); diff --git a/source/native-plugins/zynaddsubfx/Misc/Part.cpp b/source/native-plugins/zynaddsubfx/Misc/Part.cpp index 04080544e..bf79e08c2 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Part.cpp +++ b/source/native-plugins/zynaddsubfx/Misc/Part.cpp @@ -312,7 +312,7 @@ void Part::defaultsinstrument() Pdrummode = 0; for(int n = 0; n < NUM_KIT_ITEMS; ++n) { - kit[n].Penabled = false; + //kit[n].Penabled = false; kit[n].Pmuted = false; kit[n].Pminkey = 0; kit[n].Pmaxkey = 127; @@ -475,6 +475,9 @@ bool Part::NoteOn(unsigned char note, return true; } + if(Ppolymode) + notePool.makeUnsustainable(note); + //Create New Notes for(uint8_t i = 0; i < NUM_KIT_ITEMS; ++i) { auto &item = kit[i]; @@ -522,7 +525,7 @@ void Part::NoteOff(unsigned char note) //release the key monomemPop(note); for(auto &desc:notePool.activeDesc()) { - if(desc.note != note || desc.status != KEY_PLAYING) + if(desc.note != note || !desc.playing()) continue; if(!ctl.sustain.sustain) { //the sustain pedal is not pushed if((isMonoMode() || isLegatoMode()) && !monomemEmpty()) @@ -530,8 +533,12 @@ void Part::NoteOff(unsigned char note) //release the key else notePool.release(desc); } - else //the sustain pedal is pushed - desc.status = KEY_RELEASED_AND_SUSTAINED; + else { //the sustain pedal is pushed + if(desc.canSustain()) + desc.doSustain(); + else + notePool.release(desc); + } } } @@ -550,7 +557,7 @@ void Part::PolyphonicAftertouch(unsigned char note, const float vel = getVelocity(velocity, Pvelsns, Pveloffs); for(auto &d:notePool.activeDesc()) { - if(d.note == note && d.status == KEY_PLAYING) + if(d.note == note && d.playing()) for(auto &s:notePool.activeNotes(d)) s.note->setVelocity(vel); } @@ -659,7 +666,7 @@ void Part::ReleaseSustainedKeys() MonoMemRenote(); // To play most recent still held note. for(auto &d:notePool.activeDesc()) - if(d.status == KEY_RELEASED_AND_SUSTAINED) + if(d.sustained()) for(auto &s:notePool.activeNotes(d)) s.note->releasekey(); } @@ -671,7 +678,7 @@ void Part::ReleaseSustainedKeys() void Part::ReleaseAllKeys() { for(auto &d:notePool.activeDesc()) - if(d.status != KEY_RELEASED) + if(!d.released()) for(auto &s:notePool.activeNotes(d)) s.note->releasekey(); } @@ -726,7 +733,7 @@ void Part::setkeylimit(unsigned char Pkeylimit_) if(keylimit == 0) keylimit = POLYPHONY - 5; - if(notePool.getRunningNotes() > keylimit) + if(notePool.getRunningNotes() >= keylimit) notePool.enforceKeyLimit(keylimit); } @@ -840,6 +847,9 @@ void Part::setkititemstatus(unsigned kititem, bool Penabled_) delete kkit.adpars; delete kkit.subpars; delete kkit.padpars; + kkit.adpars = nullptr; + kkit.subpars = nullptr; + kkit.padpars = nullptr; kkit.Pname[0] = '\0'; notePool.killAllNotes(); diff --git a/source/native-plugins/zynaddsubfx/Misc/Part.h b/source/native-plugins/zynaddsubfx/Misc/Part.h index dec2eedb2..831440f95 100644 --- a/source/native-plugins/zynaddsubfx/Misc/Part.h +++ b/source/native-plugins/zynaddsubfx/Misc/Part.h @@ -147,9 +147,6 @@ class Part float *partfxinputl[NUM_PART_EFX + 1], //Left and right signal that pass thru part effects; *partfxinputr[NUM_PART_EFX + 1]; //partfxinput l/r [NUM_PART_EFX] is for "no effect" buffer - enum NoteStatus { - KEY_OFF, KEY_PLAYING, KEY_RELEASED_AND_SUSTAINED, KEY_RELEASED - }; float volume, oldvolumel, oldvolumer; //this is applied by Master float panning; //this is applied by Master, too diff --git a/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp b/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp index c3f4694b5..d624077ed 100644 --- a/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/ADnote.cpp @@ -1845,7 +1845,7 @@ void ADnote::releasekey() /* * Check if the note is finished */ -int ADnote::finished() const +bool ADnote::finished() const { if(NoteEnabled == ON) return 0; @@ -1853,6 +1853,11 @@ int ADnote::finished() const return 1; } +void ADnote::entomb(void) +{ + NoteGlobalPar.AmpEnvelope->forceFinish(); +} + void ADnote::Voice::releasekey() { if(!Enabled) diff --git a/source/native-plugins/zynaddsubfx/Synth/ADnote.h b/source/native-plugins/zynaddsubfx/Synth/ADnote.h index e11d839b7..1fb250664 100644 --- a/source/native-plugins/zynaddsubfx/Synth/ADnote.h +++ b/source/native-plugins/zynaddsubfx/Synth/ADnote.h @@ -52,7 +52,9 @@ class ADnote:public SynthNote int noteout(float *outl, float *outr); void releasekey(); - int finished() const; + bool finished() const; + void entomb(void); + virtual SynthNote *cloneLegato(void) override; private: diff --git a/source/native-plugins/zynaddsubfx/Synth/Envelope.cpp b/source/native-plugins/zynaddsubfx/Synth/Envelope.cpp index 4199e495c..2b22ece95 100644 --- a/source/native-plugins/zynaddsubfx/Synth/Envelope.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/Envelope.cpp @@ -101,6 +101,11 @@ void Envelope::releasekey() t = 0.0f; } +void Envelope::forceFinish(void) +{ + envfinish = true; +} + /* * Envelope Output */ diff --git a/source/native-plugins/zynaddsubfx/Synth/Envelope.h b/source/native-plugins/zynaddsubfx/Synth/Envelope.h index bfa9f1af3..6fb2d725b 100644 --- a/source/native-plugins/zynaddsubfx/Synth/Envelope.h +++ b/source/native-plugins/zynaddsubfx/Synth/Envelope.h @@ -35,6 +35,8 @@ class Envelope /**Destructor*/ ~Envelope(); void releasekey(); + /**Push Envelope to finishing state*/ + void forceFinish(void); float envout(); float envout_dB(); /**Determines the status of the Envelope diff --git a/source/native-plugins/zynaddsubfx/Synth/PADnote.cpp b/source/native-plugins/zynaddsubfx/Synth/PADnote.cpp index 27ae76e64..759c9bedb 100644 --- a/source/native-plugins/zynaddsubfx/Synth/PADnote.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/PADnote.cpp @@ -428,11 +428,16 @@ int PADnote::noteout(float *outl, float *outr) return 1; } -int PADnote::finished() const +bool PADnote::finished() const { return finished_; } +void PADnote::entomb(void) +{ + NoteGlobalPar.AmpEnvelope->forceFinish(); +} + void PADnote::releasekey() { NoteGlobalPar.FreqEnvelope->releasekey(); diff --git a/source/native-plugins/zynaddsubfx/Synth/PADnote.h b/source/native-plugins/zynaddsubfx/Synth/PADnote.h index 528f1ac92..acfa26249 100644 --- a/source/native-plugins/zynaddsubfx/Synth/PADnote.h +++ b/source/native-plugins/zynaddsubfx/Synth/PADnote.h @@ -38,7 +38,9 @@ class PADnote:public SynthNote void legatonote(LegatoParams pars); int noteout(float *outl, float *outr); - int finished() const; + bool finished() const; + void entomb(void); + void releasekey(); private: void setup(float freq, float velocity, int portamento_, diff --git a/source/native-plugins/zynaddsubfx/Synth/SUBnote.cpp b/source/native-plugins/zynaddsubfx/Synth/SUBnote.cpp index 743a5c118..31b0371c6 100644 --- a/source/native-plugins/zynaddsubfx/Synth/SUBnote.cpp +++ b/source/native-plugins/zynaddsubfx/Synth/SUBnote.cpp @@ -619,10 +619,15 @@ void SUBnote::releasekey() /* * Check if the note is finished */ -int SUBnote::finished() const +bool SUBnote::finished() const { if(NoteEnabled == OFF) return 1; else return 0; } + +void SUBnote::entomb(void) +{ + AmpEnvelope->forceFinish(); +} diff --git a/source/native-plugins/zynaddsubfx/Synth/SUBnote.h b/source/native-plugins/zynaddsubfx/Synth/SUBnote.h index 20a78388b..b57b7bd1d 100644 --- a/source/native-plugins/zynaddsubfx/Synth/SUBnote.h +++ b/source/native-plugins/zynaddsubfx/Synth/SUBnote.h @@ -38,7 +38,8 @@ class SUBnote:public SynthNote int noteout(float *outl, float *outr); //note output,return 0 if the note is finished void releasekey(); - int finished() const; + bool finished() const; + void entomb(void); private: void setup(float freq, diff --git a/source/native-plugins/zynaddsubfx/Synth/SynthNote.h b/source/native-plugins/zynaddsubfx/Synth/SynthNote.h index 38e98a554..bf5271f77 100644 --- a/source/native-plugins/zynaddsubfx/Synth/SynthNote.h +++ b/source/native-plugins/zynaddsubfx/Synth/SynthNote.h @@ -63,7 +63,10 @@ class SynthNote /**Return if note is finished. * @return finished=1 unfinished=0*/ - virtual int finished() const = 0; + virtual bool finished() const = 0; + + /**Make a note die off next buffer compute*/ + virtual void entomb(void) = 0; virtual void legatonote(LegatoParams pars) = 0; diff --git a/source/native-plugins/zynaddsubfx/UI/Connection.cpp b/source/native-plugins/zynaddsubfx/UI/Connection.cpp index b38d5cec0..b48949d17 100644 --- a/source/native-plugins/zynaddsubfx/UI/Connection.cpp +++ b/source/native-plugins/zynaddsubfx/UI/Connection.cpp @@ -134,7 +134,7 @@ void GUI::destroyUi(ui_handle_t ui) delete static_cast(ui); } -#define BEGIN(x) {x,":non-realtime\0",NULL,[](const char *m, rtosc::RtData d){ \ +#define BEGIN(x) {x,":non-realtime\0",NULL,[](const char *m, rtosc::RtData &d){ \ MasterUI *ui = static_cast(d.obj); \ rtosc_arg_t a0 = {0}, a1 = {0}; \ if(rtosc_narguments(m) > 0) \ @@ -157,6 +157,18 @@ rtosc::Ports uiPorts::ports = { BEGIN("alert:s") { fl_alert("%s",a0.s); } END + BEGIN("alert-reload:i") { + int res = fl_choice("Old autosave found, do you want to reload?", + "Delete", "Reload", "Ignore"); + // 0 1 2 + if(1==res) { + d.reply("/reload_auto_save", "i", a0.i); + ui->refresh_master_ui(); + ui->updatepanel(); + } else if(0==res) { + d.reply("/delete_auto_save", "i", a0.i); + } + } END BEGIN("session-type:s") { if(strcmp(a0.s,"LASH")) return; @@ -188,6 +200,26 @@ rtosc::Ports uiPorts::ports = { } END }; +//very tiny rtdata ext +class RtDataUI: public rtosc::RtData { +public: + + RtDataUI(Fl_Osc_Interface *osc_) + :osc(osc_) + {} + + void reply(const char *path, const char *args, ...) override + { + va_list va; + va_start(va,args); + char buf[2048]; + rtosc_vmessage(buf,sizeof(buf),path,args,va); + osc->writeRaw(buf); + va_end(va); + } + + Fl_Osc_Interface *osc; +}; void GUI::raiseUi(ui_handle_t gui, const char *message) { @@ -209,7 +241,7 @@ void GUI::raiseUi(ui_handle_t gui, const char *message) //printf("got message for UI '%s'\n", message); char buffer[1024]; memset(buffer, 0, sizeof(buffer)); - rtosc::RtData d; + RtDataUI d(mui->osc); d.loc = buffer; d.loc_size = 1024; d.obj = gui; diff --git a/source/native-plugins/zynaddsubfx/UI/MasterUI.fl b/source/native-plugins/zynaddsubfx/UI/MasterUI.fl index cd2b83d23..91aaa0591 100644 --- a/source/native-plugins/zynaddsubfx/UI/MasterUI.fl +++ b/source/native-plugins/zynaddsubfx/UI/MasterUI.fl @@ -178,18 +178,13 @@ bankui->show();} } Fl_Check_Button partenabled { label 01 - callback {o->oscWrite("Penabled", o->value() ? "T" : "F"); -if ((int) o->value()==0) panellistitemgroup->deactivate(); - else { - panellistitemgroup->activate(); - /* - if ((int)bankui->cbwig->value()!=(npart+1)){ - bankui->cbwig->value(npart+1); - bankui->cbwig->do_callback(); - };*/ -}; + callback { + if ((int) o->value()==0) panellistitemgroup->deactivate(); + else { + panellistitemgroup->activate(); + }; -o->redraw();} + o->redraw();} private xywh {5 0 45 20} down_box DOWN_BOX labeltype EMBOSSED_LABEL labelfont 1 labelsize 13 align 24 code0 {char tmp[10];snprintf(tmp,10,"%d",npart+1);o->copy_label(tmp);} code1 {o->init("Penabled");} @@ -472,7 +467,7 @@ fl_filename_setext(filename,".wav"); //TODO TODO Test if a file exists if (fl_choice("The file *might* exist. \\nOverwrite it?","No","Yes",NULL)) { - osc->write("/HDDRecorder/preparefile", "T"); + osc->write("/HDDRecorder/preparefile", "s", filename); recordbutton->activate();//TODO make this button osc controlled } @@ -765,7 +760,7 @@ stopbutton->activate(); pausebutton->activate(); pauselabel->activate(); o->oscWrite("HDDRecorder/start"); -o->oscWrite("resetvu"); +o->oscWrite("reset-vu"); mastermenu->redraw();} tooltip {Start Recording} xywh {159 46 21 21} box ROUND_UP_BOX color 88 labelfont 1 labelsize 10 align 2 deactivate class Fl_Osc_Button diff --git a/source/native-plugins/zynaddsubfx/main.cpp b/source/native-plugins/zynaddsubfx/main.cpp index deb63b0d6..b8fb80452 100644 --- a/source/native-plugins/zynaddsubfx/main.cpp +++ b/source/native-plugins/zynaddsubfx/main.cpp @@ -109,6 +109,7 @@ void exitprogram(const Config& config) { Nio::stop(); config.save(); + middleware->removeAutoSave(); GUI::destroyUi(gui); delete middleware; @@ -193,6 +194,9 @@ int main(int argc, char *argv[]) { "auto-connect", 0, NULL, 'a' }, + { + "auto-save", 0, NULL, 'A' + }, { "pid-in-client-name", 0, NULL, 'p' }, @@ -221,6 +225,7 @@ int main(int argc, char *argv[]) opterr = 0; int option_index = 0, opt, exitwithhelp = 0, exitwithversion = 0; int prefered_port = -1; + int auto_save_interval = 60; string loadfile, loadinstrument, execAfterInit, ui_title; @@ -230,7 +235,7 @@ int main(int argc, char *argv[]) /**\todo check this process for a small memory leak*/ opt = getopt_long(argc, argv, - "l:L:r:b:o:I:O:N:e:P:u:hvapSDUY", + "l:L:r:b:o:I:O:N:e:P:A:u:hvapSDUY", opts, &option_index); char *optarguments = optarg; @@ -321,6 +326,10 @@ int main(int argc, char *argv[]) if(optarguments) prefered_port = atoi(optarguments); break; + case 'A': + if(optarguments) + auto_save_interval = atoi(optarguments); + break; case 'e': GETOP(execAfterInit); break; @@ -370,6 +379,7 @@ int main(int argc, char *argv[]) " -U , --no-gui\t\t\t\t Run ZynAddSubFX without user interface\n" << " -N , --named\t\t\t\t Postfix IO Name when possible\n" << " -a , --auto-connect\t\t\t AutoConnect when using JACK\n" + << " -A , --auto-save=INTERVAL\t\t Automatically save at interval (disabled for negative intervals)\n" << " -p , --pid-in-client-name\t\t Append PID to (JACK) " "client name\n" << " -P , --preferred-port\t\t\t Preferred OSC Port\n" @@ -476,6 +486,13 @@ int main(int argc, char *argv[]) "Default IO did not initialize.\nDefaulting to NULL backend."); } + if(auto_save_interval >= 0) { + int old_save = middleware->checkAutoSave(); + if(old_save > 0) + GUI::raiseUi(gui, "/alert-reload", "i", old_save); + middleware->enableAutoSave(auto_save_interval); + } + #if USE_NSM char *nsm_url = getenv("NSM_URL");