diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index 93519ee6..b1707fb7 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -14,6 +14,7 @@ #include "pluginterfaces/vst/ivstevents.h" #include "pluginterfaces/vst/ivstparameterchanges.h" #include +#include #include template @@ -37,11 +38,12 @@ static const char* kRingIdOsc = "Osc"; static const char* kMsgIdSetNumVoices = "SetNumVoices"; static const char* kMsgIdSetOversampling = "SetOversampling"; static const char* kMsgIdSetPreloadSize = "SetPreloadSize"; -static const char* kMsgIdCheckShouldReload = "CheckShouldReload"; static const char* kMsgIdNotifyPlayState = "NotifyPlayState"; static const char* kMsgIdReceiveMessage = "ReceiveMessage"; static const char* kMsgIdNoteEvents = "NoteEvents"; +static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 500 }; + SfizzVstProcessor::SfizzVstProcessor() : _fifoToWorker(64 * 1024), _fifoMessageFromUi(64 * 1024), _oscTemp(new uint8_t[kOscTempSize]) @@ -204,7 +206,6 @@ tresult PLUGIN_API SfizzVstProcessor::setActive(TBool state) synth->setSampleRate(processSetup.sampleRate); synth->setSamplesPerBlock(processSetup.maxSamplesPerBlock); - _fileChangePeriod = static_cast(1.0 * processSetup.sampleRate); _playStateChangePeriod = static_cast(50e-3 * processSetup.sampleRate); startBackgroundWork(); @@ -271,13 +272,6 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data) synth.renderBlock(outputs, numFrames, numChannels); - _fileChangeCounter += numFrames; - if (_fileChangeCounter > _fileChangePeriod) { - _fileChangeCounter %= _fileChangePeriod; - if (writeWorkerMessage(kMsgIdCheckShouldReload, nullptr, 0)) - _semaToWorker.post(); - } - _playStateChangeCounter += numFrames; if (_playStateChangeCounter > _playStateChangePeriod) { _playStateChangeCounter %= _playStateChangePeriod; @@ -640,19 +634,28 @@ void SfizzVstProcessor::loadSfzFileOrDefault(sfz::Sfizz& synth, const std::strin void SfizzVstProcessor::doBackgroundWork() { + using Clock = std::chrono::steady_clock; + + bool haveDoneIdleWork = false; + Clock::time_point lastIdleWorkTime; + for (;;) { - _semaToWorker.wait(); + bool isNotified = _semaToWorker.timed_wait(kBackgroundIdleInterval.count()); if (!_workRunning) break; - RTMessagePtr msg = readWorkerMessage(); - if (!msg) { - fprintf(stderr, "[Sfizz] message synchronization error in worker\n"); - std::abort(); - } + const char* id = nullptr; + RTMessagePtr msg; - const char* id = msg->type; + if (isNotified) { + msg = readWorkerMessage(); + if (!msg) { + fprintf(stderr, "[Sfizz] message synchronization error in worker\n"); + std::abort(); + } + id = msg->type; + } if (id == kMsgIdSetNumVoices) { int32 value = *msg->payload(); @@ -669,23 +672,6 @@ void SfizzVstProcessor::doBackgroundWork() std::lock_guard lock(_processMutex); _synth->setPreloadSize(value); } - else if (id == kMsgIdCheckShouldReload) { - if (_synth->shouldReloadFile()) { - fprintf(stderr, "[Sfizz] sfz file has changed, reloading\n"); - std::lock_guard lock(_processMutex); - loadSfzFileOrDefault(*_synth, _state.sfzFile); - - Steinberg::OPtr reply { allocateMessage() }; - reply->setMessageID("LoadedSfz"); - reply->getAttributes()->setBinary("File", _state.sfzFile.data(), _state.sfzFile.size()); - sendMessage(reply); - } - else if (_synth->shouldReloadScala()) { - fprintf(stderr, "[Sfizz] scala file has changed, reloading\n"); - std::lock_guard lock(_processMutex); - _synth->loadScalaFile(_state.scalaFile); - } - } else if (id == kMsgIdNotifyPlayState) { SfizzPlayState playState = *msg->payload(); Steinberg::OPtr notification { allocateMessage() }; @@ -705,6 +691,32 @@ void SfizzVstProcessor::doBackgroundWork() notification->getAttributes()->setBinary("Events", msg->payload(), msg->size); sendMessage(notification); } + + Clock::time_point currentTime = Clock::now(); + if (!haveDoneIdleWork || currentTime - lastIdleWorkTime > kBackgroundIdleInterval) { + doBackgroundIdle(); + haveDoneIdleWork = true; + lastIdleWorkTime = currentTime; + } + } +} + +void SfizzVstProcessor::doBackgroundIdle() +{ + if (_synth->shouldReloadFile()) { + fprintf(stderr, "[Sfizz] sfz file has changed, reloading\n"); + std::lock_guard lock(_processMutex); + loadSfzFileOrDefault(*_synth, _state.sfzFile); + + Steinberg::OPtr reply { allocateMessage() }; + reply->setMessageID("LoadedSfz"); + reply->getAttributes()->setBinary("File", _state.sfzFile.data(), _state.sfzFile.size()); + sendMessage(reply); + } + if (_synth->shouldReloadScala()) { + fprintf(stderr, "[Sfizz] scala file has changed, reloading\n"); + std::lock_guard lock(_processMutex); + _synth->loadScalaFile(_state.scalaFile); } } diff --git a/plugins/vst/SfizzVstProcessor.h b/plugins/vst/SfizzVstProcessor.h index 2219ab6a..f34e1b4d 100644 --- a/plugins/vst/SfizzVstProcessor.h +++ b/plugins/vst/SfizzVstProcessor.h @@ -72,10 +72,6 @@ private: Ring_Buffer _fifoMessageFromUi; SpinMutex _processMutex; - // file modification periodic checker - uint32 _fileChangeCounter = 0; - uint32 _fileChangePeriod = 0; - // state notification periodic timer uint32 _playStateChangeCounter = 0; uint32 _playStateChangePeriod = 0; @@ -99,6 +95,7 @@ private: // worker void doBackgroundWork(); + void doBackgroundIdle(); void startBackgroundWork(); void stopBackgroundWork(); // writer diff --git a/src/sfizz/RTSemaphore.cpp b/src/sfizz/RTSemaphore.cpp index 685166e4..1fefb1c4 100644 --- a/src/sfizz/RTSemaphore.cpp +++ b/src/sfizz/RTSemaphore.cpp @@ -8,6 +8,7 @@ #include #include #include +#include RTSemaphore::RTSemaphore(unsigned value) { @@ -57,6 +58,15 @@ bool RTSemaphore::try_wait() return b; } +bool RTSemaphore::timed_wait(uint32_t milliseconds) +{ + std::error_code ec; + bool b = timed_wait(milliseconds, ec); + if (ec) + throw std::system_error(ec); + return b; +} + #if defined(__APPLE__) void RTSemaphore::init(std::error_code& ec, unsigned value) { @@ -100,10 +110,17 @@ void RTSemaphore::wait(std::error_code& ec) noexcept } bool RTSemaphore::try_wait(std::error_code& ec) noexcept +{ + return timed_wait(0, ec); +} + +bool RTSemaphore::timed_wait(uint32_t milliseconds, std::error_code& ec) noexcept { ec.clear(); do { - const mach_timespec_t timeout = { 0, 0 }; + mach_timespec_t timeout; + timeout.tv_sec = milliseconds / 1000; + timeout.tv_nsec = (milliseconds % 1000) * (1000L * 1000L); kern_return_t ret = semaphore_timedwait(sem_, timeout); switch (ret) { case KERN_SUCCESS: @@ -178,9 +195,14 @@ void RTSemaphore::wait(std::error_code& ec) noexcept } bool RTSemaphore::try_wait(std::error_code& ec) noexcept +{ + return timed_wait(0, ec); +} + +bool RTSemaphore::timed_wait(uint32_t milliseconds, std::error_code& ec) noexcept { ec.clear(); - DWORD ret = WaitForSingleObject(sem_, 0); + DWORD ret = WaitForSingleObject(sem_, milliseconds); switch (ret) { case WAIT_OBJECT_0: return true; @@ -251,4 +273,46 @@ bool RTSemaphore::try_wait(std::error_code& ec) noexcept } } while (1); } + +static bool absolute_timeout(uint32_t milliseconds, timespec &result, std::error_code& ec) +{ + timespec now; + if (clock_gettime(CLOCK_REALTIME, &now) != 0) { + ec = std::error_code(errno, std::generic_category()); + return false; + } + + timespec abs; + abs.tv_sec = now.tv_sec + milliseconds / 1000; + abs.tv_nsec = now.tv_nsec + (milliseconds % 1000) * (1000L * 1000L); + + long abs_nsec_sec = abs.tv_nsec / (1000L * 1000L * 1000L); + abs.tv_sec += abs_nsec_sec; + abs.tv_nsec -= abs_nsec_sec * (1000L * 1000L * 1000L); + + result = abs; + return true; +} + +bool RTSemaphore::timed_wait(uint32_t milliseconds, std::error_code& ec) noexcept +{ + ec.clear(); + timespec abs; + if (!absolute_timeout(milliseconds, abs, ec)) + return false; + do { + if (sem_timedwait(&sem_, &abs) == 0) + return true; + int e = errno; + switch (e) { + case EINTR: + break; + case ETIMEDOUT: + return false; + default: + ec = std::error_code(e, std::generic_category()); + return false; + } + } while (1); +} #endif diff --git a/src/sfizz/RTSemaphore.h b/src/sfizz/RTSemaphore.h index c17e5c2a..106a88ef 100644 --- a/src/sfizz/RTSemaphore.h +++ b/src/sfizz/RTSemaphore.h @@ -12,6 +12,7 @@ #else #include #endif +#include #include class RTSemaphore { @@ -28,10 +29,12 @@ public: void post(); void wait(); bool try_wait(); + bool timed_wait(uint32_t milliseconds); void post(std::error_code& ec) noexcept; void wait(std::error_code& ec) noexcept; bool try_wait(std::error_code& ec) noexcept; + bool timed_wait(uint32_t milliseconds, std::error_code& ec) noexcept; private: void init(std::error_code& ec, unsigned value);