Merge branch 'sfztools:develop' into develop
This commit is contained in:
commit
2198711123
22 changed files with 306 additions and 112 deletions
2
.gitmodules
vendored
2
.gitmodules
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
|
|
|||
|
|
@ -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 {};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 {};
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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", "") {
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue