Merge pull request #767 from jpcima/onetime-instrument-info
Onetime instrument info
This commit is contained in:
commit
e7290b142d
23 changed files with 935 additions and 392 deletions
|
|
@ -1,10 +1,13 @@
|
|||
add_library(plugins-common STATIC EXCLUDE_FROM_ALL
|
||||
"common/plugin/MessageUtils.h"
|
||||
"common/plugin/MessageUtils.cpp")
|
||||
"common/plugin/MessageUtils.cpp"
|
||||
"common/plugin/InstrumentDescription.h"
|
||||
"common/plugin/InstrumentDescription.cpp")
|
||||
target_include_directories(plugins-common PUBLIC "common")
|
||||
target_link_libraries(plugins-common
|
||||
PUBLIC sfizz::spin_mutex
|
||||
PUBLIC sfizz::filesystem absl::strings)
|
||||
PUBLIC sfizz::filesystem absl::strings
|
||||
PRIVATE sfizz::internal sfizz::sfizz)
|
||||
add_library(sfizz::plugins-common ALIAS plugins-common)
|
||||
|
||||
if((SFIZZ_LV2 AND SFIZZ_LV2_UI) OR SFIZZ_VST)
|
||||
|
|
|
|||
213
plugins/common/plugin/InstrumentDescription.cpp
Normal file
213
plugins/common/plugin/InstrumentDescription.cpp
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
// 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 "InstrumentDescription.h"
|
||||
#include "MessageUtils.h"
|
||||
#include "sfizz.hpp"
|
||||
#include <absl/strings/str_cat.h>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
|
||||
template <class... Args>
|
||||
static void bufferedStrCat(std::string* buffer, const Args&... args)
|
||||
{
|
||||
buffer->clear();
|
||||
absl::StrAppend(buffer, args...);
|
||||
}
|
||||
|
||||
std::string getDescriptionBlob(sfizz_synth_t* handle)
|
||||
{
|
||||
std::string blob;
|
||||
blob.reserve(128 * 1024);
|
||||
|
||||
std::vector<char> msgbuf;
|
||||
msgbuf.resize(1024);
|
||||
|
||||
std::string pathbuf;
|
||||
pathbuf.reserve(256);
|
||||
|
||||
struct ClientData {
|
||||
sfz::Sfizz* synth = nullptr;
|
||||
sfz::Client* client = nullptr;
|
||||
std::string* blob = nullptr;
|
||||
std::vector<char>* msgbuf = nullptr;
|
||||
std::string* pathbuf = nullptr;
|
||||
};
|
||||
|
||||
sfz::Sfizz synth(handle);
|
||||
ClientData cdata;
|
||||
sfz::ClientPtr client = synth.createClient(&cdata);
|
||||
cdata.synth = &synth;
|
||||
cdata.client = client.get();
|
||||
cdata.blob = &blob;
|
||||
cdata.msgbuf = &msgbuf;
|
||||
cdata.pathbuf = &pathbuf;
|
||||
|
||||
synth.setReceiveCallback(*client, [](void* data, int, const char* path, const char* sig, const sfizz_arg_t* args) {
|
||||
ClientData& cdata = *reinterpret_cast<ClientData*>(data);
|
||||
unsigned indices[8];
|
||||
|
||||
///
|
||||
uint32_t msglen = sfizz_prepare_message(cdata.msgbuf->data(), cdata.msgbuf->size(), path, sig, args);
|
||||
if (msglen > cdata.msgbuf->size()) {
|
||||
cdata.msgbuf->resize(msglen);
|
||||
sfizz_prepare_message(cdata.msgbuf->data(), cdata.msgbuf->size(), path, sig, args);
|
||||
}
|
||||
cdata.blob->append(cdata.msgbuf->data(), msglen);
|
||||
|
||||
///
|
||||
if (Messages::matchOSC("/key/slots", path, indices) && !strcmp(sig, "b")) {
|
||||
ConstBitSpan bits(args[0].b->data, 8 * args[0].b->size);
|
||||
for (unsigned key = 0; key < 128 && key < bits.bit_size(); ++key) {
|
||||
if (bits.test(key)) {
|
||||
bufferedStrCat(cdata.pathbuf, "/key", key, "/label");
|
||||
cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Messages::matchOSC("/sw/last/slots", path, indices) && !strcmp(sig, "b")) {
|
||||
ConstBitSpan bits(args[0].b->data, 8 * args[0].b->size);
|
||||
for (unsigned key = 0; key < 128 && key < bits.bit_size(); ++key) {
|
||||
if (bits.test(key)) {
|
||||
bufferedStrCat(cdata.pathbuf, "/sw/last/", key, "/label");
|
||||
cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Messages::matchOSC("/cc/slots", path, indices) && !strcmp(sig, "b")) {
|
||||
ConstBitSpan bits(args[0].b->data, 8 * args[0].b->size);
|
||||
for (unsigned cc = 0; cc < sfz::config::numCCs && cc < bits.bit_size(); ++cc) {
|
||||
if (bits.test(cc)) {
|
||||
bufferedStrCat(cdata.pathbuf, "/cc", cc, "/label");
|
||||
cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr);
|
||||
bufferedStrCat(cdata.pathbuf, "/cc", cc, "/default");
|
||||
cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
synth.sendMessage(*client, 0, "/num_regions", "", nullptr);
|
||||
synth.sendMessage(*client, 0, "/num_groups", "", nullptr);
|
||||
synth.sendMessage(*client, 0, "/num_masters", "", nullptr);
|
||||
synth.sendMessage(*client, 0, "/num_curves", "", nullptr);
|
||||
synth.sendMessage(*client, 0, "/num_samples", "", nullptr);
|
||||
synth.sendMessage(*client, 0, "/root_path", "", nullptr);
|
||||
synth.sendMessage(*client, 0, "/image", "", nullptr);
|
||||
synth.sendMessage(*client, 0, "/key/slots", "", nullptr);
|
||||
synth.sendMessage(*client, 0, "/sw/last/slots", "", nullptr);
|
||||
synth.sendMessage(*client, 0, "/cc/slots", "", nullptr);
|
||||
|
||||
blob.shrink_to_fit();
|
||||
return blob;
|
||||
}
|
||||
|
||||
InstrumentDescription parseDescriptionBlob(absl::string_view blob)
|
||||
{
|
||||
InstrumentDescription desc;
|
||||
|
||||
const uint8_t* src = reinterpret_cast<const uint8_t*>(blob.data());
|
||||
uint32_t srcSize = blob.size();
|
||||
char buffer[1024];
|
||||
|
||||
const char* path;
|
||||
const char* sig;
|
||||
const sfizz_arg_t* args;
|
||||
|
||||
int32_t byteCount;
|
||||
while ((byteCount = sfizz_extract_message(src, srcSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) {
|
||||
unsigned indices[8];
|
||||
|
||||
///
|
||||
auto copyArgToBitSpan = [](const sfizz_arg_t& arg, BitSpan bits)
|
||||
{
|
||||
size_t size = std::min<size_t>(bits.byte_size(), arg.b->size);
|
||||
memcpy(bits.data(), arg.b->data, size);
|
||||
};
|
||||
|
||||
//
|
||||
if (Messages::matchOSC("/num_regions", path, indices) && !strcmp(sig, "i"))
|
||||
desc.numRegions = uint32_t(args[0].i);
|
||||
else if (Messages::matchOSC("/num_groups", path, indices) && !strcmp(sig, "i"))
|
||||
desc.numGroups = uint32_t(args[0].i);
|
||||
else if (Messages::matchOSC("/num_masters", path, indices) && !strcmp(sig, "i"))
|
||||
desc.numMasters = uint32_t(args[0].i);
|
||||
else if (Messages::matchOSC("/num_curves", path, indices) && !strcmp(sig, "i"))
|
||||
desc.numCurves = uint32_t(args[0].i);
|
||||
else if (Messages::matchOSC("/num_samples", path, indices) && !strcmp(sig, "i"))
|
||||
desc.numSamples = uint32_t(args[0].i);
|
||||
else if (Messages::matchOSC("/root_path", path, indices) && !strcmp(sig, "s"))
|
||||
desc.rootPath = args[0].s;
|
||||
else if (Messages::matchOSC("/image", path, indices) && !strcmp(sig, "s"))
|
||||
desc.image = args[0].s;
|
||||
else if (Messages::matchOSC("/key/slots", path, indices) && !strcmp(sig, "b"))
|
||||
copyArgToBitSpan(args[0], desc.keyUsed.span());
|
||||
else if (Messages::matchOSC("/sw/last/slots", path, indices) && !strcmp(sig, "b"))
|
||||
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("/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"))
|
||||
desc.keyswitchLabel[indices[0]] = args[0].s;
|
||||
else if (Messages::matchOSC("/cc&/label", path, indices) && !strcmp(sig, "s"))
|
||||
desc.ccLabel[indices[0]] = args[0].s;
|
||||
else if (Messages::matchOSC("/cc&/default", path, indices) && !strcmp(sig, "f"))
|
||||
desc.ccDefault[indices[0]] = args[0].f;
|
||||
|
||||
src += byteCount;
|
||||
srcSize -= byteCount;
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const InstrumentDescription& desc)
|
||||
{
|
||||
os << "instrument:\n";
|
||||
|
||||
os << " regions: " << desc.numRegions << "\n";
|
||||
os << " groups: " << desc.numGroups << "\n";
|
||||
os << " masters: " << desc.numMasters << "\n";
|
||||
os << " curves: " << desc.numCurves << "\n";
|
||||
os << " samples: " << desc.numSamples << "\n";
|
||||
|
||||
os << " root_path: " << desc.rootPath << "\n";
|
||||
os << " image: " << desc.image << "\n";
|
||||
|
||||
os << " keys:\n";
|
||||
for (unsigned i = 0; i < 128; ++i) {
|
||||
if (desc.keyUsed.test(i)) {
|
||||
os << " - number: " << i << "\n";
|
||||
if (!desc.keyLabel[i].empty())
|
||||
os << " label: " << desc.keyLabel[i].c_str() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
os << " keyswitches:\n";
|
||||
for (unsigned i = 0; i < 128; ++i) {
|
||||
if (desc.keyswitchUsed.test(i)) {
|
||||
os << " - number: " << i << "\n";
|
||||
if (!desc.keyswitchLabel[i].empty())
|
||||
os << " label: " << desc.keyswitchLabel[i].c_str() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
os << " cc:\n";
|
||||
for (unsigned i = 0; i < sfz::config::numCCs; ++i) {
|
||||
if (desc.ccUsed.test(i)) {
|
||||
os << " - number: " << i << "\n";
|
||||
os << " default: " << desc.ccDefault[i] << "\n";
|
||||
if (!desc.ccLabel[i].empty())
|
||||
os << " label: " << desc.ccLabel[i].c_str() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
53
plugins/common/plugin/InstrumentDescription.h
Normal file
53
plugins/common/plugin/InstrumentDescription.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// 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
|
||||
|
||||
#pragma once
|
||||
#include "sfizz/Config.h"
|
||||
#include "sfizz/utility/bit_array/BitArray.h"
|
||||
#include <absl/strings/string_view.h>
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <iosfwd>
|
||||
struct sfizz_synth_t;
|
||||
|
||||
/**
|
||||
* @brief Description of user-interactible elements of the SFZ instrument
|
||||
*/
|
||||
struct InstrumentDescription {
|
||||
uint32_t numRegions {};
|
||||
uint32_t numGroups {};
|
||||
uint32_t numMasters {};
|
||||
uint32_t numCurves {};
|
||||
uint32_t numSamples {};
|
||||
std::string rootPath;
|
||||
std::string image;
|
||||
BitArray<128> keyUsed {};
|
||||
BitArray<128> keyswitchUsed {};
|
||||
BitArray<sfz::config::numCCs> ccUsed {};
|
||||
std::array<std::string, 128> keyLabel {};
|
||||
std::array<std::string, 128> keyswitchLabel {};
|
||||
std::array<std::string, sfz::config::numCCs> ccLabel {};
|
||||
std::array<float, sfz::config::numCCs> ccDefault {};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Produce a description of the currently loaded instrument in the synth,
|
||||
* in the form of a concatenation of OSC messages.
|
||||
*
|
||||
* This form is a message transmissible over binary channels.
|
||||
*/
|
||||
std::string getDescriptionBlob(sfizz_synth_t* handle);
|
||||
|
||||
/**
|
||||
* @brief Extract the information from the OSC blob and rearrange it in a
|
||||
* structured form.
|
||||
*/
|
||||
InstrumentDescription parseDescriptionBlob(absl::string_view blob);
|
||||
|
||||
/**
|
||||
* @brief Display the description in human-readable format.
|
||||
*/
|
||||
std::ostream& operator<<(std::ostream& os, const InstrumentDescription& desc);
|
||||
|
|
@ -22,11 +22,19 @@ enum class EditId : int {
|
|||
UserFilesDir,
|
||||
FallbackFilesDir,
|
||||
//
|
||||
Key0,
|
||||
KeyLast = Key0 + 128 - 1,
|
||||
#define KEY_RANGE(Name) Name##0, Name##Last = Name##0 + 128 - 1
|
||||
#define CC_RANGE(Name) Name##0, Name##Last = Name##0 + sfz::config::numCCs - 1
|
||||
//
|
||||
Controller0,
|
||||
ControllerLast = Controller0 + sfz::config::numCCs - 1,
|
||||
KEY_RANGE(Key),
|
||||
CC_RANGE(Controller),
|
||||
//
|
||||
KEY_RANGE(KeyUsed),
|
||||
KEY_RANGE(KeyLabel),
|
||||
KEY_RANGE(KeyswitchUsed),
|
||||
KEY_RANGE(KeyswitchLabel),
|
||||
CC_RANGE(ControllerUsed),
|
||||
CC_RANGE(ControllerDefault),
|
||||
CC_RANGE(ControllerLabel),
|
||||
//
|
||||
UINumCurves,
|
||||
UINumMasters,
|
||||
|
|
@ -35,6 +43,11 @@ enum class EditId : int {
|
|||
UINumPreloadedSamples,
|
||||
UINumActiveVoices,
|
||||
UIActivePanel,
|
||||
//
|
||||
BackgroundImage,
|
||||
//
|
||||
#undef KEY_RANGE
|
||||
#undef CC_RANGE
|
||||
};
|
||||
|
||||
struct EditRange {
|
||||
|
|
@ -48,29 +61,33 @@ struct EditRange {
|
|||
static EditRange get(EditId id);
|
||||
};
|
||||
|
||||
inline EditId editIdForCC(int cc)
|
||||
{
|
||||
return EditId(int(EditId::Controller0) + cc);
|
||||
}
|
||||
inline int ccForEditId(EditId id)
|
||||
{
|
||||
return int(id) - int(EditId::Controller0);
|
||||
}
|
||||
inline bool editIdIsCC(EditId id)
|
||||
{
|
||||
return int(id) >= int(EditId::Controller0) &&
|
||||
int(id) <= int(EditId::ControllerLast);
|
||||
}
|
||||
#define DEFINE_EDIT_ID_RANGE_HELPERS(type, Type, IdPrefix) \
|
||||
inline bool editIdIs##Type(EditId id) \
|
||||
{ \
|
||||
return int(id) >= int(EditId::IdPrefix##0) && \
|
||||
int(id) <= int(EditId::IdPrefix##Last); \
|
||||
} \
|
||||
inline EditId editIdFor##Type(int value) \
|
||||
{ \
|
||||
EditId id = EditId(int(EditId::IdPrefix##0) + value); \
|
||||
assert(editIdIs##Type(id)); \
|
||||
return id; \
|
||||
} \
|
||||
inline int type##ForEditId(EditId id) \
|
||||
{ \
|
||||
assert(editIdIs##Type(id)); \
|
||||
return int(id) - int(EditId::IdPrefix##0); \
|
||||
}
|
||||
|
||||
inline EditId editIdForKey(int key)
|
||||
{
|
||||
return EditId(int(EditId::Key0) + key);
|
||||
}
|
||||
inline int keyForEditId(EditId id)
|
||||
{
|
||||
return int(id) - int(EditId::Key0);
|
||||
}
|
||||
inline bool editIdIsKey(EditId id)
|
||||
{
|
||||
return int(id) >= int(EditId::Key0) && int(id) <= int(EditId::KeyLast);
|
||||
}
|
||||
// defines editIdForCC, ccForEditId, editIdIsCC, etc..
|
||||
DEFINE_EDIT_ID_RANGE_HELPERS(cc, CC, Controller)
|
||||
DEFINE_EDIT_ID_RANGE_HELPERS(key, Key, Key)
|
||||
DEFINE_EDIT_ID_RANGE_HELPERS(keyUsed, KeyUsed, KeyUsed)
|
||||
DEFINE_EDIT_ID_RANGE_HELPERS(keyLabel, KeyLabel, KeyLabel)
|
||||
DEFINE_EDIT_ID_RANGE_HELPERS(keyswitchUsed, KeyswitchUsed, KeyswitchUsed)
|
||||
DEFINE_EDIT_ID_RANGE_HELPERS(keyswitchLabel, KeyswitchLabel, KeyswitchLabel)
|
||||
DEFINE_EDIT_ID_RANGE_HELPERS(ccUsed, CCUsed, ControllerUsed)
|
||||
DEFINE_EDIT_ID_RANGE_HELPERS(ccDefault, CCDefault, ControllerDefault)
|
||||
DEFINE_EDIT_ID_RANGE_HELPERS(ccLabel, CCLabel, ControllerLabel)
|
||||
|
||||
#undef DEFINE_EDIT_ID_RANGE_HELPERS
|
||||
|
|
|
|||
|
|
@ -200,6 +200,7 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
|
|||
void updateKeyswitchNameLabel();
|
||||
|
||||
void updateKeyUsed(unsigned key, bool used);
|
||||
void updateKeyLabel(unsigned key, const char* label);
|
||||
void updateKeyswitchUsed(unsigned key, bool used);
|
||||
void updateCCUsed(unsigned cc, bool used);
|
||||
void updateCCValue(unsigned cc, float value);
|
||||
|
|
@ -278,12 +279,6 @@ void Editor::open(CFrame& frame)
|
|||
impl.oscSendQueueTimer_ = makeOwned<CVSTGUITimer>(
|
||||
[this](CVSTGUITimer* timer) { impl_->tickOSCQueue(timer); },
|
||||
oscSendInterval, false);
|
||||
|
||||
// request the whole Key and CC information
|
||||
impl.sendQueuedOSC("/key/slots", "", nullptr);
|
||||
impl.sendQueuedOSC("/sw/last/slots", "", nullptr);
|
||||
impl.sendQueuedOSC("/cc/slots", "", nullptr);
|
||||
impl.sendQueuedOSC("/image", "", nullptr);
|
||||
}
|
||||
|
||||
void Editor::close()
|
||||
|
|
@ -311,12 +306,6 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
|
|||
const std::string& value = v.to_string();
|
||||
currentSfzFile_ = value;
|
||||
updateSfzFileLabel(value);
|
||||
|
||||
// request the whole Key and CC information
|
||||
sendQueuedOSC("/key/slots", "", nullptr);
|
||||
sendQueuedOSC("/sw/last/slots", "", nullptr);
|
||||
sendQueuedOSC("/cc/slots", "", nullptr);
|
||||
sendQueuedOSC("/image", "", nullptr);
|
||||
}
|
||||
break;
|
||||
case EditId::Volume:
|
||||
|
|
@ -454,6 +443,12 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
|
|||
setActivePanel(value);
|
||||
}
|
||||
break;
|
||||
case EditId::BackgroundImage:
|
||||
{
|
||||
const std::string& value = v.to_string();
|
||||
updateBackgroundImage(value.c_str());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (editIdIsKey(id)) {
|
||||
const int key = keyForEditId(id);
|
||||
|
|
@ -461,6 +456,31 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
|
|||
if (SPiano* piano = piano_)
|
||||
piano->setKeyValue(key, value);
|
||||
}
|
||||
else if (editIdIsKeyUsed(id)) {
|
||||
updateKeyUsed(keyUsedForEditId(id), v.to_float() != 0);
|
||||
}
|
||||
else if (editIdIsKeyLabel(id)) {
|
||||
updateKeyLabel(keyLabelForEditId(id), v.to_string().c_str());
|
||||
}
|
||||
else if (editIdIsKeyswitchUsed(id)) {
|
||||
updateKeyswitchUsed(keyswitchUsedForEditId(id), v.to_float() != 0);
|
||||
}
|
||||
else if (editIdIsKeyswitchLabel(id)) {
|
||||
updateSWLastLabel(keyswitchLabelForEditId(id), v.to_string().c_str());
|
||||
}
|
||||
else if (editIdIsCCUsed(id)) {
|
||||
updateCCUsed(ccUsedForEditId(id), v.to_float() != 0);
|
||||
// TODO(jpc) remove value requests, when implementing CC automation
|
||||
char pathBuf[256];
|
||||
sprintf(pathBuf, "/cc%u/value", ccUsedForEditId(id));
|
||||
sendQueuedOSC(pathBuf, "", nullptr);
|
||||
}
|
||||
else if (editIdIsCCDefault(id)) {
|
||||
updateCCDefaultValue(ccDefaultForEditId(id), v.to_float());
|
||||
}
|
||||
else if (editIdIsCCLabel(id)) {
|
||||
updateCCLabel(ccLabelForEditId(id), v.to_string().c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -469,46 +489,7 @@ void Editor::Impl::uiReceiveMessage(const char* path, const char* sig, const sfi
|
|||
{
|
||||
unsigned indices[8];
|
||||
|
||||
if (Messages::matchOSC("/key/slots", path, indices) && !strcmp(sig, "b")) {
|
||||
size_t numBits = 8 * args[0].b->size;
|
||||
ConstBitSpan bits { args[0].b->data, numBits };
|
||||
for (unsigned key = 0; key < 128; ++key) {
|
||||
bool used = key < numBits && bits.test(key);
|
||||
updateKeyUsed(key, used);
|
||||
}
|
||||
}
|
||||
else if (Messages::matchOSC("/sw/last/slots", path, indices) && !strcmp(sig, "b")) {
|
||||
size_t numBits = 8 * args[0].b->size;
|
||||
ConstBitSpan bits { args[0].b->data, numBits };
|
||||
for (unsigned key = 0; key < 128; ++key) {
|
||||
bool used = key < numBits && bits.test(key);
|
||||
updateKeyswitchUsed(key, used);
|
||||
if (used) {
|
||||
char pathBuf[256];
|
||||
sprintf(pathBuf, "/sw/last/%u/label", key);
|
||||
sendQueuedOSC(pathBuf, "", nullptr);
|
||||
}
|
||||
}
|
||||
sendQueuedOSC("/sw/last/current", "", nullptr);
|
||||
}
|
||||
else if (Messages::matchOSC("/cc/slots", path, indices) && !strcmp(sig, "b")) {
|
||||
size_t numBits = 8 * args[0].b->size;
|
||||
ConstBitSpan bits { args[0].b->data, numBits };
|
||||
for (unsigned cc = 0; cc < numBits; ++cc) {
|
||||
bool used = bits.test(cc);
|
||||
updateCCUsed(cc, used);
|
||||
if (used) {
|
||||
char pathBuf[256];
|
||||
sprintf(pathBuf, "/cc%u/value", cc);
|
||||
sendQueuedOSC(pathBuf, "", nullptr);
|
||||
sprintf(pathBuf, "/cc%u/default", cc);
|
||||
sendQueuedOSC(pathBuf, "", nullptr);
|
||||
sprintf(pathBuf, "/cc%u/label", cc);
|
||||
sendQueuedOSC(pathBuf, "", nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Messages::matchOSC("/cc/changed", path, indices) && !strcmp(sig, "b")) {
|
||||
if (Messages::matchOSC("/cc/changed~", path, indices) && !strcmp(sig, "b")) {
|
||||
size_t numBits = 8 * args[0].b->size;
|
||||
ConstBitSpan bits { args[0].b->data, numBits };
|
||||
for (unsigned cc = 0; cc < numBits; ++cc) {
|
||||
|
|
@ -523,20 +504,11 @@ void Editor::Impl::uiReceiveMessage(const char* path, const char* sig, const sfi
|
|||
else if (Messages::matchOSC("/cc&/value", path, indices) && !strcmp(sig, "f")) {
|
||||
updateCCValue(indices[0], args[0].f);
|
||||
}
|
||||
else if (Messages::matchOSC("/cc&/default", path, indices) && !strcmp(sig, "f")) {
|
||||
updateCCDefaultValue(indices[0], args[0].f);
|
||||
}
|
||||
else if (Messages::matchOSC("/cc&/label", path, indices) && !strcmp(sig, "s")) {
|
||||
updateCCLabel(indices[0], args[0].s);
|
||||
}
|
||||
else if (Messages::matchOSC("/sw/last/current", path, indices) && !strcmp(sig, "i")) {
|
||||
updateSWLastCurrent(args[0].i);
|
||||
}
|
||||
else if (Messages::matchOSC("/sw/last/&/label", path, indices) && !strcmp(sig, "s")) {
|
||||
updateSWLastLabel(indices[0], args[0].s);
|
||||
}
|
||||
else if (Messages::matchOSC("/image", path, indices) && !strcmp(sig, "s")) {
|
||||
updateBackgroundImage(args[0].s);
|
||||
else if (Messages::matchOSC("/sw/last/current", path, indices) && !strcmp(sig, "N")) {
|
||||
updateSWLastCurrent(-1);
|
||||
}
|
||||
else if (Messages::matchOSC("/mem/buffers", path, indices) && !strcmp(sig, "h")) {
|
||||
updateMemoryUsed(args[0].h);
|
||||
|
|
@ -1151,12 +1123,6 @@ void Editor::Impl::changeSfzFile(const std::string& filePath)
|
|||
ctrl_->uiSendValue(EditId::SfzFile, filePath);
|
||||
currentSfzFile_ = filePath;
|
||||
updateSfzFileLabel(filePath);
|
||||
|
||||
// request the whole Key and CC information
|
||||
sendQueuedOSC("/key/slots", "", nullptr);
|
||||
sendQueuedOSC("/sw/last/slots", "", nullptr);
|
||||
sendQueuedOSC("/cc/slots", "", nullptr);
|
||||
sendQueuedOSC("/image", "", nullptr);
|
||||
}
|
||||
|
||||
void Editor::Impl::changeToNextSfzFile(long offset)
|
||||
|
|
@ -1519,6 +1485,13 @@ void Editor::Impl::updateKeyUsed(unsigned key, bool used)
|
|||
piano->setKeyUsed(key, used);
|
||||
}
|
||||
|
||||
void Editor::Impl::updateKeyLabel(unsigned key, const char* label)
|
||||
{
|
||||
// TODO nothing done with this info currently
|
||||
(void)key;
|
||||
(void)label;
|
||||
}
|
||||
|
||||
void Editor::Impl::updateKeyswitchUsed(unsigned key, bool used)
|
||||
{
|
||||
if (SPiano* piano = piano_)
|
||||
|
|
@ -1574,10 +1547,7 @@ void Editor::Impl::updateSWLastLabel(unsigned sw, const char* label)
|
|||
|
||||
void Editor::Impl::updateBackgroundImage(const char* filepath)
|
||||
{
|
||||
const fs::path sfzFilePath = fs::u8path(currentSfzFile_);
|
||||
const fs::path sfzDirPath = sfzFilePath.parent_path();
|
||||
const fs::path imagePath = sfzDirPath / fs::u8path(filepath);
|
||||
SharedPointer<CBitmap> bitmap = loadAnyFormatImage(imagePath);
|
||||
SharedPointer<CBitmap> bitmap = loadAnyFormatImage(filepath);
|
||||
|
||||
if (!bitmap)
|
||||
bitmap = owned(new CBitmap("background.png"));
|
||||
|
|
|
|||
|
|
@ -20,12 +20,14 @@ source_group("Turtle Files" FILES
|
|||
)
|
||||
add_library(${LV2PLUGIN_PRJ_NAME} MODULE
|
||||
${PROJECT_NAME}.cpp
|
||||
${PROJECT_NAME}_lv2_common.cpp
|
||||
${LV2PLUGIN_TTL_SRC_FILES})
|
||||
target_link_libraries(${LV2PLUGIN_PRJ_NAME} PRIVATE sfizz::sfizz sfizz::import sfizz::plugins-common)
|
||||
|
||||
if(SFIZZ_LV2_UI)
|
||||
add_library(${LV2PLUGIN_PRJ_NAME}_ui MODULE
|
||||
${PROJECT_NAME}_ui.cpp
|
||||
${PROJECT_NAME}_lv2_common.cpp
|
||||
vstgui_helpers.h
|
||||
vstgui_helpers.cpp)
|
||||
target_link_libraries(${LV2PLUGIN_PRJ_NAME}_ui PRIVATE sfizz::editor sfizz::vstgui sfizz::plugins-common)
|
||||
|
|
|
|||
|
|
@ -33,38 +33,15 @@
|
|||
*/
|
||||
|
||||
#include "sfizz_lv2.h"
|
||||
#include "sfizz_lv2_plugin.h"
|
||||
|
||||
#include "sfizz/import/ForeignInstrument.h"
|
||||
|
||||
#include <lv2/atom/atom.h>
|
||||
#include <lv2/atom/forge.h>
|
||||
#include <lv2/atom/util.h>
|
||||
#include <lv2/buf-size/buf-size.h>
|
||||
#include <lv2/core/lv2.h>
|
||||
#include <lv2/core/lv2_util.h>
|
||||
#include <lv2/midi/midi.h>
|
||||
#include <lv2/options/options.h>
|
||||
#include <lv2/parameters/parameters.h>
|
||||
#include <lv2/patch/patch.h>
|
||||
#include <lv2/state/state.h>
|
||||
#include <lv2/urid/urid.h>
|
||||
#include <lv2/worker/worker.h>
|
||||
#include <lv2/log/logger.h>
|
||||
#include <lv2/log/log.h>
|
||||
#include <lv2/time/time.h>
|
||||
|
||||
#include <ardour/lv2_extensions.h>
|
||||
|
||||
#include <spin_mutex.h>
|
||||
#include "plugin/InstrumentDescription.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <sfizz.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#define CHANNEL_MASK 0x0F
|
||||
#define MIDI_CHANNEL(byte) (byte & CHANNEL_MASK)
|
||||
#define MIDI_STATUS(byte) (byte & ~CHANNEL_MASK)
|
||||
|
|
@ -77,122 +54,12 @@
|
|||
#define LOG_SAMPLE_COUNT 48000
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
#define DEFAULT_SCALA_FILE "Contents/Resources/DefaultScale.scl"
|
||||
#define DEFAULT_SFZ_FILE "Contents/Resources/DefaultInstrument.sfz"
|
||||
// This assumes that the longest path is the default sfz file; if not, change it
|
||||
#define MAX_BUNDLE_PATH_SIZE (MAX_PATH_SIZE - sizeof(DEFAULT_SFZ_FILE))
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define LV2_DEBUG(...) lv2_log_note(&self->logger, "[DEBUG] " __VA_ARGS__)
|
||||
#else
|
||||
#define LV2_DEBUG(...)
|
||||
#endif
|
||||
|
||||
struct sfizz_plugin_t
|
||||
{
|
||||
// Features
|
||||
LV2_URID_Map *map {};
|
||||
LV2_URID_Unmap *unmap {};
|
||||
LV2_Worker_Schedule *worker {};
|
||||
LV2_Log_Log *log {};
|
||||
LV2_Midnam *midnam {};
|
||||
|
||||
// Ports
|
||||
const LV2_Atom_Sequence *control_port {};
|
||||
LV2_Atom_Sequence *notify_port {};
|
||||
float *output_buffers[2] {};
|
||||
const float *volume_port {};
|
||||
const float *polyphony_port {};
|
||||
const float *oversampling_port {};
|
||||
const float *preload_port {};
|
||||
const float *freewheel_port {};
|
||||
const float *scala_root_key_port {};
|
||||
const float *tuning_frequency_port {};
|
||||
const float *stretch_tuning_port {};
|
||||
float *active_voices_port {};
|
||||
float *num_curves_port {};
|
||||
float *num_masters_port {};
|
||||
float *num_groups_port {};
|
||||
float *num_regions_port {};
|
||||
float *num_samples_port {};
|
||||
|
||||
// Atom forge
|
||||
LV2_Atom_Forge forge {}; ///< Forge for writing atoms in run thread
|
||||
LV2_Atom_Forge forge_secondary {}; ///< Forge for writing into other buffers
|
||||
|
||||
// Logger
|
||||
LV2_Log_Logger logger {};
|
||||
|
||||
// URIs
|
||||
LV2_URID midi_event_uri {};
|
||||
LV2_URID options_interface_uri {};
|
||||
LV2_URID max_block_length_uri {};
|
||||
LV2_URID nominal_block_length_uri {};
|
||||
LV2_URID sample_rate_uri {};
|
||||
LV2_URID atom_object_uri {};
|
||||
LV2_URID atom_blank_uri {};
|
||||
LV2_URID atom_float_uri {};
|
||||
LV2_URID atom_double_uri {};
|
||||
LV2_URID atom_int_uri {};
|
||||
LV2_URID atom_long_uri {};
|
||||
LV2_URID atom_urid_uri {};
|
||||
LV2_URID atom_path_uri {};
|
||||
LV2_URID patch_set_uri {};
|
||||
LV2_URID patch_get_uri {};
|
||||
LV2_URID patch_put_uri {};
|
||||
LV2_URID patch_property_uri {};
|
||||
LV2_URID patch_value_uri {};
|
||||
LV2_URID patch_body_uri {};
|
||||
LV2_URID state_changed_uri {};
|
||||
LV2_URID sfizz_sfz_file_uri {};
|
||||
LV2_URID sfizz_scala_file_uri {};
|
||||
LV2_URID sfizz_num_voices_uri {};
|
||||
LV2_URID sfizz_preload_size_uri {};
|
||||
LV2_URID sfizz_oversampling_uri {};
|
||||
LV2_URID sfizz_log_status_uri {};
|
||||
LV2_URID sfizz_check_modification_uri {};
|
||||
LV2_URID sfizz_active_voices_uri {};
|
||||
LV2_URID sfizz_osc_blob_uri {};
|
||||
LV2_URID time_position_uri {};
|
||||
LV2_URID time_bar_uri {};
|
||||
LV2_URID time_bar_beat_uri {};
|
||||
LV2_URID time_beat_unit_uri {};
|
||||
LV2_URID time_beats_per_bar_uri {};
|
||||
LV2_URID time_beats_per_minute_uri {};
|
||||
LV2_URID time_speed_uri {};
|
||||
|
||||
// Sfizz related data
|
||||
sfizz_synth_t *synth {};
|
||||
sfizz_client_t *client {};
|
||||
spin_mutex_t *synth_mutex {};
|
||||
bool expect_nominal_block_length {};
|
||||
char sfz_file_path[MAX_PATH_SIZE] {};
|
||||
char scala_file_path[MAX_PATH_SIZE] {};
|
||||
int num_voices {};
|
||||
unsigned int preload_size {};
|
||||
sfizz_oversampling_factor_t oversampling {};
|
||||
float stretch_tuning {};
|
||||
volatile bool check_modification {};
|
||||
int max_block_size {};
|
||||
int sample_counter {};
|
||||
float sample_rate {};
|
||||
std::atomic<int> must_update_midnam {};
|
||||
|
||||
// Timing data
|
||||
int bar {};
|
||||
double bar_beat {};
|
||||
int beats_per_bar {};
|
||||
int beat_unit {};
|
||||
double bpm_tempo {};
|
||||
double speed {};
|
||||
|
||||
// Paths
|
||||
char bundle_path[MAX_BUNDLE_PATH_SIZE] {};
|
||||
|
||||
// OSC
|
||||
uint8_t osc_temp[OSC_TEMP_SIZE] {};
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SFIZZ_TIMEINFO_POSITION = 1 << 0,
|
||||
|
|
@ -201,6 +68,14 @@ enum
|
|||
SFIZZ_TIMEINFO_SPEED = 1 << 3,
|
||||
};
|
||||
|
||||
///
|
||||
static bool
|
||||
sfizz_lv2_load_file(sfizz_plugin_t *self, const char *file_path);
|
||||
|
||||
static bool
|
||||
sfizz_lv2_load_scala_file(sfizz_plugin_t *self, const char *file_path);
|
||||
|
||||
///
|
||||
static void
|
||||
sfizz_lv2_state_free_path(LV2_State_Free_Path_Handle handle,
|
||||
char *path)
|
||||
|
|
@ -607,11 +482,10 @@ instantiate(const LV2_Descriptor *descriptor,
|
|||
sfizz_set_broadcast_callback(self->synth, &sfizz_lv2_receive_message, self);
|
||||
sfizz_set_receive_callback(self->client, &sfizz_lv2_receive_message);
|
||||
|
||||
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);
|
||||
self->sfz_blob_mutex = new std::mutex;
|
||||
|
||||
sfizz_load_file(self->synth, self->sfz_file_path);
|
||||
sfizz_load_scala_file(self->synth, self->scala_file_path);
|
||||
sfizz_lv2_load_file(self, self->sfz_file_path);
|
||||
sfizz_lv2_load_scala_file(self, self->scala_file_path);
|
||||
|
||||
sfizz_lv2_update_timeinfo(self, 0, ~0);
|
||||
|
||||
|
|
@ -622,6 +496,8 @@ static void
|
|||
cleanup(LV2_Handle instance)
|
||||
{
|
||||
sfizz_plugin_t *self = (sfizz_plugin_t *)instance;
|
||||
delete[] self->sfz_blob_data;
|
||||
delete self->sfz_blob_mutex;
|
||||
spin_mutex_destroy(self->synth_mutex);
|
||||
sfizz_delete_client(self->client);
|
||||
sfizz_free(self->synth);
|
||||
|
|
@ -1066,6 +942,10 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
// Render the block
|
||||
sfizz_render_block(self->synth, self->output_buffers, 2, (int)sample_count);
|
||||
|
||||
// Request OSC updates
|
||||
sfizz_send_message(self->synth, self->client, 0, "/cc/changed~", "", nullptr);
|
||||
sfizz_send_message(self->synth, self->client, 0, "/sw/last/current", "", nullptr);
|
||||
|
||||
spin_mutex_unlock(self->synth_mutex);
|
||||
|
||||
if (self->midnam && self->must_update_midnam.exchange(0))
|
||||
|
|
@ -1172,6 +1052,25 @@ sfizz_lv2_update_file_info(sfizz_plugin_t* self, const char *file_path)
|
|||
self->must_update_midnam.store(1);
|
||||
}
|
||||
|
||||
static void
|
||||
sfizz_lv2_update_sfz_info(sfizz_plugin_t *self)
|
||||
{
|
||||
const std::string blob = getDescriptionBlob(self->synth);
|
||||
|
||||
uint32_t size = uint32_t(blob.size());
|
||||
uint8_t *data = new uint8_t[size];
|
||||
memcpy(data, blob.data(), size);
|
||||
|
||||
self->sfz_blob_mutex->lock();
|
||||
self->sfz_blob_serial += 1;
|
||||
const uint8_t *old_data = self->sfz_blob_data;
|
||||
self->sfz_blob_data = data;
|
||||
self->sfz_blob_size = size;
|
||||
self->sfz_blob_mutex->unlock();
|
||||
|
||||
delete[] old_data;
|
||||
}
|
||||
|
||||
static bool
|
||||
sfizz_lv2_load_file(sfizz_plugin_t *self, const char *file_path)
|
||||
{
|
||||
|
|
@ -1197,7 +1096,7 @@ sfizz_lv2_load_file(sfizz_plugin_t *self, const char *file_path)
|
|||
status = sfizz_load_string(self->synth, virtual_path.c_str(), sfz_text.c_str());
|
||||
}
|
||||
|
||||
///
|
||||
sfizz_lv2_update_sfz_info(self);
|
||||
sfizz_lv2_update_file_info(self, file_path);
|
||||
return status;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,27 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <lv2/atom/atom.h>
|
||||
#include <lv2/atom/forge.h>
|
||||
#include <lv2/atom/util.h>
|
||||
#include <lv2/buf-size/buf-size.h>
|
||||
#include <lv2/core/lv2.h>
|
||||
#include <lv2/core/lv2_util.h>
|
||||
#include <lv2/midi/midi.h>
|
||||
#include <lv2/options/options.h>
|
||||
#include <lv2/parameters/parameters.h>
|
||||
#include <lv2/patch/patch.h>
|
||||
#include <lv2/state/state.h>
|
||||
#include <lv2/urid/urid.h>
|
||||
#include <lv2/worker/worker.h>
|
||||
#include <lv2/log/logger.h>
|
||||
#include <lv2/log/log.h>
|
||||
#include <lv2/time/time.h>
|
||||
|
||||
#include <ardour/lv2_extensions.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX_PATH_SIZE 1024
|
||||
#define ATOM_TEMP_SIZE 8192
|
||||
#define OSC_TEMP_SIZE 8192
|
||||
|
|
@ -45,3 +66,22 @@ enum
|
|||
SFIZZ_NUM_REGIONS = 16,
|
||||
SFIZZ_NUM_SAMPLES = 17,
|
||||
};
|
||||
|
||||
// For use with instance-access
|
||||
struct sfizz_plugin_t;
|
||||
|
||||
/**
|
||||
* @brief Fetch a copy of the current description, if more recent than what the
|
||||
* version we already have.
|
||||
*
|
||||
* @param self The LV2 plugin
|
||||
* @param serial The optional serial number to compare against
|
||||
* @param desc The memory zone which receives the copy
|
||||
* @param sizep The memory zone which receives the size
|
||||
* @param serialp The memory zone which receives the new serial
|
||||
* @return if serial is not null and its value isn't older, false
|
||||
* otherwise, true, and the pointer returned in descp must be freed with delete[] after use
|
||||
*/
|
||||
bool sfizz_lv2_fetch_description(
|
||||
sfizz_plugin_t *self, const int *serial,
|
||||
uint8_t **descp, uint32_t *sizep, int *serialp);
|
||||
|
|
|
|||
31
plugins/lv2/sfizz_lv2_common.cpp
Normal file
31
plugins/lv2/sfizz_lv2_common.cpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// 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 "sfizz_lv2.h"
|
||||
#include "sfizz_lv2_plugin.h"
|
||||
|
||||
bool sfizz_lv2_fetch_description(
|
||||
sfizz_plugin_t *self, const int *serial,
|
||||
uint8_t **descp, uint32_t *sizep, int *serialp)
|
||||
{
|
||||
// must be thread-safe
|
||||
|
||||
if (serial && self->sfz_blob_serial == *serial)
|
||||
return false;
|
||||
|
||||
self->sfz_blob_mutex->lock();
|
||||
uint32_t size = self->sfz_blob_size;
|
||||
uint8_t *data = new uint8_t[size];
|
||||
int new_serial = self->sfz_blob_serial;
|
||||
memcpy(data, self->sfz_blob_data, size);
|
||||
self->sfz_blob_mutex->unlock();
|
||||
|
||||
*descp = data;
|
||||
*sizep = size;
|
||||
*serialp = new_serial;
|
||||
|
||||
return true;
|
||||
}
|
||||
131
plugins/lv2/sfizz_lv2_plugin.h
Normal file
131
plugins/lv2/sfizz_lv2_plugin.h
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sfizz_lv2.h"
|
||||
#include <spin_mutex.h>
|
||||
#include <sfizz.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#define DEFAULT_SCALA_FILE "Contents/Resources/DefaultScale.scl"
|
||||
#define DEFAULT_SFZ_FILE "Contents/Resources/DefaultInstrument.sfz"
|
||||
// This assumes that the longest path is the default sfz file; if not, change it
|
||||
#define MAX_BUNDLE_PATH_SIZE (MAX_PATH_SIZE - sizeof(DEFAULT_SFZ_FILE))
|
||||
|
||||
struct sfizz_plugin_t
|
||||
{
|
||||
// Features
|
||||
LV2_URID_Map *map {};
|
||||
LV2_URID_Unmap *unmap {};
|
||||
LV2_Worker_Schedule *worker {};
|
||||
LV2_Log_Log *log {};
|
||||
LV2_Midnam *midnam {};
|
||||
|
||||
// Ports
|
||||
const LV2_Atom_Sequence *control_port {};
|
||||
LV2_Atom_Sequence *notify_port {};
|
||||
float *output_buffers[2] {};
|
||||
const float *volume_port {};
|
||||
const float *polyphony_port {};
|
||||
const float *oversampling_port {};
|
||||
const float *preload_port {};
|
||||
const float *freewheel_port {};
|
||||
const float *scala_root_key_port {};
|
||||
const float *tuning_frequency_port {};
|
||||
const float *stretch_tuning_port {};
|
||||
float *active_voices_port {};
|
||||
float *num_curves_port {};
|
||||
float *num_masters_port {};
|
||||
float *num_groups_port {};
|
||||
float *num_regions_port {};
|
||||
float *num_samples_port {};
|
||||
|
||||
// Atom forge
|
||||
LV2_Atom_Forge forge {}; ///< Forge for writing atoms in run thread
|
||||
LV2_Atom_Forge forge_secondary {}; ///< Forge for writing into other buffers
|
||||
|
||||
// Logger
|
||||
LV2_Log_Logger logger {};
|
||||
|
||||
// URIs
|
||||
LV2_URID midi_event_uri {};
|
||||
LV2_URID options_interface_uri {};
|
||||
LV2_URID max_block_length_uri {};
|
||||
LV2_URID nominal_block_length_uri {};
|
||||
LV2_URID sample_rate_uri {};
|
||||
LV2_URID atom_object_uri {};
|
||||
LV2_URID atom_blank_uri {};
|
||||
LV2_URID atom_float_uri {};
|
||||
LV2_URID atom_double_uri {};
|
||||
LV2_URID atom_int_uri {};
|
||||
LV2_URID atom_long_uri {};
|
||||
LV2_URID atom_urid_uri {};
|
||||
LV2_URID atom_path_uri {};
|
||||
LV2_URID patch_set_uri {};
|
||||
LV2_URID patch_get_uri {};
|
||||
LV2_URID patch_put_uri {};
|
||||
LV2_URID patch_property_uri {};
|
||||
LV2_URID patch_value_uri {};
|
||||
LV2_URID patch_body_uri {};
|
||||
LV2_URID state_changed_uri {};
|
||||
LV2_URID sfizz_sfz_file_uri {};
|
||||
LV2_URID sfizz_scala_file_uri {};
|
||||
LV2_URID sfizz_num_voices_uri {};
|
||||
LV2_URID sfizz_preload_size_uri {};
|
||||
LV2_URID sfizz_oversampling_uri {};
|
||||
LV2_URID sfizz_log_status_uri {};
|
||||
LV2_URID sfizz_check_modification_uri {};
|
||||
LV2_URID sfizz_active_voices_uri {};
|
||||
LV2_URID sfizz_osc_blob_uri {};
|
||||
LV2_URID time_position_uri {};
|
||||
LV2_URID time_bar_uri {};
|
||||
LV2_URID time_bar_beat_uri {};
|
||||
LV2_URID time_beat_unit_uri {};
|
||||
LV2_URID time_beats_per_bar_uri {};
|
||||
LV2_URID time_beats_per_minute_uri {};
|
||||
LV2_URID time_speed_uri {};
|
||||
|
||||
// Sfizz related data
|
||||
sfizz_synth_t *synth {};
|
||||
sfizz_client_t *client {};
|
||||
spin_mutex_t *synth_mutex {};
|
||||
bool expect_nominal_block_length {};
|
||||
char sfz_file_path[MAX_PATH_SIZE] {};
|
||||
char scala_file_path[MAX_PATH_SIZE] {};
|
||||
int num_voices {};
|
||||
unsigned int preload_size {};
|
||||
sfizz_oversampling_factor_t oversampling {};
|
||||
float stretch_tuning {};
|
||||
volatile bool check_modification {};
|
||||
int max_block_size {};
|
||||
int sample_counter {};
|
||||
float sample_rate {};
|
||||
std::atomic<int> must_update_midnam {};
|
||||
|
||||
// Current instrument description
|
||||
std::mutex *sfz_blob_mutex {};
|
||||
volatile int sfz_blob_serial {};
|
||||
const uint8_t *volatile sfz_blob_data {};
|
||||
volatile uint32_t sfz_blob_size {};
|
||||
|
||||
// Timing data
|
||||
int bar {};
|
||||
double bar_beat {};
|
||||
int beats_per_bar {};
|
||||
int beat_unit {};
|
||||
double bpm_tempo {};
|
||||
double speed {};
|
||||
|
||||
// Paths
|
||||
char bundle_path[MAX_BUNDLE_PATH_SIZE] {};
|
||||
|
||||
// OSC
|
||||
uint8_t osc_temp[OSC_TEMP_SIZE] {};
|
||||
};
|
||||
|
|
@ -37,6 +37,7 @@
|
|||
#include "editor/Editor.h"
|
||||
#include "editor/EditorController.h"
|
||||
#include "editor/EditIds.h"
|
||||
#include "plugin/InstrumentDescription.h"
|
||||
#include <lv2/ui/ui.h>
|
||||
#include <lv2/atom/atom.h>
|
||||
#include <lv2/atom/forge.h>
|
||||
|
|
@ -44,6 +45,8 @@
|
|||
#include <lv2/midi/midi.h>
|
||||
#include <lv2/patch/patch.h>
|
||||
#include <lv2/urid/urid.h>
|
||||
#include <lv2/instance-access/instance-access.h>
|
||||
#include <ghc/fs_std.hpp>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
|
|
@ -86,6 +89,7 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface {
|
|||
LV2_URID_Unmap *unmap = nullptr;
|
||||
LV2UI_Resize *resize = nullptr;
|
||||
LV2UI_Touch *touch = nullptr;
|
||||
sfizz_plugin_t *plugin = nullptr;
|
||||
FrameHolder uiFrame;
|
||||
std::unique_ptr<Editor> editor;
|
||||
#if LINUX
|
||||
|
|
@ -112,6 +116,9 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface {
|
|||
uint8_t osc_temp[OSC_TEMP_SIZE];
|
||||
alignas(LV2_Atom) uint8_t atom_temp[ATOM_TEMP_SIZE];
|
||||
|
||||
int sfz_serial = 0;
|
||||
bool valid_sfz_serial = false;
|
||||
|
||||
protected:
|
||||
void uiSendValue(EditId id, const EditValue& v) override;
|
||||
void uiBeginSend(EditId id) override;
|
||||
|
|
@ -178,12 +185,18 @@ instantiate(const LV2UI_Descriptor *descriptor,
|
|||
self->touch = (LV2UI_Touch*)(**f).data;
|
||||
else if (!strcmp((**f).URI, LV2_UI__parent))
|
||||
parentWindowId = (**f).data;
|
||||
else if (!strcmp((**f).URI, LV2_INSTANCE_ACCESS_URI))
|
||||
self->plugin = (sfizz_plugin_t *)(**f).data;
|
||||
}
|
||||
|
||||
// The map feature is required
|
||||
if (!map || !unmap)
|
||||
return nullptr;
|
||||
|
||||
// The instance-access feature is required
|
||||
if (!self->plugin)
|
||||
return nullptr;
|
||||
|
||||
LV2_Atom_Forge *forge = &self->atom_forge;
|
||||
lv2_atom_forge_init(forge, map);
|
||||
self->atom_event_transfer_uri = map->map(map->handle, LV2_ATOM__eventTransfer);
|
||||
|
|
@ -358,11 +371,70 @@ port_event(LV2UI_Handle ui,
|
|||
(void)buffer_size;
|
||||
}
|
||||
|
||||
static void
|
||||
sfizz_ui_update_description(sfizz_ui_t *self, const InstrumentDescription& desc)
|
||||
{
|
||||
self->uiReceiveValue(EditId::UINumCurves, desc.numCurves);
|
||||
self->uiReceiveValue(EditId::UINumMasters, desc.numMasters);
|
||||
self->uiReceiveValue(EditId::UINumGroups, desc.numGroups);
|
||||
self->uiReceiveValue(EditId::UINumRegions, desc.numRegions);
|
||||
self->uiReceiveValue(EditId::UINumPreloadedSamples, desc.numSamples);
|
||||
|
||||
const fs::path rootPath = fs::u8path(desc.rootPath);
|
||||
const fs::path imagePath = rootPath / fs::u8path(desc.image);
|
||||
self->uiReceiveValue(EditId::BackgroundImage, imagePath.u8string());
|
||||
|
||||
for (unsigned key = 0; key < 128; ++key) {
|
||||
bool keyUsed = desc.keyUsed.test(key);
|
||||
bool keyswitchUsed = desc.keyswitchUsed.test(key);
|
||||
self->uiReceiveValue(editIdForKeyUsed(key), float(keyUsed));
|
||||
self->uiReceiveValue(editIdForKeyswitchUsed(key), float(keyswitchUsed));
|
||||
if (keyUsed)
|
||||
self->uiReceiveValue(editIdForKeyLabel(key), desc.keyLabel[key]);
|
||||
if (keyswitchUsed)
|
||||
self->uiReceiveValue(editIdForKeyswitchLabel(key), desc.keyswitchLabel[key]);
|
||||
}
|
||||
|
||||
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||
bool ccUsed = desc.ccUsed.test(cc);
|
||||
self->uiReceiveValue(editIdForCCUsed(cc), float(ccUsed));
|
||||
if (ccUsed) {
|
||||
self->uiReceiveValue(editIdForCCDefault(cc), desc.ccDefault[cc]);
|
||||
self->uiReceiveValue(editIdForCCLabel(cc), desc.ccLabel[cc]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sfizz_ui_check_sfz_update(sfizz_ui_t *self)
|
||||
{
|
||||
uint8_t *data = nullptr;
|
||||
uint32_t size = 0;
|
||||
int new_serial = 0;
|
||||
const int *serial = self->valid_sfz_serial ? &self->sfz_serial : nullptr;
|
||||
|
||||
bool update = sfizz_lv2_fetch_description(
|
||||
self->plugin, serial, &data, &size, &new_serial);
|
||||
|
||||
if (update) {
|
||||
std::unique_ptr<uint8_t[]> cleanup(data);
|
||||
self->sfz_serial = new_serial;
|
||||
self->valid_sfz_serial = true;
|
||||
|
||||
const InstrumentDescription desc = parseDescriptionBlob(
|
||||
absl::string_view(reinterpret_cast<char*>(data), size));
|
||||
sfizz_ui_update_description(self, desc);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
idle(LV2UI_Handle ui)
|
||||
{
|
||||
sfizz_ui_t *self = (sfizz_ui_t *)ui;
|
||||
|
||||
// check if there are news regarding the current SFZ
|
||||
sfizz_ui_check_sfz_update(self);
|
||||
|
||||
#if LINUX
|
||||
self->runLoop->execIdle();
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -11,4 +11,5 @@
|
|||
lv2:optionalFeature ui:parent ;
|
||||
lv2:optionalFeature ui:touch ;
|
||||
lv2:requiredFeature urid:map ;
|
||||
lv2:requiredFeature urid:unmap .
|
||||
lv2:requiredFeature urid:unmap ;
|
||||
lv2:requiredFeature <http://lv2plug.in/ns/ext/instance-access> .
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context)
|
|||
// create update objects
|
||||
oscUpdate_ = Steinberg::owned(new OSCUpdate);
|
||||
noteUpdate_ = Steinberg::owned(new NoteUpdate);
|
||||
sfzPathUpdate_ = Steinberg::owned(new FilePathUpdate(kFilePathUpdateSfz));
|
||||
scalaPathUpdate_ = Steinberg::owned(new FilePathUpdate(kFilePathUpdateScala));
|
||||
sfzUpdate_ = Steinberg::owned(new SfzUpdate);
|
||||
sfzDescriptionUpdate_ = Steinberg::owned(new SfzDescriptionUpdate);
|
||||
scalaUpdate_ = Steinberg::owned(new ScalaUpdate);
|
||||
processorStateUpdate_ = Steinberg::owned(new ProcessorStateUpdate);
|
||||
playStateUpdate_ = Steinberg::owned(new PlayStateUpdate);
|
||||
|
||||
|
|
@ -211,10 +212,10 @@ tresult PLUGIN_API SfizzVstControllerNoUi::setComponentState(IBStream* stream)
|
|||
setParam(kPidTuningFrequency, s.tuningFrequency);
|
||||
setParam(kPidStretchedTuning, s.stretchedTuning);
|
||||
|
||||
sfzPathUpdate_->setPath(s.sfzFile);
|
||||
sfzPathUpdate_->deferUpdate();
|
||||
scalaPathUpdate_->setPath(s.scalaFile);
|
||||
scalaPathUpdate_->deferUpdate();
|
||||
sfzUpdate_->setPath(s.sfzFile);
|
||||
sfzUpdate_->deferUpdate();
|
||||
scalaUpdate_->setPath(s.scalaFile);
|
||||
scalaUpdate_->deferUpdate();
|
||||
|
||||
return kResultTrue;
|
||||
}
|
||||
|
|
@ -230,35 +231,49 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message)
|
|||
const char* id = message->getMessageID();
|
||||
Vst::IAttributeList* attr = message->getAttributes();
|
||||
|
||||
if (!strcmp(id, "LoadedSfz")) {
|
||||
///
|
||||
auto stringFromBinaryAttribute = [attr](const char* id, absl::string_view& string) -> tresult {
|
||||
const void* data = nullptr;
|
||||
uint32 size = 0;
|
||||
result = attr->getBinary("File", data, size);
|
||||
tresult result = attr->getBinary(id, data, size);
|
||||
if (result == kResultTrue)
|
||||
string = absl::string_view(reinterpret_cast<const char*>(data), size);
|
||||
return result;
|
||||
};
|
||||
|
||||
///
|
||||
if (!strcmp(id, "LoadedSfz")) {
|
||||
absl::string_view sfzFile;
|
||||
absl::string_view sfzDescriptionBlob;
|
||||
|
||||
result = stringFromBinaryAttribute("File", sfzFile);
|
||||
if (result != kResultTrue)
|
||||
return result;
|
||||
|
||||
std::string sfzFile(static_cast<const char *>(data), size);
|
||||
processorStateUpdate_->access([&sfzFile](SfizzVstState& state) {
|
||||
state.sfzFile = sfzFile;
|
||||
result = stringFromBinaryAttribute("Description", sfzDescriptionBlob);
|
||||
if (result != kResultTrue)
|
||||
return result;
|
||||
|
||||
processorStateUpdate_->access([sfzFile](SfizzVstState& state) {
|
||||
state.sfzFile = std::string(sfzFile);
|
||||
});
|
||||
sfzPathUpdate_->setPath(std::move(sfzFile));
|
||||
sfzPathUpdate_->deferUpdate();
|
||||
sfzUpdate_->setPath(std::string(sfzFile));
|
||||
sfzUpdate_->deferUpdate();
|
||||
sfzDescriptionUpdate_->setDescription(std::string(sfzDescriptionBlob));
|
||||
sfzDescriptionUpdate_->deferUpdate();
|
||||
}
|
||||
else if (!strcmp(id, "LoadedScala")) {
|
||||
const void* data = nullptr;
|
||||
uint32 size = 0;
|
||||
result = attr->getBinary("File", data, size);
|
||||
absl::string_view scalaFile;
|
||||
|
||||
result = stringFromBinaryAttribute("File", scalaFile);
|
||||
if (result != kResultTrue)
|
||||
return result;
|
||||
|
||||
std::string scalaFile(static_cast<const char *>(data), size);
|
||||
processorStateUpdate_->access([&scalaFile](SfizzVstState& state) {
|
||||
state.scalaFile = scalaFile;
|
||||
processorStateUpdate_->access([scalaFile](SfizzVstState& state) {
|
||||
state.scalaFile = std::string(scalaFile);
|
||||
});
|
||||
scalaPathUpdate_->setPath(std::move(scalaFile));
|
||||
scalaPathUpdate_->deferUpdate();
|
||||
scalaUpdate_->setPath(std::string(scalaFile));
|
||||
scalaUpdate_->deferUpdate();
|
||||
}
|
||||
else if (!strcmp(id, "NotifiedPlayState")) {
|
||||
const void* data = nullptr;
|
||||
|
|
@ -314,8 +329,9 @@ IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name)
|
|||
return nullptr;
|
||||
|
||||
std::vector<FObject*> continuousUpdates;
|
||||
continuousUpdates.push_back(sfzPathUpdate_);
|
||||
continuousUpdates.push_back(scalaPathUpdate_);
|
||||
continuousUpdates.push_back(sfzUpdate_);
|
||||
continuousUpdates.push_back(sfzDescriptionUpdate_);
|
||||
continuousUpdates.push_back(scalaUpdate_);
|
||||
continuousUpdates.push_back(playStateUpdate_);
|
||||
for (uint32 i = 0, n = parameters.getParameterCount(); i < n; ++i)
|
||||
continuousUpdates.push_back(parameters.getParameterByIndex(i));
|
||||
|
|
|
|||
|
|
@ -47,8 +47,9 @@ public:
|
|||
protected:
|
||||
Steinberg::IPtr<OSCUpdate> oscUpdate_;
|
||||
Steinberg::IPtr<NoteUpdate> noteUpdate_;
|
||||
Steinberg::IPtr<FilePathUpdate> sfzPathUpdate_;
|
||||
Steinberg::IPtr<FilePathUpdate> scalaPathUpdate_;
|
||||
Steinberg::IPtr<SfzUpdate> sfzUpdate_;
|
||||
Steinberg::IPtr<SfzDescriptionUpdate> sfzDescriptionUpdate_;
|
||||
Steinberg::IPtr<ScalaUpdate> scalaUpdate_;
|
||||
Steinberg::IPtr<ProcessorStateUpdate> processorStateUpdate_;
|
||||
Steinberg::IPtr<PlayStateUpdate> playStateUpdate_;
|
||||
Vst::ParamID midiMapping_[Vst::kCountCtrlNumber] {};
|
||||
|
|
|
|||
|
|
@ -11,10 +11,12 @@
|
|||
#include "SfizzFileScan.h"
|
||||
#include "editor/Editor.h"
|
||||
#include "editor/EditIds.h"
|
||||
#include "plugin/InstrumentDescription.h"
|
||||
#include "IdleUpdateHandler.h"
|
||||
#if !defined(__APPLE__) && !defined(_WIN32)
|
||||
#include "X11RunLoop.h"
|
||||
#endif
|
||||
#include <ghc/fs_std.hpp>
|
||||
|
||||
using namespace VSTGUI;
|
||||
|
||||
|
|
@ -188,17 +190,48 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
|
|||
}
|
||||
}
|
||||
|
||||
if (FilePathUpdate* update = FCast<FilePathUpdate>(changedUnknown)) {
|
||||
if (SfzUpdate* update = FCast<SfzUpdate>(changedUnknown)) {
|
||||
const std::string path = update->getPath();
|
||||
switch (update->getType()) {
|
||||
case kFilePathUpdateSfz:
|
||||
uiReceiveValue(EditId::SfzFile, path);
|
||||
break;
|
||||
case kFilePathUpdateScala:
|
||||
uiReceiveValue(EditId::ScalaFile, path);
|
||||
break;
|
||||
uiReceiveValue(EditId::SfzFile, path);
|
||||
}
|
||||
|
||||
if (SfzDescriptionUpdate* update = FCast<SfzDescriptionUpdate>(changedUnknown)) {
|
||||
const InstrumentDescription desc = parseDescriptionBlob(update->getDescription());
|
||||
|
||||
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);
|
||||
|
||||
const fs::path rootPath = fs::u8path(desc.rootPath);
|
||||
const fs::path imagePath = rootPath / fs::u8path(desc.image);
|
||||
uiReceiveValue(EditId::BackgroundImage, imagePath.u8string());
|
||||
|
||||
for (unsigned key = 0; key < 128; ++key) {
|
||||
bool keyUsed = desc.keyUsed.test(key);
|
||||
bool keyswitchUsed = desc.keyswitchUsed.test(key);
|
||||
uiReceiveValue(editIdForKeyUsed(int(key)), float(keyUsed));
|
||||
uiReceiveValue(editIdForKeyswitchUsed(int(key)), float(keyswitchUsed));
|
||||
if (keyUsed)
|
||||
uiReceiveValue(editIdForKeyLabel(int(key)), desc.keyLabel[key]);
|
||||
if (keyswitchUsed)
|
||||
uiReceiveValue(editIdForKeyswitchLabel(int(key)), desc.keyswitchLabel[key]);
|
||||
}
|
||||
return;
|
||||
|
||||
for (unsigned cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||
bool ccUsed = desc.ccUsed.test(cc);
|
||||
uiReceiveValue(editIdForCCUsed(int(cc)), float(ccUsed));
|
||||
if (ccUsed) {
|
||||
uiReceiveValue(editIdForCCDefault(int(cc)), desc.ccDefault[cc]);
|
||||
uiReceiveValue(editIdForCCLabel(int(cc)), desc.ccLabel[cc]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ScalaUpdate* update = FCast<ScalaUpdate>(changedUnknown)) {
|
||||
const std::string path = update->getPath();
|
||||
uiReceiveValue(EditId::ScalaFile, path);
|
||||
}
|
||||
|
||||
if (ProcessorStateUpdate* update = FCast<ProcessorStateUpdate>(changedUnknown)) {
|
||||
|
|
@ -216,11 +249,6 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
|
|||
|
||||
if (PlayStateUpdate* update = FCast<PlayStateUpdate>(changedUnknown)) {
|
||||
const SfizzPlayState playState = update->getState();
|
||||
uiReceiveValue(EditId::UINumCurves, playState.curves);
|
||||
uiReceiveValue(EditId::UINumMasters, playState.masters);
|
||||
uiReceiveValue(EditId::UINumGroups, playState.groups);
|
||||
uiReceiveValue(EditId::UINumRegions, playState.regions);
|
||||
uiReceiveValue(EditId::UINumPreloadedSamples, playState.preloadedSamples);
|
||||
uiReceiveValue(EditId::UINumActiveVoices, playState.activeVoices);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "SfizzVstParameters.h"
|
||||
#include "SfizzFileScan.h"
|
||||
#include "sfizz/import/ForeignInstrument.h"
|
||||
#include "plugin/InstrumentDescription.h"
|
||||
#include "base/source/fstreamer.h"
|
||||
#include "pluginterfaces/vst/ivstevents.h"
|
||||
#include "pluginterfaces/vst/ivstparameterchanges.h"
|
||||
|
|
@ -38,11 +39,10 @@ static const char* kRingIdOsc = "Osc";
|
|||
static const char* kMsgIdSetNumVoices = "SetNumVoices";
|
||||
static const char* kMsgIdSetOversampling = "SetOversampling";
|
||||
static const char* kMsgIdSetPreloadSize = "SetPreloadSize";
|
||||
static const char* kMsgIdNotifyPlayState = "NotifyPlayState";
|
||||
static const char* kMsgIdReceiveMessage = "ReceiveMessage";
|
||||
static const char* kMsgIdNoteEvents = "NoteEvents";
|
||||
|
||||
static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 500 };
|
||||
static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 50 };
|
||||
|
||||
SfizzVstProcessor::SfizzVstProcessor()
|
||||
: _fifoToWorker(64 * 1024), _fifoMessageFromUi(64 * 1024),
|
||||
|
|
@ -97,7 +97,7 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context)
|
|||
_synth->setBroadcastCallback(onMessage, this);
|
||||
|
||||
_currentStretchedTuning = 0.0;
|
||||
loadSfzFileOrDefault(*_synth, {});
|
||||
loadSfzFileOrDefault({});
|
||||
|
||||
_synth->tempo(0, 0.5);
|
||||
_timeSigNumerator = 4;
|
||||
|
|
@ -121,6 +121,19 @@ tresult PLUGIN_API SfizzVstProcessor::setBusArrangements(Vst::SpeakerArrangement
|
|||
return AudioEffect::setBusArrangements(inputs, numIns, outputs, numOuts);
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstProcessor::connect(IConnectionPoint* other)
|
||||
{
|
||||
tresult result = AudioEffect::connect(other);
|
||||
if (result != kResultTrue)
|
||||
return result;
|
||||
|
||||
// when controller connects, send these messages that we couldn't earlier
|
||||
if (_loadedSfzMessage)
|
||||
sendMessage(_loadedSfzMessage);
|
||||
|
||||
return kResultTrue;
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstProcessor::setState(IBStream* stream)
|
||||
{
|
||||
SfizzVstState s;
|
||||
|
|
@ -173,7 +186,7 @@ void SfizzVstProcessor::syncStateToSynth()
|
|||
if (!synth)
|
||||
return;
|
||||
|
||||
loadSfzFileOrDefault(*synth, _state.sfzFile);
|
||||
loadSfzFileOrDefault(_state.sfzFile);
|
||||
synth->setVolume(_state.volume);
|
||||
synth->setNumVoices(_state.numVoices);
|
||||
synth->setOversamplingFactor(1 << _state.oversamplingLog2);
|
||||
|
|
@ -205,9 +218,6 @@ tresult PLUGIN_API SfizzVstProcessor::setActive(TBool state)
|
|||
if (state) {
|
||||
synth->setSampleRate(processSetup.sampleRate);
|
||||
synth->setSamplesPerBlock(processSetup.maxSamplesPerBlock);
|
||||
|
||||
_playStateChangePeriod = static_cast<uint32>(50e-3 * processSetup.sampleRate);
|
||||
|
||||
startBackgroundWork();
|
||||
} else {
|
||||
stopBackgroundWork();
|
||||
|
|
@ -272,19 +282,10 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
|
|||
|
||||
synth.renderBlock(outputs, numFrames, numChannels);
|
||||
|
||||
_playStateChangeCounter += numFrames;
|
||||
if (_playStateChangeCounter > _playStateChangePeriod) {
|
||||
_playStateChangeCounter %= _playStateChangePeriod;
|
||||
SfizzPlayState playState {};
|
||||
playState.curves = synth.getNumCurves();
|
||||
playState.masters = synth.getNumMasters();
|
||||
playState.groups = synth.getNumGroups();
|
||||
playState.regions = synth.getNumRegions();
|
||||
playState.preloadedSamples = synth.getNumPreloadedSamples();
|
||||
playState.activeVoices = synth.getNumActiveVoices();
|
||||
if (writeWorkerMessage(kMsgIdNotifyPlayState, &playState, sizeof(playState)))
|
||||
_semaToWorker.post();
|
||||
}
|
||||
// Request OSC updates
|
||||
sfz::Client& client = *_client;
|
||||
synth.sendMessage(client, 0, "/cc/changed~", "", nullptr);
|
||||
synth.sendMessage(client, 0, "/sw/last/current", "", nullptr);
|
||||
|
||||
//
|
||||
std::pair<uint32, float> noteEvents[128];
|
||||
|
|
@ -556,13 +557,8 @@ tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message)
|
|||
|
||||
std::unique_lock<SpinMutex> lock(_processMutex);
|
||||
_state.sfzFile.assign(static_cast<const char *>(data), size);
|
||||
loadSfzFileOrDefault(*_synth, _state.sfzFile);
|
||||
loadSfzFileOrDefault(_state.sfzFile);
|
||||
lock.unlock();
|
||||
|
||||
Steinberg::OPtr<Vst::IMessage> reply { allocateMessage() };
|
||||
reply->setMessageID("LoadedSfz");
|
||||
reply->getAttributes()->setBinary("File", _state.sfzFile.data(), _state.sfzFile.size());
|
||||
sendMessage(reply);
|
||||
}
|
||||
else if (!std::strcmp(id, "LoadScala")) {
|
||||
const void* data = nullptr;
|
||||
|
|
@ -614,8 +610,10 @@ void SfizzVstProcessor::receiveMessage(int delay, const char* path, const char*
|
|||
}
|
||||
}
|
||||
|
||||
void SfizzVstProcessor::loadSfzFileOrDefault(sfz::Sfizz& synth, const std::string& filePath)
|
||||
void SfizzVstProcessor::loadSfzFileOrDefault(const std::string& filePath)
|
||||
{
|
||||
sfz::Sfizz& synth = *_synth;
|
||||
|
||||
if (!filePath.empty()) {
|
||||
const sfz::InstrumentFormatRegistry& formatRegistry = sfz::InstrumentFormatRegistry::getInstance();
|
||||
const sfz::InstrumentFormat* format = formatRegistry.getMatchingFormat(filePath);
|
||||
|
|
@ -628,8 +626,22 @@ void SfizzVstProcessor::loadSfzFileOrDefault(sfz::Sfizz& synth, const std::strin
|
|||
synth.loadSfzString(virtualPath, sfzText);
|
||||
}
|
||||
}
|
||||
else
|
||||
else {
|
||||
synth.loadSfzString("default.sfz", defaultSfzText);
|
||||
}
|
||||
|
||||
const std::string desc = getDescriptionBlob(synth.handle());
|
||||
|
||||
Steinberg::OPtr<Vst::IMessage> message { allocateMessage() };
|
||||
message->setMessageID("LoadedSfz");
|
||||
Vst::IAttributeList* attrs = message->getAttributes();
|
||||
attrs->setBinary("File", filePath.data(), filePath.size());
|
||||
attrs->setBinary("Description", desc.data(), desc.size());
|
||||
|
||||
// sending can fail if controller is not connected yet, so keep it around
|
||||
_loadedSfzMessage = message;
|
||||
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
void SfizzVstProcessor::doBackgroundWork()
|
||||
|
|
@ -638,6 +650,7 @@ void SfizzVstProcessor::doBackgroundWork()
|
|||
|
||||
bool haveDoneIdleWork = false;
|
||||
Clock::time_point lastIdleWorkTime;
|
||||
size_t idleCounter = 0;
|
||||
|
||||
for (;;) {
|
||||
bool isNotified = _semaToWorker.timed_wait(kBackgroundIdleInterval.count());
|
||||
|
|
@ -672,13 +685,6 @@ void SfizzVstProcessor::doBackgroundWork()
|
|||
std::lock_guard<SpinMutex> lock(_processMutex);
|
||||
_synth->setPreloadSize(value);
|
||||
}
|
||||
else if (id == kMsgIdNotifyPlayState) {
|
||||
SfizzPlayState playState = *msg->payload<SfizzPlayState>();
|
||||
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
|
||||
notification->setMessageID("NotifiedPlayState");
|
||||
notification->getAttributes()->setBinary("PlayState", &playState, sizeof(playState));
|
||||
sendMessage(notification);
|
||||
}
|
||||
else if (id == kMsgIdReceiveMessage) {
|
||||
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
|
||||
notification->setMessageID("ReceivedMessage");
|
||||
|
|
@ -694,29 +700,35 @@ void SfizzVstProcessor::doBackgroundWork()
|
|||
|
||||
Clock::time_point currentTime = Clock::now();
|
||||
if (!haveDoneIdleWork || currentTime - lastIdleWorkTime > kBackgroundIdleInterval) {
|
||||
doBackgroundIdle();
|
||||
doBackgroundIdle(idleCounter++);
|
||||
haveDoneIdleWork = true;
|
||||
lastIdleWorkTime = currentTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SfizzVstProcessor::doBackgroundIdle()
|
||||
void SfizzVstProcessor::doBackgroundIdle(size_t idleCounter)
|
||||
{
|
||||
if (_synth->shouldReloadFile()) {
|
||||
fprintf(stderr, "[Sfizz] sfz file has changed, reloading\n");
|
||||
std::lock_guard<SpinMutex> lock(_processMutex);
|
||||
loadSfzFileOrDefault(*_synth, _state.sfzFile);
|
||||
|
||||
Steinberg::OPtr<Vst::IMessage> reply { allocateMessage() };
|
||||
reply->setMessageID("LoadedSfz");
|
||||
reply->getAttributes()->setBinary("File", _state.sfzFile.data(), _state.sfzFile.size());
|
||||
sendMessage(reply);
|
||||
{
|
||||
SfizzPlayState ps;
|
||||
ps.activeVoices = _synth->getNumActiveVoices();
|
||||
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
|
||||
notification->setMessageID("NotifiedPlayState");
|
||||
notification->getAttributes()->setBinary("PlayState", &ps, sizeof(ps));
|
||||
sendMessage(notification);
|
||||
}
|
||||
if (_synth->shouldReloadScala()) {
|
||||
fprintf(stderr, "[Sfizz] scala file has changed, reloading\n");
|
||||
std::lock_guard<SpinMutex> lock(_processMutex);
|
||||
_synth->loadScalaFile(_state.scalaFile);
|
||||
|
||||
if (idleCounter % 10 == 0) {
|
||||
if (_synth->shouldReloadFile()) {
|
||||
fprintf(stderr, "[Sfizz] sfz file has changed, reloading\n");
|
||||
std::lock_guard<SpinMutex> lock(_processMutex);
|
||||
loadSfzFileOrDefault(_state.sfzFile);
|
||||
}
|
||||
if (_synth->shouldReloadScala()) {
|
||||
fprintf(stderr, "[Sfizz] scala file has changed, reloading\n");
|
||||
std::lock_guard<SpinMutex> lock(_processMutex);
|
||||
_synth->loadScalaFile(_state.scalaFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ public:
|
|||
tresult PLUGIN_API initialize(FUnknown* context) override;
|
||||
tresult PLUGIN_API setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts) override;
|
||||
|
||||
tresult PLUGIN_API connect(IConnectionPoint* other) override;
|
||||
|
||||
tresult PLUGIN_API setState(IBStream* stream) override;
|
||||
tresult PLUGIN_API getState(IBStream* stream) override;
|
||||
void syncStateToSynth();
|
||||
|
|
@ -49,6 +51,7 @@ public:
|
|||
private:
|
||||
// synth state. acquire processMutex before accessing
|
||||
std::unique_ptr<sfz::Sfizz> _synth;
|
||||
Steinberg::IPtr<Vst::IMessage> _loadedSfzMessage;
|
||||
bool _isActive = false;
|
||||
SfizzVstState _state;
|
||||
float _currentStretchedTuning = 0;
|
||||
|
|
@ -59,7 +62,7 @@ private:
|
|||
void receiveMessage(int delay, const char* path, const char* sig, const sfizz_arg_t* args);
|
||||
|
||||
// misc
|
||||
static void loadSfzFileOrDefault(sfz::Sfizz& synth, const std::string& filePath);
|
||||
void loadSfzFileOrDefault(const std::string& filePath);
|
||||
|
||||
// note event tracking
|
||||
std::array<float, 128> _noteEventsCurrentCycle; // 0: off, >0: on, <0: no change
|
||||
|
|
@ -72,10 +75,6 @@ private:
|
|||
Ring_Buffer _fifoMessageFromUi;
|
||||
SpinMutex _processMutex;
|
||||
|
||||
// state notification periodic timer
|
||||
uint32 _playStateChangeCounter = 0;
|
||||
uint32 _playStateChangePeriod = 0;
|
||||
|
||||
// time info
|
||||
int _timeSigNumerator = 0;
|
||||
int _timeSigDenominator = 0;
|
||||
|
|
@ -95,7 +94,7 @@ private:
|
|||
|
||||
// worker
|
||||
void doBackgroundWork();
|
||||
void doBackgroundIdle();
|
||||
void doBackgroundIdle(size_t idleCounter);
|
||||
void startBackgroundWork();
|
||||
void stopBackgroundWork();
|
||||
// writer
|
||||
|
|
|
|||
|
|
@ -31,10 +31,5 @@ public:
|
|||
};
|
||||
|
||||
struct SfizzPlayState {
|
||||
uint32 curves;
|
||||
uint32 masters;
|
||||
uint32 groups;
|
||||
uint32 regions;
|
||||
uint32 preloadedSamples;
|
||||
uint32 activeVoices;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -67,21 +67,10 @@ private:
|
|||
};
|
||||
|
||||
/**
|
||||
* @brief Update which notifies a change of file path pseudo-parameter
|
||||
* The message ID is used to indicate which path it is.
|
||||
* @brief Update which notifies a change of SFZ file.
|
||||
*/
|
||||
class FilePathUpdate : public Steinberg::FObject {
|
||||
class SfzUpdate : public Steinberg::FObject {
|
||||
public:
|
||||
explicit FilePathUpdate(int32 type)
|
||||
: type_(type)
|
||||
{
|
||||
}
|
||||
|
||||
int32 getType() const noexcept
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
void setPath(std::string newPath)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
|
@ -94,17 +83,59 @@ public:
|
|||
return path_;
|
||||
}
|
||||
|
||||
OBJ_METHODS(FilePathUpdate, FObject)
|
||||
OBJ_METHODS(SfzUpdate, FObject)
|
||||
|
||||
private:
|
||||
int32 type_ {};
|
||||
std::string path_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
enum {
|
||||
kFilePathUpdateSfz,
|
||||
kFilePathUpdateScala,
|
||||
/**
|
||||
* @brief Update which notifies a change of SFZ description.
|
||||
*/
|
||||
class SfzDescriptionUpdate : public Steinberg::FObject {
|
||||
public:
|
||||
void setDescription(std::string newDescription)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
description_ = std::move(newDescription);
|
||||
}
|
||||
|
||||
std::string getDescription() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return description_;
|
||||
}
|
||||
|
||||
OBJ_METHODS(SfzDescriptionUpdate, FObject)
|
||||
|
||||
private:
|
||||
std::string description_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update which notifies a change of scala file.
|
||||
*/
|
||||
class ScalaUpdate : public Steinberg::FObject {
|
||||
public:
|
||||
void setPath(std::string newPath)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
path_ = std::move(newPath);
|
||||
}
|
||||
|
||||
std::string getPath() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return path_;
|
||||
}
|
||||
|
||||
OBJ_METHODS(ScalaUpdate, FObject)
|
||||
|
||||
private:
|
||||
std::string path_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -30,6 +30,6 @@ clang-tidy \
|
|||
-- -Iexternal/abseil-cpp -Iexternal/jsl/include -Iexternal/filesystem/include -Iexternal/atomic_queue/include -Iexternal/threadpool -Isrc/external/hiir -Isrc/external/pugixml/src \
|
||||
-Iexternal/st_audiofile/src -Iexternal/st_audiofile/thirdparty/dr_libs \
|
||||
-Isrc/sfizz -Isrc -Isrc/sfizz/utility/bit_array -Isrc/sfizz/utility/spin_mutex -Isrc/external/spline -Isrc/external/cpuid/src -Iexternal/simde \
|
||||
-Iplugins/vst -Iplugins/vst/external/VST_SDK/VST3_SDK -Iplugins/editor/external/vstgui4 -Iplugins/vst/external/ring_buffer \
|
||||
-Iplugins/common -Iplugins/vst -Iplugins/vst/external/VST_SDK/VST3_SDK -Iplugins/editor/external/vstgui4 -Iplugins/vst/external/ring_buffer \
|
||||
-Iplugins/editor/src \
|
||||
-DNDEBUG -std=c++17
|
||||
|
|
|
|||
|
|
@ -246,10 +246,10 @@ void Synth::Impl::clear()
|
|||
effectBuses_[0]->setSampleRate(sampleRate_);
|
||||
effectBuses_[0]->clearInputs(samplesPerBlock_);
|
||||
resources_.clear();
|
||||
rootPath_.clear();
|
||||
numGroups_ = 0;
|
||||
numMasters_ = 0;
|
||||
currentSwitch_ = absl::nullopt;
|
||||
currentSwitchChanged_ = true;
|
||||
defaultPath_ = "";
|
||||
image_ = "";
|
||||
resources_.midiState.reset();
|
||||
|
|
@ -258,6 +258,7 @@ void Synth::Impl::clear()
|
|||
clearCCLabels();
|
||||
currentUsedCCs_.clear();
|
||||
changedCCsThisCycle_.clear();
|
||||
changedCCsLastCycle_.clear();
|
||||
clearKeyLabels();
|
||||
keySlots_.clear();
|
||||
swLastSlots_.clear();
|
||||
|
|
@ -547,7 +548,11 @@ bool Synth::loadSfzString(const fs::path& path, absl::string_view text)
|
|||
|
||||
void Synth::Impl::finalizeSfzLoad()
|
||||
{
|
||||
resources_.filePool.setRootDirectory(parser_.originalDirectory());
|
||||
const fs::path& rootDirectory = parser_.originalDirectory();
|
||||
resources_.filePool.setRootDirectory(rootDirectory);
|
||||
|
||||
// a string representation used for OSC purposes
|
||||
rootPath_ = rootDirectory.u8string();
|
||||
|
||||
size_t currentRegionIndex = 0;
|
||||
size_t currentRegionCount = layers_.size();
|
||||
|
|
@ -790,8 +795,6 @@ void Synth::Impl::finalizeSfzLoad()
|
|||
swLastSlots_.set(key);
|
||||
}
|
||||
}
|
||||
// resend current keyswitch
|
||||
currentSwitchChanged_ = true;
|
||||
}
|
||||
|
||||
bool Synth::loadScalaFile(const fs::path& path)
|
||||
|
|
@ -1017,25 +1020,9 @@ void Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
|||
// Advance the clock to the end of cycle
|
||||
bc.endCycle();
|
||||
|
||||
// Send the set of changed CCs
|
||||
Client broadcaster = impl.getBroadcaster();
|
||||
const BitArray<config::numCCs>& changedCCs = impl.changedCCsThisCycle_;
|
||||
const int finalFrameNumber = int(numFrames - 1);
|
||||
if (broadcaster.canReceive()) {
|
||||
sfizz_blob_t blob { changedCCs.data(), static_cast<uint32_t>(changedCCs.byte_size()) };
|
||||
broadcaster.receive<'b'>(finalFrameNumber, "/cc/changed", &blob);
|
||||
}
|
||||
// Update sets of changed CCs
|
||||
impl.changedCCsLastCycle_ = impl.changedCCsThisCycle_;
|
||||
impl.changedCCsThisCycle_.clear();
|
||||
// Send the changed keyswitch
|
||||
if (impl.currentSwitchChanged_) {
|
||||
if (broadcaster.canReceive()) {
|
||||
int32_t value = -1;
|
||||
if (impl.currentSwitch_)
|
||||
value = *impl.currentSwitch_;
|
||||
broadcaster.receive<'i'>(finalFrameNumber, "/sw/last/current", value);
|
||||
}
|
||||
impl.currentSwitchChanged_ = false;
|
||||
}
|
||||
|
||||
{ // Clear events and advance midi time
|
||||
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
|
||||
|
|
@ -1135,7 +1122,6 @@ void Synth::Impl::noteOnDispatch(int delay, int noteNumber, float velocity) noex
|
|||
layer->keySwitched_ = false;
|
||||
}
|
||||
currentSwitch_ = noteNumber;
|
||||
currentSwitchChanged_ = true;
|
||||
}
|
||||
|
||||
for (Layer* layer : lastKeyswitchLists_[noteNumber])
|
||||
|
|
|
|||
|
|
@ -39,13 +39,35 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
|
|||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
MATCH("/num_regions", "") {
|
||||
client.receive<'i'>(delay, path, int(impl.layers_.size()));
|
||||
} break;
|
||||
|
||||
MATCH("/num_groups", "") {
|
||||
client.receive<'i'>(delay, path, impl.numGroups_);
|
||||
} break;
|
||||
|
||||
MATCH("/num_masters", "") {
|
||||
client.receive<'i'>(delay, path, impl.numMasters_);
|
||||
} break;
|
||||
|
||||
MATCH("/num_curves", "") {
|
||||
client.receive<'i'>(delay, path, int(impl.resources_.curves.getNumCurves()));
|
||||
} break;
|
||||
|
||||
MATCH("/num_samples", "") {
|
||||
client.receive<'i'>(delay, path, int(impl.resources_.filePool.getNumPreloadedSamples()));
|
||||
} break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
MATCH("/key/slots", "") {
|
||||
const BitArray<128>& keys = impl.keySlots_;
|
||||
sfizz_blob_t blob { keys.data(), static_cast<uint32_t>(keys.byte_size()) };
|
||||
client.receive<'b'>(delay, path, &blob);
|
||||
} break;
|
||||
|
||||
MATCH("/key%d/label", "") {
|
||||
MATCH("/key&/label", "") {
|
||||
if (indices[0] >= 128)
|
||||
break;
|
||||
const std::string* label = impl.getKeyLabel(indices[0]);
|
||||
|
|
@ -54,10 +76,16 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
|
|||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
MATCH("/root_path", "") {
|
||||
client.receive<'s'>(delay, path, impl.rootPath_.c_str());
|
||||
} break;
|
||||
|
||||
MATCH("/image", "") {
|
||||
client.receive<'s'>(delay, path, impl.image_.c_str());
|
||||
} break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
MATCH("/sw/last/slots", "") {
|
||||
const BitArray<128>& switches = impl.swLastSlots_;
|
||||
sfizz_blob_t blob { switches.data(), static_cast<uint32_t>(switches.byte_size()) };
|
||||
|
|
@ -65,10 +93,10 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
|
|||
} break;
|
||||
|
||||
MATCH("/sw/last/current", "") {
|
||||
int32_t value = -1;
|
||||
if (impl.currentSwitch_)
|
||||
value = *impl.currentSwitch_;
|
||||
client.receive<'i'>(delay, path, value);
|
||||
client.receive<'i'>(delay, path, *impl.currentSwitch_);
|
||||
else
|
||||
client.receive<'N'>(delay, path, {});
|
||||
} break;
|
||||
|
||||
MATCH("/sw/last/&/label", "") {
|
||||
|
|
@ -112,6 +140,18 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
|
|||
client.receive<'s'>(delay, path, label ? label->c_str() : "");
|
||||
} break;
|
||||
|
||||
MATCH("/cc/changed", "") {
|
||||
const BitArray<config::numCCs>& changedCCs = impl.changedCCsThisCycle_;
|
||||
sfizz_blob_t blob { changedCCs.data(), static_cast<uint32_t>(changedCCs.byte_size()) };
|
||||
client.receive<'b'>(delay, path, &blob);
|
||||
} break;
|
||||
|
||||
MATCH("/cc/changed~", "") {
|
||||
const BitArray<config::numCCs>& changedCCs = impl.changedCCsLastCycle_;
|
||||
sfizz_blob_t blob { changedCCs.data(), static_cast<uint32_t>(changedCCs.byte_size()) };
|
||||
client.receive<'b'>(delay, path, &blob);
|
||||
} break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
MATCH("/mem/buffers", "") {
|
||||
|
|
|
|||
|
|
@ -240,7 +240,6 @@ struct Synth::Impl final: public Parser::Listener {
|
|||
|
||||
// Set as sw_default if present in the file
|
||||
absl::optional<uint8_t> currentSwitch_;
|
||||
bool currentSwitchChanged_ = true;
|
||||
std::vector<std::string> unknownOpcodes_;
|
||||
using RegionViewVector = std::vector<Region*>;
|
||||
using LayerViewVector = std::vector<Layer*>;
|
||||
|
|
@ -279,6 +278,9 @@ struct Synth::Impl final: public Parser::Listener {
|
|||
// Singletons passed as references to the voices
|
||||
Resources resources_;
|
||||
|
||||
// Root path
|
||||
std::string rootPath_;
|
||||
|
||||
// Control opcodes
|
||||
std::string defaultPath_ { "" };
|
||||
std::string image_ { "" };
|
||||
|
|
@ -315,6 +317,7 @@ struct Synth::Impl final: public Parser::Listener {
|
|||
std::array<float, config::numCCs> defaultCCValues_;
|
||||
BitArray<config::numCCs> currentUsedCCs_;
|
||||
BitArray<config::numCCs> changedCCsThisCycle_;
|
||||
BitArray<config::numCCs> changedCCsLastCycle_;
|
||||
|
||||
// Messaging
|
||||
sfizz_receive_t* broadcastReceiver = nullptr;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue