Merge branch 'sfztools:develop' into develop

This commit is contained in:
Jośe Fernando Moyano 2021-09-21 11:14:19 +02:00 committed by GitHub
commit 2198711123
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 306 additions and 112 deletions

2
.gitmodules vendored
View file

@ -13,7 +13,7 @@
shallow = true
[submodule "vst/external/VST_SDK/VST3_SDK/public.sdk"]
path = plugins/vst/external/VST_SDK/VST3_SDK/public.sdk
url = https://github.com/sfztools/vst3_public_sdk.git
url = https://github.com/steinbergmedia/vst3_public_sdk.git
shallow = true
[submodule "vst/external/VST_SDK/VST3_SDK/vstgui4"]
path = plugins/editor/external/vstgui4

View file

@ -103,6 +103,7 @@ std::string getDescriptionBlob(sfizz_synth_t* handle)
synth.sendMessage(*client, 0, "/key/slots", "", nullptr);
synth.sendMessage(*client, 0, "/sw/last/slots", "", nullptr);
synth.sendMessage(*client, 0, "/cc/slots", "", nullptr);
synth.sendMessage(*client, 0, "/sustain_or_sostenuto/slots", "", nullptr);
blob.shrink_to_fit();
return blob;
@ -152,6 +153,8 @@ InstrumentDescription parseDescriptionBlob(absl::string_view blob)
copyArgToBitSpan(args[0], desc.keyswitchUsed.span());
else if (Messages::matchOSC("/cc/slots", path, indices) && !strcmp(sig, "b"))
copyArgToBitSpan(args[0], desc.ccUsed.span());
else if (Messages::matchOSC("/sustain_or_sostenuto/slots", path, indices) && !strcmp(sig, "b"))
copyArgToBitSpan(args[0], desc.sustainOrSostenuto.span());
else if (Messages::matchOSC("/key&/label", path, indices) && !strcmp(sig, "s"))
desc.keyLabel[indices[0]] = args[0].s;
else if (Messages::matchOSC("/sw/last/&/label", path, indices) && !strcmp(sig, "s"))

View file

@ -26,6 +26,7 @@ struct InstrumentDescription {
std::string image;
BitArray<128> keyUsed {};
BitArray<128> keyswitchUsed {};
BitArray<128> sustainOrSostenuto {};
BitArray<sfz::config::numCCs> ccUsed {};
std::array<std::string, 128> keyLabel {};
std::array<std::string, 128> keyswitchLabel {};

View file

@ -120,6 +120,7 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self)
self->sfizz_num_voices_uri = map->map(map->handle, SFIZZ__numVoices);
self->sfizz_preload_size_uri = map->map(map->handle, SFIZZ__preloadSize);
self->sfizz_oversampling_uri = map->map(map->handle, SFIZZ__oversampling);
self->sfizz_last_keyswitch_uri = map->map(map->handle, SFIZZ__lastKeyswitch);
self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus);
self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification);
self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob);
@ -323,6 +324,14 @@ sfizz_lv2_receive_message(void* data, int delay, const char* path, const char* s
sfizz_plugin_t *self = (sfizz_plugin_t *)data;
if (!strcmp(path, "/sw/last/current") && sig)
{
if (sig[0] == 'i')
self->last_keyswitch = args[0].i;
else if (sig[0] == 'N')
self->last_keyswitch = -1;
}
// transmit to UI as OSC blob
uint8_t *osc_temp = self->osc_temp;
uint32_t osc_size = sfizz_prepare_message(osc_temp, OSC_TEMP_SIZE, path, sig, args);
@ -683,6 +692,12 @@ sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, int delay, const LV2_Atom_Obj
}
}
static bool
sfizz_is_sustain_or_sostenuto(sfizz_plugin_t* self, unsigned cc)
{
return (self->sustain_or_sostenuto[cc / 8] & (1 << (cc % 8)));
}
static void
sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev)
{
@ -704,14 +719,23 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev)
(int)msg[1],
msg[2]);
break;
// Note(jpc) CC must be mapped by host, not handled here.
// See LV2 midi:binding.
#if defined(SFIZZ_LV2_PSA)
case LV2_MIDI_MSG_CONTROLLER:
{
unsigned cc = msg[1];
float value = float(msg[2]) * (1.0f / 127.0f);
// Send
if (sfizz_is_sustain_or_sostenuto(self, cc)) {
sfizz_automate_hdcc(self->synth,
(int)ev->time.frames,
(int)cc,
value);
break;
}
// Note(jpc) CC must be mapped by host, not handled here.
// See LV2 midi:binding.
#if defined(SFIZZ_LV2_PSA)
switch (cc)
{
default:
@ -733,9 +757,9 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev)
sfizz_all_sound_off(self->synth);
break;
}
#endif
}
break;
#endif
case LV2_MIDI_MSG_CHANNEL_PRESSURE:
sfizz_send_channel_aftertouch(self->synth,
(int)ev->time.frames,
@ -1218,7 +1242,7 @@ sfizz_lv2_update_sfz_info(sfizz_plugin_t *self)
//
const InstrumentDescription desc = parseDescriptionBlob(blob);
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
if (desc.ccUsed.test(cc)) {
if (desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc)) {
// Mark all the used CCs for automation with default values
self->ccauto[cc] = desc.ccDefault[cc];
self->have_ccauto = true;
@ -1226,6 +1250,9 @@ sfizz_lv2_update_sfz_info(sfizz_plugin_t *self)
self->cc_current[cc] = desc.ccDefault[cc];
}
}
memcpy(self->sustain_or_sostenuto, desc.sustainOrSostenuto.data(),
sizeof(self->sustain_or_sostenuto));
}
static bool
@ -1283,6 +1310,7 @@ restore(LV2_Handle instance,
}
// Set default values
self->last_keyswitch = -1;
sfizz_lv2_get_default_sfz_path(self, self->sfz_file_path, MAX_PATH_SIZE);
sfizz_lv2_get_default_scala_path(self, self->scala_file_path, MAX_PATH_SIZE);
@ -1333,6 +1361,13 @@ restore(LV2_Handle instance,
}
}
value = retrieve(handle, self->sfizz_last_keyswitch_uri, &size, &type, &val_flags);
if (value)
{
int last_keyswitch = *(const int*)value;
self->last_keyswitch = last_keyswitch;
}
// Collect all CC values present in the state
std::unique_ptr<absl::optional<float>[]> cc_values(
new absl::optional<float>[sfz::config::numCCs]);
@ -1387,6 +1422,11 @@ restore(LV2_Handle instance,
}
}
if (self->last_keyswitch >= 0 && self->last_keyswitch <= 127) {
sfizz_send_hd_note_on(self->synth, 0, self->last_keyswitch, 1.0f);
sfizz_send_hd_note_off(self->synth, 1, self->last_keyswitch, 0.0f);
}
spin_mutex_unlock(self->synth_mutex);
return status;
@ -1456,8 +1496,17 @@ save(LV2_Handle instance,
absl::string_view((const char*)self->sfz_blob_data, self->sfz_blob_size));
self->sfz_blob_mutex->unlock();
if (self->last_keyswitch >= 0 && self->last_keyswitch <= 127) {
store(handle,
self->sfizz_last_keyswitch_uri,
&self->last_keyswitch,
sizeof(int),
self->atom_int_uri,
LV2_STATE_IS_POD);
}
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
if (desc.ccUsed.test(cc)) {
if (desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc)) {
LV2_URID urid = sfizz_lv2_ccmap_map(self->ccmap, int(cc));
store(handle,
urid,

View file

@ -39,6 +39,7 @@
#define SFIZZ__numVoices SFIZZ_URI ":" "numvoices"
#define SFIZZ__preloadSize SFIZZ_URI ":" "preload_size"
#define SFIZZ__oversampling SFIZZ_URI ":" "oversampling"
#define SFIZZ__lastKeyswitch SFIZZ_URI ":" "last_keyswitch"
// These ones are just for the worker
#define SFIZZ__logStatus SFIZZ_URI ":" "log_status"
#define SFIZZ__checkModification SFIZZ_URI ":" "check_modification"

View file

@ -86,6 +86,7 @@ struct sfizz_plugin_t
LV2_URID sfizz_num_voices_uri {};
LV2_URID sfizz_preload_size_uri {};
LV2_URID sfizz_oversampling_uri {};
LV2_URID sfizz_last_keyswitch_uri {};
LV2_URID sfizz_log_status_uri {};
LV2_URID sfizz_check_modification_uri {};
LV2_URID sfizz_active_voices_uri {};
@ -120,6 +121,7 @@ struct sfizz_plugin_t
float sample_rate {};
std::atomic<int> must_update_midnam {};
volatile bool must_automate_cc {};
int last_keyswitch { -1 };
// Current instrument description
std::mutex *sfz_blob_mutex {};
@ -127,6 +129,9 @@ struct sfizz_plugin_t
const uint8_t *volatile sfz_blob_data {};
volatile uint32_t sfz_blob_size {};
// Sostenuto or sustain
char sustain_or_sostenuto[16] {};
// Current CC values in the synth (synchronized by `synth_mutex`)
// updated by hdcc or file load
float *cc_current {};

View file

@ -447,7 +447,7 @@ sfizz_ui_update_description(sfizz_ui_t *self, const InstrumentDescription& desc)
}
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
bool ccUsed = desc.ccUsed.test(cc);
bool ccUsed = desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc);
self->uiReceiveValue(editIdForCCUsed(cc), float(ccUsed));
if (ccUsed) {
self->uiReceiveValue(editIdForCCDefault(cc), desc.ccDefault[cc]);

View file

@ -364,8 +364,7 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message)
}
if (Vst::IComponentHandler* componentHandler = getComponentHandler())
// NOTE(jpc) I think that's the right one, but it needs confirmation
componentHandler->restartComponent(Vst::kNoteExpressionChanged);
componentHandler->restartComponent(Vst::kKeyswitchChanged);
// update the parameter titles and notify
for (uint32 cc = 0; cc < sfz::config::numCCs; ++cc) {

View file

@ -59,11 +59,8 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p
config = &x11config;
#endif
Editor* editor = editor_.get();
if (!editor) {
editor = new Editor(*this);
editor_.reset(editor);
}
Editor* editor = new Editor(*this);
editor_.reset(editor);
if (!frame->open(parent, platformType, config)) {
fprintf(stderr, "[sfizz] error opening frame\n");
@ -122,12 +119,21 @@ void PLUGIN_API SfizzVstEditor::close()
for (FObject* update : updates_)
update->removeDependent(this);
if (editor_)
if (editor_) {
editor_->close();
editor_ = nullptr;
}
if (frame->getNbReference() != 1)
frame->forget();
else
else {
frame->close();
#if !defined(__APPLE__) && !defined(_WIN32)
// if vstgui is done using the runloop, destroy it
if (!RunLoop::get())
_runLoop = nullptr;
#endif
}
this->frame = nullptr;
}
@ -158,8 +164,6 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message)
// notifier of X11 events is working. If there is, remove this and
// avoid polluting Linux hosts which implement the loop correctly.
runLoop->processSomeEvents();
runLoop->cleanupDeadHandlers();
}
}
#endif
@ -246,7 +250,7 @@ bool SfizzVstEditor::processUpdate(FUnknown* changedUnknown, int32 message)
}
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
bool ccUsed = desc.ccUsed.test(cc);
bool ccUsed = desc.ccUsed.test(cc) && !desc.sustainOrSostenuto.test(cc);
uiReceiveValue(editIdForCCUsed(int(cc)), float(ccUsed));
if (ccUsed) {
uiReceiveValue(editIdForCCDefault(int(cc)), desc.ccDefault[cc]);

View file

@ -227,6 +227,10 @@ void SfizzVstProcessor::syncStateToSynth()
synth->setScalaRootKey(_state.scalaRootKey);
synth->setTuningFrequency(_state.tuningFrequency);
synth->loadStretchTuningByRatio(_state.stretchedTuning);
if (_state.lastKeyswitch >= 0 && _state.lastKeyswitch <= 127) {
synth->hdNoteOn(0, _state.lastKeyswitch, 1.0f);
synth->hdNoteOff(1, _state.lastKeyswitch, 0.0f);
}
}
tresult PLUGIN_API SfizzVstProcessor::canProcessSampleSize(int32 symbolicSampleSize)
@ -669,6 +673,14 @@ bool SfizzVstProcessor::processUpdate(FUnknown* changedUnknown, int32 message)
void SfizzVstProcessor::receiveOSC(int delay, const char* path, const char* sig, const sfizz_arg_t* args)
{
if (!strcmp(path, "/sw/last/current") && sig)
{
if (sig[0] == 'i')
_state.lastKeyswitch = args[0].i;
else if (sig[0] == 'N')
_state.lastKeyswitch = -1;
}
uint8_t* oscTemp = _oscTemp.get();
uint32 oscSize = sfizz_prepare_message(oscTemp, kOscTempSize, path, sig, args);
if (oscSize <= kOscTempSize) {
@ -740,8 +752,13 @@ void SfizzVstProcessor::doBackgroundWork()
for (;;) {
bool isNotified = _semaToWorker.timed_wait(kBackgroundIdleInterval.count());
if (!_workRunning)
if (!_workRunning) {
// if the quit signal is sent, the semaphore is also signaled
// make sure the count is kept consistent
if (!isNotified)
_semaToWorker.wait();
break;
}
const char* id = nullptr;
RTMessagePtr msg;

View file

@ -73,6 +73,14 @@ tresult SfizzVstState::load(IBStream* state)
oscillatorQuality = defaults.oscillatorQuality;
}
if (version >= 4) {
if (!s.readInt32(lastKeyswitch))
return kResultFalse;
}
else {
lastKeyswitch = -1;
}
controllers.clear();
if (version >= 2) {
uint32 count;
@ -135,6 +143,9 @@ tresult SfizzVstState::store(IBStream* state) const
if (!s.writeInt32(oscillatorQuality))
return kResultFalse;
if (!s.writeInt32(lastKeyswitch))
return kResultFalse;
{
uint32 ccCount = 0;
uint32 ccLimit = uint32(std::min(controllers.size(), size_t(0x10000)));

View file

@ -27,9 +27,10 @@ public:
float stretchedTuning = 0.0;
int32 sampleQuality = 2;
int32 oscillatorQuality = 1;
int32 lastKeyswitch = -1;
std::vector<absl::optional<float>> controllers;
static constexpr uint64 currentStateVersion = 3;
static constexpr uint64 currentStateVersion = 4;
tresult load(IBStream* state);
tresult store(IBStream* state) const;

View file

@ -1,33 +1,38 @@
// SPDX-License-Identifier: GPL-3.0
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#if !defined(__APPLE__) && !defined(_WIN32)
#include "X11RunLoop.h"
#include "vstgui/lib/platform/linux/x11platform.h"
#include "base/source/fobject.h"
#include <vector>
#include <typeinfo>
#include <cstdio>
#include <cassert>
namespace VSTGUI {
RunLoop::RunLoop(Steinberg::FUnknown* runLoop)
: runLoop(runLoop)
{
}
struct RunLoop::Impl {
struct EventHandler;
struct TimerHandler;
RunLoop::~RunLoop() {}
using EventHandlers = std::vector<Steinberg::IPtr<EventHandler>>;
using TimerHandlers = std::vector<Steinberg::IPtr<TimerHandler>>;
SharedPointer<RunLoop> RunLoop::get()
{
return X11::RunLoop::get().cast<VSTGUI::RunLoop>();
}
EventHandlers eventHandlers;
TimerHandlers timerHandlers;
Steinberg::FUnknownPtr<Steinberg::Linux::IRunLoop> runLoop;
};
struct RunLoop::EventHandler final : Steinberg::Linux::IEventHandler, public Steinberg::FObject {
//------------------------------------------------------------------------------
struct RunLoop::Impl::EventHandler final : Steinberg::Linux::IEventHandler, public Steinberg::FObject {
X11::IEventHandler* handler { nullptr };
bool alive { false };
void PLUGIN_API onFDIsSet(Steinberg::Linux::FileDescriptor) override
{
if (alive && handler)
handler->onEvent();
}
void PLUGIN_API onFDIsSet(Steinberg::Linux::FileDescriptor) override;
DELEGATE_REFCOUNT(Steinberg::FObject)
DEFINE_INTERFACES
@ -35,15 +40,11 @@ struct RunLoop::EventHandler final : Steinberg::Linux::IEventHandler, public Ste
END_DEFINE_INTERFACES(Steinberg::FObject)
};
struct RunLoop::TimerHandler final : Steinberg::Linux::ITimerHandler, public Steinberg::FObject {
struct RunLoop::Impl::TimerHandler final : Steinberg::Linux::ITimerHandler, public Steinberg::FObject {
X11::ITimerHandler* handler { nullptr };
bool alive { false };
void PLUGIN_API onTimer() override
{
if (alive && handler)
handler->onTimer();
}
void PLUGIN_API onTimer() override;
DELEGATE_REFCOUNT(Steinberg::FObject)
DEFINE_INTERFACES
@ -51,45 +52,126 @@ struct RunLoop::TimerHandler final : Steinberg::Linux::ITimerHandler, public Ste
END_DEFINE_INTERFACES(Steinberg::FObject)
};
//------------------------------------------------------------------------------
void PLUGIN_API RunLoop::Impl::EventHandler::onFDIsSet(Steinberg::Linux::FileDescriptor)
{
SharedPointer<RunLoop> runLoop = RunLoop::get();
if (!runLoop) {
fprintf(stderr, "[x11] event has fired without active runloop\n");
return;
}
if (alive && handler)
handler->onEvent();
}
void PLUGIN_API RunLoop::Impl::TimerHandler::onTimer()
{
SharedPointer<RunLoop> runLoop = RunLoop::get();
if (!runLoop) {
fprintf(stderr, "[x11] timer has fired without active runloop\n");
return;
}
if (alive && handler)
handler->onTimer();
}
//------------------------------------------------------------------------------
RunLoop::RunLoop(Steinberg::FUnknown* runLoop)
: impl(new Impl)
{
impl->runLoop = runLoop;
}
RunLoop::~RunLoop()
{
//dumpCurrentState();
if (0) {
// remove any leftover handlers
for (size_t i = 0; i < impl->eventHandlers.size(); ++i) {
const auto& eh = impl->eventHandlers[i];
if (eh->alive && eh->handler) {
impl->runLoop->unregisterEventHandler(eh.get());
}
}
for (size_t i = 0; i < impl->timerHandlers.size(); ++i) {
const auto& th = impl->timerHandlers[i];
if (th->alive && th->handler) {
impl->runLoop->unregisterTimer(th.get());
}
}
}
}
SharedPointer<RunLoop> RunLoop::get()
{
return X11::RunLoop::get().cast<VSTGUI::RunLoop>();
}
void RunLoop::processSomeEvents()
{
for (size_t i = 0; i < eventHandlers.size(); ++i) {
const auto& eh = eventHandlers[i];
for (size_t i = 0; i < impl->eventHandlers.size(); ++i) {
const auto& eh = impl->eventHandlers[i];
if (eh->alive && eh->handler) {
eh->handler->onEvent();
}
}
}
void RunLoop::cleanupDeadHandlers()
void RunLoop::dumpCurrentState()
{
for (size_t i = 0; i < eventHandlers.size(); ++i) {
const auto& eh = eventHandlers[i];
if (!eh->alive) {
runLoop->unregisterEventHandler(eh);
eventHandlers.erase(eventHandlers.begin() + i--);
}
fprintf(stderr, "=== X11 runloop ===\n");
fprintf(stderr, "\t" "Event slots:\n");
for (size_t i = 0, n = impl->eventHandlers.size(); i < n; ++i) {
Impl::EventHandler *eh = impl->eventHandlers[i].get();
fprintf(stderr, "\t\t" "(%lu) alive=%d handler=%p type=%s\n", i, eh->alive, eh->handler, (eh->alive && eh->handler) ? typeid(*eh->handler).name() : "");
}
for (size_t i = 0; i < timerHandlers.size(); ++i) {
const auto& th = timerHandlers[i];
if (!th->alive) {
runLoop->unregisterTimer(th);
timerHandlers.erase(timerHandlers.begin() + i--);
}
fprintf(stderr, "\t" "Timer slots:\n");
for (size_t i = 0, n = impl->timerHandlers.size(); i < n; ++i) {
Impl::TimerHandler *th = impl->timerHandlers[i].get();
fprintf(stderr, "\t\t" "(%lu) alive=%d handler=%p type=%s\n", i, th->alive, th->handler, (th->alive && th->handler) ? typeid(*th->handler).name() : "");
}
fprintf(stderr, "===/X11 runloop ===\n");
}
template <class T>
static void insertHandler(std::vector<Steinberg::IPtr<T>>& list, Steinberg::IPtr<T> handler)
{
size_t i = 0;
size_t n = list.size();
while (i < n && list[i]->alive)
++i;
if (i < n)
list[i] = handler;
else
list.emplace_back(handler);
}
template <class T, class U>
static size_t findHandler(const std::vector<Steinberg::IPtr<T>>& list, U* handler)
{
for (size_t i = 0, n = list.size(); i < n; ++i) {
if (list[i]->alive && list[i]->handler == handler)
return i;
}
return ~size_t(0);
}
bool RunLoop::registerEventHandler(int fd, X11::IEventHandler* handler)
{
if (!runLoop)
if (!impl->runLoop)
return false;
auto smtgHandler = Steinberg::owned(new EventHandler());
auto smtgHandler = Steinberg::owned(new Impl::EventHandler);
smtgHandler->handler = handler;
smtgHandler->alive = true;
if (runLoop->registerEventHandler(smtgHandler, fd) == Steinberg::kResultTrue) {
eventHandlers.push_back(smtgHandler);
if (impl->runLoop->registerEventHandler(smtgHandler, fd) == Steinberg::kResultTrue) {
insertHandler(impl->eventHandlers, smtgHandler);
return true;
}
return false;
@ -97,29 +179,31 @@ bool RunLoop::registerEventHandler(int fd, X11::IEventHandler* handler)
bool RunLoop::unregisterEventHandler(X11::IEventHandler* handler)
{
if (!runLoop)
if (!impl->runLoop)
return false;
for (size_t i = 0; i < eventHandlers.size(); ++i) {
const auto& eh = eventHandlers[i];
if (eh->alive && eh->handler == handler) {
eh->alive = false;
return true;
}
}
return false;
size_t index = findHandler(impl->eventHandlers, handler);
if (index == ~size_t(0))
return false;
Impl::EventHandler *eh = impl->eventHandlers[index].get();
if (!impl->runLoop->unregisterEventHandler(eh))
return false;
eh->alive = false;
return true;
}
bool RunLoop::registerTimer(uint64_t interval, X11::ITimerHandler* handler)
{
if (!runLoop)
if (!impl->runLoop)
return false;
auto smtgHandler = Steinberg::owned(new TimerHandler());
auto smtgHandler = Steinberg::owned(new Impl::TimerHandler);
smtgHandler->handler = handler;
smtgHandler->alive = true;
if (runLoop->registerTimer(smtgHandler, interval) == Steinberg::kResultTrue) {
timerHandlers.push_back(smtgHandler);
if (impl->runLoop->registerTimer(smtgHandler, interval) == Steinberg::kResultTrue) {
insertHandler(impl->timerHandlers, smtgHandler);
return true;
}
return false;
@ -127,17 +211,19 @@ bool RunLoop::registerTimer(uint64_t interval, X11::ITimerHandler* handler)
bool RunLoop::unregisterTimer(X11::ITimerHandler* handler)
{
if (!runLoop)
if (!impl->runLoop)
return false;
for (size_t i = 0; i < timerHandlers.size(); ++i) {
const auto& th = timerHandlers[i];
if (th->alive && th->handler == handler) {
th->alive = false;
return true;
}
}
return false;
size_t index = findHandler(impl->timerHandlers, handler);
if (index == ~size_t(0))
return false;
Impl::TimerHandler *th = impl->timerHandlers[index].get();
if (!impl->runLoop->unregisterTimer(th))
return false;
th->alive = false;
return true;
}
} // namespace VSTGUI

View file

@ -1,16 +1,28 @@
// SPDX-License-Identifier: GPL-3.0
/*
This is a modified version the X11 run loop from vst3editor.cpp.
// SPDX-License-Identifier: BSD-2-Clause
This version is edited to add more safeguards to protect against host bugs.
It also permits to call event processing externally in case the host has a
defective X11 event loop notifier.
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
/*
This runloop connects to X11 VSTGUI, it connects VST3 and VSTGUI together.
The Windows and macOS runloops do not need this, the OS-provided
functionality is used instead.
Previously, this was based on VSTGUI code provided by Steinberg.
This is replaced with a rewrite, because the original code has too many
issues. For example, it has no robustness in case handlers get added or
removed within the execution of the handler.
This version allows to call event processing externally, in case the host
has a defective X11 event loop notifier. (some versions of Bitwig do)
*/
#pragma once
#if !defined(__APPLE__) && !defined(_WIN32)
#include "vstgui/lib/platform/linux/x11frame.h"
#include "pluginterfaces/gui/iplugview.h"
#include <memory>
namespace VSTGUI {
@ -22,24 +34,16 @@ public:
static SharedPointer<RunLoop> get();
void processSomeEvents();
void cleanupDeadHandlers();
void dumpCurrentState();
// X11::IRunLoop
bool registerEventHandler(int fd, X11::IEventHandler* handler);
bool unregisterEventHandler(X11::IEventHandler* handler);
bool registerTimer(uint64_t interval, X11::ITimerHandler* handler);
bool unregisterTimer(X11::ITimerHandler* handler);
bool registerEventHandler(int fd, X11::IEventHandler* handler) override;
bool unregisterEventHandler(X11::IEventHandler* handler) override;
bool registerTimer(uint64_t interval, X11::ITimerHandler* handler) override;
bool unregisterTimer(X11::ITimerHandler* handler) override;
private:
struct EventHandler;
struct TimerHandler;
private:
using EventHandlers = std::vector<Steinberg::IPtr<EventHandler>>;
using TimerHandlers = std::vector<Steinberg::IPtr<TimerHandler>>;
EventHandlers eventHandlers;
TimerHandlers timerHandlers;
Steinberg::FUnknownPtr<Steinberg::Linux::IRunLoop> runLoop;
struct Impl;
std::unique_ptr<Impl> impl;
};
} // namespace VSTGUI

View file

@ -30,7 +30,8 @@ add_library(vst3sdk STATIC EXCLUDE_FROM_ALL
"${VST3SDK_BASEDIR}/public.sdk/source/vst/vstnoteexpressiontypes.cpp"
"${VST3SDK_BASEDIR}/public.sdk/source/vst/vstparameters.cpp"
"${VST3SDK_BASEDIR}/public.sdk/source/vst/vstpresetfile.cpp"
"${VST3SDK_BASEDIR}/public.sdk/source/vst/vstrepresentation.cpp")
"${VST3SDK_BASEDIR}/public.sdk/source/vst/vstrepresentation.cpp"
"${VST3SDK_BASEDIR}/public.sdk/source/vst/utility/stringconvert.cpp")
if(WIN32)
target_sources(vst3sdk PRIVATE
"${VST3SDK_BASEDIR}/public.sdk/source/common/threadchecker_win32.cpp")

@ -1 +1 @@
Subproject commit b8566ef3b2a0cba60a96e3ef2001224c865c8b36
Subproject commit 93cef1afb7061e488625045ba5a82abaa83d27fe

@ -1 +1 @@
Subproject commit d69d011fa08c3977928f7da0010f8938f93fd370
Subproject commit 9589800ed94573354bc29de45eec5744523fbfcb

View file

@ -26,7 +26,7 @@
#include <simde/x86/sse.h>
#endif
#if __cplusplus >= 201703L
#if __cplusplus >= 201703L && defined(__cpp_lib_math_special_functions)
static double i0(double x) { return std::cyl_bessel_i(0.0, x); }
#else
// external Bessel function from cephes

View file

@ -271,6 +271,7 @@ void Synth::Impl::clear()
filePool.setRamLoading(config::loadInRam);
clearCCLabels();
currentUsedCCs_.clear();
sustainOrSostenuto_.clear();
changedCCsThisCycle_.clear();
changedCCsLastCycle_.clear();
clearKeyLabels();
@ -2111,8 +2112,11 @@ void Synth::Impl::collectUsedCCsFromModulations(BitArray<config::numCCs>& usedCC
BitArray<config::numCCs> Synth::Impl::collectAllUsedCCs()
{
BitArray<config::numCCs> used;
for (const LayerPtr& layerPtr : layers_)
for (const LayerPtr& layerPtr : layers_) {
collectUsedCCsFromRegion(used, layerPtr->getRegion());
sustainOrSostenuto_.set(layerPtr->region_.sustainCC);
sustainOrSostenuto_.set(layerPtr->region_.sostenutoCC);
}
collectUsedCCsFromModulations(used, resources_.getModMatrix());
return used;
}

View file

@ -180,6 +180,13 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
client.receive<'b'>(delay, path, &blob);
} break;
MATCH("/sustain_or_sostenuto/slots", "") {
const BitArray<128>& sustainOrSostenuto = impl.sustainOrSostenuto_;
sfizz_blob_t blob { sustainOrSostenuto.data(),
static_cast<uint32_t>(sustainOrSostenuto.byte_size()) };
client.receive<'b'>(delay, path, &blob);
} break;
//----------------------------------------------------------------------
MATCH("/mem/buffers", "") {

View file

@ -248,6 +248,7 @@ struct Synth::Impl final: public Parser::Listener {
std::map<int, size_t> keyLabelsMap_;
BitArray<128> keySlots_;
BitArray<128> swLastSlots_;
BitArray<128> sustainOrSostenuto_;
std::vector<NoteNamePair> keyswitchLabels_;
std::map<int, size_t> keyswitchLabelsMap_;

View file

@ -202,7 +202,7 @@ void DecentSamplerInstrumentImporter::emitRegionalOpcodes(std::ostream& os, pugi
break;
case hash("loopEnabled"):
os << "loop_mode="
<< ((xmlOpcode.value == "true") ? "loop_continuous" : "one_shot") << "\n";
<< ((xmlOpcode.value == "true") ? "loop_continuous" : "no_loop") << "\n";
break;
case hash("attack"):
convertToReal("ampeg_attack");