diff --git a/plugins/editor/src/editor/Editor.cpp b/plugins/editor/src/editor/Editor.cpp index 83507bd68..64f5d91ba 100644 --- a/plugins/editor/src/editor/Editor.cpp +++ b/plugins/editor/src/editor/Editor.cpp @@ -165,6 +165,7 @@ struct Editor::Impl : EditorController::Receiver, IControlListener { void updateStretchedTuningLabel(float stretchedTuning); void updateKeyUsed(unsigned key, bool used); + void updateKeyswitchUsed(unsigned key, bool used); void updateCCUsed(unsigned cc, bool used); void updateCCValue(unsigned cc, float value); void updateCCDefaultValue(unsigned cc, float value); @@ -223,6 +224,7 @@ void Editor::open(CFrame& frame) // request the whole Key and CC information impl.sendQueuedOSC("/key/slots", "", nullptr); + impl.sendQueuedOSC("/sw/slots", "", nullptr); impl.sendQueuedOSC("/cc/slots", "", nullptr); } @@ -249,6 +251,7 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v) // request the whole Key and CC information sendQueuedOSC("/key/slots", "", nullptr); + sendQueuedOSC("/sw/slots", "", nullptr); sendQueuedOSC("/cc/slots", "", nullptr); } break; @@ -410,6 +413,14 @@ void Editor::Impl::uiReceiveMessage(const char* path, const char* sig, const sfi updateKeyUsed(key, used); } } + else if (Messages::matchOSC("/sw/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); + } + } 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 }; @@ -983,6 +994,7 @@ void Editor::Impl::changeSfzFile(const std::string& filePath) // request the whole Key and CC information sendQueuedOSC("/key/slots", "", nullptr); + sendQueuedOSC("/sw/slots", "", nullptr); sendQueuedOSC("/cc/slots", "", nullptr); } @@ -1294,6 +1306,12 @@ void Editor::Impl::updateKeyUsed(unsigned key, bool used) piano->setKeyUsed(key, used); } +void Editor::Impl::updateKeyswitchUsed(unsigned key, bool used) +{ + if (SPiano* piano = piano_) + piano->setKeyswitchUsed(key, used); +} + void Editor::Impl::updateCCUsed(unsigned cc, bool used) { if (SControlsPanel* panel = controlsPanel_) diff --git a/plugins/editor/src/editor/GUIPiano.cpp b/plugins/editor/src/editor/GUIPiano.cpp index 6dc990f18..0aa16586d 100644 --- a/plugins/editor/src/editor/GUIPiano.cpp +++ b/plugins/editor/src/editor/GUIPiano.cpp @@ -50,6 +50,18 @@ void SPiano::setKeyUsed(unsigned key, bool used) invalid(); } +void SPiano::setKeyswitchUsed(unsigned key, bool used) +{ + if (key >= 128) + return; + + if (keyswitchUsed_.test(key) == used) + return; + + keyswitchUsed_.set(key, used); + invalid(); +} + void SPiano::setKeyValue(unsigned key, float value) { if (key >= 128) @@ -64,6 +76,19 @@ void SPiano::setKeyValue(unsigned key, float value) invalid(); } +SPiano::KeyRole SPiano::getKeyRole(unsigned key) +{ + if (key >= 128) + return KeyRole::Unused; + + if (keyUsed_.test(key)) + return KeyRole::Note; + if (keyswitchUsed_.test(key)) + return KeyRole::Switch; + + return KeyRole::Unused; +} + void SPiano::draw(CDrawContext* dc) { const Dimensions dim = getDimensions(false); @@ -85,12 +110,24 @@ void SPiano::draw(CDrawContext* dc) if (!black[key % 12]) { CRect rect = keyRect(key); - SColorHCY hcy(keyUsedHue_, 1.0, whiteKeyLuma_); - if (!keyUsed_[key] || allKeysUsed) { + SColorHCY hcy(0.0, 1.0, whiteKeyLuma_); + + switch (getKeyRole(key)) { + case KeyRole::Note: + if (allKeysUsed) + goto whiteKeyDefault; + hcy.h = keyUsedHue_; + break; + case KeyRole::Switch: + hcy.h = keySwitchHue_; + break; + default: whiteKeyDefault: hcy.y = 1.0; if (keyval_[key]) hcy.c = 0.0; + break; } + if (keyval_[key]) hcy.y = std::max(0.0f, hcy.y - keyLumaPressDelta_); @@ -113,9 +150,22 @@ void SPiano::draw(CDrawContext* dc) if (black[key % 12]) { CRect rect = keyRect(key); - SColorHCY hcy(keyUsedHue_, 1.0, blackKeyLuma_); - if (!keyUsed_[key] || allKeysUsed) + SColorHCY hcy(0.0, 1.0, blackKeyLuma_); + + switch (getKeyRole(key)) { + case KeyRole::Note: + if (allKeysUsed) + goto blackKeyDefault; + hcy.h = keyUsedHue_; + break; + case KeyRole::Switch: + hcy.h = keySwitchHue_; + break; + default: blackKeyDefault: hcy.c = 0.0; + break; + } + if (keyval_[key]) hcy.y = std::max(0.0f, hcy.y - keyLumaPressDelta_); diff --git a/plugins/editor/src/editor/GUIPiano.h b/plugins/editor/src/editor/GUIPiano.h index eafe115cd..b01603c09 100644 --- a/plugins/editor/src/editor/GUIPiano.h +++ b/plugins/editor/src/editor/GUIPiano.h @@ -26,8 +26,17 @@ class SPiano : public CView { void setNumOctaves(unsigned octs); void setKeyUsed(unsigned key, bool used); + void setKeyswitchUsed(unsigned key, bool used); void setKeyValue(unsigned key, float value); + enum class KeyRole { + Unused, + Note, + Switch, + }; + + KeyRole getKeyRole(unsigned key); + std::function onKeyPressed; std::function onKeyReleased; @@ -57,6 +66,7 @@ class SPiano : public CView { unsigned octs_ {}; std::vector keyval_; std::bitset<128> keyUsed_; + std::bitset<128> keyswitchUsed_; unsigned mousePressedKey_ = ~0u; CCoord innerPaddingX_ = 4.0; @@ -67,6 +77,7 @@ class SPiano : public CView { float backgroundRadius_ = 5.0; float keyUsedHue_ = 0.55; + float keySwitchHue_ = 0.0; float whiteKeyLuma_ = 0.9; float blackKeyLuma_ = 0.5; float keyLumaPressDelta_ = 0.2; diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index c2f733197..48295c404 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -250,6 +250,7 @@ void Synth::Impl::clear() changedCCsThisCycle_.clear(); keyLabels_.clear(); keySlots_.clear(); + swSlots_.clear(); keyswitchLabels_.clear(); globalOpcodes_.clear(); masterOpcodes_.clear(); @@ -745,6 +746,18 @@ void Synth::Impl::finalizeSfzLoad() for (unsigned key = loKey; key <= hiKey; ++key) keySlots_.set(key); } + // cache the set of keyswitches assigned + for (const RegionPtr& regionPtr : regions_) { + if (absl::optional sw = regionPtr->lastKeyswitch) { + swSlots_.set(*sw); + } + else if (absl::optional> swRange = regionPtr->lastKeyswitchRange) { + unsigned loKey = swRange->getStart(); + unsigned hiKey = swRange->getEnd(); + for (unsigned key = loKey; key <= hiKey; ++key) + swSlots_.set(key); + } + } } bool Synth::loadScalaFile(const fs::path& path) diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index f8672cca4..c441807ea 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -46,6 +46,14 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co //---------------------------------------------------------------------- + MATCH("/sw/slots", "") { + const BitArray<128>& switches = impl.swSlots_; + sfizz_blob_t blob { switches.data(), static_cast(switches.byte_size()) }; + client.receive<'b'>(delay, path, &blob); + } break; + + //---------------------------------------------------------------------- + MATCH("/cc/slots", "") { const BitArray& ccs = impl.currentUsedCCs_; sfizz_blob_t blob { ccs.data(), static_cast(ccs.byte_size()) }; diff --git a/src/sfizz/SynthPrivate.h b/src/sfizz/SynthPrivate.h index aaf6f72ab..5c8b4479b 100644 --- a/src/sfizz/SynthPrivate.h +++ b/src/sfizz/SynthPrivate.h @@ -217,6 +217,7 @@ struct Synth::Impl final: public Parser::Listener { std::map ccLabelsMap_; std::vector keyLabels_; BitArray<128> keySlots_; + BitArray<128> swSlots_; std::vector keyswitchLabels_; // Set as sw_default if present in the file