diff --git a/plugins/editor/layout/main.fl b/plugins/editor/layout/main.fl index 588466f1..86ef24ca 100644 --- a/plugins/editor/layout/main.fl +++ b/plugins/editor/layout/main.fl @@ -60,9 +60,9 @@ widget_class mainView {open xywh {195 11 250 31} labelsize 20 align 20 class ClickableLabel } - Fl_Box {} { - label {Key switch:} - xywh {195 44 250 30} labelsize 20 align 20 + Fl_Box keyswitchLabel_ { + label {Key switch:} selected + xywh {195 44 360 30} labelsize 20 align 20 class Label } Fl_Box {} { @@ -221,7 +221,7 @@ widget_class mainView {open class LogicalGroup } { Fl_Group {} { - label Engine open selected + label Engine open xywh {305 135 195 100} box ROUNDED_BOX labelsize 12 align 17 class TitleGroup } { diff --git a/plugins/editor/src/editor/Editor.cpp b/plugins/editor/src/editor/Editor.cpp index 72013cca..9a9c77cb 100644 --- a/plugins/editor/src/editor/Editor.cpp +++ b/plugins/editor/src/editor/Editor.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,10 @@ struct Editor::Impl : EditorController::Receiver, IControlListener { std::string userFilesDir_; std::string fallbackFilesDir_; + int currentKeyswitch_ = -1; + std::unordered_map keyswitchNames_; + std::string keyswitchLabelPrefix_; + SharedPointer memQueryTimer_; enum { @@ -102,6 +107,7 @@ struct Editor::Impl : EditorController::Receiver, IControlListener { CTextLabel* tuningFrequencyLabel_ = nullptr; CControl *stretchedTuningSlider_ = nullptr; CTextLabel* stretchedTuningLabel_ = nullptr; + CTextLabel* keyswitchLabel_ = nullptr; STitleContainer* userFilesGroup_ = nullptr; STextButton* userFilesDirButton_ = nullptr; @@ -170,12 +176,17 @@ struct Editor::Impl : EditorController::Receiver, IControlListener { void updateTuningFrequencyLabel(float tuningFrequency); void updateStretchedTuningLabel(float stretchedTuning); + absl::string_view getCurrentKeyswitchName() const; + void updateKeyswitchNameLabel(); + 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); void updateCCLabel(unsigned cc, const char* label); + void updateSWLastCurrent(int sw); + void updateSWLastLabel(unsigned sw, const char* label); void updateMemoryUsed(uint64_t mem); // edition of CC by UI @@ -237,7 +248,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("/sw/last/slots", "", nullptr); impl.sendQueuedOSC("/cc/slots", "", nullptr); } @@ -268,7 +279,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("/sw/last/slots", "", nullptr); sendQueuedOSC("/cc/slots", "", nullptr); } break; @@ -430,13 +441,19 @@ 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")) { + 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; @@ -476,6 +493,12 @@ void Editor::Impl::uiReceiveMessage(const char* path, const char* sig, const sfi else if (Messages::matchOSC("/cc&/label", path, indices) && !strcmp(sig, "s")) { updateCCLabel(indices[0], args[0].s); } + else if (Messages::matchOSC("/sw/last/current", path, indices) && !strcmp(sig, "i")) { + updateSWLastCurrent(args[0].i); + } + else if (Messages::matchOSC("/sw/last/&/label", path, indices) && !strcmp(sig, "s")) { + updateSWLastLabel(indices[0], args[0].s); + } else if (Messages::matchOSC("/mem/buffers", path, indices) && !strcmp(sig, "h")) { updateMemoryUsed(args[0].h); } @@ -808,6 +831,10 @@ void Editor::Impl::createFrameContents() mainView_ = owned(mainView); } + /// + if (keyswitchLabel_) + keyswitchLabelPrefix_ = std::string(keyswitchLabel_->getText()) + ' '; + /// SharedPointer fileDropTarget = owned(new SFileDropTarget); @@ -1033,7 +1060,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("/sw/last/slots", "", nullptr); sendQueuedOSC("/cc/slots", "", nullptr); } @@ -1347,6 +1374,27 @@ void Editor::Impl::updateStretchedTuningLabel(float stretchedTuning) label->setText(text); } +absl::string_view Editor::Impl::getCurrentKeyswitchName() const +{ + int sw = currentKeyswitch_; + if (sw == -1) + return {}; + + auto it = keyswitchNames_.find(static_cast(sw)); + if (it == keyswitchNames_.end()) + return {}; + + return it->second; +} + +void Editor::Impl::updateKeyswitchNameLabel() +{ + if (CTextLabel* label = keyswitchLabel_) { + std::string name { getCurrentKeyswitchName() }; + label->setText((keyswitchLabelPrefix_ + name).c_str()); + } +} + void Editor::Impl::updateKeyUsed(unsigned key, bool used) { if (SPiano* piano = piano_) @@ -1383,6 +1431,21 @@ void Editor::Impl::updateCCLabel(unsigned cc, const char* label) panel->setControlLabelText(cc, label); } +void Editor::Impl::updateSWLastCurrent(int sw) +{ + if (currentKeyswitch_ == sw) + return; + currentKeyswitch_ = sw; + updateKeyswitchNameLabel(); +} + +void Editor::Impl::updateSWLastLabel(unsigned sw, const char* label) +{ + keyswitchNames_[sw].assign(label); + if ((unsigned)currentKeyswitch_ == sw) + updateKeyswitchNameLabel(); +} + void Editor::Impl::updateMemoryUsed(uint64_t mem) { if (CTextLabel* label = memoryLabel_) { diff --git a/plugins/editor/src/editor/layout/main.hpp b/plugins/editor/src/editor/layout/main.hpp index e53cdeff..3a124a18 100644 --- a/plugins/editor/src/editor/layout/main.hpp +++ b/plugins/editor/src/editor/layout/main.hpp @@ -25,7 +25,8 @@ view__8->addView(view__10); auto* const view__11 = createClickableLabel(CRect(10, 6, 260, 37), kTagLoadSfzFile, "DefaultInstrument.sfz", kLeftText, 20); sfzFileLabel_ = view__11; view__8->addView(view__11); -auto* const view__12 = createLabel(CRect(10, 39, 260, 69), -1, "Key switch:", kLeftText, 20); +auto* const view__12 = createLabel(CRect(10, 39, 370, 69), -1, "Key switch:", kLeftText, 20); +keyswitchLabel_ = view__12; view__8->addView(view__12); auto* const view__13 = createLabel(CRect(10, 71, 70, 96), -1, "Voices:", kRightText, 12); view__8->addView(view__13); diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 5edb5e1e..f292875d 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -241,6 +241,7 @@ void Synth::Impl::clear() numGroups_ = 0; numMasters_ = 0; currentSwitch_ = absl::nullopt; + currentSwitchChanged_ = true; defaultPath_ = ""; resources_.midiState.reset(); resources_.filePool.clear(); @@ -250,8 +251,8 @@ void Synth::Impl::clear() changedCCsThisCycle_.clear(); keyLabels_.clear(); keySlots_.clear(); - swSlots_.clear(); - keyswitchLabels_.clear(); + swLastSlots_.clear(); + clearKeyswitchLabels(); globalOpcodes_.clear(); masterOpcodes_.clear(); groupOpcodes_.clear(); @@ -623,7 +624,7 @@ void Synth::Impl::finalizeSfzLoad() region->keySwitched = (*currentSwitch_ == *region->lastKeyswitch); if (region->keyswitchLabel) - insertPairUniquely(keyswitchLabels_, *region->lastKeyswitch, *region->keyswitchLabel); + setKeyswitchLabel(*region->lastKeyswitch, *region->keyswitchLabel); } if (region->lastKeyswitchRange) { @@ -633,7 +634,7 @@ void Synth::Impl::finalizeSfzLoad() if (region->keyswitchLabel) { for (uint8_t note = range.getStart(), end = range.getEnd(); note <= end; note++) - insertPairUniquely(keyswitchLabels_, note, *region->keyswitchLabel); + setKeyswitchLabel(note, *region->keyswitchLabel); } } @@ -749,15 +750,17 @@ void Synth::Impl::finalizeSfzLoad() // cache the set of keyswitches assigned for (const RegionPtr& regionPtr : regions_) { if (absl::optional sw = regionPtr->lastKeyswitch) { - swSlots_.set(*sw); + swLastSlots_.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); + swLastSlots_.set(key); } } + // resend current keyswitch + currentSwitchChanged_ = true; } bool Synth::loadScalaFile(const fs::path& path) @@ -985,6 +988,16 @@ void Synth::renderBlock(AudioSpan buffer) noexcept broadcaster.receive<'b'>(numFrames - 1, "/cc/changed", &blob); } 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'>(numFrames - 1, "/sw/last/current", value); + } + impl.currentSwitchChanged_ = false; + } { // Clear events and advance midi time ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; @@ -1081,6 +1094,7 @@ void Synth::Impl::noteOnDispatch(int delay, int noteNumber, float velocity) noex region->keySwitched = false; } currentSwitch_ = noteNumber; + currentSwitchChanged_ = true; } for (auto& region : lastKeyswitchLists_[noteNumber]) @@ -1869,12 +1883,36 @@ void Synth::Impl::setCCLabel(int ccNumber, std::string name) } } +const std::string* Synth::Impl::getKeyswitchLabel(int swNumber) +{ + auto it = keyswitchLabelsMap_.find(swNumber); + return (it == keyswitchLabelsMap_.end()) ? nullptr : &keyswitchLabels_[it->second].second; +} + +void Synth::Impl::setKeyswitchLabel(int swNumber, std::string name) +{ + auto it = keyswitchLabelsMap_.find(swNumber); + if (it != keyswitchLabelsMap_.end()) + keyswitchLabels_[it->second].second = std::move(name); + else { + size_t index = keyswitchLabels_.size(); + keyswitchLabels_.emplace_back(swNumber, std::move(name)); + keyswitchLabelsMap_[swNumber] = index; + } +} + void Synth::Impl::clearCCLabels() { ccLabels_.clear(); ccLabelsMap_.clear(); } +void Synth::Impl::clearKeyswitchLabels() +{ + keyswitchLabels_.clear(); + keyswitchLabelsMap_.clear(); +} + Parser& Synth::getParser() noexcept { Impl& impl = *impl_; diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index f8451d4f..67407e3c 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -46,12 +46,26 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co //---------------------------------------------------------------------- - MATCH("/sw/slots", "") { - const BitArray<128>& switches = impl.swSlots_; + MATCH("/sw/last/slots", "") { + const BitArray<128>& switches = impl.swLastSlots_; sfizz_blob_t blob { switches.data(), static_cast(switches.byte_size()) }; client.receive<'b'>(delay, path, &blob); } break; + MATCH("/sw/last/current", "") { + int32_t value = -1; + if (impl.currentSwitch_) + value = *impl.currentSwitch_; + client.receive<'i'>(delay, path, value); + } break; + + MATCH("/sw/last/&/label", "") { + if (indices[0] >= 128) + break; + const std::string* label = impl.getKeyswitchLabel(indices[0]); + client.receive<'s'>(delay, path, label ? label->c_str() : ""); + } break; + //---------------------------------------------------------------------- MATCH("/cc/slots", "") { diff --git a/src/sfizz/SynthPrivate.h b/src/sfizz/SynthPrivate.h index 5c8b4479..bb69fdc7 100644 --- a/src/sfizz/SynthPrivate.h +++ b/src/sfizz/SynthPrivate.h @@ -184,6 +184,9 @@ struct Synth::Impl final: public Parser::Listener { const std::string* getCCLabel(int ccNumber); void setCCLabel(int ccNumber, std::string name); void clearCCLabels(); + const std::string* getKeyswitchLabel(int swNumber); + void setKeyswitchLabel(int swNumber, std::string name); + void clearKeyswitchLabels(); /** * @brief Perform a CC event @@ -217,11 +220,13 @@ struct Synth::Impl final: public Parser::Listener { std::map ccLabelsMap_; std::vector keyLabels_; BitArray<128> keySlots_; - BitArray<128> swSlots_; + BitArray<128> swLastSlots_; std::vector keyswitchLabels_; + std::map keyswitchLabelsMap_; // Set as sw_default if present in the file absl::optional currentSwitch_; + bool currentSwitchChanged_ = true; std::vector unknownOpcodes_; using RegionViewVector = std::vector; using VoiceViewVector = std::vector;