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/match.h>
|
||||
#include <absl/strings/ascii.h>
|
||||
#include <absl/strings/numbers.h>
|
||||
#include <ghc/fs_std.hpp>
|
||||
#include <array>
|
||||
#include <queue>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <system_error>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "utility/vstgui_before.h"
|
||||
#include "vstgui/vstgui.h"
|
||||
|
|
@ -106,6 +109,13 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
|
|||
void uiReceiveValue(EditId id, const EditValue& v) 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();
|
||||
|
||||
template <class Control>
|
||||
|
|
@ -142,6 +152,10 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
|
|||
void updateTuningFrequencyLabel(float tuningFrequency);
|
||||
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);
|
||||
|
||||
static void formatLabel(CTextLabel* label, const char* fmt, ...);
|
||||
|
|
@ -164,6 +178,11 @@ Editor::Editor(EditorController& ctrl)
|
|||
ctrl.decorate(&impl);
|
||||
|
||||
impl.createFrameContents();
|
||||
|
||||
uint32_t oscSendInterval = 1; // milliseconds
|
||||
impl.oscSendQueueTimer_ = makeOwned<CVSTGUITimer>(
|
||||
[this](CVSTGUITimer* timer) { impl_->tickOSCQueue(timer); },
|
||||
oscSendInterval, false);
|
||||
}
|
||||
|
||||
Editor::~Editor()
|
||||
|
|
@ -182,6 +201,9 @@ void Editor::open(CFrame& frame)
|
|||
|
||||
impl.frame_ = &frame;
|
||||
frame.addView(impl.mainView_.get());
|
||||
|
||||
// request the whole CC information
|
||||
impl.sendQueuedOSC("/cc/slots", "", nullptr);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// 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()
|
||||
|
|
@ -1062,6 +1167,24 @@ void Editor::Impl::updateStretchedTuningLabel(float stretchedTuning)
|
|||
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)
|
||||
{
|
||||
panelId = std::max(0, std::min(kNumPanels - 1, static_cast<int>(panelId)));
|
||||
|
|
|
|||
|
|
@ -696,7 +696,7 @@ void Synth::Impl::finalizeSfzLoad()
|
|||
regions_.resize(currentRegionCount);
|
||||
|
||||
// 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_) {
|
||||
const Region& region = *regionPtr;
|
||||
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) {
|
||||
if (anonymousCCs[i]) {
|
||||
for (unsigned i = 0, n = std::min<unsigned>(128, anonymousCCs.bit_size()); i < n; ++i) {
|
||||
if (anonymousCCs.test(i)) {
|
||||
pugi::xml_node cn = cns.append_child("Control");
|
||||
cn.append_attribute("Type").set_value("7bit");
|
||||
cn.append_attribute("Number").set_value(std::to_string(i).c_str());
|
||||
|
|
@ -1716,7 +1716,7 @@ void Synth::allSoundOff() noexcept
|
|||
effectBus->clear();
|
||||
}
|
||||
|
||||
const std::bitset<config::numCCs>& Synth::getUsedCCs() const noexcept
|
||||
const BitArray<config::numCCs>& Synth::getUsedCCs() const noexcept
|
||||
{
|
||||
Impl& impl = *impl_;
|
||||
return impl.currentUsedCCs_;
|
||||
|
|
@ -1729,7 +1729,7 @@ void sfz::Synth::setBroadcastCallback(sfizz_receive_t* broadcast, void* 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.amplitudeEG.ccAttack);
|
||||
|
|
@ -1763,11 +1763,11 @@ void Synth::Impl::collectUsedCCsFromRegion(std::bitset<config::numCCs>& usedCCs,
|
|||
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 {
|
||||
public:
|
||||
explicit CCSourceCollector(std::bitset<config::numCCs>& used)
|
||||
explicit CCSourceCollector(BitArray<config::numCCs>& used)
|
||||
: used_(used)
|
||||
{
|
||||
}
|
||||
|
|
@ -1778,16 +1778,16 @@ void Synth::Impl::collectUsedCCsFromModulations(std::bitset<config::numCCs>& use
|
|||
used_.set(key.parameters().cc);
|
||||
return true;
|
||||
}
|
||||
std::bitset<config::numCCs>& used_;
|
||||
BitArray<config::numCCs>& used_;
|
||||
};
|
||||
|
||||
CCSourceCollector vtor(usedCCs);
|
||||
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_)
|
||||
collectUsedCCsFromRegion(used, *region);
|
||||
collectUsedCCsFromModulations(used, resources_.modMatrix);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <bitset>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
template <size_t> class BitArray;
|
||||
|
||||
namespace sfz {
|
||||
|
||||
|
|
@ -599,7 +600,7 @@ public:
|
|||
*
|
||||
* @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
|
||||
|
|
|
|||
|
|
@ -39,13 +39,8 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
|
|||
//----------------------------------------------------------------------
|
||||
|
||||
MATCH("/cc/slots", "") {
|
||||
uint8_t data[(config::numCCs + 7) / 8] = {};
|
||||
|
||||
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) };
|
||||
const BitArray<config::numCCs>& ccs = impl.currentUsedCCs_;
|
||||
sfizz_blob_t blob { ccs.data(), static_cast<uint32_t>(ccs.byte_size()) };
|
||||
client.receive<'b'>(delay, path, &blob);
|
||||
} break;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "modulations/sources/Controller.h"
|
||||
#include "modulations/sources/FlexEnvelope.h"
|
||||
#include "modulations/sources/LFO.h"
|
||||
#include "utility/BitArray.h"
|
||||
|
||||
namespace sfz {
|
||||
|
||||
|
|
@ -168,16 +169,16 @@ struct Synth::Impl final: public Parser::Listener {
|
|||
void finalizeSfzLoad();
|
||||
|
||||
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)
|
||||
usedCCs[mod.cc] = true;
|
||||
usedCCs.set(mod.cc);
|
||||
}
|
||||
|
||||
static void collectUsedCCsFromRegion(std::bitset<config::numCCs>& usedCCs, const Region& region);
|
||||
static void collectUsedCCsFromModulations(std::bitset<config::numCCs>& usedCCs, const ModMatrix& mm);
|
||||
static void collectUsedCCsFromRegion(BitArray<config::numCCs>& usedCCs, const Region& region);
|
||||
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);
|
||||
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_ { };
|
||||
|
||||
std::array<float, config::numCCs> defaultCCValues_;
|
||||
std::bitset<config::numCCs> currentUsedCCs_;
|
||||
BitArray<config::numCCs> currentUsedCCs_;
|
||||
|
||||
// Messaging
|
||||
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/SfzHelpers.h"
|
||||
#include "sfizz/utility/NumericId.h"
|
||||
#include "sfizz/utility/BitArray.h"
|
||||
#include "TestHelpers.h"
|
||||
#include <algorithm>
|
||||
#include "catch2/catch.hpp"
|
||||
|
|
@ -1087,18 +1088,18 @@ TEST_CASE("[Synth] Used CCs")
|
|||
<region> start_locc44=200 hikey=-1 sample=*sine
|
||||
)");
|
||||
auto usedCCs = synth.getUsedCCs();
|
||||
REQUIRE( usedCCs[1] );
|
||||
REQUIRE( usedCCs[2] );
|
||||
REQUIRE( !usedCCs[3] );
|
||||
REQUIRE( usedCCs[4] );
|
||||
REQUIRE( usedCCs[5] );
|
||||
REQUIRE( !usedCCs[6] );
|
||||
REQUIRE( usedCCs[42] );
|
||||
REQUIRE( usedCCs[44] );
|
||||
REQUIRE( usedCCs[56] );
|
||||
REQUIRE( usedCCs[67] );
|
||||
REQUIRE( usedCCs[98] );
|
||||
REQUIRE( !usedCCs[127] );
|
||||
REQUIRE( usedCCs.test(1) );
|
||||
REQUIRE( usedCCs.test(2) );
|
||||
REQUIRE( !usedCCs.test(3) );
|
||||
REQUIRE( usedCCs.test(4) );
|
||||
REQUIRE( usedCCs.test(5) );
|
||||
REQUIRE( !usedCCs.test(6) );
|
||||
REQUIRE( usedCCs.test(42) );
|
||||
REQUIRE( usedCCs.test(44) );
|
||||
REQUIRE( usedCCs.test(56) );
|
||||
REQUIRE( usedCCs.test(67) );
|
||||
REQUIRE( usedCCs.test(98) );
|
||||
REQUIRE( !usedCCs.test(127) );
|
||||
}
|
||||
|
||||
TEST_CASE("[Synth] Used CCs EGs")
|
||||
|
|
@ -1135,31 +1136,31 @@ TEST_CASE("[Synth] Used CCs EGs")
|
|||
sample=*sine
|
||||
)");
|
||||
auto usedCCs = synth.getUsedCCs();
|
||||
REQUIRE( usedCCs[1] );
|
||||
REQUIRE( usedCCs[2] );
|
||||
REQUIRE( usedCCs[3] );
|
||||
REQUIRE( usedCCs[4] );
|
||||
REQUIRE( usedCCs[5] );
|
||||
REQUIRE( usedCCs[6] );
|
||||
REQUIRE( usedCCs[7] );
|
||||
REQUIRE( usedCCs.test(1) );
|
||||
REQUIRE( usedCCs.test(2) );
|
||||
REQUIRE( usedCCs.test(3) );
|
||||
REQUIRE( usedCCs.test(4) );
|
||||
REQUIRE( usedCCs.test(5) );
|
||||
REQUIRE( usedCCs.test(6) );
|
||||
REQUIRE( usedCCs.test(7) );
|
||||
// FIXME: enable when supported
|
||||
// REQUIRE( !usedCCs[8] );
|
||||
// REQUIRE( usedCCs[11] );
|
||||
// REQUIRE( usedCCs[12] );
|
||||
// REQUIRE( usedCCs[13] );
|
||||
// REQUIRE( usedCCs[14] );
|
||||
// REQUIRE( usedCCs[15] );
|
||||
// REQUIRE( usedCCs[16] );
|
||||
// REQUIRE( usedCCs[17] );
|
||||
// REQUIRE( !usedCCs[18] );
|
||||
// REQUIRE( usedCCs[21] );
|
||||
// REQUIRE( usedCCs[22] );
|
||||
// REQUIRE( usedCCs[23] );
|
||||
// REQUIRE( usedCCs[24] );
|
||||
// REQUIRE( usedCCs[25] );
|
||||
// REQUIRE( usedCCs[26] );
|
||||
// REQUIRE( usedCCs[27] );
|
||||
// REQUIRE( !usedCCs[28] );
|
||||
// REQUIRE( !usedCCs.test(8) );
|
||||
// REQUIRE( usedCCs.test(11) );
|
||||
// REQUIRE( usedCCs.test(12) );
|
||||
// REQUIRE( usedCCs.test(13) );
|
||||
// REQUIRE( usedCCs.test(14) );
|
||||
// REQUIRE( usedCCs.test(15) );
|
||||
// REQUIRE( usedCCs.test(16) );
|
||||
// REQUIRE( usedCCs.test(17) );
|
||||
// REQUIRE( !usedCCs.test(18) );
|
||||
// REQUIRE( usedCCs.test(21) );
|
||||
// REQUIRE( usedCCs.test(22) );
|
||||
// REQUIRE( usedCCs.test(23) );
|
||||
// REQUIRE( usedCCs.test(24) );
|
||||
// REQUIRE( usedCCs.test(25) );
|
||||
// REQUIRE( usedCCs.test(26) );
|
||||
// REQUIRE( usedCCs.test(27) );
|
||||
// REQUIRE( !usedCCs.test(28) );
|
||||
}
|
||||
|
||||
TEST_CASE("[Synth] Activate also on the sustain CC")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue