diff --git a/CHANGELOG.md b/CHANGELOG.md index 89ab15b6..f6a4885e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ CHANGELOG Next Release ----- +* Core: add attributes and send type to send params +* DPF: add "read only" outputParameter type based on send params * JS Bugfix: printHook and sendHook for AudioWorklet; mention emsdk limitations in docs * Bugfix: stripnote missing right inlet diff --git a/hvcc/core/hv2ir/HIrSend.py b/hvcc/core/hv2ir/HIrSend.py index 71449fc7..a71b63a6 100644 --- a/hvcc/core/hv2ir/HIrSend.py +++ b/hvcc/core/hv2ir/HIrSend.py @@ -46,8 +46,10 @@ def get_ir_control_list(self) -> List: on_message_list = [x for o in receive_objs for x in o.get_ir_on_message(inlet_index=0)] return [{ "id": self.id, + "type": "send", "onMessage": [on_message_list], "extern": self.args["extern"], + "attributes": self.args["attributes"], "hash": self.args["hash"], "display": self.args["name"], "name": ((f"_{self.args['name']}") if re.match(r"\d", self.args["name"]) else self.args["name"]) diff --git a/hvcc/generators/c2dpf/c2dpf.py b/hvcc/generators/c2dpf/c2dpf.py index 2041d21d..d5ffb195 100644 --- a/hvcc/generators/c2dpf/c2dpf.py +++ b/hvcc/generators/c2dpf/c2dpf.py @@ -45,6 +45,7 @@ def compile( out_dir = os.path.join(out_dir, "plugin") receiver_list = externs['parameters']['in'] + sender_list = externs["parameters"]["out"] if patch_meta: patch_name = patch_meta.get("name", patch_name) @@ -87,6 +88,7 @@ def compile( num_input_channels=num_input_channels, num_output_channels=num_output_channels, receivers=receiver_list, + senders=sender_list, copyright=copyright_c)) dpf_cpp_path = os.path.join(source_dir, f"HeavyDPF_{patch_name}.cpp") with open(dpf_cpp_path, "w") as f: @@ -97,6 +99,7 @@ def compile( num_input_channels=num_input_channels, num_output_channels=num_output_channels, receivers=receiver_list, + senders=sender_list, pool_sizes_kb=externs["memoryPoolSizesKb"], copyright=copyright_c)) if dpf_meta.get("enable_ui"): @@ -106,9 +109,8 @@ def compile( name=patch_name, meta=dpf_meta, class_name=f"HeavyDPF_{patch_name}", - num_input_channels=num_input_channels, - num_output_channels=num_output_channels, receivers=receiver_list, + senders=sender_list, copyright=copyright_c)) dpf_h_path = os.path.join(source_dir, "DistrhoPluginInfo.h") with open(dpf_h_path, "w") as f: @@ -118,7 +120,6 @@ def compile( class_name=f"HeavyDPF_{patch_name}", num_input_channels=num_input_channels, num_output_channels=num_output_channels, - receivers=receiver_list, pool_sizes_kb=externs["memoryPoolSizesKb"], copyright=copyright_c)) diff --git a/hvcc/generators/c2dpf/templates/HeavyDPF.cpp b/hvcc/generators/c2dpf/templates/HeavyDPF.cpp index a942d9b8..d7f69a7d 100644 --- a/hvcc/generators/c2dpf/templates/HeavyDPF.cpp +++ b/hvcc/generators/c2dpf/templates/HeavyDPF.cpp @@ -5,7 +5,7 @@ #include -#define HV_LV2_NUM_PARAMETERS {{receivers|length}} +#define HV_DPF_NUM_PARAMETER {{receivers|length + senders|length}} #define HV_HASH_NOTEIN 0x67E37CA3 #define HV_HASH_CTLIN 0x41BE0f9C @@ -55,6 +55,7 @@ static void hvSendHookFunc(HeavyContextInterface *c, const char *sendName, uint3 {{class_name}}* plugin = ({{class_name}}*)c->getUserData(); if (plugin != nullptr) { + plugin->setOutputParameter(sendHash, m); {%- if meta.midi_output is defined and meta.midi_output == 1 %} #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT plugin->handleMidiSend(sendHash, m); @@ -78,9 +79,9 @@ static void hvPrintHookFunc(HeavyContextInterface *c, const char *printLabel, co // Main DPF plugin class {{class_name}}::{{class_name}}() - : Plugin(HV_LV2_NUM_PARAMETERS, 0, 0) + : Plugin(HV_DPF_NUM_PARAMETER, 0, 0) { - {% for k, v in receivers -%} + {% for k, v in receivers + senders -%} _parameters[{{loop.index-1}}] = {{v.attributes.default}}f; {% endfor %} @@ -91,7 +92,7 @@ static void hvPrintHookFunc(HeavyContextInterface *c, const char *printLabel, co {% if receivers|length > 0 %} // ensure that the new context has the current parameters - for (int i = 0; i < HV_LV2_NUM_PARAMETERS; ++i) { + for (int i = 0; i < HV_DPF_NUM_PARAMETER; ++i) { setParameterValue(i, _parameters[i]); } {%- endif %} @@ -102,62 +103,18 @@ static void hvPrintHookFunc(HeavyContextInterface *c, const char *printLabel, co } {%- if meta.port_groups is defined %} -{% include 'HeavyDPF_PortGroups.cpp' %} +{% include 'portGroups.cpp' %} {%- endif %} void {{class_name}}::initParameter(uint32_t index, Parameter& parameter) { - {%- if receivers|length > 0 -%} + {%- if (receivers|length > 0) or (senders|length > 0) -%} // initialise parameters with defaults switch (index) { - {% for k, v in receivers -%} - case param{{v.display}}: - parameter.name = "{{v.display.replace('_', ' ')}}"; - parameter.symbol = "{{v.display|lower}}"; - {%- if v.attributes.type == 'db': %} - parameter.unit = "dB"; - {%- elif v.attributes.type in ['hz', 'log_hz']: %} - parameter.unit = "Hz"; - {%- endif %} - parameter.hints = kParameterIsAutomatable - {%- if v.attributes.type == 'bool': %} - | kParameterIsBoolean - {%- elif v.attributes.type == 'trig': -%} - | kParameterIsTrigger - {%- elif v.attributes.type == 'int': -%} - | kParameterIsInteger - {%- elif v.attributes.type in ['log', 'log_hz']: -%} - | kParameterIsLogarithmic - {%- endif %}; - parameter.ranges.min = {{v.attributes.min}}f; - parameter.ranges.max = {{v.attributes.max}}f; - parameter.ranges.def = {{v.attributes.default}}f; - {%- if v.attributes.type == 'db' and not (meta.enumerators is defined and meta.enumerators[v.display] is defined): %} - { - ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[1]; - enumValues[0].value = {{v.attributes.min}}f; - enumValues[0].label = "-inf"; - parameter.enumValues.count = 1; - parameter.enumValues.values = enumValues; - } - {%- endif %} - {%- if meta.enumerators is defined and meta.enumerators[v.display] is defined %} - {% set enums = meta.enumerators[v.display] %} - {% set enumlen = enums|length %} - if (ParameterEnumerationValue *values = new ParameterEnumerationValue[{{enumlen}}]) - { - parameter.enumValues.restrictedMode = true; - {% for i in enums -%} - values[{{loop.index - 1}}].value = {{loop.index - 1}}.0f; - values[{{loop.index - 1}}].label = "{{i}}"; - {% endfor -%} - parameter.enumValues.count = {{enumlen}}; - parameter.enumValues.values = values; - } - {%- endif %} - break; - {% endfor %} + {% for k, v in receivers + senders %} +{% include 'initParameter.cpp' %} + {% endfor -%} } {% endif %} } @@ -167,7 +124,7 @@ void {{class_name}}::initParameter(uint32_t index, Parameter& parameter) float {{class_name}}::getParameterValue(uint32_t index) const { - {%- if receivers|length > 0 %} + {%- if (receivers|length > 0) or (senders|length > 0) %} return _parameters[index]; {% else %} return 0.0f; @@ -194,6 +151,19 @@ void {{class_name}}::setParameterValue(uint32_t index, float value) {%- endif %} } +void {{class_name}}::setOutputParameter(uint32_t sendHash, const HvMessage *m) +{ + {%- if senders|length > 0 %} + switch (sendHash) { + {% for k, v in senders -%} + case {{v.hash}}: // {{v.display}} + _parameters[param{{v.display}}] = hv_msg_getFloat(m, 0); + break; + {% endfor %} + } + {%- endif %} +} + // ------------------------------------------------------------------- // Process @@ -210,13 +180,13 @@ void {{class_name}}::setParameterValue(uint32_t index, float value) {%- if meta.midi_input is defined and meta.midi_input == 1 %} #if DISTRHO_PLUGIN_WANT_MIDI_INPUT -{% include 'HeavyDPF_MIDI_Input.cpp' %} +{% include 'midiInput.cpp' %} #endif {% endif %} {%- if meta.midi_output is defined and meta.midi_output == 1 %} #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT -{% include 'HeavyDPF_MIDI_Output.cpp' %} +{% include 'midiOutput.cpp' %} #endif {% endif %} @@ -254,7 +224,7 @@ void {{class_name}}::sampleRateChanged(double newSampleRate) {% if receivers|length > 0 -%} // ensure that the new context has the current parameters - for (int i = 0; i < HV_LV2_NUM_PARAMETERS; ++i) { + for (int i = 0; i < HV_DPF_NUM_PARAMETER; ++i) { setParameterValue(i, _parameters[i]); } {%- endif %} diff --git a/hvcc/generators/c2dpf/templates/HeavyDPF.hpp b/hvcc/generators/c2dpf/templates/HeavyDPF.hpp index df6480cd..8094ebb5 100644 --- a/hvcc/generators/c2dpf/templates/HeavyDPF.hpp +++ b/hvcc/generators/c2dpf/templates/HeavyDPF.hpp @@ -20,6 +20,9 @@ class {{class_name}} : public Plugin {% for k, v in receivers -%} param{{v.display}}, {% endfor %} + {% for k, v in senders -%} + param{{v.display}}, + {% endfor %} }; {% if meta.port_groups is defined %} @@ -44,6 +47,7 @@ class {{class_name}} : public Plugin void handleMidiInput(uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount); void handleMidiSend(uint32_t sendHash, const HvMessage *m); + void setOutputParameter(uint32_t sendHash, const HvMessage *m); protected: // ------------------------------------------------------------------- @@ -135,9 +139,9 @@ class {{class_name}} : public Plugin // ------------------------------------------------------------------- private: - {%- if receivers|length > 0 %} + {%- if (receivers|length > 0) or senders|length > 0 %} // parameters - float _parameters[{{receivers|length}}]; // in range of [0,1] + float _parameters[{{receivers|length + senders|length}}]; // in range of [0,1] {%- endif %} // transport values diff --git a/hvcc/generators/c2dpf/templates/HeavyDPF_UI.cpp b/hvcc/generators/c2dpf/templates/HeavyDPF_UI.cpp index 8ab79302..0aa2982d 100644 --- a/hvcc/generators/c2dpf/templates/HeavyDPF_UI.cpp +++ b/hvcc/generators/c2dpf/templates/HeavyDPF_UI.cpp @@ -6,9 +6,9 @@ START_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- -{%- if receivers|length > 0 %} +{%- if (receivers|length > 0) or (senders|length > 0) %} enum HeavyParams { - {%- for k, v in receivers %} + {%- for k, v in receivers + senders -%} {{v.display|upper}}, {%- endfor %} }; @@ -16,7 +16,7 @@ enum HeavyParams { class ImGuiPluginUI : public UI { - {% for k, v in receivers -%} + {% for k, v in receivers + senders -%} {%- if v.attributes.type == 'bool': %} bool f{{v.display|lower}} = {{v.attributes.default}}f != 0.0f; {%- elif v.attributes.type == 'int': %} @@ -56,9 +56,9 @@ class ImGuiPluginUI : public UI */ void parameterChanged(uint32_t index, float value) override { - {%- if receivers|length > 0 %} + {%- if (receivers|length > 0) or (senders|length > 0) %} switch (index) { - {% for k, v in receivers -%} + {% for k, v in receivers + senders -%} case {{v.display|upper}}: {%- if v.attributes.type == 'bool': %} f{{v.display|lower}} = value != 0.0f; @@ -92,7 +92,7 @@ class ImGuiPluginUI : public UI if (ImGui::Begin("{{name.replace('_', ' ')}}", nullptr, ImGuiWindowFlags_NoResize + ImGuiWindowFlags_NoCollapse)) { - {%- for k, v in receivers %} + {%- for k, v in receivers + senders %} {%- set v_display = v.display|lower %} {%- if meta.enumerators is defined and meta.enumerators[v.display] is defined -%} {%- set enums = meta.enumerators[v.display] -%} @@ -130,17 +130,19 @@ class ImGuiPluginUI : public UI if (ImGui::SliderFloat("{{v.display.replace('_', ' ')}}", &f{{v_display}}, {{v.attributes.min}}f, {{v.attributes.max}}f)) {%- endif %} { + {%- if not v.type == "send" %} if (ImGui::IsItemActivated()) { editParameter({{v.display|upper}}, true); } setParameterValue({{v.display|upper}}, f{{v_display}}); + {%- endif %} } {%- endif %} {% endfor %} if (ImGui::IsItemDeactivated()) { - {%- for k, v in receivers -%} + {% for k, v in receivers + senders -%} editParameter({{v.display|upper}}, false); {% endfor -%} } diff --git a/hvcc/generators/c2dpf/templates/initParameter.cpp b/hvcc/generators/c2dpf/templates/initParameter.cpp new file mode 100644 index 00000000..47628021 --- /dev/null +++ b/hvcc/generators/c2dpf/templates/initParameter.cpp @@ -0,0 +1,49 @@ + case param{{v.display}}: + parameter.name = "{{v.display.replace('_', ' ')}}"; + parameter.symbol = "{{v.display|lower}}"; + {%- if v.attributes.type == 'db': %} + parameter.unit = "dB"; + {%- elif v.attributes.type in ['hz', 'log_hz']: %} + parameter.unit = "Hz"; + {%- endif %} + {%- if v.type == "send" %} + parameter.hints = kParameterIsOutput + {%- else %} + parameter.hints = kParameterIsAutomatable + {%- endif %} + {%- if v.attributes.type == 'bool': %} + | kParameterIsBoolean + {%- elif v.attributes.type == 'trig': -%} + | kParameterIsTrigger + {%- elif v.attributes.type == 'int': -%} + | kParameterIsInteger + {%- elif v.attributes.type in ['log', 'log_hz']: -%} + | kParameterIsLogarithmic + {%- endif %}; + parameter.ranges.min = {{v.attributes.min}}f; + parameter.ranges.max = {{v.attributes.max}}f; + parameter.ranges.def = {{v.attributes.default}}f; + {%- if v.attributes.type == 'db' and not (meta.enumerators is defined and meta.enumerators[v.display] is defined): %} + { + ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[1]; + enumValues[0].value = {{v.attributes.min}}f; + enumValues[0].label = "-inf"; + parameter.enumValues.count = 1; + parameter.enumValues.values = enumValues; + } + {%- endif %} + {%- if meta.enumerators is defined and meta.enumerators[v.display] is defined %} + {% set enums = meta.enumerators[v.display] %} + {% set enumlen = enums|length %} + if (ParameterEnumerationValue *values = new ParameterEnumerationValue[{{enumlen}}]) + { + parameter.enumValues.restrictedMode = true; + {% for i in enums -%} + values[{{loop.index - 1}}].value = {{loop.index - 1}}.0f; + values[{{loop.index - 1}}].label = "{{i}}"; + {% endfor -%} + parameter.enumValues.count = {{enumlen}}; + parameter.enumValues.values = values; + } + {%- endif %} + break; diff --git a/hvcc/generators/c2dpf/templates/HeavyDPF_MIDI_Input.cpp b/hvcc/generators/c2dpf/templates/midiInput.cpp similarity index 100% rename from hvcc/generators/c2dpf/templates/HeavyDPF_MIDI_Input.cpp rename to hvcc/generators/c2dpf/templates/midiInput.cpp diff --git a/hvcc/generators/c2dpf/templates/HeavyDPF_MIDI_Output.cpp b/hvcc/generators/c2dpf/templates/midiOutput.cpp similarity index 100% rename from hvcc/generators/c2dpf/templates/HeavyDPF_MIDI_Output.cpp rename to hvcc/generators/c2dpf/templates/midiOutput.cpp diff --git a/hvcc/generators/c2dpf/templates/HeavyDPF_PortGroups.cpp b/hvcc/generators/c2dpf/templates/portGroups.cpp similarity index 100% rename from hvcc/generators/c2dpf/templates/HeavyDPF_PortGroups.cpp rename to hvcc/generators/c2dpf/templates/portGroups.cpp diff --git a/hvcc/interpreters/pd2hv/PdSendObject.py b/hvcc/interpreters/pd2hv/PdSendObject.py index 2f9af518..8772d2d8 100644 --- a/hvcc/interpreters/pd2hv/PdSendObject.py +++ b/hvcc/interpreters/pd2hv/PdSendObject.py @@ -34,7 +34,7 @@ def __init__( self.__send_name = "" self.__extern_type = None - self.__attributes = {} + self.__attributes: Dict = {} try: # send objects don't necessarily need to have a name @@ -49,6 +49,37 @@ def __init__( except Exception: pass + if self.__extern_type == "param": + try: + self.__attributes = { + "min": 0.0, + "max": 1.0, + "default": 0.5, + "type": "float" + } + self.__attributes["min"] = float(self.obj_args[2]) + self.__attributes["max"] = float(self.obj_args[3]) + self.__attributes["default"] = float(self.obj_args[4]) + self.__attributes["type"] = str(self.obj_args[5]) + except ValueError: + self.add_warning( + f"Minimum, maximum, and default values for Parameter {self.__send_name} must be numbers.") + except Exception: + pass + + if not (self.__attributes["min"] <= self.__attributes["default"]): + self.add_error("Default parameter value is less than the minimum. " + "Send will not be exported: {0:g} < {1:g}".format( + self.__attributes["default"], + self.__attributes["min"])) + self.__extern_type = None + if not (self.__attributes["default"] <= self.__attributes["max"]): + self.add_error("Default parameter value is greater than the maximum. " + "Send will not be exported: {0:g} > {1:g}".format( + self.__attributes["default"], + self.__attributes["max"])) + self.__extern_type = None + if '@raw' in self.obj_args or '@owl' in self.obj_args: # TODO(dromer): deprecate @owl on next stable release try: pd_raw_args = parse_pd_raw_args(self.obj_args)