Add the reception of CC messages for editor
This commit is contained in:
parent
c81f74100b
commit
751e4f96bb
7 changed files with 263 additions and 61 deletions
|
|
@ -13,14 +13,17 @@
|
||||||
#include <absl/strings/string_view.h>
|
#include <absl/strings/string_view.h>
|
||||||
#include <absl/strings/match.h>
|
#include <absl/strings/match.h>
|
||||||
#include <absl/strings/ascii.h>
|
#include <absl/strings/ascii.h>
|
||||||
|
#include <absl/strings/numbers.h>
|
||||||
#include <ghc/fs_std.hpp>
|
#include <ghc/fs_std.hpp>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <queue>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "utility/vstgui_before.h"
|
#include "utility/vstgui_before.h"
|
||||||
#include "vstgui/vstgui.h"
|
#include "vstgui/vstgui.h"
|
||||||
|
|
@ -106,6 +109,13 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
|
||||||
void uiReceiveValue(EditId id, const EditValue& v) override;
|
void uiReceiveValue(EditId id, const EditValue& v) override;
|
||||||
void uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args) override;
|
void uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args) override;
|
||||||
|
|
||||||
|
// queued OSC API; sends OSC with intermediate delay between messages
|
||||||
|
// to prevent message bursts overloading the buffer
|
||||||
|
void sendQueuedOSC(const char* path, const char* sig, const sfizz_arg_t* args);
|
||||||
|
void tickOSCQueue(CVSTGUITimer* timer);
|
||||||
|
std::queue<std::string> oscSendQueue_;
|
||||||
|
SharedPointer<CVSTGUITimer> oscSendQueueTimer_;
|
||||||
|
|
||||||
void createFrameContents();
|
void createFrameContents();
|
||||||
|
|
||||||
template <class Control>
|
template <class Control>
|
||||||
|
|
@ -142,6 +152,10 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
|
||||||
void updateTuningFrequencyLabel(float tuningFrequency);
|
void updateTuningFrequencyLabel(float tuningFrequency);
|
||||||
void updateStretchedTuningLabel(float stretchedTuning);
|
void updateStretchedTuningLabel(float stretchedTuning);
|
||||||
|
|
||||||
|
void updateCCUsed(unsigned cc, bool used);
|
||||||
|
void updateCCValue(unsigned cc, float value);
|
||||||
|
void updateCCLabel(unsigned cc, const char* label);
|
||||||
|
|
||||||
void setActivePanel(unsigned panelId);
|
void setActivePanel(unsigned panelId);
|
||||||
|
|
||||||
static void formatLabel(CTextLabel* label, const char* fmt, ...);
|
static void formatLabel(CTextLabel* label, const char* fmt, ...);
|
||||||
|
|
@ -164,6 +178,11 @@ Editor::Editor(EditorController& ctrl)
|
||||||
ctrl.decorate(&impl);
|
ctrl.decorate(&impl);
|
||||||
|
|
||||||
impl.createFrameContents();
|
impl.createFrameContents();
|
||||||
|
|
||||||
|
uint32_t oscSendInterval = 1; // milliseconds
|
||||||
|
impl.oscSendQueueTimer_ = makeOwned<CVSTGUITimer>(
|
||||||
|
[this](CVSTGUITimer* timer) { impl_->tickOSCQueue(timer); },
|
||||||
|
oscSendInterval, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Editor::~Editor()
|
Editor::~Editor()
|
||||||
|
|
@ -182,6 +201,9 @@ void Editor::open(CFrame& frame)
|
||||||
|
|
||||||
impl.frame_ = &frame;
|
impl.frame_ = &frame;
|
||||||
frame.addView(impl.mainView_.get());
|
frame.addView(impl.mainView_.get());
|
||||||
|
|
||||||
|
// request the whole CC information
|
||||||
|
impl.sendQueuedOSC("/cc/slots", "", nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::close()
|
void Editor::close()
|
||||||
|
|
@ -336,9 +358,92 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
static constexpr unsigned kMessageMaxIndices = 8;
|
||||||
|
|
||||||
|
static bool matchMessage(const char* pattern, const char* path, unsigned* indices)
|
||||||
|
{
|
||||||
|
unsigned nthIndex = 0;
|
||||||
|
|
||||||
|
while (const char *endp = strchr(pattern, '&')) {
|
||||||
|
if (nthIndex == kMessageMaxIndices)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t length = endp - pattern;
|
||||||
|
if (strncmp(pattern, path, length))
|
||||||
|
return false;
|
||||||
|
pattern += length;
|
||||||
|
path += length;
|
||||||
|
|
||||||
|
length = 0;
|
||||||
|
while (absl::ascii_isdigit(path[length]))
|
||||||
|
++length;
|
||||||
|
|
||||||
|
if (!absl::SimpleAtoi(absl::string_view(path, length), &indices[nthIndex++]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pattern += 1;
|
||||||
|
path += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !strcmp(path, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
void Editor::Impl::uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args)
|
void Editor::Impl::uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args)
|
||||||
{
|
{
|
||||||
// TODO handle the message...
|
unsigned indices[kMessageMaxIndices];
|
||||||
|
|
||||||
|
if (!strcmp(path, "/cc/slots") && !strcmp(sig, "b")) {
|
||||||
|
const uint8_t* bitChunks = args[0].b->data;
|
||||||
|
uint32_t byteSize = args[0].b->size;
|
||||||
|
|
||||||
|
for (unsigned cc = 0; cc < 8 * byteSize; ++cc) {
|
||||||
|
bool used = bitChunks[cc / 8] & (1u << (cc % 8));
|
||||||
|
updateCCUsed(cc, used);
|
||||||
|
if (used) {
|
||||||
|
char pathBuf[256];
|
||||||
|
sprintf(pathBuf, "/cc%u/value", cc);
|
||||||
|
sendQueuedOSC(pathBuf, "", nullptr);
|
||||||
|
sprintf(pathBuf, "/cc%u/label", cc);
|
||||||
|
sendQueuedOSC(pathBuf, "", nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (matchMessage("/cc&/value", path, indices) && !strcmp(sig, "f")) {
|
||||||
|
updateCCValue(indices[0], args[0].f);
|
||||||
|
}
|
||||||
|
else if (matchMessage("/cc&/label", path, indices) && !strcmp(sig, "s")) {
|
||||||
|
updateCCLabel(indices[0], args[0].s);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//fprintf(stderr, "Receive unhandled OSC: %s\n", path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Editor::Impl::sendQueuedOSC(const char* path, const char* sig, const sfizz_arg_t* args)
|
||||||
|
{
|
||||||
|
uint32_t oscSize = sfizz_prepare_message(nullptr, 0, path, sig, args);
|
||||||
|
std::string oscData(oscSize, '\0');
|
||||||
|
sfizz_prepare_message(&oscData[0], oscSize, path, sig, args);
|
||||||
|
oscSendQueue_.push(std::move(oscData));
|
||||||
|
oscSendQueueTimer_->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Editor::Impl::tickOSCQueue(CVSTGUITimer* timer)
|
||||||
|
{
|
||||||
|
if (oscSendQueue_.empty()) {
|
||||||
|
timer->stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const std::string& msg = oscSendQueue_.front();
|
||||||
|
const char* path;
|
||||||
|
const char* sig;
|
||||||
|
const sfizz_arg_t* args;
|
||||||
|
uint8_t buffer[1024];
|
||||||
|
if (sfizz_extract_message(msg.data(), msg.size(), buffer, sizeof(buffer), &path, &sig, &args) > 0)
|
||||||
|
ctrl_->uiSendMessage(path, sig, args);
|
||||||
|
oscSendQueue_.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::Impl::createFrameContents()
|
void Editor::Impl::createFrameContents()
|
||||||
|
|
@ -1062,6 +1167,24 @@ void Editor::Impl::updateStretchedTuningLabel(float stretchedTuning)
|
||||||
label->setText(text);
|
label->setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Editor::Impl::updateCCUsed(unsigned cc, bool used)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
fprintf(stderr, "CC%u used: %d\n", cc, used);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Editor::Impl::updateCCValue(unsigned cc, float value)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
fprintf(stderr, "CC%u value: %f\n", cc, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Editor::Impl::updateCCLabel(unsigned cc, const char* label)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
fprintf(stderr, "CC%u label: %s\n", cc, label);
|
||||||
|
}
|
||||||
|
|
||||||
void Editor::Impl::setActivePanel(unsigned panelId)
|
void Editor::Impl::setActivePanel(unsigned panelId)
|
||||||
{
|
{
|
||||||
panelId = std::max(0, std::min(kNumPanels - 1, static_cast<int>(panelId)));
|
panelId = std::max(0, std::min(kNumPanels - 1, static_cast<int>(panelId)));
|
||||||
|
|
|
||||||
|
|
@ -696,7 +696,7 @@ void Synth::Impl::finalizeSfzLoad()
|
||||||
regions_.resize(currentRegionCount);
|
regions_.resize(currentRegionCount);
|
||||||
|
|
||||||
// collect all CCs used in regions, with matrix not yet connected
|
// collect all CCs used in regions, with matrix not yet connected
|
||||||
std::bitset<config::numCCs> usedCCs;
|
BitArray<config::numCCs> usedCCs;
|
||||||
for (const RegionPtr& regionPtr : regions_) {
|
for (const RegionPtr& regionPtr : regions_) {
|
||||||
const Region& region = *regionPtr;
|
const Region& region = *regionPtr;
|
||||||
collectUsedCCsFromRegion(usedCCs, region);
|
collectUsedCCsFromRegion(usedCCs, region);
|
||||||
|
|
@ -1328,8 +1328,8 @@ std::string Synth::exportMidnam(absl::string_view model) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned i = 0, n = std::min<unsigned>(128, anonymousCCs.size()); i < n; ++i) {
|
for (unsigned i = 0, n = std::min<unsigned>(128, anonymousCCs.bit_size()); i < n; ++i) {
|
||||||
if (anonymousCCs[i]) {
|
if (anonymousCCs.test(i)) {
|
||||||
pugi::xml_node cn = cns.append_child("Control");
|
pugi::xml_node cn = cns.append_child("Control");
|
||||||
cn.append_attribute("Type").set_value("7bit");
|
cn.append_attribute("Type").set_value("7bit");
|
||||||
cn.append_attribute("Number").set_value(std::to_string(i).c_str());
|
cn.append_attribute("Number").set_value(std::to_string(i).c_str());
|
||||||
|
|
@ -1716,7 +1716,7 @@ void Synth::allSoundOff() noexcept
|
||||||
effectBus->clear();
|
effectBus->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::bitset<config::numCCs>& Synth::getUsedCCs() const noexcept
|
const BitArray<config::numCCs>& Synth::getUsedCCs() const noexcept
|
||||||
{
|
{
|
||||||
Impl& impl = *impl_;
|
Impl& impl = *impl_;
|
||||||
return impl.currentUsedCCs_;
|
return impl.currentUsedCCs_;
|
||||||
|
|
@ -1729,7 +1729,7 @@ void sfz::Synth::setBroadcastCallback(sfizz_receive_t* broadcast, void* data)
|
||||||
impl.broadcastData = data;
|
impl.broadcastData = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::Impl::collectUsedCCsFromRegion(std::bitset<config::numCCs>& usedCCs, const Region& region)
|
void Synth::Impl::collectUsedCCsFromRegion(BitArray<config::numCCs>& usedCCs, const Region& region)
|
||||||
{
|
{
|
||||||
collectUsedCCsFromCCMap(usedCCs, region.offsetCC);
|
collectUsedCCsFromCCMap(usedCCs, region.offsetCC);
|
||||||
collectUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccAttack);
|
collectUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccAttack);
|
||||||
|
|
@ -1763,11 +1763,11 @@ void Synth::Impl::collectUsedCCsFromRegion(std::bitset<config::numCCs>& usedCCs,
|
||||||
collectUsedCCsFromCCMap(usedCCs, region.crossfadeCCOutRange);
|
collectUsedCCsFromCCMap(usedCCs, region.crossfadeCCOutRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::Impl::collectUsedCCsFromModulations(std::bitset<config::numCCs>& usedCCs, const ModMatrix& mm)
|
void Synth::Impl::collectUsedCCsFromModulations(BitArray<config::numCCs>& usedCCs, const ModMatrix& mm)
|
||||||
{
|
{
|
||||||
class CCSourceCollector : public ModMatrix::KeyVisitor {
|
class CCSourceCollector : public ModMatrix::KeyVisitor {
|
||||||
public:
|
public:
|
||||||
explicit CCSourceCollector(std::bitset<config::numCCs>& used)
|
explicit CCSourceCollector(BitArray<config::numCCs>& used)
|
||||||
: used_(used)
|
: used_(used)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -1778,16 +1778,16 @@ void Synth::Impl::collectUsedCCsFromModulations(std::bitset<config::numCCs>& use
|
||||||
used_.set(key.parameters().cc);
|
used_.set(key.parameters().cc);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
std::bitset<config::numCCs>& used_;
|
BitArray<config::numCCs>& used_;
|
||||||
};
|
};
|
||||||
|
|
||||||
CCSourceCollector vtor(usedCCs);
|
CCSourceCollector vtor(usedCCs);
|
||||||
mm.visitSources(vtor);
|
mm.visitSources(vtor);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::bitset<config::numCCs> Synth::Impl::collectAllUsedCCs()
|
BitArray<config::numCCs> Synth::Impl::collectAllUsedCCs()
|
||||||
{
|
{
|
||||||
std::bitset<config::numCCs> used;
|
BitArray<config::numCCs> used;
|
||||||
for (const Impl::RegionPtr& region : regions_)
|
for (const Impl::RegionPtr& region : regions_)
|
||||||
collectUsedCCsFromRegion(used, *region);
|
collectUsedCCsFromRegion(used, *region);
|
||||||
collectUsedCCsFromModulations(used, resources_.modMatrix);
|
collectUsedCCsFromModulations(used, resources_.modMatrix);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
template <size_t> class BitArray;
|
||||||
|
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
|
|
||||||
|
|
@ -599,7 +600,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return const std::bitset<config::numCCs>&
|
* @return const std::bitset<config::numCCs>&
|
||||||
*/
|
*/
|
||||||
const std::bitset<config::numCCs>& getUsedCCs() const noexcept;
|
const BitArray<config::numCCs>& getUsedCCs() const noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Dispatch the incoming message to the synth engine
|
* @brief Dispatch the incoming message to the synth engine
|
||||||
|
|
|
||||||
|
|
@ -39,13 +39,8 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
MATCH("/cc/slots", "") {
|
MATCH("/cc/slots", "") {
|
||||||
uint8_t data[(config::numCCs + 7) / 8] = {};
|
const BitArray<config::numCCs>& ccs = impl.currentUsedCCs_;
|
||||||
|
sfizz_blob_t blob { ccs.data(), static_cast<uint32_t>(ccs.byte_size()) };
|
||||||
const std::bitset<config::numCCs>& ccs = impl.currentUsedCCs_;
|
|
||||||
for (unsigned i = 0; i < config::numCCs; ++i)
|
|
||||||
data[i / 8] |= ccs.test(i) << (i % 8);
|
|
||||||
|
|
||||||
sfizz_blob_t blob { data, sizeof(data) };
|
|
||||||
client.receive<'b'>(delay, path, &blob);
|
client.receive<'b'>(delay, path, &blob);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include "modulations/sources/Controller.h"
|
#include "modulations/sources/Controller.h"
|
||||||
#include "modulations/sources/FlexEnvelope.h"
|
#include "modulations/sources/FlexEnvelope.h"
|
||||||
#include "modulations/sources/LFO.h"
|
#include "modulations/sources/LFO.h"
|
||||||
|
#include "utility/BitArray.h"
|
||||||
|
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
|
|
||||||
|
|
@ -168,16 +169,16 @@ struct Synth::Impl final: public Parser::Listener {
|
||||||
void finalizeSfzLoad();
|
void finalizeSfzLoad();
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
static void collectUsedCCsFromCCMap(std::bitset<config::numCCs>& usedCCs, const CCMap<T> map) noexcept
|
static void collectUsedCCsFromCCMap(BitArray<config::numCCs>& usedCCs, const CCMap<T> map) noexcept
|
||||||
{
|
{
|
||||||
for (auto& mod : map)
|
for (auto& mod : map)
|
||||||
usedCCs[mod.cc] = true;
|
usedCCs.set(mod.cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void collectUsedCCsFromRegion(std::bitset<config::numCCs>& usedCCs, const Region& region);
|
static void collectUsedCCsFromRegion(BitArray<config::numCCs>& usedCCs, const Region& region);
|
||||||
static void collectUsedCCsFromModulations(std::bitset<config::numCCs>& usedCCs, const ModMatrix& mm);
|
static void collectUsedCCsFromModulations(BitArray<config::numCCs>& usedCCs, const ModMatrix& mm);
|
||||||
|
|
||||||
std::bitset<config::numCCs> collectAllUsedCCs();
|
BitArray<config::numCCs> collectAllUsedCCs();
|
||||||
|
|
||||||
const std::string* getCCLabel(int ccNumber);
|
const std::string* getCCLabel(int ccNumber);
|
||||||
void setCCLabel(int ccNumber, std::string name);
|
void setCCLabel(int ccNumber, std::string name);
|
||||||
|
|
@ -283,7 +284,7 @@ struct Synth::Impl final: public Parser::Listener {
|
||||||
absl::optional<fs::file_time_type> modificationTime_ { };
|
absl::optional<fs::file_time_type> modificationTime_ { };
|
||||||
|
|
||||||
std::array<float, config::numCCs> defaultCCValues_;
|
std::array<float, config::numCCs> defaultCCValues_;
|
||||||
std::bitset<config::numCCs> currentUsedCCs_;
|
BitArray<config::numCCs> currentUsedCCs_;
|
||||||
|
|
||||||
// Messaging
|
// Messaging
|
||||||
sfizz_receive_t* broadcastReceiver = nullptr;
|
sfizz_receive_t* broadcastReceiver = nullptr;
|
||||||
|
|
|
||||||
81
src/sfizz/utility/BitArray.h
Normal file
81
src/sfizz/utility/BitArray.h
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
// 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 <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
///
|
||||||
|
class ConstBitSpan {
|
||||||
|
public:
|
||||||
|
ConstBitSpan() noexcept = default;
|
||||||
|
ConstBitSpan(const uint8_t* data, size_t bits) noexcept : data_(data), bits_(bits) {}
|
||||||
|
const uint8_t* data() const noexcept { return data_; }
|
||||||
|
size_t bit_size() const noexcept { return bits_; }
|
||||||
|
size_t byte_size() const noexcept { return (bits_ + 7) / 8; }
|
||||||
|
bool test(size_t i) const noexcept { return data_[i / 8] & (1u << (i % 8)); }
|
||||||
|
bool all() const noexcept
|
||||||
|
{
|
||||||
|
size_t n = bits_;
|
||||||
|
for (size_t i = 0; i < n / 8; ++i) {
|
||||||
|
if (data_[i] != 0xff)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return n % 8 == 0 || data_[n / 8] == (1u << (n % 8)) - 1u;
|
||||||
|
}
|
||||||
|
bool any() const noexcept
|
||||||
|
{
|
||||||
|
size_t n = bits_;
|
||||||
|
for (size_t i = 0; i < n / 8; ++i) {
|
||||||
|
if (data_[i] != 0x00)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return n % 8 != 0 && (data_[n / 8] & ((1u << (n % 8)) - 1u)) != 0;
|
||||||
|
}
|
||||||
|
bool none() const noexcept { return !any(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const uint8_t* data_ = nullptr;
|
||||||
|
size_t bits_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
class BitSpan : public ConstBitSpan {
|
||||||
|
public:
|
||||||
|
BitSpan() noexcept = default;
|
||||||
|
BitSpan(const uint8_t* data, size_t bits) noexcept : ConstBitSpan(data, bits) {}
|
||||||
|
uint8_t* data() const noexcept { return const_cast<uint8_t*>(ConstBitSpan::data()); }
|
||||||
|
void clear() { memset(data(), 0, byte_size()); }
|
||||||
|
void set(size_t i) noexcept { data()[i / 8] |= 1u << (i % 8); }
|
||||||
|
void set(size_t i, bool b) noexcept { if (b) set(i); else reset(i); }
|
||||||
|
void reset(size_t i) noexcept { data()[i / 8] &= ~(1u << (i % 8)); }
|
||||||
|
void flip(size_t i) noexcept { data()[i / 8] ^= 1u << (i % 8); }
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
template <size_t N>
|
||||||
|
class BitArray {
|
||||||
|
public:
|
||||||
|
BitArray() noexcept = default;
|
||||||
|
uint8_t* data() noexcept { return data_; }
|
||||||
|
const uint8_t* data() const noexcept { return data_; }
|
||||||
|
static constexpr size_t bit_size() noexcept { return N; }
|
||||||
|
static constexpr size_t byte_size() noexcept { return (N + 7) / 8; }
|
||||||
|
BitSpan span() noexcept { return BitSpan(data_, N); };
|
||||||
|
ConstBitSpan span() const noexcept { return ConstBitSpan(data_, N); };
|
||||||
|
bool test(size_t i) const noexcept { return span().test(i); }
|
||||||
|
void set(size_t i) noexcept { span().set(i); }
|
||||||
|
void set(size_t i, bool b) noexcept { span().set(i, b); }
|
||||||
|
void reset(size_t i) noexcept { span().reset(i); }
|
||||||
|
void flip(size_t i) noexcept { span().flip(i); }
|
||||||
|
bool all() const noexcept { return span().all(); }
|
||||||
|
bool any() const noexcept { return span().any(); }
|
||||||
|
bool none() const noexcept { return span().none(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t data_[byte_size()] {};
|
||||||
|
};
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include "sfizz/SisterVoiceRing.h"
|
#include "sfizz/SisterVoiceRing.h"
|
||||||
#include "sfizz/SfzHelpers.h"
|
#include "sfizz/SfzHelpers.h"
|
||||||
#include "sfizz/utility/NumericId.h"
|
#include "sfizz/utility/NumericId.h"
|
||||||
|
#include "sfizz/utility/BitArray.h"
|
||||||
#include "TestHelpers.h"
|
#include "TestHelpers.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "catch2/catch.hpp"
|
#include "catch2/catch.hpp"
|
||||||
|
|
@ -1087,18 +1088,18 @@ TEST_CASE("[Synth] Used CCs")
|
||||||
<region> start_locc44=200 hikey=-1 sample=*sine
|
<region> start_locc44=200 hikey=-1 sample=*sine
|
||||||
)");
|
)");
|
||||||
auto usedCCs = synth.getUsedCCs();
|
auto usedCCs = synth.getUsedCCs();
|
||||||
REQUIRE( usedCCs[1] );
|
REQUIRE( usedCCs.test(1) );
|
||||||
REQUIRE( usedCCs[2] );
|
REQUIRE( usedCCs.test(2) );
|
||||||
REQUIRE( !usedCCs[3] );
|
REQUIRE( !usedCCs.test(3) );
|
||||||
REQUIRE( usedCCs[4] );
|
REQUIRE( usedCCs.test(4) );
|
||||||
REQUIRE( usedCCs[5] );
|
REQUIRE( usedCCs.test(5) );
|
||||||
REQUIRE( !usedCCs[6] );
|
REQUIRE( !usedCCs.test(6) );
|
||||||
REQUIRE( usedCCs[42] );
|
REQUIRE( usedCCs.test(42) );
|
||||||
REQUIRE( usedCCs[44] );
|
REQUIRE( usedCCs.test(44) );
|
||||||
REQUIRE( usedCCs[56] );
|
REQUIRE( usedCCs.test(56) );
|
||||||
REQUIRE( usedCCs[67] );
|
REQUIRE( usedCCs.test(67) );
|
||||||
REQUIRE( usedCCs[98] );
|
REQUIRE( usedCCs.test(98) );
|
||||||
REQUIRE( !usedCCs[127] );
|
REQUIRE( !usedCCs.test(127) );
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Synth] Used CCs EGs")
|
TEST_CASE("[Synth] Used CCs EGs")
|
||||||
|
|
@ -1135,31 +1136,31 @@ TEST_CASE("[Synth] Used CCs EGs")
|
||||||
sample=*sine
|
sample=*sine
|
||||||
)");
|
)");
|
||||||
auto usedCCs = synth.getUsedCCs();
|
auto usedCCs = synth.getUsedCCs();
|
||||||
REQUIRE( usedCCs[1] );
|
REQUIRE( usedCCs.test(1) );
|
||||||
REQUIRE( usedCCs[2] );
|
REQUIRE( usedCCs.test(2) );
|
||||||
REQUIRE( usedCCs[3] );
|
REQUIRE( usedCCs.test(3) );
|
||||||
REQUIRE( usedCCs[4] );
|
REQUIRE( usedCCs.test(4) );
|
||||||
REQUIRE( usedCCs[5] );
|
REQUIRE( usedCCs.test(5) );
|
||||||
REQUIRE( usedCCs[6] );
|
REQUIRE( usedCCs.test(6) );
|
||||||
REQUIRE( usedCCs[7] );
|
REQUIRE( usedCCs.test(7) );
|
||||||
// FIXME: enable when supported
|
// FIXME: enable when supported
|
||||||
// REQUIRE( !usedCCs[8] );
|
// REQUIRE( !usedCCs.test(8) );
|
||||||
// REQUIRE( usedCCs[11] );
|
// REQUIRE( usedCCs.test(11) );
|
||||||
// REQUIRE( usedCCs[12] );
|
// REQUIRE( usedCCs.test(12) );
|
||||||
// REQUIRE( usedCCs[13] );
|
// REQUIRE( usedCCs.test(13) );
|
||||||
// REQUIRE( usedCCs[14] );
|
// REQUIRE( usedCCs.test(14) );
|
||||||
// REQUIRE( usedCCs[15] );
|
// REQUIRE( usedCCs.test(15) );
|
||||||
// REQUIRE( usedCCs[16] );
|
// REQUIRE( usedCCs.test(16) );
|
||||||
// REQUIRE( usedCCs[17] );
|
// REQUIRE( usedCCs.test(17) );
|
||||||
// REQUIRE( !usedCCs[18] );
|
// REQUIRE( !usedCCs.test(18) );
|
||||||
// REQUIRE( usedCCs[21] );
|
// REQUIRE( usedCCs.test(21) );
|
||||||
// REQUIRE( usedCCs[22] );
|
// REQUIRE( usedCCs.test(22) );
|
||||||
// REQUIRE( usedCCs[23] );
|
// REQUIRE( usedCCs.test(23) );
|
||||||
// REQUIRE( usedCCs[24] );
|
// REQUIRE( usedCCs.test(24) );
|
||||||
// REQUIRE( usedCCs[25] );
|
// REQUIRE( usedCCs.test(25) );
|
||||||
// REQUIRE( usedCCs[26] );
|
// REQUIRE( usedCCs.test(26) );
|
||||||
// REQUIRE( usedCCs[27] );
|
// REQUIRE( usedCCs.test(27) );
|
||||||
// REQUIRE( !usedCCs[28] );
|
// REQUIRE( !usedCCs.test(28) );
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Synth] Activate also on the sustain CC")
|
TEST_CASE("[Synth] Activate also on the sustain CC")
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue