Skip to content

Commit

Permalink
[layer titles] This is #292, give the adaptation the possibility to d…
Browse files Browse the repository at this point in the history
…efine custom layer titles
  • Loading branch information
christofmuc committed Jan 21, 2024
1 parent 6230eb9 commit 7c8c78d
Show file tree
Hide file tree
Showing 11 changed files with 61 additions and 3 deletions.
2 changes: 1 addition & 1 deletion MidiKraft
7 changes: 6 additions & 1 deletion The-Orm/CurrentPatchDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,13 @@ void CurrentPatchDisplay::setupPatchProperties(std::shared_ptr<midikraft::PatchH
// Check if the patch is a layered patch
auto layers = midikraft::Capability::hasCapability<midikraft::LayeredPatchCapability>(patch->patch());
if (layers) {
auto titles = layers->layerTitles();
for (int i = 0; i < layers->numberOfLayers(); i++) {
TypedNamedValue v("Layer " + String(i), "Patch name", String(layers->layerName(i)), 20);
String title = "Layer " + String(i);
if (i < (int) titles.size()) {
title = titles[i];
}
TypedNamedValue v(title, "Patch name", String(layers->layerName(i)), 20);
metaDataValues_.push_back(std::make_shared<TypedNamedValue>(v));
}
}
Expand Down
11 changes: 11 additions & 0 deletions adaptations/Adaptation Programming Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,17 @@ Optionally, if you want to allow the user to change a layer name, implement the

This work like renamePatch, just with the added layerNo parameter.

Optionally, you can implement this to return a list of names for the layers itself, or rather the layer titles. Without it, the
UI will just display Layer 0, Layer 1, Layer 2, ...

def friendlyLayerTitles():

To make it work, just return a list of strings like this:

def friendlyLayerTitles():
return ["Patch", "Upper tone", "Lower tone"]


## Importing categories stored in the patch in the synth

Some synths store sound categories or tags. As the KnobKraft Orm has these as a central part of the UI, of course we want to be able to import them as well. It is easy enough and requires two steps: Implementing a function and then defining an import mapping.
Expand Down
1 change: 1 addition & 0 deletions adaptations/GenericAdaptation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ namespace knobkraft {
* kExtractPatchesFromBank = "extractPatchesFromBank",
* kExtractPatchesFromAllBankMessages = "extractPatchesFromAllBankMessages",
* kNumberOfLayers = "numberOfLayers",
* kLayerTitles = "friendlyLayerTitles",
* kLayerName = "layerName",
* kSetLayerName = "setLayerName",
* kGeneralMessageDelay = "generalMessageDelay",
Expand Down
1 change: 1 addition & 0 deletions adaptations/GenericAdaptation.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ namespace knobkraft {
*kIsSingleProgramDump, *kIsPartOfSingleProgramDump, *kCreateProgramDumpRequest, *kConvertToProgramDump, *kNumberFromDump,
*kCreateBankDumpRequest, *kIsPartOfBankDump, *kIsBankDumpFinished, *kExtractPatchesFromBank, *kExtractPatchesFromAllBankMessages,
*kNumberOfLayers,
*kLayerTitles,
*kLayerName,
*kSetLayerName,
*kGetStoredTags
Expand Down
28 changes: 27 additions & 1 deletion adaptations/GenericPatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,37 @@ namespace knobkraft {
return 1;
}

std::vector<std::string> GenericLayeredPatchCapability::layerTitles() const {
py::gil_scoped_acquire acquire;
if (!me_.expired()) {
auto patch = me_.lock();
if (patch->pythonModuleHasFunction(kLayerTitles)) {
try {
py::object result = patch->callMethod(kLayerTitles);
return py::cast<std::vector<std::string>>(result);
}
catch (py::error_already_set& ex) {
if (!me_.expired())
me_.lock()->logAdaptationError(kLayerTitles, ex);
ex.restore();
}
catch (std::exception& ex) {
if (!me_.expired())
me_.lock()->logAdaptationError(kLayerTitles, ex);
}
catch (...) {
spdlog::error("Uncaught exception in {} of Patch of GenericAdaptation", kLayerTitles);
}
}
}
return {};
}

std::string GenericLayeredPatchCapability::layerName(int layerNo) const
{
py::gil_scoped_acquire acquire;
if (!me_.expired()) {
auto patch = me_.lock();
auto patch = me_.lock();
try {
std::vector<int> v(me_.lock()->data().data(), me_.lock()->data().data() + me_.lock()->data().size());
py::object result = patch->callMethod(kLayerName, v, layerNo);
Expand Down
1 change: 1 addition & 0 deletions adaptations/GenericPatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ namespace knobkraft {
virtual ~GenericLayeredPatchCapability() = default;
virtual LayerMode layerMode() const override;
virtual int numberOfLayers() const override;
virtual std::vector<std::string> layerTitles() const override;
virtual std::string layerName(int layerNo) const override;
virtual void setLayerName(int layerNo, std::string const& layerName) override;

Expand Down
4 changes: 4 additions & 0 deletions adaptations/Roland_MKS70V4.py
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,10 @@ def numberOfLayers(messages):
return 3


def friendlyLayerTitles():
return ["Patch", "Upper tone", "Lower tone"]


def layerName(messages, layerNo):
index = knobkraft.sysex.findSysexDelimiters(messages)
if isEditBufferDump2(messages):
Expand Down
4 changes: 4 additions & 0 deletions adaptations/sequential/GenericSequential.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ def renamePatch(self, message, new_name):
def numberOfLayers(self, messages):
return self.number_of_layers

def friendlyLayerTitles(self):
return ["Layer A", "Layer B"]

def layerName(self, messages, layerNo):
dataBlock = self.getDataBlock(messages)
if len(dataBlock) > 0:
Expand Down Expand Up @@ -286,5 +289,6 @@ def install(self, module):
setattr(module, 'friendlyProgramName', self.friendly_program_name)
if self.number_of_layers is not None:
setattr(module, 'numberOfLayers', self.numberOfLayers)
setattr(module, 'friendlyLayerTitles', self.friendlyLayerTitles)
setattr(module, 'layerName', self.layerName)
setattr(module, 'setLayerName', self.setLayerName)
4 changes: 4 additions & 0 deletions synths/sequential-rev2/Rev2Patch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,10 @@ namespace midikraft {
return 2;
}

std::vector<std::string> Rev2Patch::layerTitles() const {
return {"Layer A", "Layer B"};
}

std::string Rev2Patch::layerName(int layerNo) const
{
// The Rev2 has a 20 character patch name storage for each of the 2 layers...
Expand Down
1 change: 1 addition & 0 deletions synths/sequential-rev2/Rev2Patch.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace midikraft {
// Interface for layered Patches (for the Librarian)
virtual LayerMode layerMode() const override;
virtual int numberOfLayers() const override;
virtual std::vector<std::string> layerTitles() const override;
virtual std::string layerName(int layerNo) const override;
virtual void setLayerName(int layerNo, std::string const &layerName) override;

Expand Down

0 comments on commit 7c8c78d

Please sign in to comment.