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
|
add_library(plugins-common STATIC EXCLUDE_FROM_ALL
|
||||||
"common/plugin/MessageUtils.h"
|
"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_include_directories(plugins-common PUBLIC "common")
|
||||||
target_link_libraries(plugins-common
|
target_link_libraries(plugins-common
|
||||||
PUBLIC sfizz::spin_mutex
|
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)
|
add_library(sfizz::plugins-common ALIAS plugins-common)
|
||||||
|
|
||||||
if((SFIZZ_LV2 AND SFIZZ_LV2_UI) OR SFIZZ_VST)
|
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,
|
UserFilesDir,
|
||||||
FallbackFilesDir,
|
FallbackFilesDir,
|
||||||
//
|
//
|
||||||
Key0,
|
#define KEY_RANGE(Name) Name##0, Name##Last = Name##0 + 128 - 1
|
||||||
KeyLast = Key0 + 128 - 1,
|
#define CC_RANGE(Name) Name##0, Name##Last = Name##0 + sfz::config::numCCs - 1
|
||||||
//
|
//
|
||||||
Controller0,
|
KEY_RANGE(Key),
|
||||||
ControllerLast = Controller0 + sfz::config::numCCs - 1,
|
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,
|
UINumCurves,
|
||||||
UINumMasters,
|
UINumMasters,
|
||||||
|
|
@ -35,6 +43,11 @@ enum class EditId : int {
|
||||||
UINumPreloadedSamples,
|
UINumPreloadedSamples,
|
||||||
UINumActiveVoices,
|
UINumActiveVoices,
|
||||||
UIActivePanel,
|
UIActivePanel,
|
||||||
|
//
|
||||||
|
BackgroundImage,
|
||||||
|
//
|
||||||
|
#undef KEY_RANGE
|
||||||
|
#undef CC_RANGE
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EditRange {
|
struct EditRange {
|
||||||
|
|
@ -48,29 +61,33 @@ struct EditRange {
|
||||||
static EditRange get(EditId id);
|
static EditRange get(EditId id);
|
||||||
};
|
};
|
||||||
|
|
||||||
inline EditId editIdForCC(int cc)
|
#define DEFINE_EDIT_ID_RANGE_HELPERS(type, Type, IdPrefix) \
|
||||||
{
|
inline bool editIdIs##Type(EditId id) \
|
||||||
return EditId(int(EditId::Controller0) + cc);
|
{ \
|
||||||
}
|
return int(id) >= int(EditId::IdPrefix##0) && \
|
||||||
inline int ccForEditId(EditId id)
|
int(id) <= int(EditId::IdPrefix##Last); \
|
||||||
{
|
} \
|
||||||
return int(id) - int(EditId::Controller0);
|
inline EditId editIdFor##Type(int value) \
|
||||||
}
|
{ \
|
||||||
inline bool editIdIsCC(EditId id)
|
EditId id = EditId(int(EditId::IdPrefix##0) + value); \
|
||||||
{
|
assert(editIdIs##Type(id)); \
|
||||||
return int(id) >= int(EditId::Controller0) &&
|
return id; \
|
||||||
int(id) <= int(EditId::ControllerLast);
|
} \
|
||||||
}
|
inline int type##ForEditId(EditId id) \
|
||||||
|
{ \
|
||||||
|
assert(editIdIs##Type(id)); \
|
||||||
|
return int(id) - int(EditId::IdPrefix##0); \
|
||||||
|
}
|
||||||
|
|
||||||
inline EditId editIdForKey(int key)
|
// defines editIdForCC, ccForEditId, editIdIsCC, etc..
|
||||||
{
|
DEFINE_EDIT_ID_RANGE_HELPERS(cc, CC, Controller)
|
||||||
return EditId(int(EditId::Key0) + key);
|
DEFINE_EDIT_ID_RANGE_HELPERS(key, Key, Key)
|
||||||
}
|
DEFINE_EDIT_ID_RANGE_HELPERS(keyUsed, KeyUsed, KeyUsed)
|
||||||
inline int keyForEditId(EditId id)
|
DEFINE_EDIT_ID_RANGE_HELPERS(keyLabel, KeyLabel, KeyLabel)
|
||||||
{
|
DEFINE_EDIT_ID_RANGE_HELPERS(keyswitchUsed, KeyswitchUsed, KeyswitchUsed)
|
||||||
return int(id) - int(EditId::Key0);
|
DEFINE_EDIT_ID_RANGE_HELPERS(keyswitchLabel, KeyswitchLabel, KeyswitchLabel)
|
||||||
}
|
DEFINE_EDIT_ID_RANGE_HELPERS(ccUsed, CCUsed, ControllerUsed)
|
||||||
inline bool editIdIsKey(EditId id)
|
DEFINE_EDIT_ID_RANGE_HELPERS(ccDefault, CCDefault, ControllerDefault)
|
||||||
{
|
DEFINE_EDIT_ID_RANGE_HELPERS(ccLabel, CCLabel, ControllerLabel)
|
||||||
return int(id) >= int(EditId::Key0) && int(id) <= int(EditId::KeyLast);
|
|
||||||
}
|
#undef DEFINE_EDIT_ID_RANGE_HELPERS
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,7 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
|
||||||
void updateKeyswitchNameLabel();
|
void updateKeyswitchNameLabel();
|
||||||
|
|
||||||
void updateKeyUsed(unsigned key, bool used);
|
void updateKeyUsed(unsigned key, bool used);
|
||||||
|
void updateKeyLabel(unsigned key, const char* label);
|
||||||
void updateKeyswitchUsed(unsigned key, bool used);
|
void updateKeyswitchUsed(unsigned key, bool used);
|
||||||
void updateCCUsed(unsigned cc, bool used);
|
void updateCCUsed(unsigned cc, bool used);
|
||||||
void updateCCValue(unsigned cc, float value);
|
void updateCCValue(unsigned cc, float value);
|
||||||
|
|
@ -278,12 +279,6 @@ void Editor::open(CFrame& frame)
|
||||||
impl.oscSendQueueTimer_ = makeOwned<CVSTGUITimer>(
|
impl.oscSendQueueTimer_ = makeOwned<CVSTGUITimer>(
|
||||||
[this](CVSTGUITimer* timer) { impl_->tickOSCQueue(timer); },
|
[this](CVSTGUITimer* timer) { impl_->tickOSCQueue(timer); },
|
||||||
oscSendInterval, false);
|
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()
|
void Editor::close()
|
||||||
|
|
@ -311,12 +306,6 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
|
||||||
const std::string& value = v.to_string();
|
const std::string& value = v.to_string();
|
||||||
currentSfzFile_ = value;
|
currentSfzFile_ = value;
|
||||||
updateSfzFileLabel(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;
|
break;
|
||||||
case EditId::Volume:
|
case EditId::Volume:
|
||||||
|
|
@ -454,6 +443,12 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
|
||||||
setActivePanel(value);
|
setActivePanel(value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case EditId::BackgroundImage:
|
||||||
|
{
|
||||||
|
const std::string& value = v.to_string();
|
||||||
|
updateBackgroundImage(value.c_str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (editIdIsKey(id)) {
|
if (editIdIsKey(id)) {
|
||||||
const int key = keyForEditId(id);
|
const int key = keyForEditId(id);
|
||||||
|
|
@ -461,6 +456,31 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
|
||||||
if (SPiano* piano = piano_)
|
if (SPiano* piano = piano_)
|
||||||
piano->setKeyValue(key, value);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -469,46 +489,7 @@ void Editor::Impl::uiReceiveMessage(const char* path, const char* sig, const sfi
|
||||||
{
|
{
|
||||||
unsigned indices[8];
|
unsigned indices[8];
|
||||||
|
|
||||||
if (Messages::matchOSC("/key/slots", 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 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")) {
|
|
||||||
size_t numBits = 8 * args[0].b->size;
|
size_t numBits = 8 * args[0].b->size;
|
||||||
ConstBitSpan bits { args[0].b->data, numBits };
|
ConstBitSpan bits { args[0].b->data, numBits };
|
||||||
for (unsigned cc = 0; cc < numBits; ++cc) {
|
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")) {
|
else if (Messages::matchOSC("/cc&/value", path, indices) && !strcmp(sig, "f")) {
|
||||||
updateCCValue(indices[0], args[0].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")) {
|
else if (Messages::matchOSC("/sw/last/current", path, indices) && !strcmp(sig, "i")) {
|
||||||
updateSWLastCurrent(args[0].i);
|
updateSWLastCurrent(args[0].i);
|
||||||
}
|
}
|
||||||
else if (Messages::matchOSC("/sw/last/&/label", path, indices) && !strcmp(sig, "s")) {
|
else if (Messages::matchOSC("/sw/last/current", path, indices) && !strcmp(sig, "N")) {
|
||||||
updateSWLastLabel(indices[0], args[0].s);
|
updateSWLastCurrent(-1);
|
||||||
}
|
|
||||||
else if (Messages::matchOSC("/image", path, indices) && !strcmp(sig, "s")) {
|
|
||||||
updateBackgroundImage(args[0].s);
|
|
||||||
}
|
}
|
||||||
else if (Messages::matchOSC("/mem/buffers", path, indices) && !strcmp(sig, "h")) {
|
else if (Messages::matchOSC("/mem/buffers", path, indices) && !strcmp(sig, "h")) {
|
||||||
updateMemoryUsed(args[0].h);
|
updateMemoryUsed(args[0].h);
|
||||||
|
|
@ -1151,12 +1123,6 @@ void Editor::Impl::changeSfzFile(const std::string& filePath)
|
||||||
ctrl_->uiSendValue(EditId::SfzFile, filePath);
|
ctrl_->uiSendValue(EditId::SfzFile, filePath);
|
||||||
currentSfzFile_ = filePath;
|
currentSfzFile_ = filePath;
|
||||||
updateSfzFileLabel(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)
|
void Editor::Impl::changeToNextSfzFile(long offset)
|
||||||
|
|
@ -1519,6 +1485,13 @@ void Editor::Impl::updateKeyUsed(unsigned key, bool used)
|
||||||
piano->setKeyUsed(key, 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)
|
void Editor::Impl::updateKeyswitchUsed(unsigned key, bool used)
|
||||||
{
|
{
|
||||||
if (SPiano* piano = piano_)
|
if (SPiano* piano = piano_)
|
||||||
|
|
@ -1574,10 +1547,7 @@ void Editor::Impl::updateSWLastLabel(unsigned sw, const char* label)
|
||||||
|
|
||||||
void Editor::Impl::updateBackgroundImage(const char* filepath)
|
void Editor::Impl::updateBackgroundImage(const char* filepath)
|
||||||
{
|
{
|
||||||
const fs::path sfzFilePath = fs::u8path(currentSfzFile_);
|
SharedPointer<CBitmap> bitmap = loadAnyFormatImage(filepath);
|
||||||
const fs::path sfzDirPath = sfzFilePath.parent_path();
|
|
||||||
const fs::path imagePath = sfzDirPath / fs::u8path(filepath);
|
|
||||||
SharedPointer<CBitmap> bitmap = loadAnyFormatImage(imagePath);
|
|
||||||
|
|
||||||
if (!bitmap)
|
if (!bitmap)
|
||||||
bitmap = owned(new CBitmap("background.png"));
|
bitmap = owned(new CBitmap("background.png"));
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,14 @@ source_group("Turtle Files" FILES
|
||||||
)
|
)
|
||||||
add_library(${LV2PLUGIN_PRJ_NAME} MODULE
|
add_library(${LV2PLUGIN_PRJ_NAME} MODULE
|
||||||
${PROJECT_NAME}.cpp
|
${PROJECT_NAME}.cpp
|
||||||
|
${PROJECT_NAME}_lv2_common.cpp
|
||||||
${LV2PLUGIN_TTL_SRC_FILES})
|
${LV2PLUGIN_TTL_SRC_FILES})
|
||||||
target_link_libraries(${LV2PLUGIN_PRJ_NAME} PRIVATE sfizz::sfizz sfizz::import sfizz::plugins-common)
|
target_link_libraries(${LV2PLUGIN_PRJ_NAME} PRIVATE sfizz::sfizz sfizz::import sfizz::plugins-common)
|
||||||
|
|
||||||
if(SFIZZ_LV2_UI)
|
if(SFIZZ_LV2_UI)
|
||||||
add_library(${LV2PLUGIN_PRJ_NAME}_ui MODULE
|
add_library(${LV2PLUGIN_PRJ_NAME}_ui MODULE
|
||||||
${PROJECT_NAME}_ui.cpp
|
${PROJECT_NAME}_ui.cpp
|
||||||
|
${PROJECT_NAME}_lv2_common.cpp
|
||||||
vstgui_helpers.h
|
vstgui_helpers.h
|
||||||
vstgui_helpers.cpp)
|
vstgui_helpers.cpp)
|
||||||
target_link_libraries(${LV2PLUGIN_PRJ_NAME}_ui PRIVATE sfizz::editor sfizz::vstgui sfizz::plugins-common)
|
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.h"
|
||||||
|
#include "sfizz_lv2_plugin.h"
|
||||||
|
|
||||||
#include "sfizz/import/ForeignInstrument.h"
|
#include "sfizz/import/ForeignInstrument.h"
|
||||||
|
#include "plugin/InstrumentDescription.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 <math.h>
|
#include <math.h>
|
||||||
#include <sfizz.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
#define CHANNEL_MASK 0x0F
|
#define CHANNEL_MASK 0x0F
|
||||||
#define MIDI_CHANNEL(byte) (byte & CHANNEL_MASK)
|
#define MIDI_CHANNEL(byte) (byte & CHANNEL_MASK)
|
||||||
#define MIDI_STATUS(byte) (byte & ~CHANNEL_MASK)
|
#define MIDI_STATUS(byte) (byte & ~CHANNEL_MASK)
|
||||||
|
|
@ -77,122 +54,12 @@
|
||||||
#define LOG_SAMPLE_COUNT 48000
|
#define LOG_SAMPLE_COUNT 48000
|
||||||
#define UNUSED(x) (void)(x)
|
#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
|
#ifndef NDEBUG
|
||||||
#define LV2_DEBUG(...) lv2_log_note(&self->logger, "[DEBUG] " __VA_ARGS__)
|
#define LV2_DEBUG(...) lv2_log_note(&self->logger, "[DEBUG] " __VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
#define LV2_DEBUG(...)
|
#define LV2_DEBUG(...)
|
||||||
#endif
|
#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
|
enum
|
||||||
{
|
{
|
||||||
SFIZZ_TIMEINFO_POSITION = 1 << 0,
|
SFIZZ_TIMEINFO_POSITION = 1 << 0,
|
||||||
|
|
@ -201,6 +68,14 @@ enum
|
||||||
SFIZZ_TIMEINFO_SPEED = 1 << 3,
|
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
|
static void
|
||||||
sfizz_lv2_state_free_path(LV2_State_Free_Path_Handle handle,
|
sfizz_lv2_state_free_path(LV2_State_Free_Path_Handle handle,
|
||||||
char *path)
|
char *path)
|
||||||
|
|
@ -607,11 +482,10 @@ instantiate(const LV2_Descriptor *descriptor,
|
||||||
sfizz_set_broadcast_callback(self->synth, &sfizz_lv2_receive_message, self);
|
sfizz_set_broadcast_callback(self->synth, &sfizz_lv2_receive_message, self);
|
||||||
sfizz_set_receive_callback(self->client, &sfizz_lv2_receive_message);
|
sfizz_set_receive_callback(self->client, &sfizz_lv2_receive_message);
|
||||||
|
|
||||||
sfizz_lv2_get_default_sfz_path(self, self->sfz_file_path, MAX_PATH_SIZE);
|
self->sfz_blob_mutex = new std::mutex;
|
||||||
sfizz_lv2_get_default_scala_path(self, self->scala_file_path, MAX_PATH_SIZE);
|
|
||||||
|
|
||||||
sfizz_load_file(self->synth, self->sfz_file_path);
|
sfizz_lv2_load_file(self, self->sfz_file_path);
|
||||||
sfizz_load_scala_file(self->synth, self->scala_file_path);
|
sfizz_lv2_load_scala_file(self, self->scala_file_path);
|
||||||
|
|
||||||
sfizz_lv2_update_timeinfo(self, 0, ~0);
|
sfizz_lv2_update_timeinfo(self, 0, ~0);
|
||||||
|
|
||||||
|
|
@ -622,6 +496,8 @@ static void
|
||||||
cleanup(LV2_Handle instance)
|
cleanup(LV2_Handle instance)
|
||||||
{
|
{
|
||||||
sfizz_plugin_t *self = (sfizz_plugin_t *)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);
|
spin_mutex_destroy(self->synth_mutex);
|
||||||
sfizz_delete_client(self->client);
|
sfizz_delete_client(self->client);
|
||||||
sfizz_free(self->synth);
|
sfizz_free(self->synth);
|
||||||
|
|
@ -1066,6 +942,10 @@ run(LV2_Handle instance, uint32_t sample_count)
|
||||||
// Render the block
|
// Render the block
|
||||||
sfizz_render_block(self->synth, self->output_buffers, 2, (int)sample_count);
|
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);
|
spin_mutex_unlock(self->synth_mutex);
|
||||||
|
|
||||||
if (self->midnam && self->must_update_midnam.exchange(0))
|
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);
|
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
|
static bool
|
||||||
sfizz_lv2_load_file(sfizz_plugin_t *self, const char *file_path)
|
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());
|
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);
|
sfizz_lv2_update_file_info(self, file_path);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,27 @@
|
||||||
|
|
||||||
#pragma once
|
#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 MAX_PATH_SIZE 1024
|
||||||
#define ATOM_TEMP_SIZE 8192
|
#define ATOM_TEMP_SIZE 8192
|
||||||
#define OSC_TEMP_SIZE 8192
|
#define OSC_TEMP_SIZE 8192
|
||||||
|
|
@ -45,3 +66,22 @@ enum
|
||||||
SFIZZ_NUM_REGIONS = 16,
|
SFIZZ_NUM_REGIONS = 16,
|
||||||
SFIZZ_NUM_SAMPLES = 17,
|
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/Editor.h"
|
||||||
#include "editor/EditorController.h"
|
#include "editor/EditorController.h"
|
||||||
#include "editor/EditIds.h"
|
#include "editor/EditIds.h"
|
||||||
|
#include "plugin/InstrumentDescription.h"
|
||||||
#include <lv2/ui/ui.h>
|
#include <lv2/ui/ui.h>
|
||||||
#include <lv2/atom/atom.h>
|
#include <lv2/atom/atom.h>
|
||||||
#include <lv2/atom/forge.h>
|
#include <lv2/atom/forge.h>
|
||||||
|
|
@ -44,6 +45,8 @@
|
||||||
#include <lv2/midi/midi.h>
|
#include <lv2/midi/midi.h>
|
||||||
#include <lv2/patch/patch.h>
|
#include <lv2/patch/patch.h>
|
||||||
#include <lv2/urid/urid.h>
|
#include <lv2/urid/urid.h>
|
||||||
|
#include <lv2/instance-access/instance-access.h>
|
||||||
|
#include <ghc/fs_std.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
@ -86,6 +89,7 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface {
|
||||||
LV2_URID_Unmap *unmap = nullptr;
|
LV2_URID_Unmap *unmap = nullptr;
|
||||||
LV2UI_Resize *resize = nullptr;
|
LV2UI_Resize *resize = nullptr;
|
||||||
LV2UI_Touch *touch = nullptr;
|
LV2UI_Touch *touch = nullptr;
|
||||||
|
sfizz_plugin_t *plugin = nullptr;
|
||||||
FrameHolder uiFrame;
|
FrameHolder uiFrame;
|
||||||
std::unique_ptr<Editor> editor;
|
std::unique_ptr<Editor> editor;
|
||||||
#if LINUX
|
#if LINUX
|
||||||
|
|
@ -112,6 +116,9 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface {
|
||||||
uint8_t osc_temp[OSC_TEMP_SIZE];
|
uint8_t osc_temp[OSC_TEMP_SIZE];
|
||||||
alignas(LV2_Atom) uint8_t atom_temp[ATOM_TEMP_SIZE];
|
alignas(LV2_Atom) uint8_t atom_temp[ATOM_TEMP_SIZE];
|
||||||
|
|
||||||
|
int sfz_serial = 0;
|
||||||
|
bool valid_sfz_serial = false;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void uiSendValue(EditId id, const EditValue& v) override;
|
void uiSendValue(EditId id, const EditValue& v) override;
|
||||||
void uiBeginSend(EditId id) override;
|
void uiBeginSend(EditId id) override;
|
||||||
|
|
@ -178,12 +185,18 @@ instantiate(const LV2UI_Descriptor *descriptor,
|
||||||
self->touch = (LV2UI_Touch*)(**f).data;
|
self->touch = (LV2UI_Touch*)(**f).data;
|
||||||
else if (!strcmp((**f).URI, LV2_UI__parent))
|
else if (!strcmp((**f).URI, LV2_UI__parent))
|
||||||
parentWindowId = (**f).data;
|
parentWindowId = (**f).data;
|
||||||
|
else if (!strcmp((**f).URI, LV2_INSTANCE_ACCESS_URI))
|
||||||
|
self->plugin = (sfizz_plugin_t *)(**f).data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The map feature is required
|
// The map feature is required
|
||||||
if (!map || !unmap)
|
if (!map || !unmap)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
// The instance-access feature is required
|
||||||
|
if (!self->plugin)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
LV2_Atom_Forge *forge = &self->atom_forge;
|
LV2_Atom_Forge *forge = &self->atom_forge;
|
||||||
lv2_atom_forge_init(forge, map);
|
lv2_atom_forge_init(forge, map);
|
||||||
self->atom_event_transfer_uri = map->map(map->handle, LV2_ATOM__eventTransfer);
|
self->atom_event_transfer_uri = map->map(map->handle, LV2_ATOM__eventTransfer);
|
||||||
|
|
@ -358,11 +371,70 @@ port_event(LV2UI_Handle ui,
|
||||||
(void)buffer_size;
|
(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
|
static int
|
||||||
idle(LV2UI_Handle ui)
|
idle(LV2UI_Handle ui)
|
||||||
{
|
{
|
||||||
sfizz_ui_t *self = (sfizz_ui_t *)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
|
#if LINUX
|
||||||
self->runLoop->execIdle();
|
self->runLoop->execIdle();
|
||||||
#else
|
#else
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,5 @@
|
||||||
lv2:optionalFeature ui:parent ;
|
lv2:optionalFeature ui:parent ;
|
||||||
lv2:optionalFeature ui:touch ;
|
lv2:optionalFeature ui:touch ;
|
||||||
lv2:requiredFeature urid:map ;
|
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
|
// create update objects
|
||||||
oscUpdate_ = Steinberg::owned(new OSCUpdate);
|
oscUpdate_ = Steinberg::owned(new OSCUpdate);
|
||||||
noteUpdate_ = Steinberg::owned(new NoteUpdate);
|
noteUpdate_ = Steinberg::owned(new NoteUpdate);
|
||||||
sfzPathUpdate_ = Steinberg::owned(new FilePathUpdate(kFilePathUpdateSfz));
|
sfzUpdate_ = Steinberg::owned(new SfzUpdate);
|
||||||
scalaPathUpdate_ = Steinberg::owned(new FilePathUpdate(kFilePathUpdateScala));
|
sfzDescriptionUpdate_ = Steinberg::owned(new SfzDescriptionUpdate);
|
||||||
|
scalaUpdate_ = Steinberg::owned(new ScalaUpdate);
|
||||||
processorStateUpdate_ = Steinberg::owned(new ProcessorStateUpdate);
|
processorStateUpdate_ = Steinberg::owned(new ProcessorStateUpdate);
|
||||||
playStateUpdate_ = Steinberg::owned(new PlayStateUpdate);
|
playStateUpdate_ = Steinberg::owned(new PlayStateUpdate);
|
||||||
|
|
||||||
|
|
@ -211,10 +212,10 @@ tresult PLUGIN_API SfizzVstControllerNoUi::setComponentState(IBStream* stream)
|
||||||
setParam(kPidTuningFrequency, s.tuningFrequency);
|
setParam(kPidTuningFrequency, s.tuningFrequency);
|
||||||
setParam(kPidStretchedTuning, s.stretchedTuning);
|
setParam(kPidStretchedTuning, s.stretchedTuning);
|
||||||
|
|
||||||
sfzPathUpdate_->setPath(s.sfzFile);
|
sfzUpdate_->setPath(s.sfzFile);
|
||||||
sfzPathUpdate_->deferUpdate();
|
sfzUpdate_->deferUpdate();
|
||||||
scalaPathUpdate_->setPath(s.scalaFile);
|
scalaUpdate_->setPath(s.scalaFile);
|
||||||
scalaPathUpdate_->deferUpdate();
|
scalaUpdate_->deferUpdate();
|
||||||
|
|
||||||
return kResultTrue;
|
return kResultTrue;
|
||||||
}
|
}
|
||||||
|
|
@ -230,35 +231,49 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message)
|
||||||
const char* id = message->getMessageID();
|
const char* id = message->getMessageID();
|
||||||
Vst::IAttributeList* attr = message->getAttributes();
|
Vst::IAttributeList* attr = message->getAttributes();
|
||||||
|
|
||||||
if (!strcmp(id, "LoadedSfz")) {
|
///
|
||||||
|
auto stringFromBinaryAttribute = [attr](const char* id, absl::string_view& string) -> tresult {
|
||||||
const void* data = nullptr;
|
const void* data = nullptr;
|
||||||
uint32 size = 0;
|
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)
|
if (result != kResultTrue)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
std::string sfzFile(static_cast<const char *>(data), size);
|
result = stringFromBinaryAttribute("Description", sfzDescriptionBlob);
|
||||||
processorStateUpdate_->access([&sfzFile](SfizzVstState& state) {
|
if (result != kResultTrue)
|
||||||
state.sfzFile = sfzFile;
|
return result;
|
||||||
|
|
||||||
|
processorStateUpdate_->access([sfzFile](SfizzVstState& state) {
|
||||||
|
state.sfzFile = std::string(sfzFile);
|
||||||
});
|
});
|
||||||
sfzPathUpdate_->setPath(std::move(sfzFile));
|
sfzUpdate_->setPath(std::string(sfzFile));
|
||||||
sfzPathUpdate_->deferUpdate();
|
sfzUpdate_->deferUpdate();
|
||||||
|
sfzDescriptionUpdate_->setDescription(std::string(sfzDescriptionBlob));
|
||||||
|
sfzDescriptionUpdate_->deferUpdate();
|
||||||
}
|
}
|
||||||
else if (!strcmp(id, "LoadedScala")) {
|
else if (!strcmp(id, "LoadedScala")) {
|
||||||
const void* data = nullptr;
|
absl::string_view scalaFile;
|
||||||
uint32 size = 0;
|
|
||||||
result = attr->getBinary("File", data, size);
|
|
||||||
|
|
||||||
|
result = stringFromBinaryAttribute("File", scalaFile);
|
||||||
if (result != kResultTrue)
|
if (result != kResultTrue)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
std::string scalaFile(static_cast<const char *>(data), size);
|
processorStateUpdate_->access([scalaFile](SfizzVstState& state) {
|
||||||
processorStateUpdate_->access([&scalaFile](SfizzVstState& state) {
|
state.scalaFile = std::string(scalaFile);
|
||||||
state.scalaFile = scalaFile;
|
|
||||||
});
|
});
|
||||||
scalaPathUpdate_->setPath(std::move(scalaFile));
|
scalaUpdate_->setPath(std::string(scalaFile));
|
||||||
scalaPathUpdate_->deferUpdate();
|
scalaUpdate_->deferUpdate();
|
||||||
}
|
}
|
||||||
else if (!strcmp(id, "NotifiedPlayState")) {
|
else if (!strcmp(id, "NotifiedPlayState")) {
|
||||||
const void* data = nullptr;
|
const void* data = nullptr;
|
||||||
|
|
@ -314,8 +329,9 @@ IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
std::vector<FObject*> continuousUpdates;
|
std::vector<FObject*> continuousUpdates;
|
||||||
continuousUpdates.push_back(sfzPathUpdate_);
|
continuousUpdates.push_back(sfzUpdate_);
|
||||||
continuousUpdates.push_back(scalaPathUpdate_);
|
continuousUpdates.push_back(sfzDescriptionUpdate_);
|
||||||
|
continuousUpdates.push_back(scalaUpdate_);
|
||||||
continuousUpdates.push_back(playStateUpdate_);
|
continuousUpdates.push_back(playStateUpdate_);
|
||||||
for (uint32 i = 0, n = parameters.getParameterCount(); i < n; ++i)
|
for (uint32 i = 0, n = parameters.getParameterCount(); i < n; ++i)
|
||||||
continuousUpdates.push_back(parameters.getParameterByIndex(i));
|
continuousUpdates.push_back(parameters.getParameterByIndex(i));
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,9 @@ public:
|
||||||
protected:
|
protected:
|
||||||
Steinberg::IPtr<OSCUpdate> oscUpdate_;
|
Steinberg::IPtr<OSCUpdate> oscUpdate_;
|
||||||
Steinberg::IPtr<NoteUpdate> noteUpdate_;
|
Steinberg::IPtr<NoteUpdate> noteUpdate_;
|
||||||
Steinberg::IPtr<FilePathUpdate> sfzPathUpdate_;
|
Steinberg::IPtr<SfzUpdate> sfzUpdate_;
|
||||||
Steinberg::IPtr<FilePathUpdate> scalaPathUpdate_;
|
Steinberg::IPtr<SfzDescriptionUpdate> sfzDescriptionUpdate_;
|
||||||
|
Steinberg::IPtr<ScalaUpdate> scalaUpdate_;
|
||||||
Steinberg::IPtr<ProcessorStateUpdate> processorStateUpdate_;
|
Steinberg::IPtr<ProcessorStateUpdate> processorStateUpdate_;
|
||||||
Steinberg::IPtr<PlayStateUpdate> playStateUpdate_;
|
Steinberg::IPtr<PlayStateUpdate> playStateUpdate_;
|
||||||
Vst::ParamID midiMapping_[Vst::kCountCtrlNumber] {};
|
Vst::ParamID midiMapping_[Vst::kCountCtrlNumber] {};
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,12 @@
|
||||||
#include "SfizzFileScan.h"
|
#include "SfizzFileScan.h"
|
||||||
#include "editor/Editor.h"
|
#include "editor/Editor.h"
|
||||||
#include "editor/EditIds.h"
|
#include "editor/EditIds.h"
|
||||||
|
#include "plugin/InstrumentDescription.h"
|
||||||
#include "IdleUpdateHandler.h"
|
#include "IdleUpdateHandler.h"
|
||||||
#if !defined(__APPLE__) && !defined(_WIN32)
|
#if !defined(__APPLE__) && !defined(_WIN32)
|
||||||
#include "X11RunLoop.h"
|
#include "X11RunLoop.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include <ghc/fs_std.hpp>
|
||||||
|
|
||||||
using namespace VSTGUI;
|
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();
|
const std::string path = update->getPath();
|
||||||
switch (update->getType()) {
|
uiReceiveValue(EditId::SfzFile, path);
|
||||||
case kFilePathUpdateSfz:
|
}
|
||||||
uiReceiveValue(EditId::SfzFile, path);
|
|
||||||
break;
|
if (SfzDescriptionUpdate* update = FCast<SfzDescriptionUpdate>(changedUnknown)) {
|
||||||
case kFilePathUpdateScala:
|
const InstrumentDescription desc = parseDescriptionBlob(update->getDescription());
|
||||||
uiReceiveValue(EditId::ScalaFile, path);
|
|
||||||
break;
|
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)) {
|
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)) {
|
if (PlayStateUpdate* update = FCast<PlayStateUpdate>(changedUnknown)) {
|
||||||
const SfizzPlayState playState = update->getState();
|
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);
|
uiReceiveValue(EditId::UINumActiveVoices, playState.activeVoices);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include "SfizzVstParameters.h"
|
#include "SfizzVstParameters.h"
|
||||||
#include "SfizzFileScan.h"
|
#include "SfizzFileScan.h"
|
||||||
#include "sfizz/import/ForeignInstrument.h"
|
#include "sfizz/import/ForeignInstrument.h"
|
||||||
|
#include "plugin/InstrumentDescription.h"
|
||||||
#include "base/source/fstreamer.h"
|
#include "base/source/fstreamer.h"
|
||||||
#include "pluginterfaces/vst/ivstevents.h"
|
#include "pluginterfaces/vst/ivstevents.h"
|
||||||
#include "pluginterfaces/vst/ivstparameterchanges.h"
|
#include "pluginterfaces/vst/ivstparameterchanges.h"
|
||||||
|
|
@ -38,11 +39,10 @@ static const char* kRingIdOsc = "Osc";
|
||||||
static const char* kMsgIdSetNumVoices = "SetNumVoices";
|
static const char* kMsgIdSetNumVoices = "SetNumVoices";
|
||||||
static const char* kMsgIdSetOversampling = "SetOversampling";
|
static const char* kMsgIdSetOversampling = "SetOversampling";
|
||||||
static const char* kMsgIdSetPreloadSize = "SetPreloadSize";
|
static const char* kMsgIdSetPreloadSize = "SetPreloadSize";
|
||||||
static const char* kMsgIdNotifyPlayState = "NotifyPlayState";
|
|
||||||
static const char* kMsgIdReceiveMessage = "ReceiveMessage";
|
static const char* kMsgIdReceiveMessage = "ReceiveMessage";
|
||||||
static const char* kMsgIdNoteEvents = "NoteEvents";
|
static const char* kMsgIdNoteEvents = "NoteEvents";
|
||||||
|
|
||||||
static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 500 };
|
static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 50 };
|
||||||
|
|
||||||
SfizzVstProcessor::SfizzVstProcessor()
|
SfizzVstProcessor::SfizzVstProcessor()
|
||||||
: _fifoToWorker(64 * 1024), _fifoMessageFromUi(64 * 1024),
|
: _fifoToWorker(64 * 1024), _fifoMessageFromUi(64 * 1024),
|
||||||
|
|
@ -97,7 +97,7 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context)
|
||||||
_synth->setBroadcastCallback(onMessage, this);
|
_synth->setBroadcastCallback(onMessage, this);
|
||||||
|
|
||||||
_currentStretchedTuning = 0.0;
|
_currentStretchedTuning = 0.0;
|
||||||
loadSfzFileOrDefault(*_synth, {});
|
loadSfzFileOrDefault({});
|
||||||
|
|
||||||
_synth->tempo(0, 0.5);
|
_synth->tempo(0, 0.5);
|
||||||
_timeSigNumerator = 4;
|
_timeSigNumerator = 4;
|
||||||
|
|
@ -121,6 +121,19 @@ tresult PLUGIN_API SfizzVstProcessor::setBusArrangements(Vst::SpeakerArrangement
|
||||||
return AudioEffect::setBusArrangements(inputs, numIns, outputs, numOuts);
|
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)
|
tresult PLUGIN_API SfizzVstProcessor::setState(IBStream* stream)
|
||||||
{
|
{
|
||||||
SfizzVstState s;
|
SfizzVstState s;
|
||||||
|
|
@ -173,7 +186,7 @@ void SfizzVstProcessor::syncStateToSynth()
|
||||||
if (!synth)
|
if (!synth)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
loadSfzFileOrDefault(*synth, _state.sfzFile);
|
loadSfzFileOrDefault(_state.sfzFile);
|
||||||
synth->setVolume(_state.volume);
|
synth->setVolume(_state.volume);
|
||||||
synth->setNumVoices(_state.numVoices);
|
synth->setNumVoices(_state.numVoices);
|
||||||
synth->setOversamplingFactor(1 << _state.oversamplingLog2);
|
synth->setOversamplingFactor(1 << _state.oversamplingLog2);
|
||||||
|
|
@ -205,9 +218,6 @@ tresult PLUGIN_API SfizzVstProcessor::setActive(TBool state)
|
||||||
if (state) {
|
if (state) {
|
||||||
synth->setSampleRate(processSetup.sampleRate);
|
synth->setSampleRate(processSetup.sampleRate);
|
||||||
synth->setSamplesPerBlock(processSetup.maxSamplesPerBlock);
|
synth->setSamplesPerBlock(processSetup.maxSamplesPerBlock);
|
||||||
|
|
||||||
_playStateChangePeriod = static_cast<uint32>(50e-3 * processSetup.sampleRate);
|
|
||||||
|
|
||||||
startBackgroundWork();
|
startBackgroundWork();
|
||||||
} else {
|
} else {
|
||||||
stopBackgroundWork();
|
stopBackgroundWork();
|
||||||
|
|
@ -272,19 +282,10 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
|
||||||
|
|
||||||
synth.renderBlock(outputs, numFrames, numChannels);
|
synth.renderBlock(outputs, numFrames, numChannels);
|
||||||
|
|
||||||
_playStateChangeCounter += numFrames;
|
// Request OSC updates
|
||||||
if (_playStateChangeCounter > _playStateChangePeriod) {
|
sfz::Client& client = *_client;
|
||||||
_playStateChangeCounter %= _playStateChangePeriod;
|
synth.sendMessage(client, 0, "/cc/changed~", "", nullptr);
|
||||||
SfizzPlayState playState {};
|
synth.sendMessage(client, 0, "/sw/last/current", "", nullptr);
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
std::pair<uint32, float> noteEvents[128];
|
std::pair<uint32, float> noteEvents[128];
|
||||||
|
|
@ -556,13 +557,8 @@ tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message)
|
||||||
|
|
||||||
std::unique_lock<SpinMutex> lock(_processMutex);
|
std::unique_lock<SpinMutex> lock(_processMutex);
|
||||||
_state.sfzFile.assign(static_cast<const char *>(data), size);
|
_state.sfzFile.assign(static_cast<const char *>(data), size);
|
||||||
loadSfzFileOrDefault(*_synth, _state.sfzFile);
|
loadSfzFileOrDefault(_state.sfzFile);
|
||||||
lock.unlock();
|
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")) {
|
else if (!std::strcmp(id, "LoadScala")) {
|
||||||
const void* data = nullptr;
|
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()) {
|
if (!filePath.empty()) {
|
||||||
const sfz::InstrumentFormatRegistry& formatRegistry = sfz::InstrumentFormatRegistry::getInstance();
|
const sfz::InstrumentFormatRegistry& formatRegistry = sfz::InstrumentFormatRegistry::getInstance();
|
||||||
const sfz::InstrumentFormat* format = formatRegistry.getMatchingFormat(filePath);
|
const sfz::InstrumentFormat* format = formatRegistry.getMatchingFormat(filePath);
|
||||||
|
|
@ -628,8 +626,22 @@ void SfizzVstProcessor::loadSfzFileOrDefault(sfz::Sfizz& synth, const std::strin
|
||||||
synth.loadSfzString(virtualPath, sfzText);
|
synth.loadSfzString(virtualPath, sfzText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
synth.loadSfzString("default.sfz", defaultSfzText);
|
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()
|
void SfizzVstProcessor::doBackgroundWork()
|
||||||
|
|
@ -638,6 +650,7 @@ void SfizzVstProcessor::doBackgroundWork()
|
||||||
|
|
||||||
bool haveDoneIdleWork = false;
|
bool haveDoneIdleWork = false;
|
||||||
Clock::time_point lastIdleWorkTime;
|
Clock::time_point lastIdleWorkTime;
|
||||||
|
size_t idleCounter = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
bool isNotified = _semaToWorker.timed_wait(kBackgroundIdleInterval.count());
|
bool isNotified = _semaToWorker.timed_wait(kBackgroundIdleInterval.count());
|
||||||
|
|
@ -672,13 +685,6 @@ void SfizzVstProcessor::doBackgroundWork()
|
||||||
std::lock_guard<SpinMutex> lock(_processMutex);
|
std::lock_guard<SpinMutex> lock(_processMutex);
|
||||||
_synth->setPreloadSize(value);
|
_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) {
|
else if (id == kMsgIdReceiveMessage) {
|
||||||
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
|
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
|
||||||
notification->setMessageID("ReceivedMessage");
|
notification->setMessageID("ReceivedMessage");
|
||||||
|
|
@ -694,29 +700,35 @@ void SfizzVstProcessor::doBackgroundWork()
|
||||||
|
|
||||||
Clock::time_point currentTime = Clock::now();
|
Clock::time_point currentTime = Clock::now();
|
||||||
if (!haveDoneIdleWork || currentTime - lastIdleWorkTime > kBackgroundIdleInterval) {
|
if (!haveDoneIdleWork || currentTime - lastIdleWorkTime > kBackgroundIdleInterval) {
|
||||||
doBackgroundIdle();
|
doBackgroundIdle(idleCounter++);
|
||||||
haveDoneIdleWork = true;
|
haveDoneIdleWork = true;
|
||||||
lastIdleWorkTime = currentTime;
|
lastIdleWorkTime = currentTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SfizzVstProcessor::doBackgroundIdle()
|
void SfizzVstProcessor::doBackgroundIdle(size_t idleCounter)
|
||||||
{
|
{
|
||||||
if (_synth->shouldReloadFile()) {
|
{
|
||||||
fprintf(stderr, "[Sfizz] sfz file has changed, reloading\n");
|
SfizzPlayState ps;
|
||||||
std::lock_guard<SpinMutex> lock(_processMutex);
|
ps.activeVoices = _synth->getNumActiveVoices();
|
||||||
loadSfzFileOrDefault(*_synth, _state.sfzFile);
|
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
|
||||||
|
notification->setMessageID("NotifiedPlayState");
|
||||||
Steinberg::OPtr<Vst::IMessage> reply { allocateMessage() };
|
notification->getAttributes()->setBinary("PlayState", &ps, sizeof(ps));
|
||||||
reply->setMessageID("LoadedSfz");
|
sendMessage(notification);
|
||||||
reply->getAttributes()->setBinary("File", _state.sfzFile.data(), _state.sfzFile.size());
|
|
||||||
sendMessage(reply);
|
|
||||||
}
|
}
|
||||||
if (_synth->shouldReloadScala()) {
|
|
||||||
fprintf(stderr, "[Sfizz] scala file has changed, reloading\n");
|
if (idleCounter % 10 == 0) {
|
||||||
std::lock_guard<SpinMutex> lock(_processMutex);
|
if (_synth->shouldReloadFile()) {
|
||||||
_synth->loadScalaFile(_state.scalaFile);
|
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 initialize(FUnknown* context) override;
|
||||||
tresult PLUGIN_API setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts) 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 setState(IBStream* stream) override;
|
||||||
tresult PLUGIN_API getState(IBStream* stream) override;
|
tresult PLUGIN_API getState(IBStream* stream) override;
|
||||||
void syncStateToSynth();
|
void syncStateToSynth();
|
||||||
|
|
@ -49,6 +51,7 @@ public:
|
||||||
private:
|
private:
|
||||||
// synth state. acquire processMutex before accessing
|
// synth state. acquire processMutex before accessing
|
||||||
std::unique_ptr<sfz::Sfizz> _synth;
|
std::unique_ptr<sfz::Sfizz> _synth;
|
||||||
|
Steinberg::IPtr<Vst::IMessage> _loadedSfzMessage;
|
||||||
bool _isActive = false;
|
bool _isActive = false;
|
||||||
SfizzVstState _state;
|
SfizzVstState _state;
|
||||||
float _currentStretchedTuning = 0;
|
float _currentStretchedTuning = 0;
|
||||||
|
|
@ -59,7 +62,7 @@ private:
|
||||||
void receiveMessage(int delay, const char* path, const char* sig, const sfizz_arg_t* args);
|
void receiveMessage(int delay, const char* path, const char* sig, const sfizz_arg_t* args);
|
||||||
|
|
||||||
// misc
|
// misc
|
||||||
static void loadSfzFileOrDefault(sfz::Sfizz& synth, const std::string& filePath);
|
void loadSfzFileOrDefault(const std::string& filePath);
|
||||||
|
|
||||||
// note event tracking
|
// note event tracking
|
||||||
std::array<float, 128> _noteEventsCurrentCycle; // 0: off, >0: on, <0: no change
|
std::array<float, 128> _noteEventsCurrentCycle; // 0: off, >0: on, <0: no change
|
||||||
|
|
@ -72,10 +75,6 @@ private:
|
||||||
Ring_Buffer _fifoMessageFromUi;
|
Ring_Buffer _fifoMessageFromUi;
|
||||||
SpinMutex _processMutex;
|
SpinMutex _processMutex;
|
||||||
|
|
||||||
// state notification periodic timer
|
|
||||||
uint32 _playStateChangeCounter = 0;
|
|
||||||
uint32 _playStateChangePeriod = 0;
|
|
||||||
|
|
||||||
// time info
|
// time info
|
||||||
int _timeSigNumerator = 0;
|
int _timeSigNumerator = 0;
|
||||||
int _timeSigDenominator = 0;
|
int _timeSigDenominator = 0;
|
||||||
|
|
@ -95,7 +94,7 @@ private:
|
||||||
|
|
||||||
// worker
|
// worker
|
||||||
void doBackgroundWork();
|
void doBackgroundWork();
|
||||||
void doBackgroundIdle();
|
void doBackgroundIdle(size_t idleCounter);
|
||||||
void startBackgroundWork();
|
void startBackgroundWork();
|
||||||
void stopBackgroundWork();
|
void stopBackgroundWork();
|
||||||
// writer
|
// writer
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,5 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SfizzPlayState {
|
struct SfizzPlayState {
|
||||||
uint32 curves;
|
|
||||||
uint32 masters;
|
|
||||||
uint32 groups;
|
|
||||||
uint32 regions;
|
|
||||||
uint32 preloadedSamples;
|
|
||||||
uint32 activeVoices;
|
uint32 activeVoices;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -67,21 +67,10 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Update which notifies a change of file path pseudo-parameter
|
* @brief Update which notifies a change of SFZ file.
|
||||||
* The message ID is used to indicate which path it is.
|
|
||||||
*/
|
*/
|
||||||
class FilePathUpdate : public Steinberg::FObject {
|
class SfzUpdate : public Steinberg::FObject {
|
||||||
public:
|
public:
|
||||||
explicit FilePathUpdate(int32 type)
|
|
||||||
: type_(type)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 getType() const noexcept
|
|
||||||
{
|
|
||||||
return type_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPath(std::string newPath)
|
void setPath(std::string newPath)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
|
@ -94,17 +83,59 @@ public:
|
||||||
return path_;
|
return path_;
|
||||||
}
|
}
|
||||||
|
|
||||||
OBJ_METHODS(FilePathUpdate, FObject)
|
OBJ_METHODS(SfzUpdate, FObject)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int32 type_ {};
|
|
||||||
std::string path_;
|
std::string path_;
|
||||||
mutable std::mutex mutex_;
|
mutable std::mutex mutex_;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
/**
|
||||||
kFilePathUpdateSfz,
|
* @brief Update which notifies a change of SFZ description.
|
||||||
kFilePathUpdateScala,
|
*/
|
||||||
|
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/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 \
|
-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 \
|
-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 \
|
-Iplugins/editor/src \
|
||||||
-DNDEBUG -std=c++17
|
-DNDEBUG -std=c++17
|
||||||
|
|
|
||||||
|
|
@ -246,10 +246,10 @@ void Synth::Impl::clear()
|
||||||
effectBuses_[0]->setSampleRate(sampleRate_);
|
effectBuses_[0]->setSampleRate(sampleRate_);
|
||||||
effectBuses_[0]->clearInputs(samplesPerBlock_);
|
effectBuses_[0]->clearInputs(samplesPerBlock_);
|
||||||
resources_.clear();
|
resources_.clear();
|
||||||
|
rootPath_.clear();
|
||||||
numGroups_ = 0;
|
numGroups_ = 0;
|
||||||
numMasters_ = 0;
|
numMasters_ = 0;
|
||||||
currentSwitch_ = absl::nullopt;
|
currentSwitch_ = absl::nullopt;
|
||||||
currentSwitchChanged_ = true;
|
|
||||||
defaultPath_ = "";
|
defaultPath_ = "";
|
||||||
image_ = "";
|
image_ = "";
|
||||||
resources_.midiState.reset();
|
resources_.midiState.reset();
|
||||||
|
|
@ -258,6 +258,7 @@ void Synth::Impl::clear()
|
||||||
clearCCLabels();
|
clearCCLabels();
|
||||||
currentUsedCCs_.clear();
|
currentUsedCCs_.clear();
|
||||||
changedCCsThisCycle_.clear();
|
changedCCsThisCycle_.clear();
|
||||||
|
changedCCsLastCycle_.clear();
|
||||||
clearKeyLabels();
|
clearKeyLabels();
|
||||||
keySlots_.clear();
|
keySlots_.clear();
|
||||||
swLastSlots_.clear();
|
swLastSlots_.clear();
|
||||||
|
|
@ -547,7 +548,11 @@ bool Synth::loadSfzString(const fs::path& path, absl::string_view text)
|
||||||
|
|
||||||
void Synth::Impl::finalizeSfzLoad()
|
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 currentRegionIndex = 0;
|
||||||
size_t currentRegionCount = layers_.size();
|
size_t currentRegionCount = layers_.size();
|
||||||
|
|
@ -790,8 +795,6 @@ void Synth::Impl::finalizeSfzLoad()
|
||||||
swLastSlots_.set(key);
|
swLastSlots_.set(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// resend current keyswitch
|
|
||||||
currentSwitchChanged_ = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Synth::loadScalaFile(const fs::path& path)
|
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
|
// Advance the clock to the end of cycle
|
||||||
bc.endCycle();
|
bc.endCycle();
|
||||||
|
|
||||||
// Send the set of changed CCs
|
// Update sets of changed CCs
|
||||||
Client broadcaster = impl.getBroadcaster();
|
impl.changedCCsLastCycle_ = impl.changedCCsThisCycle_;
|
||||||
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);
|
|
||||||
}
|
|
||||||
impl.changedCCsThisCycle_.clear();
|
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
|
{ // Clear events and advance midi time
|
||||||
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
|
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;
|
layer->keySwitched_ = false;
|
||||||
}
|
}
|
||||||
currentSwitch_ = noteNumber;
|
currentSwitch_ = noteNumber;
|
||||||
currentSwitchChanged_ = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Layer* layer : lastKeyswitchLists_[noteNumber])
|
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", "") {
|
MATCH("/key/slots", "") {
|
||||||
const BitArray<128>& keys = impl.keySlots_;
|
const BitArray<128>& keys = impl.keySlots_;
|
||||||
sfizz_blob_t blob { keys.data(), static_cast<uint32_t>(keys.byte_size()) };
|
sfizz_blob_t blob { keys.data(), static_cast<uint32_t>(keys.byte_size()) };
|
||||||
client.receive<'b'>(delay, path, &blob);
|
client.receive<'b'>(delay, path, &blob);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
MATCH("/key%d/label", "") {
|
MATCH("/key&/label", "") {
|
||||||
if (indices[0] >= 128)
|
if (indices[0] >= 128)
|
||||||
break;
|
break;
|
||||||
const std::string* label = impl.getKeyLabel(indices[0]);
|
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", "") {
|
MATCH("/image", "") {
|
||||||
client.receive<'s'>(delay, path, impl.image_.c_str());
|
client.receive<'s'>(delay, path, impl.image_.c_str());
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
MATCH("/sw/last/slots", "") {
|
MATCH("/sw/last/slots", "") {
|
||||||
const BitArray<128>& switches = impl.swLastSlots_;
|
const BitArray<128>& switches = impl.swLastSlots_;
|
||||||
sfizz_blob_t blob { switches.data(), static_cast<uint32_t>(switches.byte_size()) };
|
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;
|
} break;
|
||||||
|
|
||||||
MATCH("/sw/last/current", "") {
|
MATCH("/sw/last/current", "") {
|
||||||
int32_t value = -1;
|
|
||||||
if (impl.currentSwitch_)
|
if (impl.currentSwitch_)
|
||||||
value = *impl.currentSwitch_;
|
client.receive<'i'>(delay, path, *impl.currentSwitch_);
|
||||||
client.receive<'i'>(delay, path, value);
|
else
|
||||||
|
client.receive<'N'>(delay, path, {});
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
MATCH("/sw/last/&/label", "") {
|
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() : "");
|
client.receive<'s'>(delay, path, label ? label->c_str() : "");
|
||||||
} break;
|
} 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", "") {
|
MATCH("/mem/buffers", "") {
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,6 @@ struct Synth::Impl final: public Parser::Listener {
|
||||||
|
|
||||||
// Set as sw_default if present in the file
|
// Set as sw_default if present in the file
|
||||||
absl::optional<uint8_t> currentSwitch_;
|
absl::optional<uint8_t> currentSwitch_;
|
||||||
bool currentSwitchChanged_ = true;
|
|
||||||
std::vector<std::string> unknownOpcodes_;
|
std::vector<std::string> unknownOpcodes_;
|
||||||
using RegionViewVector = std::vector<Region*>;
|
using RegionViewVector = std::vector<Region*>;
|
||||||
using LayerViewVector = std::vector<Layer*>;
|
using LayerViewVector = std::vector<Layer*>;
|
||||||
|
|
@ -279,6 +278,9 @@ struct Synth::Impl final: public Parser::Listener {
|
||||||
// Singletons passed as references to the voices
|
// Singletons passed as references to the voices
|
||||||
Resources resources_;
|
Resources resources_;
|
||||||
|
|
||||||
|
// Root path
|
||||||
|
std::string rootPath_;
|
||||||
|
|
||||||
// Control opcodes
|
// Control opcodes
|
||||||
std::string defaultPath_ { "" };
|
std::string defaultPath_ { "" };
|
||||||
std::string image_ { "" };
|
std::string image_ { "" };
|
||||||
|
|
@ -315,6 +317,7 @@ struct Synth::Impl final: public Parser::Listener {
|
||||||
std::array<float, config::numCCs> defaultCCValues_;
|
std::array<float, config::numCCs> defaultCCValues_;
|
||||||
BitArray<config::numCCs> currentUsedCCs_;
|
BitArray<config::numCCs> currentUsedCCs_;
|
||||||
BitArray<config::numCCs> changedCCsThisCycle_;
|
BitArray<config::numCCs> changedCCsThisCycle_;
|
||||||
|
BitArray<config::numCCs> changedCCsLastCycle_;
|
||||||
|
|
||||||
// Messaging
|
// Messaging
|
||||||
sfizz_receive_t* broadcastReceiver = nullptr;
|
sfizz_receive_t* broadcastReceiver = nullptr;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue