diff --git a/examples/observer_scope.py b/examples/observer_scope.py index 63a721e..86116e3 100644 --- a/examples/observer_scope.py +++ b/examples/observer_scope.py @@ -35,6 +35,9 @@ def on_effect_status_toggled(self, effect, **kwargs): def on_effect_updated(self, effect, update_type, index, origin, **kwargs): pass + def on_preset_changed(self, effect, **kwargs): + pass + def on_param_value_changed(self, param, **kwargs): pass diff --git a/pluginsmanager/jeanam.code-workspace b/pluginsmanager/jeanam.code-workspace new file mode 100644 index 0000000..95d89cc --- /dev/null +++ b/pluginsmanager/jeanam.code-workspace @@ -0,0 +1,11 @@ +{ + "folders": [ + { + "path": "../../../jeanam" + }, + { + "path": ".." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/pluginsmanager/model/effect.py b/pluginsmanager/model/effect.py index df0059a..ccbd832 100644 --- a/pluginsmanager/model/effect.py +++ b/pluginsmanager/model/effect.py @@ -52,8 +52,11 @@ class Effect(metaclass=ABCMeta): def __init__(self): self.pedalboard = None self._active = True - + self._preset = None + self._patches = () self._params = () + self._availablePatches = DictTuple([], lambda: None) + self._availablePresets = DictTuple([], lambda: None) self._inputs = DictTuple([], lambda: None) self._outputs = DictTuple([], lambda: None) self._midi_inputs = DictTuple([], lambda: None) @@ -65,12 +68,18 @@ def __init__(self): def observer(self): return self._observer + @property + def availablePresets(self): + return self._availablePresets + @observer.setter def observer(self, observer): self._observer = observer for param in self.params: param.observer = self.observer + for patch in self.patches: + patch.observer = self.observer @property def params(self): @@ -79,6 +88,13 @@ def params(self): """ return self._params + @property + def patches(self): + """ + :return list[Param]: Params of effect + """ + return self._patches + @property def inputs(self): """ @@ -126,6 +142,22 @@ def active(self, status): self._active = status self.observer.on_effect_status_toggled(self) + @availablePresets.setter + def availablePresets(self, options): + self._availablePresets = options + + @property + def preset(self): + return self._preset + + @preset.setter + def preset(self, name): + if name in self._availablePresets: + + self._preset = self._availablePresets[name] + print(f"Setting preset => {self._preset}") + self.observer.on_preset_changed(self._preset) + def toggle(self): """ Toggle the effect status: ``self.active = not self.active`` diff --git a/pluginsmanager/model/lv2/lv2_effect.py b/pluginsmanager/model/lv2/lv2_effect.py index 9afaf04..9f4a573 100644 --- a/pluginsmanager/model/lv2/lv2_effect.py +++ b/pluginsmanager/model/lv2/lv2_effect.py @@ -14,6 +14,8 @@ from pluginsmanager.model.effect import Effect from pluginsmanager.model.lv2.lv2_param import Lv2Param +from pluginsmanager.model.lv2.lv2_patch import Lv2Patch +from pluginsmanager.model.lv2.lv2_preset import Lv2Preset from pluginsmanager.model.lv2.lv2_input import Lv2Input from pluginsmanager.model.lv2.lv2_output import Lv2Output from pluginsmanager.model.lv2.lv2_midi_input import Lv2MidiInput @@ -46,6 +48,12 @@ def __init__(self, plugin): params = [Lv2Param(self, param) for param in plugin["ports"]["control"]["input"]] self._params = DictTuple(params, lambda param: param.symbol) + patches = [Lv2Patch(self, p['label'], p['uri'], None) for p in plugin["patches"]] + self._patches = DictTuple(patches, lambda patch: patch.label) + + presets = [Lv2Preset(self, p['label'], p['uri']) for p in plugin["presets"]] + self._availablePresets = DictTuple(presets, lambda preset: preset.label) + inputs = [Lv2Input(self, effect_input) for effect_input in plugin['ports']['audio']['input']] self._inputs = DictTuple(inputs, lambda _input: _input.symbol) @@ -64,8 +72,9 @@ def __str__(self): return str(self.plugin) def __repr__(self): - return "<{} object as '{}' {} active at 0x{:x}>".format( + return "<{} object as '{}' {} active at 0x{:x}, preset:{}>".format( self.__class__.__name__, + self._preset.label, str(self), '' if self.active else 'not', id(self) @@ -78,6 +87,8 @@ def __dict__(self): 'plugin': self.plugin['uri'], 'active': self.active, 'params': [param.json for param in self.params], + 'patches': [patch.json for patch in self.patches], + 'preset': self.preset.label, 'version': self.version } diff --git a/pluginsmanager/model/lv2/lv2_effect_builder.py b/pluginsmanager/model/lv2/lv2_effect_builder.py index 4dd16be..217ce49 100644 --- a/pluginsmanager/model/lv2/lv2_effect_builder.py +++ b/pluginsmanager/model/lv2/lv2_effect_builder.py @@ -66,6 +66,8 @@ def reload(self, metadata, ignore_unsupported_plugins=True): if not ignore_unsupported_plugins \ or plugin['uri'] in supported_plugins: self._plugins[plugin['uri']] = Lv2Plugin(plugin) + else: + print(f"Ignoring plugin: {plugin['uri']}") @property def _supported_plugins(self): diff --git a/pluginsmanager/model/lv2/lv2_patch.py b/pluginsmanager/model/lv2/lv2_patch.py new file mode 100644 index 0000000..d260651 --- /dev/null +++ b/pluginsmanager/model/lv2/lv2_patch.py @@ -0,0 +1,38 @@ +# Copyright 2017 SrMouraSilva +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from pluginsmanager.model.patch import Patch + + +class Lv2Patch(Patch): + """ + Representation of a Lv2 patch. + + For general input use, see :class:`.Patch` class documentation. + + :param Lv2Effect effect: Effect that contains the patchy + :param str symbol: symbol for the effect + + .. _patch: http://lv2plug.in/ns/ext/patch# + """ + + def __init__(self, effect, label, uri, default): + super(Lv2Patch, self).__init__(effect, label, uri, default) + print(f"Creating patch {label} {uri}") + + + @property + def __dict__(self): + dictionary = super(Lv2Patch, self).__dict__ + return dictionary diff --git a/pluginsmanager/model/lv2/lv2_preset.py b/pluginsmanager/model/lv2/lv2_preset.py new file mode 100644 index 0000000..5ea6791 --- /dev/null +++ b/pluginsmanager/model/lv2/lv2_preset.py @@ -0,0 +1,36 @@ +# Copyright 2017 SrMouraSilva +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from pluginsmanager.model.preset import Preset + + +class Lv2Preset(Preset): + """ + Representation of a Lv2 Preset. + + For general input use, see :class:`.Preset` class documentation. + + :param Lv2Effect effect: Effect that contains the Presety + :param str symbol: symbol for the effect + + .. _Preset: http://lv2plug.in/ns/ext/Preset# + """ + + def __init__(self, effect, label, uri): + super(Lv2Preset, self).__init__(effect, label, uri) + + @property + def __dict__(self): + dictionary = super(Lv2Preset, self).__dict__ + return dictionary diff --git a/pluginsmanager/model/patch.py b/pluginsmanager/model/patch.py new file mode 100644 index 0000000..d9dbdcb --- /dev/null +++ b/pluginsmanager/model/patch.py @@ -0,0 +1,130 @@ +# Copyright 2017 SrMouraSilva +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from abc import ABCMeta, abstractmethod + +from unittest.mock import MagicMock + + +class PatchError(Exception): + + def __init__(self, message): + super(PatchError, self).__init__(message) + self.message = message + + +class Patch(metaclass=ABCMeta): + """ + :class:`.Patch` represents an Audio Plugin Patch:: + + >>> my_awesome_effect + + >>> my_awesome_effect.patch + (>> param.default + 1.5 + >>> param.value = 14 + + >>> symbol = param.symbol + >>> symbol + 'model' + >>> param == my_awesome_effect.patches[symbol] + True + + :param Effect effect: Effect in which this parameter belongs + """ + + def __init__(self, effect, label, uri, default): + self._uri = uri + self._label = label + self._effect = effect + self._default = default + self._value = default + + self.observer = MagicMock() + + @property + def effect(self): + """ + :return: Effect in which this parameter belongs + """ + return self._effect + + @property + def default(self): + """ + Parameter default + + :getter: Current default + :setter: Set the current default + """ + return self._default + + @property + def uri(self): + """ + Parameter uri + + :getter: Current uri + :setter: Set the current uri + """ + return self._uri + + @property + def label(self): + return self._label + + @property + def value(self): + """ + Parameter value + + :getter: Current value + :setter: Set the current value + """ + return self._value + + @value.setter + def value(self, new_value): + if self._value == new_value: + return + self._value = new_value + self.observer.on_patch_value_changed(self) + + def __repr__(self, *args, **kwargs): + return "<{} object as uri={}, value={} label={} at 0x{:x}>".format( + self.__class__.__name__, + self._uri, + self._value, + self._label, + id(self) + ) + + @property + def json(self): + """ + Get a json decodable representation of this param + + :return dict: json representation + """ + return self.__dict__ + + @property + def __dict__(self): + return { + 'uri': self._uri, + 'label': self._label, + 'value': self._value + } diff --git a/pluginsmanager/model/preset.py b/pluginsmanager/model/preset.py new file mode 100644 index 0000000..cc17542 --- /dev/null +++ b/pluginsmanager/model/preset.py @@ -0,0 +1,73 @@ +# Copyright 2017 SrMouraSilva +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from abc import ABCMeta, abstractmethod + +from unittest.mock import MagicMock + + +class PresetError(Exception): + + def __init__(self, message): + super(PresetError, self).__init__(message) + self.message = message + + +class Preset(metaclass=ABCMeta): + """ + :class:`.Patch` represents an Audio Plugin Patch:: + + :param Effect effect: Effect in which this parameter belongs + """ + + def __init__(self, effect, label, uri): + self._uri = uri + self._label = label + self._effect = effect + + @property + def label(self): + return self._label + + @property + def effect(self): + """ + :return: Effect in which this parameter belongs + """ + return self._effect + + @property + def uri(self): + """ + Parameter uri + + :getter: Current uri + :setter: Set the current uri + """ + return self._uri + + def __repr__(self, *args, **kwargs): + return "<{} object as uri={}, value={} at 0x{:x}>".format( + self.__class__.__name__, + self._uri, + self._label, + id(self) + ) + + @property + def __dict__(self): + return { + 'label': self._label, + 'uri': self._uri + } diff --git a/pluginsmanager/observer/host_observer/host_observer.py b/pluginsmanager/observer/host_observer/host_observer.py index 30d5645..fd34a6e 100644 --- a/pluginsmanager/observer/host_observer/host_observer.py +++ b/pluginsmanager/observer/host_observer/host_observer.py @@ -112,6 +112,7 @@ def on_effect_updated(self, effect, update_type, index, origin, **kwargs): if update_type == UpdateType.CREATED: self._add_effect(effect) self._load_params_of(effect) + self._load_patches_of(effect) self.on_effect_status_toggled(effect) if update_type == UpdateType.DELETED: @@ -126,18 +127,39 @@ def _load_params_of(self, effect): if param.value != param.default: self._set_param_value(param) + def _load_patches_of(self, effect): + """ + Called only when a effect has created + Patches changes calls :meth:`~pluginsmanager.observer.host_observer.host_observer.HostObserver.on_patch_value_changed()` + """ + for patch in effect.patches: + if patch.value != patch.default: + self._set_patch_value(patch) + def on_effect_status_toggled(self, effect, **kwargs): if effect.pedalboard != self.pedalboard: return self._set_effect_status(effect) + def on_preset_changed(self, preset, **kwargs): + if preset.effect.pedalboard != self.pedalboard: + return + self._set_preset(preset) + def on_param_value_changed(self, param, **kwargs): if param.effect.pedalboard != self.pedalboard: return self._set_param_value(param) + def on_patch_value_changed(self, patch, **kwargs): + if patch.effect.pedalboard != self.pedalboard: + return + + self._set_patch_value(patch) + + def on_connection_updated(self, connection, update_type, pedalboard, **kwargs): if pedalboard != self.pedalboard: return @@ -229,7 +251,12 @@ def _disconnect(self, connection): @abstractmethod def _set_param_value(self, param): pass - + @abstractmethod + def _set_patch_value(self, param): + pass @abstractmethod def _set_effect_status(self, effect): pass + @abstractmethod + def _set_preset(self, effect): + pass diff --git a/pluginsmanager/observer/mod_host/host.py b/pluginsmanager/observer/mod_host/host.py index 0ffc26c..d0f232e 100644 --- a/pluginsmanager/observer/mod_host/host.py +++ b/pluginsmanager/observer/mod_host/host.py @@ -43,6 +43,17 @@ def __init__(self, address='localhost', port=5555): self.instance_index = 0 + def set_bpm(self, num): + self.connection.send(ProtocolParser.set_bpm(num)); + def set_bpb(self, num): + self.connection.send(ProtocolParser.set_bpb(num)); + def transport(self, rolling, bpb, bpm): + self.connection.send(ProtocolParser.transport(rolling, bpb, bpm)) + def transport_sync(self, mode): + self.connection.send(ProtocolParser.transport_sync(mode)) + def save(self, filename): + self.connection.send(ProtocolParser.save(filename)) + def add(self, effect): """ Add an LV2 plugin encapsulated as a jack client @@ -86,6 +97,20 @@ def set_param_value(self, param): """ self.connection.send(ProtocolParser.param_set(param)) + def set_patch_value(self, patch): + """ + Set a value to given patch + + :param Lv2Patch patch: Patch that the value will be updated + """ + self.connection.send(ProtocolParser.patch_set(patch)) + def set_preset(self, preset): + """ + Set an effect to given preset + + :param Lv2Effect effect: Effect that the preset will be updated + """ + self.connection.send(ProtocolParser.preset_load(preset)) def set_status(self, effect): """ Toggle effect processing diff --git a/pluginsmanager/observer/mod_host/mod_host.py b/pluginsmanager/observer/mod_host/mod_host.py index 198f82b..afaa806 100644 --- a/pluginsmanager/observer/mod_host/mod_host.py +++ b/pluginsmanager/observer/mod_host/mod_host.py @@ -130,6 +130,17 @@ def connect(self): """ self.host = Host(self.address, self.port) + def set_bpm(self, num): + self.host.set_bpm(num) + def set_bpb(self, num): + self.host.set_bpb(num) + def transport(self, rolling, bpb, bpm): + self.host.transport(rolling, bpb, bpm) + def transport_sync(self, mode): + self.host.transport_sync(mode) + def save(self, filename): + self.host.save(filename) + def __del__(self): """ Calls :meth:`~pluginsmanager.observer.mod_host.ModHost.close()` method for @@ -166,12 +177,18 @@ def close(self): else: self.host.close() + #################################### # Observer #################################### def _set_param_value(self, param): self.host.set_param_value(param) + def _set_patch_value(self, patch): + self.host.set_patch_value(patch) + + def _set_preset(self, preset): + self.host.set_preset(preset) def _remove_effect(self, effect): self.host.remove(effect) diff --git a/pluginsmanager/observer/mod_host/protocol_parser.py b/pluginsmanager/observer/mod_host/protocol_parser.py index 834d44c..b84a8e1 100644 --- a/pluginsmanager/observer/mod_host/protocol_parser.py +++ b/pluginsmanager/observer/mod_host/protocol_parser.py @@ -115,9 +115,55 @@ def disconnect(connection): ProtocolParser._get_out_name_of(connection.output), ProtocolParser._get_in_name_of(connection.input) ) + @staticmethod + def set_bpm(num): + """ + set_bpm + + * set the global beats per minute transport value + + e.g.: set_bpm 120 + """ + return 'set_bpm {}'.format(num) + + @staticmethod + def set_bpb(num): + """ + set_bpb + + * set the global beats per bar transport value + + e.g.: set_bpb 4 + """ + return 'set_bpb {}'.format(num) + + @staticmethod + def save(filename): + return 'save {}'.format(filename) + + @staticmethod + def transport(rolling, bpb, bpm): + """ + transport + + * change the global transport state + + e.g.: transport 1 4 120 + """ + return 'transport {} {} {}'.format(rolling, bpb, bpm) + + @staticmethod + def transport_sync(self, mode): + """ + transport_sync + * change the transport sync mode + * mode can be one of "none", "link" or "midi" + e.g.: transport_sync "midi" + """ + return 'transport_sync {}'.format(mode) @staticmethod - def preset_load(): + def preset_load(preset): """ ``preset_load `` @@ -126,15 +172,11 @@ def preset_load(): e.g.:: preset_load 0 "http://drobilla.net/plugins/mda/presets#JX10-moogcury-lite" - - .. note:: - - Not implemented yet """ - pass + return 'preset_load {} {}'.format(preset.effect.instance, preset.uri); @staticmethod - def preset_save(): + def preset_save(effect, name, dir, uri): """ ``preset_save `` @@ -143,15 +185,11 @@ def preset_save(): e.g.:: preset_save 0 "My Preset" /home/user/.lv2/my-presets.lv2 mypreset.ttl - - .. note:: - - Not implemented yet """ - pass + return 'preset_save {} {} {} {}'.format(effect.instance, name, dir, uri) @staticmethod - def preset_show(): + def preset_show(effect, uri): """ ``preset_show `` @@ -165,7 +203,7 @@ def preset_show(): Not implemented yet """ - pass + return 'preset_show {} {}'.format(effect.instance, uri) @staticmethod def param_set(param): @@ -218,6 +256,13 @@ def param_monitor(): """ pass + @staticmethod + def patch_set(patch): + """ + ``patch_set 0 nam#model "amp.nam"`` + """ + return 'patch_set {} {} "{}"'.format(patch.effect.instance, patch.uri, patch.value) + @staticmethod def monitor(): """ diff --git a/pluginsmanager/observer/observer_manager.py b/pluginsmanager/observer/observer_manager.py index 02380fe..5248bfd 100644 --- a/pluginsmanager/observer/observer_manager.py +++ b/pluginsmanager/observer/observer_manager.py @@ -71,6 +71,15 @@ def on_param_value_changed(self, param, **kwargs): if observer != self.scope: observer.on_param_value_changed(param, **kwargs) + def on_patch_value_changed(self, patch, **kwargs): + for observer in self.observers: + if observer != self.scope: + observer.on_patch_value_changed(patch, **kwargs) + def on_preset_changed(self, preset, **kwargs): + for observer in self.observers: + if observer != self.scope: + observer.on_preset_changed(preset, **kwargs) + def on_connection_updated(self, connection, update_type, pedalboard, **kwargs): for observer in self.observers: if observer != self.scope: diff --git a/pluginsmanager/observer/updates_observer.py b/pluginsmanager/observer/updates_observer.py index 4cba21f..2b744f5 100644 --- a/pluginsmanager/observer/updates_observer.py +++ b/pluginsmanager/observer/updates_observer.py @@ -94,6 +94,16 @@ def on_param_value_changed(self, param, **kwargs): """ pass + @abstractmethod + def on_patch_value_changed(self, patch, **kwargs): + """ + Called when a param value change + + :param Param param: Param with value changed + """ + pass + + @abstractmethod def on_connection_updated(self, connection, update_type, pedalboard, **kwargs): """ diff --git a/pluginsmanager/util/builder/lv2_json_builder.py b/pluginsmanager/util/builder/lv2_json_builder.py index a890bfa..09342ba 100644 --- a/pluginsmanager/util/builder/lv2_json_builder.py +++ b/pluginsmanager/util/builder/lv2_json_builder.py @@ -55,6 +55,8 @@ def build(self, json): for param, param_json in zip(effect.params, json['params']): param.value = param_json['value'] + for patch, patch_json in zip(effect.patches, json['patches']): + patch.value = patch_json['value'] effect.active = json['active'] diff --git a/pluginsmanager/util/dict_tuple.py b/pluginsmanager/util/dict_tuple.py index 0961f7d..dd7f57d 100644 --- a/pluginsmanager/util/dict_tuple.py +++ b/pluginsmanager/util/dict_tuple.py @@ -41,6 +41,7 @@ def __getitem__(self, index): else: return self._dict[index] - + def __str__(self): + return self._dict.__str__() def __contains__(self, item): return item in self._dict diff --git a/pluginsmanager/util/persistence_decoder.py b/pluginsmanager/util/persistence_decoder.py index 1ae002f..0348a5a 100644 --- a/pluginsmanager/util/persistence_decoder.py +++ b/pluginsmanager/util/persistence_decoder.py @@ -44,10 +44,14 @@ def read(self, json): class BankReader(Reader): + def __init__(self, system_effect, builder): + super(BankReader, self).__init__(system_effect) + self.builder = builder + def read(self, json): bank = Bank(json['name']) - pedalboard_reader = PedalboardReader(self.system_effect) + pedalboard_reader = PedalboardReader(self.system_effect, self.builder) for pedalboard_json in json['pedalboards']: bank.append(pedalboard_reader.read(pedalboard_json)) @@ -56,10 +60,14 @@ def read(self, json): class PedalboardReader(Reader): + def __init__(self, system_effect, builder): + super(PedalboardReader, self).__init__(system_effect) + self.builder = builder + def read(self, json): pedalboard = Pedalboard(json['name']) - effect_reader = EffectReader(self.system_effect) + effect_reader = EffectReader(self.system_effect, self.builder) for effect_json in json['effects']: pedalboard.append(effect_reader.read(effect_json)) @@ -76,9 +84,9 @@ def read(self, json): class EffectReader(Reader): - def __init__(self, system_effect): + def __init__(self, system_effect, builder): super(EffectReader, self).__init__(system_effect) - self.builder = Lv2LilvEffectBuilder() + self.builder = builder def read(self, json): return self.generate_builder(json).build(json) diff --git a/test.mod b/test.mod new file mode 100644 index 0000000..875fff9 --- /dev/null +++ b/test.mod @@ -0,0 +1,31 @@ +add http://github.com/mikeoliphant/neural-amp-modeler-lv2 0 +bypass 0 1 +add http://distrho.sf.net/plugins/3BandEQ 1 +bypass 1 1 +add http://eq10q.sourceforge.net/eq/eq4qm 2 +bypass 2 1 +add http://lsp-plug.in/plugins/lv2/impulse_responses_stereo 3 +bypass 3 1 +add urn:dragonfly:room 4 +bypass 4 1 +connect system:capture_2 effect_0:input +connect effect_0:output effect_1:lv2_audio_in_1 +connect effect_1:lv2_audio_out_1 effect_2:input +connect effect_2:output effect_3:in_l +connect effect_2:output effect_3:in_r +connect effect_3:out_l effect_4:lv2_audio_in_1 +connect effect_3:out_r effect_4:lv2_audio_in_2 +connect effect_4:lv2_audio_out_1 system:playback_1 +connect effect_4:lv2_audio_out_2 system:playback_2 +preset_load 0 "file:///home/jon/.lv2/Neural_Amp_Modeler_EnglLead1.preset.lv2/EnglLead1.ttl" +param_set 4 dry_level 100 +preset_load 4 "file:///home/jon/.lv2/Dragonfly_Room_Reverb_VocalHall.preset.lv2/VocalHall.ttl" +param_set 1 low_mid 120 +param_set 1 mid_high 2300 +param_set 2 filter4_enable 1 +param_set 2 filter4_gain -12 +param_set 2 filter4_freq 8000 +bypass 0 0 +bypass 4 0 +bypass 2 0 +bypass 1 0 diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..6b843cc --- /dev/null +++ b/test.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -x + +while read p; do + echo "$p" | nc -q 1 localhost 5555 +done < test.mod