sfizz/plugins/vst/SfizzVstEditor.cpp

479 lines
15 KiB
C++
Raw Normal View History

2020-03-05 11:16:47 +01:00
// 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
#include "SfizzVstEditor.h"
#include "SfizzVstState.h"
2021-02-04 02:48:49 +01:00
#include "SfizzVstParameters.h"
#include "SfizzVstUpdates.h"
#include "SfizzFileScan.h"
2020-08-29 04:54:18 +02:00
#include "editor/Editor.h"
#include "editor/EditIds.h"
#include "plugin/InstrumentDescription.h"
2021-02-04 02:48:49 +01:00
#include "IdleUpdateHandler.h"
2020-03-05 11:16:47 +01:00
#if !defined(__APPLE__) && !defined(_WIN32)
2020-03-12 00:41:35 +01:00
#include "X11RunLoop.h"
2020-03-05 11:16:47 +01:00
#endif
2021-04-03 23:58:46 +02:00
#include <ghc/fs_std.hpp>
2020-03-05 11:16:47 +01:00
using namespace VSTGUI;
2020-08-29 04:54:18 +02:00
static ViewRect sfizzUiViewRect { 0, 0, Editor::viewWidth, Editor::viewHeight };
2020-10-19 02:58:01 +02:00
enum {
kOscTempSize = 8192,
2020-11-24 08:24:39 +01:00
kOscQueueSize = 65536,
2021-02-23 09:27:20 +01:00
kNoteEventQueueSize = 8192,
2020-10-19 02:58:01 +02:00
};
2021-02-04 02:48:49 +01:00
SfizzVstEditor::SfizzVstEditor(
SfizzVstController* controller,
absl::Span<FObject*> continuousUpdates,
absl::Span<FObject*> triggerUpdates)
2020-10-19 02:58:01 +02:00
: VSTGUIEditor(controller, &sfizzUiViewRect),
2021-02-04 02:48:49 +01:00
oscTemp_(new uint8_t[kOscTempSize]),
continuousUpdates_(continuousUpdates.begin(), continuousUpdates.end()),
triggerUpdates_(triggerUpdates.begin(), triggerUpdates.end())
2020-03-05 11:16:47 +01:00
{
}
SfizzVstEditor::~SfizzVstEditor()
{
}
bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& platformType)
{
fprintf(stderr, "[sfizz] about to open view with parent %p\n", parent);
CRect wsize(0, 0, sfizzUiViewRect.getWidth(), sfizzUiViewRect.getHeight());
2020-03-05 11:16:47 +01:00
CFrame *frame = new CFrame(wsize, this);
this->frame = frame;
IPlatformFrameConfig* config = nullptr;
#if !defined(__APPLE__) && !defined(_WIN32)
X11::FrameConfig x11config;
2020-03-12 00:41:35 +01:00
if (!_runLoop)
_runLoop = new RunLoop(plugFrame);
x11config.runLoop = _runLoop;
2020-03-05 11:16:47 +01:00
config = &x11config;
#endif
2020-08-29 04:54:18 +02:00
Editor* editor = editor_.get();
if (!editor) {
editor = new Editor(*this);
editor_.reset(editor);
}
2021-02-04 02:48:49 +01:00
{
std::lock_guard<std::mutex> lock(oscQueueMutex_);
2020-12-07 09:41:36 +01:00
OscByteVec* queue = new OscByteVec;
oscQueue_.reset(queue);
queue->reserve(kOscQueueSize);
2021-02-04 02:48:49 +01:00
}
2021-02-23 09:27:20 +01:00
{
std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
NoteEventsVec* queue = new NoteEventsVec;
noteEventQueue_.reset(queue);
queue->reserve(kNoteEventQueueSize);
}
2020-03-05 11:16:47 +01:00
if (!frame->open(parent, platformType, config)) {
fprintf(stderr, "[sfizz] error opening frame\n");
return false;
}
2020-08-29 04:54:18 +02:00
editor->open(*frame);
2021-02-04 02:48:49 +01:00
for (FObject* update : continuousUpdates_)
update->addDependent(this);
for (FObject* update : triggerUpdates_)
update->addDependent(this);
Steinberg::IdleUpdateHandler::start();
for (FObject* update : continuousUpdates_)
update->deferUpdate();
absl::optional<fs::path> userFilesDir = SfizzPaths::getSfzConfigDefaultPath();
uiReceiveValue(EditId::CanEditUserFilesDir, 1);
uiReceiveValue(EditId::UserFilesDir, userFilesDir.value_or(fs::path()).u8string());
uiReceiveValue(EditId::FallbackFilesDir, SfizzPaths::getSfzFallbackDefaultPath().u8string());
2020-03-05 11:16:47 +01:00
return true;
}
void PLUGIN_API SfizzVstEditor::close()
{
CFrame *frame = this->frame;
if (frame) {
2021-02-04 02:48:49 +01:00
Steinberg::IdleUpdateHandler::stop();
for (FObject* update : continuousUpdates_)
update->removeDependent(this);
for (FObject* update : triggerUpdates_)
update->removeDependent(this);
2020-08-29 04:54:18 +02:00
if (editor_)
editor_->close();
2020-03-12 00:41:35 +01:00
if (frame->getNbReference() != 1)
2020-08-29 04:54:18 +02:00
frame->forget();
else
2020-03-12 00:41:35 +01:00
frame->close();
this->frame = nullptr;
2020-03-05 11:16:47 +01:00
}
2020-11-24 08:24:39 +01:00
2021-02-23 09:27:20 +01:00
{
std::lock_guard<std::mutex> lock(oscQueueMutex_);
oscQueue_.reset();
}
{
std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
noteEventQueue_.reset();
}
2020-03-05 11:16:47 +01:00
}
///
2020-03-12 00:41:35 +01:00
CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message)
{
CMessageResult result = VSTGUIEditor::notify(sender, message);
if (result != kMessageNotified)
return result;
#if !defined(__APPLE__) && !defined(_WIN32)
if (message == CVSTGUITimer::kMsgTimer) {
SharedPointer<VSTGUI::RunLoop> runLoop = RunLoop::get();
if (runLoop) {
// note(jpc) I don't find a reliable way to check if the host
// 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
2020-11-24 08:24:39 +01:00
if (message == CVSTGUITimer::kMsgTimer) {
processOscQueue();
2021-02-23 09:27:20 +01:00
processNoteEventQueue();
2020-11-24 08:24:39 +01:00
}
2020-11-24 07:24:24 +01:00
2020-03-12 00:41:35 +01:00
return result;
}
2021-02-04 02:48:49 +01:00
void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
2020-03-05 11:16:47 +01:00
{
2021-02-04 02:48:49 +01:00
if (OSCUpdate* update = FCast<OSCUpdate>(changedUnknown)) {
// this update is synchronous: may happen from non-UI thread
uint32 size = update->size();
if (size > 0) {
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(update->data());
std::lock_guard<std::mutex> lock(oscQueueMutex_);
if (OscByteVec* queue = oscQueue_.get())
std::copy(bytes, bytes + size, std::back_inserter(*queue));
}
return;
}
2020-03-05 11:16:47 +01:00
2021-02-23 09:27:20 +01:00
if (NoteUpdate* update = FCast<NoteUpdate>(changedUnknown)) {
// this update is synchronous: may happen from non-UI thread
uint32 count = update->count();
if (count > 0) {
const auto* events = update->events();
std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
if (NoteEventsVec* queue = noteEventQueue_.get())
std::copy(events, events + count, std::back_inserter(*queue));
}
return;
2021-02-23 09:27:20 +01:00
}
2021-04-02 04:05:14 +02:00
if (SfzUpdate* update = FCast<SfzUpdate>(changedUnknown)) {
2021-02-04 02:48:49 +01:00
const std::string path = update->getPath();
2021-04-02 04:05:14 +02:00
uiReceiveValue(EditId::SfzFile, path);
return;
2021-04-02 04:05:14 +02:00
}
if (SfzDescriptionUpdate* update = FCast<SfzDescriptionUpdate>(changedUnknown)) {
const InstrumentDescription desc = parseDescriptionBlob(update->getDescription());
2021-04-02 18:39:52 +02:00
uiReceiveValue(EditId::UINumCurves, desc.numCurves);
uiReceiveValue(EditId::UINumMasters, desc.numMasters);
uiReceiveValue(EditId::UINumGroups, desc.numGroups);
uiReceiveValue(EditId::UINumRegions, desc.numRegions);
uiReceiveValue(EditId::UINumPreloadedSamples, desc.numSamples);
2021-04-03 23:58:46 +02:00
const fs::path rootPath = fs::u8path(desc.rootPath);
const fs::path imagePath = rootPath / fs::u8path(desc.image);
uiReceiveValue(EditId::BackgroundImage, imagePath.u8string());
2021-04-02 18:39:52 +02:00
for (unsigned key = 0; key < 128; ++key) {
bool keyUsed = desc.keyUsed.test(key);
bool keyswitchUsed = desc.keyswitchUsed.test(key);
2021-04-03 14:39:56 +02:00
uiReceiveValue(editIdForKeyUsed(int(key)), float(keyUsed));
uiReceiveValue(editIdForKeyswitchUsed(int(key)), float(keyswitchUsed));
2021-04-02 18:39:52 +02:00
if (keyUsed)
2021-04-03 14:39:56 +02:00
uiReceiveValue(editIdForKeyLabel(int(key)), desc.keyLabel[key]);
2021-04-02 18:39:52 +02:00
if (keyswitchUsed)
2021-04-03 14:39:56 +02:00
uiReceiveValue(editIdForKeyswitchLabel(int(key)), desc.keyswitchLabel[key]);
2021-04-02 18:39:52 +02:00
}
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
bool ccUsed = desc.ccUsed.test(cc);
2021-04-03 14:39:56 +02:00
uiReceiveValue(editIdForCCUsed(int(cc)), float(ccUsed));
2021-04-02 18:39:52 +02:00
if (ccUsed) {
2021-04-03 14:39:56 +02:00
uiReceiveValue(editIdForCCDefault(int(cc)), desc.ccDefault[cc]);
uiReceiveValue(editIdForCCLabel(int(cc)), desc.ccLabel[cc]);
2021-04-02 18:39:52 +02:00
}
}
return;
}
2021-04-02 04:05:14 +02:00
if (ScalaUpdate* update = FCast<ScalaUpdate>(changedUnknown)) {
const std::string path = update->getPath();
uiReceiveValue(EditId::ScalaFile, path);
return;
2021-02-04 02:48:49 +01:00
}
2020-11-24 07:24:24 +01:00
2021-02-04 02:48:49 +01:00
if (PlayStateUpdate* update = FCast<PlayStateUpdate>(changedUnknown)) {
const SfizzPlayState playState = update->getState();
uiReceiveValue(EditId::UINumActiveVoices, playState.activeVoices);
return;
}
2020-11-24 08:24:39 +01:00
2021-02-04 02:48:49 +01:00
if (Vst::RangeParameter* param = Steinberg::FCast<Vst::RangeParameter>(changedUnknown)) {
const Vst::ParamValue value = param->getNormalized();
const Vst::ParamID id = param->getInfo().id;
const SfizzRange range = SfizzRange::getForParameter(id);
switch (id) {
case kPidVolume:
uiReceiveValue(EditId::Volume, range.denormalize(value));
break;
case kPidNumVoices:
uiReceiveValue(EditId::Polyphony, range.denormalize(value));
break;
case kPidOversampling:
2021-04-01 16:36:42 +02:00
uiReceiveValue(EditId::Oversampling, float(1u << (int32)range.denormalize(value)));
2021-02-04 02:48:49 +01:00
break;
case kPidPreloadSize:
uiReceiveValue(EditId::PreloadSize, range.denormalize(value));
break;
case kPidScalaRootKey:
uiReceiveValue(EditId::ScalaRootKey, range.denormalize(value));
break;
case kPidTuningFrequency:
uiReceiveValue(EditId::TuningFrequency, range.denormalize(value));
break;
case kPidStretchedTuning:
uiReceiveValue(EditId::StretchTuning, range.denormalize(value));
break;
default:
if (id >= kPidCC0 && id <= kPidCCLast) {
int cc = id - kPidCC0;
uiReceiveValue(editIdForCC(cc), range.denormalize(value));
}
break;
2020-12-07 09:41:36 +01:00
}
2021-02-04 02:48:49 +01:00
return;
}
Vst::VSTGUIEditor::update(changedUnknown, message);
2020-11-24 08:24:39 +01:00
}
void SfizzVstEditor::processOscQueue()
{
2021-02-04 02:48:49 +01:00
std::lock_guard<std::mutex> lock(oscQueueMutex_);
OscByteVec* queue = oscQueue_.get();
if (!queue)
return;
const uint8_t* oscData = queue->data();
size_t oscSize = queue->size();
const char* path;
const char* sig;
const sfizz_arg_t* args;
uint8_t buffer[1024];
uint32_t msgSize;
while ((msgSize = sfizz_extract_message(oscData, oscSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) {
uiReceiveMessage(path, sig, args);
oscData += msgSize;
oscSize -= msgSize;
}
2020-11-24 08:24:39 +01:00
2021-02-04 02:48:49 +01:00
queue->clear();
2020-10-19 02:58:01 +02:00
}
2021-02-23 09:27:20 +01:00
void SfizzVstEditor::processNoteEventQueue()
{
std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
NoteEventsVec* queue = noteEventQueue_.get();
if (!queue)
return;
for (std::pair<uint32, float> event : *queue)
uiReceiveValue(editIdForKey(event.first), event.second);
queue->clear();
}
2020-03-05 11:16:47 +01:00
///
2020-08-29 04:54:18 +02:00
void SfizzVstEditor::uiSendValue(EditId id, const EditValue& v)
2020-03-05 11:16:47 +01:00
{
if (id == EditId::SfzFile)
2020-08-31 12:52:23 +02:00
loadSfzFile(v.to_string());
2020-08-29 04:54:18 +02:00
else if (id == EditId::ScalaFile)
2020-08-31 12:52:23 +02:00
loadScalaFile(v.to_string());
2020-08-29 04:54:18 +02:00
else {
SfizzVstController* ctrl = getController();
2021-02-04 02:48:49 +01:00
auto normalizeAndSet = [ctrl](Vst::ParamID pid, float value) {
float normValue = SfizzRange::getForParameter(pid).normalize(value);
2020-08-29 04:54:18 +02:00
ctrl->setParamNormalized(pid, normValue);
ctrl->performEdit(pid, normValue);
};
switch (id) {
case EditId::Volume:
2021-02-04 02:48:49 +01:00
normalizeAndSet(kPidVolume, v.to_float());
2020-08-29 04:54:18 +02:00
break;
case EditId::Polyphony:
2021-02-04 02:48:49 +01:00
normalizeAndSet(kPidNumVoices, v.to_float());
2020-08-29 04:54:18 +02:00
break;
case EditId::Oversampling:
{
2021-02-04 02:48:49 +01:00
const int32 factor = static_cast<int32>(v.to_float());
normalizeAndSet(kPidOversampling, integerLog2(factor));
2020-08-29 04:54:18 +02:00
}
break;
case EditId::PreloadSize:
2021-02-04 02:48:49 +01:00
normalizeAndSet(kPidPreloadSize, v.to_float());
2020-08-29 04:54:18 +02:00
break;
case EditId::ScalaRootKey:
2021-02-04 02:48:49 +01:00
normalizeAndSet(kPidScalaRootKey, v.to_float());
2020-08-29 04:54:18 +02:00
break;
case EditId::TuningFrequency:
2021-02-04 02:48:49 +01:00
normalizeAndSet(kPidTuningFrequency, v.to_float());
2020-08-29 04:54:18 +02:00
break;
case EditId::StretchTuning:
2021-02-04 02:48:49 +01:00
normalizeAndSet(kPidStretchedTuning, v.to_float());
2020-08-29 04:54:18 +02:00
break;
2020-03-05 11:16:47 +01:00
case EditId::UserFilesDir:
SfizzPaths::setSfzConfigDefaultPath(fs::u8path(v.to_string()));
break;
2020-08-29 04:54:18 +02:00
default:
if (editIdIsCC(id))
normalizeAndSet(kPidCC0 + ccForEditId(id), v.to_float());
2020-08-29 04:54:18 +02:00
break;
}
2020-03-05 11:16:47 +01:00
}
}
2020-08-29 04:54:18 +02:00
void SfizzVstEditor::uiBeginSend(EditId id)
{
Vst::ParamID pid = parameterOfEditId(id);
2021-02-08 20:45:55 +01:00
if (pid != Vst::kNoParamId)
2020-08-29 04:54:18 +02:00
getController()->beginEdit(pid);
}
void SfizzVstEditor::uiEndSend(EditId id)
{
Vst::ParamID pid = parameterOfEditId(id);
2021-02-08 20:45:55 +01:00
if (pid != Vst::kNoParamId)
2020-08-29 04:54:18 +02:00
getController()->endEdit(pid);
}
2020-10-14 20:04:59 +02:00
void SfizzVstEditor::uiSendMIDI(const uint8_t* data, uint32_t len)
2020-08-29 04:54:18 +02:00
{
2020-10-14 20:04:59 +02:00
SfizzVstController* ctl = getController();
Steinberg::OPtr<Vst::IMessage> msg { ctl->allocateMessage() };
if (!msg) {
fprintf(stderr, "[Sfizz] UI could not allocate message\n");
return;
}
msg->setMessageID("MidiMessage");
Vst::IAttributeList* attr = msg->getAttributes();
attr->setBinary("Data", data, len);
ctl->sendMessage(msg);
2020-08-29 04:54:18 +02:00
}
2020-10-19 02:58:01 +02:00
void SfizzVstEditor::uiSendMessage(const char* path, const char* sig, const sfizz_arg_t* args)
{
SfizzVstController* ctl = getController();
Steinberg::OPtr<Vst::IMessage> msg { ctl->allocateMessage() };
if (!msg) {
fprintf(stderr, "[Sfizz] UI could not allocate message\n");
return;
}
uint8_t* oscTemp = oscTemp_.get();
uint32_t oscSize = sfizz_prepare_message(oscTemp, kOscTempSize, path, sig, args);
if (oscSize <= kOscTempSize) {
msg->setMessageID("OscMessage");
Vst::IAttributeList* attr = msg->getAttributes();
attr->setBinary("Data", oscTemp, oscSize);
ctl->sendMessage(msg);
}
}
2020-08-29 04:54:18 +02:00
///
2020-03-05 11:16:47 +01:00
void SfizzVstEditor::loadSfzFile(const std::string& filePath)
{
2020-03-05 18:21:46 +01:00
SfizzVstController* ctl = getController();
2020-03-05 15:53:01 +01:00
2020-06-20 05:49:00 +02:00
Steinberg::OPtr<Vst::IMessage> msg { ctl->allocateMessage() };
2020-03-05 15:53:01 +01:00
if (!msg) {
fprintf(stderr, "[Sfizz] UI could not allocate message\n");
return;
2020-03-05 11:16:47 +01:00
}
2020-03-05 15:53:01 +01:00
msg->setMessageID("LoadSfz");
Vst::IAttributeList* attr = msg->getAttributes();
2020-03-28 17:00:07 +01:00
attr->setBinary("File", filePath.data(), filePath.size());
2020-03-05 15:53:01 +01:00
ctl->sendMessage(msg);
2020-06-20 05:49:00 +02:00
}
void SfizzVstEditor::loadScalaFile(const std::string& filePath)
{
SfizzVstController* ctl = getController();
Steinberg::OPtr<Vst::IMessage> msg { ctl->allocateMessage() };
if (!msg) {
fprintf(stderr, "[Sfizz] UI could not allocate message\n");
return;
}
msg->setMessageID("LoadScala");
Vst::IAttributeList* attr = msg->getAttributes();
attr->setBinary("File", filePath.data(), filePath.size());
ctl->sendMessage(msg);
2020-03-05 11:16:47 +01:00
}
2020-08-29 04:54:18 +02:00
Vst::ParamID SfizzVstEditor::parameterOfEditId(EditId id)
2020-06-20 05:49:00 +02:00
{
2020-08-29 04:54:18 +02:00
switch (id) {
case EditId::Volume: return kPidVolume;
case EditId::Polyphony: return kPidNumVoices;
case EditId::Oversampling: return kPidOversampling;
case EditId::PreloadSize: return kPidPreloadSize;
case EditId::ScalaRootKey: return kPidScalaRootKey;
case EditId::TuningFrequency: return kPidTuningFrequency;
case EditId::StretchTuning: return kPidStretchedTuning;
default:
if (editIdIsCC(id))
return kPidCC0 + ccForEditId(id);
return Vst::kNoParamId;
2020-03-05 16:41:59 +01:00
}
}