diff --git a/.gitignore b/.gitignore index c77818b..c9ebea5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,9 @@ *.egg-info htmlcov .coverage +.cache docs/build .eggs dist/ -build/ \ No newline at end of file +build/ +test/autosaver_data diff --git a/.travis.yml b/.travis.yml index bbf8029..6a7dfe6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,20 +12,19 @@ sudo: required install: - sudo apt-get install -y portaudio19-dev python-all-dev --no-install-recommends - sudo apt-get install -y lilv-utils calf-plugins guitarix --no-install-recommends + - sudo apt-get install -y lv2-dev --no-install-recommends - pip3 install sphinx - - pip3 install coveralls - pip3 install cffi - - pip3 install JACK-Client + - python setup.py develop + - pip3 install pytest pytest-cov + - make install-tests-requirements script: - lv2ls - - cd .. - - mkdir data - - cd data - - mkdir autosaver_data - - cd ../PluginsManager - - coverage3 run --source=pluginsmanager setup.py test - - cd docs - - make html + - make test + - make docs + - #make test-docs + - #pytest --doctest-modules + after_success: - bash <(curl -s https://codecov.io/bash) diff --git a/CHANGES b/CHANGES index e10a7ef..107b5b4 100644 --- a/CHANGES +++ b/CHANGES @@ -1,15 +1,58 @@ -Version 0.5.1 -- released 06//17 +Version 0.6.0 -- released 11/30/17 ********************************** - - `Issue #52`_ - :class:`Autosaver` - Change connection with :class:`SystemInput` and :class:`SystemOutput` causes error; - - `Issue #53`_ - :class:`Autosaver` - Remove effect with connections breaks. + - Add makefile. Now is possible run tests and generate docs easly (`make help`); + - Improve :class:`.SystemInput` and :class:`.SystemOutputs` documentation; + - `Issue #57`_ - Implementing midi support: + - Now :class:`.Effect` list yours `midi_inputs` and `midi_outputs`; + - :class:`.SystemEffect` now supports `midi_inputs` and `midi_outputs`; + - :class:`.Lv2Effect` now supports `midi_inputs` and `midi_outputs`; + - Created :class:`.MidiPort`, :class:`.MidiInput`, :class:`.MidiOutput`; + - Created :class:`.SystemMidiInput`, :class:`.SystemMidiOutput`; + - Created :class:`.Lv2MidiInput`, :class:`.Lv2MidiOutput`; + - :class:`.SystemEffectBuilder` now creates :class:`.SystemEffect` with your midi outputs and midi inputs; + - Fix autosaver_test creation folder. Now is more easily configure test workspace; + - Refactored :class:`.Input`, :class:`.Output`: Created :class:`.Port` for remove duplicated code; + - Refactored :class:`.SystemInput`, :class:`.SystemOutput`: Created :class:`.SystemPortMixing` for remove duplicated code; + - Refactored :class:`.Lv2Input`, :class:`.Lv2Output`: Created :class:`.Lv2PortMixing` for remove duplicated code; + - :class:`.JackClient` - Add attributes: `audio_inputs`, `audio_outputs`, `midi_inputs`, `midi_outputs`; + - Break change: Removed :meth:`Output.connect()` and :meth:`Output.disconnect()` :class:`.Output` methods. + Use instead :meth:`~pluginsmanager.model.pedalboard.Pedalboard.connect()`, :meth:`~pluginsmanager.model.pedalboard.Pedalboard.disconnect()` :class:`.Pedalboard` methods; + - `Issue #67`_ - Created :meth:`~pluginsmanager.model.pedalboard.Pedalboard.connect()`, :meth:`~pluginsmanager.model.pedalboard.Pedalboard.disconnect()` :class:`.Pedalboard` methods; + - Fixed Changelog: Now is possible see it in the documentation; + - `Issue #38`_ - Raise erros then add sys_effect in any Pedalboard; + - `Issue #65`_ - Fix documentation bug `SystemEffectBuilder(client).build()` instead `SystemEffectBuilder(client)`; + - `Issue #68`_ - Remove current mod-host pedalboard don't removes systems connection (system.output to system.input); + - `Issue #66`_ - JSON effect improviments: Add plugin version. Remove `min` and `max`; + - `Issue #62`_ - Create a converter MOD pedalboard -> PluginsManager pedalboard; + - `Issue #77`_ - Fix MidiConnection bugs (:class:`SystemMidiInput` and :class:`SystemMidiOutput` doesn't works in :class:`ModHost`); + - `Issue #78`_ - Improve lv2 effect builder error message when plugin not exists; + - :class:`.Lv2EffectBuilder` - Add parameter `ignore_unsupported_plugins` for ignore audio plugins errors if it doesn't installed in the system. + The previous versions raises error if a audio plugin hasn't installed in the system. + Now, is possible use it if `plugins_json` parameter contains your metadata. + Observes that, how the audio plugin aren't installed, your use with mod-host or other host will raises errors. + +.. _Issue #38: https://github.com/PedalPi/PluginsManager/issues/38 +.. _Issue #57: https://github.com/PedalPi/PluginsManager/issues/57 +.. _Issue #62: https://github.com/PedalPi/PluginsManager/issues/65 +.. _Issue #65: https://github.com/PedalPi/PluginsManager/issues/65 +.. _Issue #66: https://github.com/PedalPi/PluginsManager/issues/66 +.. _Issue #67: https://github.com/PedalPi/PluginsManager/issues/67 +.. _Issue #68: https://github.com/PedalPi/PluginsManager/issues/68 +.. _Issue #77: https://github.com/PedalPi/PluginsManager/issues/77 +.. _Issue #78: https://github.com/PedalPi/PluginsManager/issues/78 + +Version 0.5.1 -- released 08/16/17 +********************************** + - `Issue #52`_ - :class:`.Autosaver` - Change connection with :class:`.SystemInput` and :class:`.SystemOutput` causes error; + - `Issue #53`_ - :class:`.Autosaver` - Remove effect with connections breaks. Disable connections removed notification when a effect with connections has removed; - - :class:`Autosaver` - Add :attr:`.Observable.real_list` attribute for access the list of :class:`ObservableList`. - - `Issue #54`_ - :class:`Mod-host` - Fix `feedback_socket optional`_ problem + - :class:`.Autosaver` - Add :attr:`.Observable.real_list` attribute for access the list of :class:`.ObservableList`; + - `Issue #54`_ - :class:`.Mod-host` - Fix `feedback_socket optional`_ problem. .. _Issue #52: https://github.com/PedalPi/PluginsManager/issues/52 .. _Issue #53: https://github.com/PedalPi/PluginsManager/issues/53 .. _Issue #54: https://github.com/PedalPi/PluginsManager/issues/54 -.. _default feedback_socket disabled: https://github.com/moddevices/mod-host/commit/31b1d04deb91c88420e1e0dd0cc4fad523f55712 +.. _feedback_socket optional: https://github.com/moddevices/mod-host/commit/31b1d04deb91c88420e1e0dd0cc4fad523f55712 Version 0.5.0 -- released 05/29/17 ********************************** diff --git a/README.rst b/README.rst index f183d8a..6401caf 100644 --- a/README.rst +++ b/README.rst @@ -157,7 +157,7 @@ It requires a `JackClient` instance, that uses `JACK-Client`_. client = JackClient() from pluginsmanager.model.system.system_effect_builder import SystemEffectBuilder - sys_effect = SystemEffectBuilder(client) + sys_effect = SystemEffectBuilder(client).build() For manual input and output sound card definition, use: @@ -261,43 +261,20 @@ library can be accessed in the `Observer section `__. .. _Carla: https://github.com/falkTX/Carla -Changelog ---------- - -..include:: ../../CHANGES Maintenance ----------- -Test -**** - -It is not necessary for the mod_host process to be running - -.. code-block:: bash - - coverage3 run --source=pluginsmanager setup.py test +Makefile +******** - coverage3 report - coverage3 html - firefox htmlcov/index.html +Execute ``make help`` for see the options Generate documentation ********************** This project uses `Sphinx`_ + `Read the Docs`_. -You can generate the documentation in your local machine: - -.. code-block:: bash - - pip3 install sphinx - - cd docs - make html - - firefox build/html/index.html - .. _Sphinx: http://www.sphinx-doc.org/ .. _Read the Docs: http://readthedocs.org .. _Calf: http://calf-studio-gear.org/ diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst new file mode 100644 index 0000000..f8b67d6 --- /dev/null +++ b/docs/source/changelog.rst @@ -0,0 +1,4 @@ +Changelog +========= + +.. include:: ../../CHANGES diff --git a/docs/source/conf.py b/docs/source/conf.py index 0baaed3..76d8b5a 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -42,6 +42,7 @@ 'sphinx.ext.viewcode', 'sphinx.ext.todo', 'sphinx.ext.graphviz', + #'sphinx.ext.inheritance_diagram', # 'sphinx.ext.githubpages', ] diff --git a/docs/source/index.rst b/docs/source/index.rst index 4872ca6..5837ec3 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,5 +1,13 @@ .. include:: ../../README.rst +Changelog +--------- + +.. toctree:: + :maxdepth: 2 + + changelog + API --- diff --git a/docs/source/model.rst b/docs/source/model.rst index 3632f9e..5e1f693 100644 --- a/docs/source/model.rst +++ b/docs/source/model.rst @@ -39,24 +39,47 @@ This page contains the model classes. .. graphviz:: - digraph classes { - graph [rankdir=BT]; - node [shape=rect, style=filled, color="#298029", fontname=Sans, fontcolor="#ffffff", fontsize=10]; + digraph classes { + graph [rankdir=BT]; + node [shape=rect, style=filled, color="#298029", fontname=Sans, fontcolor="#ffffff", fontsize=10]; - Lv2Effect->Lv2Plugin[ - dir="backward", arrowhead="diamond", arrowtail="normal" - ]; - Lv2Effect->Effect; - SystemEffect->Effect; + AudioPort->Port; - Lv2Input->Input; - SystemInput->Input; + Input->AudioPort; + Output->AudioPort; - Lv2Output->Output; - SystemOutput->Output; + MidiPort->Port; + MidiInput->MidiPort; + MidiOutput->MidiPort; - Lv2Param->Param; - } + Lv2Input->Input; + Lv2Output->Output; + Lv2MidiInput->MidiInput; + Lv2MidiOutput->MidiOutput; + + SystemInput->Input; + SystemOutput->Output; + SystemMidiInput->MidiInput; + SystemMidiOutput->MidiOutput; + + } + +.. graphviz:: + + digraph classes { + graph [rankdir=BT]; + node [shape=rect, style=filled, color="#298029", fontname=Sans, fontcolor="#ffffff", fontsize=10]; + + Lv2Effect->Lv2Plugin[ + dir="backward", arrowhead="diamond", arrowtail="normal" + ]; + Lv2Effect->Effect; + SystemEffect->Effect; + + Lv2Param->Param; + + MidiConnection->Connection; + } .. graphviz:: @@ -71,6 +94,11 @@ This page contains the model classes. AutoSaver->UpdatesObserver } +.. .. inheritance-diagram:: + pluginsmanager.model.output.Output + pluginsmanager.model.input.Input + + BanksManager ------------ @@ -87,6 +115,14 @@ Bank :special-members: :exclude-members: __weakref__ +Pedalboard +---------- + +.. autoclass:: pluginsmanager.model.pedalboard.Pedalboard + :members: + :special-members: + :exclude-members: __weakref__ + Connection ---------- @@ -95,6 +131,14 @@ Connection :special-members: :exclude-members: __weakref__ +MidiConnection +-------------- + +.. autoclass:: pluginsmanager.model.midi_connection.MidiConnection + :members: + :special-members: + :exclude-members: __weakref__ + Effect ------ @@ -103,6 +147,22 @@ Effect :special-members: :exclude-members: __weakref__ +Port +---- + +.. autoclass:: pluginsmanager.model.port.Port + :members: + :special-members: + :exclude-members: __weakref__ + +AudioPort +--------- + +.. autoclass:: pluginsmanager.model.audio_port.AudioPort + :members: + :special-members: + :exclude-members: __weakref__ + Input ----- @@ -119,18 +179,36 @@ Output :special-members: :exclude-members: __weakref__ -Param ------ +MidiPort +-------- -.. autoclass:: pluginsmanager.model.param.Param +.. autoclass:: pluginsmanager.model.midi_port.MidiPort :members: :special-members: :exclude-members: __weakref__ -Pedalboard + +MidiInput +--------- + +.. autoclass:: pluginsmanager.model.midi_input.MidiInput + :members: + :special-members: + :exclude-members: __weakref__ + +MidiOutput ---------- -.. autoclass:: pluginsmanager.model.pedalboard.Pedalboard +.. autoclass:: pluginsmanager.model.midi_output.MidiOutput + :members: + :special-members: + :exclude-members: __weakref__ + +Param +----- + +.. autoclass:: pluginsmanager.model.param.Param :members: :special-members: :exclude-members: __weakref__ + diff --git a/docs/source/model_system.rst b/docs/source/model_system.rst index 36f3952..f5afefd 100644 --- a/docs/source/model_system.rst +++ b/docs/source/model_system.rst @@ -32,3 +32,28 @@ SystemOutput :members: :special-members: :exclude-members: __weakref__ + + +SystemMidiInput +--------------- + +.. autoclass:: pluginsmanager.model.system.system_midi_input.SystemMidiInput + :members: + :special-members: + :exclude-members: __weakref__ + +SystemMidiOutput +---------------- + +.. autoclass:: pluginsmanager.model.system.system_midi_output.SystemMidiOutput + :members: + :special-members: + :exclude-members: __weakref__ + +SystemPortMixing +---------------- + +.. autoclass:: pluginsmanager.model.system.system_port_mixing.SystemPortMixing + :members: + :special-members: + :exclude-members: __weakref__ diff --git a/docs/source/observer.rst b/docs/source/observer.rst index 3104ffb..8839908 100644 --- a/docs/source/observer.rst +++ b/docs/source/observer.rst @@ -203,10 +203,11 @@ of the change:: We'll now limit the notification by telling you who performed the actions:: >>> with observer1: - >>> del manager.banks[0] + >>> with observer1: + ... del manager.banks[0] "Hi! I am observer2" >>> with observer2: - >>> manager.banks.append(bank) + ... manager.banks.append(bank) "Hi! I am observer1" diff --git a/docs/source/util.rst b/docs/source/util.rst index 0da7bc7..d1bf696 100644 --- a/docs/source/util.rst +++ b/docs/source/util.rst @@ -1,32 +1,40 @@ PedalPi - PluginsManager - Util =============================== -pluginsmanager.util.dict_tuple.DictTuple ----------------------------------------- +DictTuple +--------- .. autoclass:: pluginsmanager.util.dict_tuple.DictTuple :members: :special-members: :exclude-members: __weakref__ -pluginsmanager.util.pairs_list.PairsList ----------------------------------------- +ModPedalboardConverter +---------------------- + +.. autoclass:: pluginsmanager.util.mod_pedalboard_converter.ModPedalboardConverter + :members: + :special-members: + :exclude-members: __weakref__ + +PairsList +--------- .. autoclass:: pluginsmanager.util.pairs_list.PairsList :members: :special-members: :exclude-members: __weakref__ -pluginsmanager.util.pairs_list.PairsListResult ----------------------------------------------- +PairsListResult +--------------- .. autoclass:: pluginsmanager.util.pairs_list.PairsListResult :members: :special-members: :exclude-members: __weakref__ -pluginsmanager.util.persistence_decoder ---------------------------------------- +persistence_decoder +------------------- .. autoclass:: pluginsmanager.util.persistence_decoder.PersistenceDecoderError :members: diff --git a/makefile b/makefile new file mode 100644 index 0000000..bd54f1e --- /dev/null +++ b/makefile @@ -0,0 +1,78 @@ +BROWSER=firefox +BOLD=\033[1m +NORMAL=\033[0m + +default: help + +clean: clean-pyc clean-test clean-build clean-docs + +clean-build: + rm -rf .eggs + rm -rf build + rm -rf dist + +clean-pyc: + find . -name '*.pyc' -exec rm --force {} + + find . -name '*.pyo' -exec rm --force {} + + +clean-test: + rm -rf .cache + rm -f .coverage + rm -rf htmlcov + rm -rf test/autosaver_data + +clean-docs: + rm -rf docs/build + +docs: clean-docs + cd docs && $(MAKE) html + +docs-see: docs + $(BROWSER) docs/build/html/index.html + +install-tests-requirements: + # For midi tests - https://github.com/x42/midifilter.lv2 + cd /tmp && git clone git://github.com/x42/midifilter.lv2.git && \ + cd midifilter.lv2 && \ + make && \ + sudo make install PREFIX=/usr + +run: + @echo "Run option isn't created =)" + +test: clean-test + mkdir test/autosaver_data + pytest --cov=pluginsmanager + +test-docs: + python -m doctest *.rst -v + python -m doctest docs/*/*.rst -v + +test-details: test + coverage3 html + $(BROWSER) htmlcov/index.html + +help: cabecalho + @echo "" + @echo "Commands" + @echo " $(BOLD)clean$(NORMAL)" + @echo " Clean files" + @echo " $(BOLD)docs$(NORMAL)" + @echo " Make the docs" + @echo " $(BOLD)docs-see$(NORMAL)" + @echo " Make the docs and open it in BROWSER" + @echo " $(BOLD)test$(NORMAL)" + @echo " Execute the tests" + @echo " $(BOLD)test-details$(NORMAL)" + @echo " Execute the tests and shows the result in BROWSER" + @echo " - BROWSER=firefox" + @echo " $(BOLD)help$(NORMAL)" + @echo " Show the valid commands" + +cabecalho: + @echo "$(BOLD) _____ _ _ _____" + @echo "| _ || | _ _ ___ |_| ___ ___ | | ___ ___ ___ ___ ___ ___" + @echo "| __|| || | || . || || ||_ -|| | | || .'|| || .'|| . || -_|| _|" + @echo "|__| |_||___||_ ||_||_|_||___||_|_|_||__,||_|_||__,||_ ||___||_|" + @echo " |___| |___|" + @echo "Github$(NORMAL): https://pypi.org/project/PedalPi-PluginsManager/" diff --git a/pluginsmanager/banks_manager.py b/pluginsmanager/banks_manager.py index f7fc244..b45f715 100644 --- a/pluginsmanager/banks_manager.py +++ b/pluginsmanager/banks_manager.py @@ -47,8 +47,9 @@ def __iter__(self): """ Iterates banks of the banksmanager:: + >>> banks_manager = BanksManager() >>> for index, bank in enumerate(banks_manager): - >>> print(index, '-', bank) + ... print(index, '-', bank) :return: Iterator for banks list """ diff --git a/pluginsmanager/jack/jack_client.py b/pluginsmanager/jack/jack_client.py index fa8f186..8a06b75 100644 --- a/pluginsmanager/jack/jack_client.py +++ b/pluginsmanager/jack/jack_client.py @@ -62,9 +62,37 @@ def shutdown(status, reason): self.client.activate() + @property + def audio_inputs(self): + """ + :return: A list of audio input :class:`Ports`. + """ + return self.client.get_ports(is_audio=True, is_physical=True, is_input=True) + + @property + def audio_outputs(self): + """ + :return: A list of audio output :class:`Ports`. + """ + return self.client.get_ports(is_audio=True, is_physical=True, is_output=True) + + @property + def midi_inputs(self): + """ + :return: A list of MIDI input :class:`Ports`. + """ + return self.client.get_ports(is_midi=True, is_physical=True, is_input=True) + + @property + def midi_outputs(self): + """ + :return: A list of MIDI output :class:`Ports`. + """ + return self.client.get_ports(is_midi=True, is_physical=True, is_output=True) + def close(self): """ - Deactive and closes the jack client + Deactivate and closes the jack client """ self.client.deactivate() self.client.close() diff --git a/pluginsmanager/jack/jack_interface.py b/pluginsmanager/jack/jack_interface.py index a6eb323..4799638 100644 --- a/pluginsmanager/jack/jack_interface.py +++ b/pluginsmanager/jack/jack_interface.py @@ -1,3 +1,17 @@ +# 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. + import pyaudio diff --git a/pluginsmanager/model/audio_port.py b/pluginsmanager/model/audio_port.py new file mode 100644 index 0000000..accadea --- /dev/null +++ b/pluginsmanager/model/audio_port.py @@ -0,0 +1,31 @@ +# 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 + +from pluginsmanager.model.port import Port + + +class AudioPort(Port, metaclass=ABCMeta): + """ + Port is a parent abstraction for audio inputs and audio outputs + """ + + @property + def connection_class(self): + """ + :return Connection: Class used for connections in this port + """ + from pluginsmanager.model.connection import Connection + return Connection diff --git a/pluginsmanager/model/connection.py b/pluginsmanager/model/connection.py index c70cfde..6fabd4c 100644 --- a/pluginsmanager/model/connection.py +++ b/pluginsmanager/model/connection.py @@ -13,6 +13,9 @@ # limitations under the License. +from pluginsmanager.model.audio_port import AudioPort + + class ConnectionError(Exception): def __init__(self, message): super(ConnectionError, self).__init__(message) @@ -21,9 +24,10 @@ def __init__(self, message): class Connection(object): """ - :class:`pluginsmanager.model.connection.Connection` represents a connection between two - distinct effects by your ports (effect :class:`.Output` with effect :class:`.Input`):: + :class:`~pluginsmanager.model.connection.Connection` represents a connection between two + distinct effects by your :class:`.AudioPort` (effect :class:`.Output` with effect :class:`.Input`):: + >>> from pluginsmanager.model.pedalboard import Pedalboard >>> californication = Pedalboard('Californication') >>> californication.append(driver) >>> californication.append(reverb) @@ -42,21 +46,38 @@ class Connection(object): Another way to use implicitly connections:: - >>> guitar_output.connect(driver_input) - >>> driver_output.connect(reverb_input) - >>> reverb_output.connect(amp_input) + >>> californication.connect(guitar_output, driver_input) + >>> californication.connect(driver_output, reverb_input) + >>> californication.connect(reverb_output, amp_input) - :param Output effect_output: Output port that will be connected with input port - :param Input effect_input: Input port that will be connected with output port + :param Output output_port: Audio output port that will be connected with audio input port + :param Input input_port: Audio input port that will be connected with audio output port """ - def __init__(self, effect_output, effect_input): - if effect_output.effect == effect_input.effect\ - and not effect_output.effect.is_possible_connect_itself: - raise ConnectionError('Effect of output and effect of input are equals') + def __init__(self, output_port, input_port): + if not self._valid_instance(output_port)\ + or not self._valid_instance(input_port): + raise ConnectionError("'{}' only accepts ports that inherits {}".format(self.__class__.__name__, self.ports_class.__name__)) + + if output_port.effect == input_port.effect\ + and not output_port.effect.is_possible_connect_itself: + effect_name = str(input_port.effect) + raise ConnectionError("The output {} and input {} are from the same effect {}. " + "This effect doesn't accept connections between itself instance." + .format(effect_name, str(output_port), str(input_port))) + + self._output = output_port + self._input = input_port - self._output = effect_output - self._input = effect_input + def _valid_instance(self, port): + return isinstance(port, self.ports_class) + + @property + def ports_class(self): + """ + :return class: Port class that this connection only accepts + """ + return AudioPort @property def output(self): @@ -101,4 +122,5 @@ def __dict__(self): return { 'output': self.output.json, 'input': self.input.json, + 'type': 'audio' } diff --git a/pluginsmanager/model/effect.py b/pluginsmanager/model/effect.py index ca92def..7cfedd7 100644 --- a/pluginsmanager/model/effect.py +++ b/pluginsmanager/model/effect.py @@ -13,16 +13,17 @@ # limitations under the License. from abc import ABCMeta, abstractmethod - from unittest.mock import MagicMock +from pluginsmanager.util.dict_tuple import DictTuple + class Effect(metaclass=ABCMeta): """ Representation of a audio plugin instance - LV2 plugin encapsulated as a jack client. Effect contains a `active` status (off=bypass), a list of :class:`.Param`, - a list of :class:`.Input` and a list of :class:`pluginsmanager.mod_host.connection.Connection`:: + a list of :class:`.Input` and a list of :class:`~pluginsmanager.model.connection.Connection`:: >>> reverb = builder.build('http://calf.sourceforge.net/plugins/Reverb') >>> pedalboard.append(reverb) @@ -53,8 +54,10 @@ def __init__(self): self._active = True self._params = () - self._inputs = () - self._outputs = () + self._inputs = DictTuple([], lambda: None) + self._outputs = DictTuple([], lambda: None) + self._midi_inputs = DictTuple([], lambda: None) + self._midi_outputs = DictTuple([], lambda: None) self._observer = MagicMock() @@ -90,6 +93,20 @@ def outputs(self): """ return self._outputs + @property + def midi_inputs(self): + """ + :return list[MidiInput]: MidiInputs of effect + """ + return self._midi_inputs + + @property + def midi_outputs(self): + """ + :return list[MidiOutput]: MidiOutputs of effect + """ + return self._midi_outputs + @property def active(self): """ @@ -155,3 +172,39 @@ def is_possible_connect_itself(self): return bool: Is possible connect the with it self? """ return False + + @property + def is_unique_for_all_pedalboards(self): + """ + return bool: Is unique for all pedalboards? + Example: :class:`.SystemEffect` is unique for all pedalboards + """ + return False + + @property + def use_real_identifier(self): + """ + Instances of audio plugins are dynamically created, so the effect identifier for the jack can be set. + + However, SystemEffect correspond (mostly) to the audio interfaces already present in the computational system. + The identifier for their jack has already been set. + + return bool: For this audio plugin, is necessary use the real effect identifier? + Example: :class:`.Lv2Effect` is False + Example: :class:`.SystemEffect` is True + """ + return False + + def __repr__(self): + return "<{} object as '{}' at 0x{:x}>".format( + self.__class__.__name__, + str(self), + id(self) + ) + + @property + def version(self): + """ + :return string: Effect version + """ + return '' diff --git a/pluginsmanager/model/input.py b/pluginsmanager/model/input.py index e214bf5..9f7754b 100644 --- a/pluginsmanager/model/input.py +++ b/pluginsmanager/model/input.py @@ -12,12 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from abc import ABCMeta, abstractmethod +from abc import ABCMeta -from unittest.mock import MagicMock +from pluginsmanager.model.audio_port import AudioPort -class Input(metaclass=ABCMeta): +class Input(AudioPort, metaclass=ABCMeta): """ Input is the medium in which the audio will go into effect to be processed. @@ -43,53 +43,15 @@ class Input(metaclass=ABCMeta): >>> my_awesome_effect.inputs[symbol] == effect_input True - For connections between effects, view :class:`pluginsmanager.mod_host.connection.Connection`. + For connections between effects, see :meth:`~pluginsmanager.model.pedalboard.Pedalboard.connect()` + and :meth:`~pluginsmanager.model.pedalboard.Pedalboard.disconnect()` :class:`.Pedalboard` class methods. :param Effect effect: Effect of input """ - def __init__(self, effect): - self._effect = effect - - self.observer = MagicMock() - - self._unique_for_all_pedalboards = False - - @property - def effect(self): - """ - :return: Effect of input - """ - return self._effect - - @property - @abstractmethod - def symbol(self): - """ - :return: Input identifier - """ - pass - - @property - def json(self): - """ - Get a json decodable representation of this input - - :return dict: json representation - """ - return self.__dict__ - - @property - def __dict__(self): - return { - 'effect': self.effect.index, - 'symbol': self.symbol, - 'index': self.index, - } - @property def index(self): """ - :return Input index in the your effect + :return: Input index in the your effect """ return self.effect.inputs.index(self) diff --git a/pluginsmanager/model/lv2/lv2_effect.py b/pluginsmanager/model/lv2/lv2_effect.py index d690465..9afaf04 100644 --- a/pluginsmanager/model/lv2/lv2_effect.py +++ b/pluginsmanager/model/lv2/lv2_effect.py @@ -16,6 +16,8 @@ from pluginsmanager.model.lv2.lv2_param import Lv2Param 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 +from pluginsmanager.model.lv2.lv2_midi_output import Lv2MidiOutput from pluginsmanager.util.dict_tuple import DictTuple @@ -50,6 +52,12 @@ def __init__(self, plugin): outputs = [Lv2Output(self, effect_output) for effect_output in plugin['ports']['audio']['output']] self._outputs = DictTuple(outputs, lambda _output: _output.symbol) + midi_inputs = [Lv2MidiInput(self, midi_input) for midi_input in plugin['ports']['midi']['input']] + self._midi_inputs = DictTuple(midi_inputs, lambda _output: _output.symbol) + + midi_outputs = [Lv2MidiOutput(self, midi_output) for midi_output in plugin['ports']['midi']['output']] + self._midi_outputs = DictTuple(midi_outputs, lambda _output: _output.symbol) + self.instance = None def __str__(self): @@ -70,4 +78,12 @@ def __dict__(self): 'plugin': self.plugin['uri'], 'active': self.active, 'params': [param.json for param in self.params], + 'version': self.version } + + @property + def version(self): + """ + :return string: Version of plugin of effect + """ + return self.plugin.version diff --git a/pluginsmanager/model/lv2/lv2_effect_builder.py b/pluginsmanager/model/lv2/lv2_effect_builder.py index e8f530e..4dd16be 100644 --- a/pluginsmanager/model/lv2/lv2_effect_builder.py +++ b/pluginsmanager/model/lv2/lv2_effect_builder.py @@ -20,6 +20,10 @@ from pluginsmanager.model.lv2.lv2_effect import Lv2Effect +class Lv2EffectBuilderError(Exception): + pass + + class Lv2EffectBuilder(object): """ Generates lv2 audio plugins instance (as :class:`.Lv2Effect` object). @@ -30,6 +34,7 @@ class Lv2EffectBuilder(object): in *plugins.json*. :param Path plugins_json: Plugins json path file + :param bool ignore_unsupported_plugins: Not allows instantiation of uninstalled or unrecognized audio plugins? """ plugins_json_file = os.path.dirname(os.path.abspath(__file__)) + '/plugins.json' @@ -37,7 +42,7 @@ class Lv2EffectBuilder(object): Informs the path of the `plugins.json` file. This file contains the lv2 plugins metadata info """ - def __init__(self, plugins_json=None): + def __init__(self, plugins_json=None, ignore_unsupported_plugins=True): self._plugins = {} if plugins_json is None: @@ -46,17 +51,20 @@ def __init__(self, plugins_json=None): with open(str(plugins_json)) as data_file: data = json.load(data_file) - self.reload(data) + self.reload(data, ignore_unsupported_plugins=ignore_unsupported_plugins) - def reload(self, metadata): + def reload(self, metadata, ignore_unsupported_plugins=True): """ Loads the metadata. They will be used so that it is possible to generate lv2 audio plugins. :param list metadata: lv2 audio plugins metadata + :param bool ignore_unsupported_plugins: Not allows instantiation of uninstalled or unrecognized audio plugins? """ supported_plugins = self._supported_plugins + for plugin in metadata: - if plugin['uri'] in supported_plugins: + if not ignore_unsupported_plugins \ + or plugin['uri'] in supported_plugins: self._plugins[plugin['uri']] = Lv2Plugin(plugin) @property @@ -78,7 +86,15 @@ def build(self, lv2_uri): :param string lv2_uri: :return Lv2Effect: Effect created """ - return Lv2Effect(self._plugins[lv2_uri]) + try: + plugin = self._plugins[lv2_uri] + except KeyError: + raise Lv2EffectBuilderError( + "Lv2EffectBuilder not contains metadata information about the plugin '{}'. \n" + "Try re-scan the installed plugins using the reload method::\n" + " >>> lv2_effect_builder.reload(lv2_effect_builder.lv2_plugins_data())".format(lv2_uri)) + + return Lv2Effect(plugin) def lv2_plugins_data(self): """ diff --git a/pluginsmanager/model/lv2/lv2_input.py b/pluginsmanager/model/lv2/lv2_input.py index 2cd6a3e..1875ff3 100644 --- a/pluginsmanager/model/lv2/lv2_input.py +++ b/pluginsmanager/model/lv2/lv2_input.py @@ -13,9 +13,10 @@ # limitations under the License. from pluginsmanager.model.input import Input +from pluginsmanager.model.lv2.lv2_port_mixing import Lv2PortMixing -class Lv2Input(Input): +class Lv2Input(Lv2PortMixing, Input): """ Representation of a Lv2 `input audio port`_ instance. @@ -24,30 +25,13 @@ class Lv2Input(Input): .. _input audio port: http://lv2plug.in/ns/lv2core/#InputPort :param Lv2Effect effect: Effect that contains the input - :param dict effect_input: *input audio port* json representation + :param dict data: *input audio port* json representation """ - def __init__(self, effect, effect_input): + def __init__(self, effect, data): super(Lv2Input, self).__init__(effect) - self._input = effect_input - - def __str__(self): - return self._input['name'] - - def __repr__(self): - return "<{} object as {} at 0x{:x}>".format( - self.__class__.__name__, - str(self), - id(self) - ) + self._data = data @property - def symbol(self): - return self._input['symbol'] - - @property - def __dict__(self): - dictionary = super(Lv2Input, self).__dict__ - dictionary['index'] = self._input['index'] - - return dictionary + def data(self): + return self._data diff --git a/pluginsmanager/model/lv2/lv2_midi_input.py b/pluginsmanager/model/lv2/lv2_midi_input.py new file mode 100644 index 0000000..64e62e3 --- /dev/null +++ b/pluginsmanager/model/lv2/lv2_midi_input.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.midi_input import MidiInput +from pluginsmanager.model.lv2.lv2_port_mixing import Lv2PortMixing + + +class Lv2MidiInput(Lv2PortMixing, MidiInput): + """ + Representation of a Lv2 midi input port instance. + + For general input use, see :class:`.MidiInput` and + :class:`.Input` classes documentation. + + :param Lv2Effect effect: Effect that contains the midi input + :param dict data: *midi input port* json representation + """ + + def __init__(self, effect, data): + super(Lv2MidiInput, self).__init__(effect) + self._data = data + + @property + def data(self): + return self._data diff --git a/pluginsmanager/model/lv2/lv2_midi_output.py b/pluginsmanager/model/lv2/lv2_midi_output.py new file mode 100644 index 0000000..4b87685 --- /dev/null +++ b/pluginsmanager/model/lv2/lv2_midi_output.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.midi_output import MidiOutput +from pluginsmanager.model.lv2.lv2_port_mixing import Lv2PortMixing + + +class Lv2MidiOutput(Lv2PortMixing, MidiOutput): + """ + Representation of a Lv2 midi output port instance. + + For general input use, see :class:`.MidiOutput` and + :class:`.Output` classes documentation. + + :param Lv2Effect effect: Effect that contains the midi output + :param dict data: *midi output port* json representation + """ + + def __init__(self, effect, data): + super(Lv2MidiOutput, self).__init__(effect) + self._data = data + + @property + def data(self): + return self._data diff --git a/pluginsmanager/model/lv2/lv2_output.py b/pluginsmanager/model/lv2/lv2_output.py index f3c6dfb..18e1187 100644 --- a/pluginsmanager/model/lv2/lv2_output.py +++ b/pluginsmanager/model/lv2/lv2_output.py @@ -13,9 +13,10 @@ # limitations under the License. from pluginsmanager.model.output import Output +from pluginsmanager.model.lv2.lv2_port_mixing import Lv2PortMixing -class Lv2Output(Output): +class Lv2Output(Lv2PortMixing, Output): """ Representation of a Lv2 `output audio port`_ instance. @@ -24,30 +25,13 @@ class Lv2Output(Output): .. _output audio port: http://lv2plug.in/ns/lv2core/#OutputPort :param Lv2Effect effect: Effect that contains the output - :param dict effect_output: *output audio port* json representation + :param dict data: *output audio port* json representation """ - def __init__(self, effect, effect_output): + def __init__(self, effect, data): super(Lv2Output, self).__init__(effect) - self._output = effect_output - - def __str__(self): - return self._output['name'] - - def __repr__(self): - return "<{} object as {} at 0x{:x}>".format( - self.__class__.__name__, - str(self), - id(self) - ) + self._data = data @property - def symbol(self): - return self._output['symbol'] - - @property - def __dict__(self): - dictionary = super(Lv2Output, self).__dict__ - dictionary['index'] = self._output['index'] - - return dictionary + def data(self): + return self._data diff --git a/pluginsmanager/model/lv2/lv2_plugin.py b/pluginsmanager/model/lv2/lv2_plugin.py index 31394a8..170e669 100644 --- a/pluginsmanager/model/lv2/lv2_plugin.py +++ b/pluginsmanager/model/lv2/lv2_plugin.py @@ -43,3 +43,7 @@ def __repr__(self): str(self), id(self) ) + + @property + def version(self): + return '' if 'version' not in self.json else self.json['version'] diff --git a/pluginsmanager/model/lv2/lv2_port_mixing.py b/pluginsmanager/model/lv2/lv2_port_mixing.py new file mode 100644 index 0000000..15db289 --- /dev/null +++ b/pluginsmanager/model/lv2/lv2_port_mixing.py @@ -0,0 +1,50 @@ +# 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 + + +class Lv2PortMixing(object, metaclass=ABCMeta): + """ + Contains the default implementation of System ports: + :class:`.Lv2Input`, :class:`.Lv2Output`, + :class:`.Lv2MidiInput` and :class:`.Lv2MidiInput` + """ + + def __init__(self, *args, **kwargs): + super(Lv2PortMixing, self).__init__(*args, **kwargs) + self._data = None + + @property + @abstractmethod + def data(self): + """ + :return dict: Metadata used for provide the required information + in this object + """ + pass + + def __str__(self): + return self.data['name'] + + @property + def symbol(self): + return self.data['symbol'] + + @property + def __dict__(self): + dictionary = super(Lv2PortMixing, self).__dict__ + dictionary['index'] = self.data['index'] + + return dictionary diff --git a/pluginsmanager/model/midi_connection.py b/pluginsmanager/model/midi_connection.py new file mode 100644 index 0000000..fe97605 --- /dev/null +++ b/pluginsmanager/model/midi_connection.py @@ -0,0 +1,53 @@ +# 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.connection import Connection +from pluginsmanager.model.midi_port import MidiPort + + +class MidiConnection(Connection): + """ + :class:`.MidiConnection` represents a connection between two + distinct effects by your :class:`.MidiPort` (effect :class:`.MidiOutput` with effect :class:`.MidiInput`):: + + >>> californication = Pedalboard('Californication') + >>> californication.append(driver) + >>> californication.append(reverb) + + >>> output_port = cctonode1.midi_outputs[0] + >>> input_port = cctonode2.midi_inputs[0] + + >>> californication.connections.append(MidiConnection(output_port, input_port)) + + Another way to use implicitly connections:: + + >>> californication.connect(output_port, input_port) + + :param MidiOutput output_port: MidiOutput port that will be connected with midi input port + :param MidiInput input_port: MidiInput port that will be connected with midi output port + """ + + @property + def ports_class(self): + """ + :return class: Port class that this connection only accepts + """ + return MidiPort + + @property + def __dict__(self): + dictionary = super(MidiConnection, self).__dict__ + dictionary['type'] = 'midi' + + return dictionary diff --git a/pluginsmanager/model/midi_input.py b/pluginsmanager/model/midi_input.py new file mode 100644 index 0000000..5dc3b10 --- /dev/null +++ b/pluginsmanager/model/midi_input.py @@ -0,0 +1,54 @@ +# 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 + +from pluginsmanager.model.midi_port import MidiPort + + +class MidiInput(MidiPort, metaclass=ABCMeta): + """ + MidiInput is the medium in which the midi input port will go into + effect to be processed. + + For obtains the inputs:: + + >>> cctonode + + >>> cctonode.midi_inputs + (,) + + >>> midi_input = cctonode.midi_inputs[0] + >>> midi_input + + + >>> symbol = midi_input.symbol + >>> symbol + 'midiin' + + >>> cctonode.midi_inputs[symbol] == midi_input + True + + For connections between effects, see :meth:`~pluginsmanager.model.pedalboard.Pedalboard.connect()` + and :meth:`~pluginsmanager.model.pedalboard.Pedalboard.disconnect()` :class:`.Pedalboard` class methods. + + :param Effect effect: Effect of midi input + """ + + @property + def index(self): + """ + :return: MidiInput index in the your effect + """ + return self.effect.midi_inputs.index(self) diff --git a/pluginsmanager/model/midi_output.py b/pluginsmanager/model/midi_output.py new file mode 100644 index 0000000..878c7b8 --- /dev/null +++ b/pluginsmanager/model/midi_output.py @@ -0,0 +1,54 @@ +# 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 + +from pluginsmanager.model.midi_port import MidiPort + + +class MidiOutput(MidiPort, metaclass=ABCMeta): + """ + MidiOutput is the medium in which the midi output processed + by the effect is returned. + + For obtains the outputs:: + + >>> cctonode + + >>> cctonode.outputs + (,) + + >>> midi_output = cctonode.midi_outputs[0] + >>> midi_output + + + >>> symbol = midi_output.symbol + >>> symbol + 'midiout' + + >>> cctonode.midi_outputs[symbol] == midi_output + True + + For connections between effects, see :meth:`~pluginsmanager.model.pedalboard.Pedalboard.connect()` + and :meth:`~pluginsmanager.model.pedalboard.Pedalboard.disconnect()` :class:`.Pedalboard` class methods. + + :param Effect effect: Effect that contains the output + """ + + @property + def index(self): + """ + :return: Output index in the your effect + """ + return self.effect.midi_outputs.index(self) diff --git a/pluginsmanager/model/midi_port.py b/pluginsmanager/model/midi_port.py new file mode 100644 index 0000000..37177de --- /dev/null +++ b/pluginsmanager/model/midi_port.py @@ -0,0 +1,32 @@ +# 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 + +from pluginsmanager.model.port import Port + + +class MidiPort(Port, metaclass=ABCMeta): + """ + Port is a parent abstraction for midi inputs and midi outputs + """ + pass + + @property + def connection_class(self): + """ + :return MidiConnection: Class used for connections in this port + """ + from pluginsmanager.model.midi_connection import MidiConnection + return MidiConnection diff --git a/pluginsmanager/model/output.py b/pluginsmanager/model/output.py index 6fcd876..86acbfb 100644 --- a/pluginsmanager/model/output.py +++ b/pluginsmanager/model/output.py @@ -12,14 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from abc import ABCMeta, abstractmethod +from abc import ABCMeta -from pluginsmanager.model.connection import Connection, ConnectionError +from pluginsmanager.model.audio_port import AudioPort -from unittest.mock import MagicMock - -class Output(metaclass=ABCMeta): +class Output(AudioPort, metaclass=ABCMeta): """ Output is the medium in which the audio processed by the effect is returned. @@ -43,110 +41,15 @@ class Output(metaclass=ABCMeta): >>> my_awesome_effect.outputs[symbol] == output True - For connections between effects, view :class:`.pluginsmanager.mod_host.connection.Connection`. + For connections between effects, see :meth:`~pluginsmanager.model.pedalboard.Pedalboard.connect()` + and :meth:`~pluginsmanager.model.pedalboard.Pedalboard.disconnect()` :class:`.Pedalboard` class methods. :param Effect effect: Effect that contains the output """ - def __init__(self, effect): - self._effect = effect - - self.observer = MagicMock() - - self._unique_for_all_pedalboards = False - - @property - def effect(self): - """ - :return: Effect of output - """ - return self._effect - - def connect(self, effect_input): - """ - Connect it with effect_input:: - - >>> driver_output = driver.outputs[0] - >>> reverb_input = reverb.inputs[0] - >>> Connection(driver_output, reverb_input) in driver.effect.connections - False - >>> driver_output.connect(reverb_input) - >>> Connection(driver_output, reverb_input) in driver.effect.connections - True - - .. note:: - - This method does not work for all cases. - class:`SystemOutput` can not be connected with class:`SystemInput` this way. - For this case, use :: - - >>> pedalboard.connections.append(Connection(system_output, system_input)) - - :param Input effect_input: Input that will be connected with it - """ - if self._unique_for_all_pedalboards and effect_input._unique_for_all_pedalboards: - error = "Isn't possible connect this way. Please use pedalboard.connect(Connection(output, input))" - raise ConnectionError(error) - - pedalboard = self.effect.pedalboard if not self._unique_for_all_pedalboards else effect_input.effect.pedalboard - pedalboard.connections.append(Connection(self, effect_input)) - - def disconnect(self, effect_input): - """ - Disconnect it with effect_input - - >>> driver_output = driver.outputs[0] - >>> reverb_input = reverb.inputs[0] - >>> Connection(driver_output, reverb_input) in driver.effect.connections - True - >>> driver_output.disconnect(reverb_input) - >>> Connection(driver_output, reverb_input) in driver.effect.connections - False - - .. note:: - - This method does not work for all cases. - class:`SystemOutput` can not be disconnected with class:`SystemInput` this way. - For this case, use :: - - >>> pedalboard.connections.remove(Connection(system_output, system_input)) - - :param Input effect_input: Input that will be disconnected with it - """ - if self._unique_for_all_pedalboards and effect_input._unique_for_all_pedalboards: - error = "Isn't possible connect this way. Please use pedalboard.connect(Connection(output, input))" - raise ConnectionError(error) - - self.effect.pedalboard.connections.remove(Connection(self, effect_input)) - - @property - @abstractmethod - def symbol(self): - """ - :return: Output identifier - """ - pass - - @property - def json(self): - """ - Get a json decodable representation of this output - - :return dict: json representation - """ - return self.__dict__ - - @property - def __dict__(self): - return { - 'effect': self.effect.index, - 'symbol': self.symbol, - 'index': self.index, - } - @property def index(self): """ - :return Output index in the your effect + :return: Output index in the your effect """ return self.effect.outputs.index(self) diff --git a/pluginsmanager/model/param.py b/pluginsmanager/model/param.py index 353a5c3..cc8a892 100644 --- a/pluginsmanager/model/param.py +++ b/pluginsmanager/model/param.py @@ -148,8 +148,6 @@ def json(self): def __dict__(self): return { 'index': self.index, - 'minimum': self.minimum, - 'maximum': self.maximum, 'symbol': self.symbol, 'value': self.value, } diff --git a/pluginsmanager/model/pedalboard.py b/pluginsmanager/model/pedalboard.py index 002dec9..06eff1c 100644 --- a/pluginsmanager/model/pedalboard.py +++ b/pluginsmanager/model/pedalboard.py @@ -18,10 +18,14 @@ from unittest.mock import MagicMock +class PedalboardError(Exception): + pass + + class Pedalboard(object): """ Pedalboard is a patch representation: your structure contains - :class:`.Effect` and :class:`pluginsmanager.mod_host.connection.Connection`:: + :class:`.Effect` and :class:`~pluginsmanager.model.connection.Connection`:: >>> pedalboard = Pedalboard('Rocksmith') >>> bank.append(pedalboard) @@ -42,7 +46,8 @@ class Pedalboard(object): >>> pedalboard.connections.append(Connection(sys_effect.outputs[0], fuzz.inputs[0])) # View SystemEffect for more details >>> pedalboard.connections.append(Connection(fuzz.outputs[0], reverb.inputs[0])) >>> # It works too - >>> reverb.outputs[1].connect(sys_effect.inputs[0]) + >>> pedalboard.connect(reverb.outputs[1], sys_effect.inputs[0]) + >>> pedalboard.connections ObservableList: [ GxFuzzFaceFullerMod.In' at 0x7f60f45f3f60>, Calf Reverb.In L' at 0x7f60f45f57f0>, system.playback_1' at 0x7f60f45dacc0>] >>> pedalboard.data @@ -150,6 +155,10 @@ def append(self, effect): :param Effect effect: Effect that will be added """ + if effect.is_unique_for_all_pedalboards: + raise PedalboardError("The effect '{}' is unique for all pedalboards. " + "Then, isn't allowed add it in any pedalboard.".format(str(effect))) + self.effects.append(effect) @property @@ -187,3 +196,46 @@ def index(self): raise IndexError('Pedalboard not contains a bank') return self.bank.pedalboards.index(self) + + def connect(self, output_port, input_port): + """ + Connect two :class:`.Effect` instances in this pedalboard. + For this, is necessary informs the output port origin and the input port destination:: + + >>> pedalboard.append(driver) + >>> pedalboard.append(reverb) + >>> driver_output = driver.outputs[0] + >>> reverb_input = reverb.inputs[0] + >>> Connection(driver_output, reverb_input) in driver.connections + False + >>> pedalboard.connect(driver_output, reverb_input) + >>> Connection(driver_output, reverb_input) in driver.connections + True + + :param Port output_port: Effect output port + :param Port input_port: Effect input port + """ + ConnectionClass = output_port.connection_class + self.connections.append(ConnectionClass(output_port, input_port)) + + def disconnect(self, output_port, input_port): + """ + Remove a connection between (two ports of) :class:`.Effect` instances. + For this, is necessary informs the output port origin and the input port destination:: + + >>> pedalboard.append(driver) + >>> pedalboard.append(reverb) + >>> driver_output = driver.outputs[0] + >>> reverb_input = reverb.inputs[0] + >>> pedalboard.connect(driver_output, reverb_input) + >>> Connection(driver_output, reverb_input) in driver.connections + True + >>> pedalboard.disconnect(driver_output, reverb_input) + >>> Connection(driver_output, reverb_input) in driver.connections + False + + :param Port output_port: Effect output port + :param Port input_port: Effect input port + """ + ConnectionClass = output_port.connection_class + self.connections.remove(ConnectionClass(output_port, input_port)) diff --git a/pluginsmanager/model/port.py b/pluginsmanager/model/port.py new file mode 100644 index 0000000..1404c6d --- /dev/null +++ b/pluginsmanager/model/port.py @@ -0,0 +1,86 @@ +# 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 Port(metaclass=ABCMeta): + """ + Port is a parent abstraction for inputs and outputs + + :param Effect effect: Effect that contains port + """ + + def __init__(self, effect): + self._effect = effect + self.observer = MagicMock() + + @property + @abstractmethod + def symbol(self): + """ + :return: Identifier for this port + """ + pass + + @property + def effect(self): + """ + :return: Effect that this port is related + """ + return self._effect + + @property + def json(self): + """ + Get a json decodable representation + + :return dict: json representation + """ + return self.__dict__ + + @property + @abstractmethod + def index(self): + """ + :return: Index in the effect related based in your category. + As example, if this port is a :class:`input`, the + index returns your position in the inputs ports. + """ + pass + + @property + def __dict__(self): + return { + 'effect': self.effect.index, + 'symbol': self.symbol, + 'index': self.index, + } + + def __repr__(self): + return "<{} object as {} at 0x{:x}>".format( + self.__class__.__name__, + str(self), + id(self) + ) + + @property + @abstractmethod + def connection_class(self): + """ + :return: Class used for connections in this port + """ + pass diff --git a/pluginsmanager/model/system/system_effect.py b/pluginsmanager/model/system/system_effect.py index c89ac64..47aea8d 100644 --- a/pluginsmanager/model/system/system_effect.py +++ b/pluginsmanager/model/system/system_effect.py @@ -15,13 +15,15 @@ from pluginsmanager.model.effect import Effect from pluginsmanager.model.system.system_input import SystemInput from pluginsmanager.model.system.system_output import SystemOutput +from pluginsmanager.model.system.system_midi_input import SystemMidiInput +from pluginsmanager.model.system.system_midi_output import SystemMidiOutput from pluginsmanager.util.dict_tuple import DictTuple class SystemEffect(Effect): """ - Representation of the system instance (audio cards). + Representation of the system instance: audio and/or midi interfaces. System output is equivalent with audio input: You connect the instrument in the audio card input and it captures and send the @@ -47,44 +49,92 @@ class SystemEffect(Effect): However the pedalboard must have the connections:: - >>> pedalboard.connections.append(Connection(sys_effect.outputs[0], reverb.inputs[0])) + >>> pedalboard.connect(sys_effect.outputs[0], reverb.inputs[0]) An bypass example:: >>> pedalboard = Pedalboard('Bypass example') >>> sys_effect = SystemEffect('system', ('capture_1', 'capture_2'), ('playback_1', 'playback_2')) - >>> pedalboard.connections.append(Connection(sys_effect.outputs[0], sys_effect.inputs[0])) - >>> pedalboard.connections.append(Connection(sys_effect.outputs[1], sys_effect.inputs[1])) + >>> pedalboard.connect(sys_effect.outputs[0], sys_effect.inputs[0]) + >>> pedalboard.connect(sys_effect.outputs[1], sys_effect.inputs[1]) + + You can create multiple SystemEffect for multiple audio/midi interfaces. In the following example, + exists Jack provides audio system ports and two midi ports are added by I/O ports:: + + >>> audio_system = SystemEffect('system', inputs=['playback_1', 'playback_2']) + >>> midi_system = SystemEffect('ttymidi', midi_outputs=['MIDI_in'], midi_inputs=['MIDI_out']) + >>> pedalboard = Pedalboard('MDA-EP') + >>> ep = builder.build('http://moddevices.com/plugins/mda/EPiano') + >>> pedalboard.connect(ep.outputs[0], audio_system.inputs[0]) + >>> pedalboard.connect(ep.outputs[1], audio_system.inputs[1]) + >>> pedalboard.connect(audio_system.midi_outputs[0], ep.midi_inputs[0]) + + You can check the audio/midi ports defined in your environment using `jack_lsp`_:: + + root@zynthian:~ # As example in Zynthian project: http://zynthian.org + root@zynthian:~ jack_lsp -A + system:playback_1 + alsa_pcm:hw:0:in1 + system:playback_2 + alsa_pcm:hw:0:in2 + ttymidi:MIDI_in + ttymidi:MIDI_out + Zyncoder:output + Zyncoder:input + + .. _jack_lsp: http://manpages.ubuntu.com/manpages/xenial/man1/jack_lsp.1.html + + If you prefer, you can use a unique SystemEffect if `alias` the ports:: + + localhost@localdomain:~ jack_alias system:midi_capture1 ttymidi:MIDI_in + localhost@localdomain:~ jack_alias system:midi_playback1 ttymidi:MIDI_out + + >>> sys_effect = SystemEffect( + ... 'system', + ... inputs=['playback_1', 'playback_2'], + ... midi_outputs=['midi_capture1'], + ... midi_inputs=['midi_playback1'] + ... ) + >>> pedalboard = Pedalboard('MDA-EP') + >>> ep = builder.build('http://moddevices.com/plugins/mda/EPiano') + >>> pedalboard.connect(ep.outputs[0], sys_effect.inputs[0]) + >>> pedalboard.connect(ep.outputs[1], sys_effect.inputs[1]) + >>> pedalboard.connect(sys_effect.midi_outputs[0], ep.midi_inputs[0]) :param string representation: Audio card representation. Usually 'system' :param tuple(string) outputs: Tuple of outputs representation. Usually a output representation - starts with `capture_` + starts with ``capture_`` :param tuple(string) inputs: Tuple of inputs representation. Usually a input representation - starts with `playback_` + starts with ``playback_`` + :param tuple(string) midi_outputs: Tuple of midi outputs representation. + :param tuple(string) midi_inputs: Tuple of midi inputs representation. """ - def __init__(self, representation, outputs, inputs): + def __init__(self, representation, outputs=None, inputs=None, midi_outputs=None, midi_inputs=None): super(SystemEffect, self).__init__() self.representation = representation self._params = tuple() + inputs = inputs if inputs is not None else [] inputs = [SystemInput(self, effect_input) for effect_input in inputs] self._inputs = DictTuple(inputs, lambda _input: str(_input)) + outputs = outputs if outputs is not None else [] outputs = [SystemOutput(self, effect_output) for effect_output in outputs] self._outputs = DictTuple(outputs, lambda _output: str(_output)) + midi_inputs = midi_inputs if midi_inputs is not None else [] + midi_inputs = [SystemMidiInput(self, effect_input) for effect_input in midi_inputs] + self._midi_inputs = DictTuple(midi_inputs, lambda _input: str(_input)) + + midi_outputs = midi_outputs if midi_outputs is not None else [] + midi_outputs = [SystemMidiOutput(self, effect_output) for effect_output in midi_outputs] + self._midi_outputs = DictTuple(midi_outputs, lambda _output: str(_output)) + def __str__(self): return self.representation - def __repr__(self): - return "<{} object as '{}' at 0x{:x}>".format( - self.__class__.__name__, - str(self), - id(self) - ) - @property def __dict__(self): return { @@ -97,3 +147,18 @@ def is_possible_connect_itself(self): return bool: Is possible connect the with it self? """ return True + + @property + def is_unique_for_all_pedalboards(self): + """ + return bool: Is unique for all pedalboards? + Example: :class:`.SystemEffect` is unique for all pedalboards + """ + return True + + @property + def use_real_identifier(self): + """ + return bool: For this audio plugin, is necessary use the real effect identifier? + """ + return True diff --git a/pluginsmanager/model/system/system_effect_builder.py b/pluginsmanager/model/system/system_effect_builder.py index f4c8636..8854cd7 100644 --- a/pluginsmanager/model/system/system_effect_builder.py +++ b/pluginsmanager/model/system/system_effect_builder.py @@ -15,9 +15,16 @@ from pluginsmanager.model.system.system_effect import SystemEffect + class SystemEffectBuilder(object): """ - Automatic system physical ports detection + Automatic system physical ports detection. + + Maybe the midi ports not will recognize. In these cases, + you need to start `a2jmidid`_ to get MIDI-ALSA ports automatically + mapped to JACK-MIDI ports. + + .. _a2jmidid: http://manual.ardour.org/setting-up-your-system/setting-up-midi/midi-on-linux/ :param JackClient jack_client: :class:`.JackClient` instance that will get the information to generate :class:`.SystemEffect` @@ -26,13 +33,9 @@ def __init__(self, jack_client): self.client = jack_client def build(self): - inputs = [] - outputs = [] - - for port in self.client.client.get_ports(is_audio=True, is_physical=True): - if port.is_input: - inputs.append(port.shortname) - else: - outputs.append(port.shortname) + inputs = (port.shortname for port in self.client.audio_inputs) + outputs = (port.shortname for port in self.client.audio_outputs) + midi_inputs = (port.shortname for port in self.client.midi_inputs) + midi_outputs = (port.shortname for port in self.client.midi_outputs) - return SystemEffect('system', tuple(outputs), tuple(inputs)) + return SystemEffect('system', outputs, inputs, midi_outputs, midi_inputs) diff --git a/pluginsmanager/model/system/system_input.py b/pluginsmanager/model/system/system_input.py index 4ba941b..fc82dde 100644 --- a/pluginsmanager/model/system/system_input.py +++ b/pluginsmanager/model/system/system_input.py @@ -13,32 +13,23 @@ # limitations under the License. from pluginsmanager.model.input import Input +from pluginsmanager.model.system.system_port_mixing import SystemPortMixing -class SystemInput(Input): +class SystemInput(SystemPortMixing, Input): + """ + Representation of a System input audio port instance. - def __init__(self, effect, system_input): - super(SystemInput, self).__init__(effect) - self._input = system_input - self._unique_for_all_pedalboards = True + For general input use, see :class:`.Input` class documentation. - def __str__(self): - return self._input + :param SystemEffect effect: Effect that contains the input + :param string symbol: *input audio port* symbol identifier + """ - def __repr__(self): - return "<{} object as {} at 0x{:x}>".format( - self.__class__.__name__, - str(self), - id(self) - ) + def __init__(self, effect, symbol): + super(SystemInput, self).__init__(effect) + self._symbol = symbol @property def symbol(self): - return str(self) - - @property - def __dict__(self): - return { - 'symbol': self.symbol, - 'index': self.effect.inputs.index(self), - } + return self._symbol diff --git a/pluginsmanager/model/system/system_midi_input.py b/pluginsmanager/model/system/system_midi_input.py new file mode 100644 index 0000000..eb7b2f3 --- /dev/null +++ b/pluginsmanager/model/system/system_midi_input.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.midi_input import MidiInput +from pluginsmanager.model.system.system_port_mixing import SystemPortMixing + + +class SystemMidiInput(SystemPortMixing, MidiInput): + """ + Representation of a System midi input port instance. + + For general input use, see :class:`.Input` and + :class:`.MidiInput` classes documentation. + + :param SystemEffect effect: Effect that contains the input + :param string symbol: *midi input port* symbol identifier + """ + + def __init__(self, effect, symbol): + super(SystemMidiInput, self).__init__(effect) + self._symbol = symbol + + @property + def symbol(self): + return self._symbol diff --git a/pluginsmanager/model/system/system_midi_output.py b/pluginsmanager/model/system/system_midi_output.py new file mode 100644 index 0000000..9364ddb --- /dev/null +++ b/pluginsmanager/model/system/system_midi_output.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.midi_output import MidiOutput +from pluginsmanager.model.system.system_port_mixing import SystemPortMixing + + +class SystemMidiOutput(SystemPortMixing, MidiOutput): + """ + Representation of a System midi output port instance. + + For general input use, see :class:`.Output` and + :class:`.MidiOutput` classes documentation. + + :param SystemEffect effect: Effect that contains the input + :param string symbol: *midi output port* symbol identifier + """ + + def __init__(self, effect, symbol): + super(SystemMidiOutput, self).__init__(effect) + self._symbol = symbol + + @property + def symbol(self): + return self._symbol diff --git a/pluginsmanager/model/system/system_output.py b/pluginsmanager/model/system/system_output.py index 64f1afe..3258359 100644 --- a/pluginsmanager/model/system/system_output.py +++ b/pluginsmanager/model/system/system_output.py @@ -13,32 +13,23 @@ # limitations under the License. from pluginsmanager.model.output import Output +from pluginsmanager.model.system.system_port_mixing import SystemPortMixing -class SystemOutput(Output): +class SystemOutput(SystemPortMixing, Output): + """ + Representation of a System output audio port instance. - def __init__(self, effect, output): - super(SystemOutput, self).__init__(effect) - self._output = output - self._unique_for_all_pedalboards = True + For general input use, see :class:`.Output` class documentation. - def __str__(self): - return self._output + :param SystemEffect effect: Effect that contains the input + :param string symbol: *output audio port* symbol identifier + """ - def __repr__(self): - return "<{} object as {} at 0x{:x}>".format( - self.__class__.__name__, - str(self), - id(self) - ) + def __init__(self, effect, symbol): + super(SystemOutput, self).__init__(effect) + self._symbol = symbol @property def symbol(self): - return str(self) - - @property - def __dict__(self): - return { - 'symbol': self.symbol, - 'index': self.effect.outputs.index(self), - } + return self._symbol diff --git a/pluginsmanager/model/system/system_port_mixing.py b/pluginsmanager/model/system/system_port_mixing.py new file mode 100644 index 0000000..33cc1a3 --- /dev/null +++ b/pluginsmanager/model/system/system_port_mixing.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 abc import ABCMeta + + +class SystemPortMixing(object, metaclass=ABCMeta): + """ + Contains the default implementation of System ports: + :class:`.SystemInput`, :class:`.SystemOutput`, + :class:`.SystemMidiInput` and :class:`.SystemMidiInput` + """ + + def __init__(self, *args, **kwargs): + super(SystemPortMixing, self).__init__(*args, **kwargs) + + def __str__(self): + return self.symbol + + @property + def __dict__(self): + return { + 'symbol': self.symbol, + 'index': self.index, + } diff --git a/pluginsmanager/observer/mod_host/mod_host.py b/pluginsmanager/observer/mod_host/mod_host.py index 644cd01..e00bf36 100644 --- a/pluginsmanager/observer/mod_host/mod_host.py +++ b/pluginsmanager/observer/mod_host/mod_host.py @@ -292,6 +292,7 @@ def _change_pedalboard(self, pedalboard): def _remove_pedalboard(self, pedalboard): self._remove_effects(pedalboard.effects) + self._remove_connections_of(pedalboard) def _remove_connections_of(self, pedalboard): for connection in pedalboard.connections: diff --git a/pluginsmanager/observer/mod_host/protocol_parser.py b/pluginsmanager/observer/mod_host/protocol_parser.py index 9db2dbc..834d44c 100644 --- a/pluginsmanager/observer/mod_host/protocol_parser.py +++ b/pluginsmanager/observer/mod_host/protocol_parser.py @@ -80,22 +80,22 @@ def _connect_message(origin_port, destination_port): return 'connect {} {}'.format(origin_port, destination_port) @staticmethod - def _get_out_name_of(effect_output): - effect = effect_output.effect + def _get_out_name_of(output_of_effect): + effect = output_of_effect.effect - if isinstance(effect_output, SystemOutput): - return '{}:{}'.format(effect, effect_output) + if effect.use_real_identifier: + return '{}:{}'.format(effect, output_of_effect) - return 'effect_{}:{}'.format(effect.instance, effect_output.symbol) + return 'effect_{}:{}'.format(effect.instance, output_of_effect.symbol) @staticmethod - def _get_in_name_of(effect_input): - effect = effect_input.effect + def _get_in_name_of(input_of_effect): + effect = input_of_effect.effect - if isinstance(effect_input, SystemInput): - return '{}:{}'.format(effect, effect_input) + if effect.use_real_identifier: + return '{}:{}'.format(effect, input_of_effect) - return 'effect_{}:{}'.format(effect.instance, effect_input.symbol) + return 'effect_{}:{}'.format(effect.instance, input_of_effect.symbol) @staticmethod def disconnect(connection): diff --git a/pluginsmanager/util/builder/builder.py b/pluginsmanager/util/builder/builder.py index 84d8835..3580f60 100644 --- a/pluginsmanager/util/builder/builder.py +++ b/pluginsmanager/util/builder/builder.py @@ -34,7 +34,21 @@ def build_input(self, json): @abstractmethod def build_output(self, json): """ - :return Output: Input of an effect defined in json + :return Output: Output of an effect defined in json + """ + pass + + @abstractmethod + def build_midi_input(self, json): + """ + :return MidiInput: MidiInput of an effect defined in json + """ + pass + + @abstractmethod + def build_midi_output(self, json): + """ + :return MidiOutput: MidiOutput of an effect defined in json """ pass diff --git a/pluginsmanager/util/builder/lv2_json_builder.py b/pluginsmanager/util/builder/lv2_json_builder.py index 7e51fac..a890bfa 100644 --- a/pluginsmanager/util/builder/lv2_json_builder.py +++ b/pluginsmanager/util/builder/lv2_json_builder.py @@ -30,6 +30,14 @@ def build_output(self, json): symbol = json['symbol'] return self.get_effect(json).outputs[symbol] + def build_midi_input(self, json): + symbol = json['symbol'] + return self.get_effect(json).midi_inputs[symbol] + + def build_midi_output(self, json): + symbol = json['symbol'] + return self.get_effect(json).midi_outputs[symbol] + def get_effect(self, json): effect_index = json['effect'] return self.pedalboard.effects[effect_index] @@ -51,4 +59,3 @@ def build(self, json): effect.active = json['active'] return effect - diff --git a/pluginsmanager/util/builder/system_json_builder.py b/pluginsmanager/util/builder/system_json_builder.py index 35cb394..f2ab3ab 100644 --- a/pluginsmanager/util/builder/system_json_builder.py +++ b/pluginsmanager/util/builder/system_json_builder.py @@ -30,3 +30,11 @@ def build_input(self, json): def build_output(self, json): symbol = json['symbol'] return self.system_effect.outputs[symbol] + + def build_midi_input(self, json): + symbol = json['symbol'] + return self.system_effect.midi_inputs[symbol] + + def build_midi_output(self, json): + symbol = json['symbol'] + return self.system_effect.midi_outputs[symbol] diff --git a/pluginsmanager/util/dict_tuple.py b/pluginsmanager/util/dict_tuple.py index d22f584..0961f7d 100644 --- a/pluginsmanager/util/dict_tuple.py +++ b/pluginsmanager/util/dict_tuple.py @@ -16,7 +16,7 @@ class DictTuple(tuple): """ Dict tuple is a union with dicts and tuples. It's possible obtains an element - by index or by a key. + by index or by a key The key is not been a int or long instance @@ -41,3 +41,6 @@ def __getitem__(self, index): else: return self._dict[index] + + def __contains__(self, item): + return item in self._dict diff --git a/pluginsmanager/util/mod_pedalboard_converter.py b/pluginsmanager/util/mod_pedalboard_converter.py new file mode 100644 index 0000000..65b4f09 --- /dev/null +++ b/pluginsmanager/util/mod_pedalboard_converter.py @@ -0,0 +1,190 @@ +# 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. + +import sys +from pathlib import Path + +from pluginsmanager.model.pedalboard import Pedalboard +from pluginsmanager.model.system.system_effect import SystemEffect + + +class PortNotFoundError(Exception): + pass + + +class ModPedalboardConverter(object): + """ + ModPedalboardConverter is a utility to convert MOD [#]_ pedalboards structure + in plugins manager pedalboard. + + For use, is necessary that the computer system contains the mod_ui with your codes compiled [#]_ + and the pedalboard:: + + >>> path = Path('/home/user/git/mod/mod_ui/') + >>> builder = Lv2EffectBuilder() + >>> converter = ModPedalboardConverter(path, builder) + >>> pedalboard_path = Path('/home/user/.pedalboards/pedalboard_name.pedalboard') + >>> system_effect = SystemEffect('system', ['capture_1', 'capture_2'], ['playback_1', 'playback_2']) + >>> pedalboard = converter.convert(pedalboard_path, system_effect) + + ModPedalboardConverter can try discover the `system_pedalboard` by the pedalboard:: + + >>> path = Path('/home/user/git/mod/mod_ui/') + >>> builder = Lv2EffectBuilder() + >>> converter = ModPedalboardConverter(path, builder) + >>> pedalboard_path = Path('/home/user/.pedalboards/pedalboard_name.pedalboard') + >>> pedalboard = converter.convert(pedalboard_path) + + If you needs only obtain the system effect:: + + >>> path = Path('/home/user/git/mod/mod_ui/') + >>> builder = Lv2EffectBuilder() + >>> converter = ModPedalboardConverter(path, builder) + >>> pedalboard_path = Path('/home/user/.pedalboards/pedalboard_name.pedalboard') + >>> pedalboard_info = converter.get_pedalboard_info(pedalboard_path) + >>> system_effect = converter.discover_system_effect(pedalboard_info) + + .. [#] `MOD`_, an awesome music enterprise, create the `mod-ui`_, a visual interface + that enable create pedalboards in a simple way. + .. [#] See the docs: https://github.com/moddevices/mod-ui#install + + .. _MOD: http://moddevices.com/ + .. _mod-ui: https://github.com/moddevices/mod-ui + + :param Path mod_ui_path: path that mod_ui has in the computer. + :param Lv2EffectBuilder builder: Builder for generate the lv2 effects + :param bool ignore_errors: Ignore pedalboard problems like connections with undefined ports + """ + + def __init__(self, mod_ui_path, builder, ignore_errors=False): + self._load_mod_ui_libraries(mod_ui_path) + self.builder = builder + self.ignore_errors = ignore_errors + + def _load_mod_ui_libraries(self, path): + """ + :param Path path: + """ + path = path / Path('mod') + sys.path.append(str(path)) + + def get_pedalboard_info(self, path): + """ + :param Path path: Path that the pedalboard has been persisted. + Generally is in format ``path/to/pedalboard/name.pedalboard`` + :return dict: pedalboard persisted configurations + """ + from utils import get_pedalboard_info + + return get_pedalboard_info(str(path)) + + def convert(self, pedalboard_path, system_effect=None): + """ + :param Path pedalboard_path: Path that the pedalboard has been persisted. + Generally is in format `path/to/pedalboard/name.pedalboard` + :param SystemEffect system_effect: Effect that contains the audio interface outputs and inputs + or None for **auto discover** + :return Pedalboard: Pedalboard loaded + """ + info = self.get_pedalboard_info(pedalboard_path) + + if system_effect is None: + system_effect = self.discover_system_effect(info) + + pedalboard = Pedalboard(info['title']) + + effects_instance = {} + + for effect_data in info['plugins']: + effect = self._generate_effect(effect_data) + pedalboard.append(effect) + effects_instance[effect_data['instance']] = effect + + try: + for connection_data in info['connections']: + output_port = self._get_port(connection_data['source'], effects_instance, system_effect) + input_port = self._get_port(connection_data['target'], effects_instance, system_effect) + + pedalboard.connect(output_port, input_port) + except PortNotFoundError as e: + if self.ignore_errors: + print("WARNING:", e) + else: + raise e + + return pedalboard + + def _generate_effect(self, effect_data): + effect = self.builder.build(effect_data['uri']) + effect.active = not effect_data['bypassed'] + + for param_data in effect_data['ports']: + effect.params[param_data['symbol']].value = param_data['value'] + + return effect + + def _get_port(self, name, effects_instance, system_effect): + effect, port = self.filter_effect_port_symbol(name, effects_instance, system_effect) + + possible_ports = (effect.outputs, effect.midi_outputs, effect.inputs, effect.midi_inputs) + filtered = filter(lambda ports: port in ports, possible_ports) + + ports = list(filtered)[0] + return ports[port] + + def filter_effect_port_symbol(self, name, effects_instance, system_effect): + if '/' in name: + instance, port = name.split('/') + effect = effects_instance[instance] + elif self._is_system_effect(name, system_effect): + effect = system_effect + port = name + else: + raise PortNotFoundError("Port '{}' registered in system_effect?".format(name)) + + return effect, port + + def _is_system_effect(self, name, system_effect): + return name in system_effect.inputs or \ + name in system_effect.midi_inputs or \ + name in system_effect.outputs or \ + name in system_effect.midi_outputs + + def discover_system_effect(self, pedalboard_info): + """ + Generate the system effect based in pedalboard_info + + :param dict pedalboard_info: For obtain this, see + :meth:`~pluginsmanager.util.mod_pedalboard_converter.ModPedalboardConvert.get_pedalboard_info()` + :return SystemEffect: SystemEffect generated based in pedalboard_info + """ + # MOD swap ins and outs!!! + hardware = pedalboard_info['hardware'] + + total_audio_outs = hardware['audio_ins'] + total_audio_ins = hardware['audio_outs'] + + outputs = ['capture_{}'.format(i) for i in range(1, total_audio_outs+1)] + inputs = ['playback_{}'.format(i) for i in range(1, total_audio_ins+1)] + + midi_inputs = [ + 'serial_midi_out' if hardware['serial_midi_out'] else midi_out['symbol'] + for midi_out in hardware['midi_outs'] if midi_out['valid'] + ] + midi_outputs = [ + 'serial_midi_in' if hardware['serial_midi_in'] else midi_in['symbol'] + for midi_in in hardware['midi_ins'] if midi_in['valid'] + ] + + return SystemEffect('system', outputs, inputs, midi_outputs, midi_inputs) diff --git a/pluginsmanager/util/persistence_decoder.py b/pluginsmanager/util/persistence_decoder.py index 289478f..1ae002f 100644 --- a/pluginsmanager/util/persistence_decoder.py +++ b/pluginsmanager/util/persistence_decoder.py @@ -13,10 +13,8 @@ # limitations under the License. from pluginsmanager.model.bank import Bank -from pluginsmanager.model.pedalboard import Pedalboard -from pluginsmanager.model.connection import Connection - from pluginsmanager.model.lv2.lv2_effect_builder import Lv2EffectBuilder as Lv2LilvEffectBuilder +from pluginsmanager.model.pedalboard import Pedalboard from pluginsmanager.util.builder.lv2_json_builder import Lv2AudioPortBuilder, Lv2EffectBuilder from pluginsmanager.util.builder.system_json_builder import SystemAudioPortBuilder @@ -67,7 +65,8 @@ def read(self, json): connection_reader = ConnectionReader(pedalboard, self.system_effect) for connection_json in json['connections']: - pedalboard.connections.append(connection_reader.read(connection_json)) + port_output, port_input = connection_reader.read(connection_json) + pedalboard.connect(port_output, port_input) if 'data' in json: pedalboard.data = json['data'] @@ -100,10 +99,14 @@ def __init__(self, pedalboard, system_effect): self.pedalboard = pedalboard def read(self, json): - connection_output = self.generate_builder(json, 'output').build_output(json['output']) - connection_input = self.generate_builder(json, 'input').build_input(json['input']) + if json['type'] == 'audio': + connection_output = self.generate_builder(json, 'output').build_output(json['output']) + connection_input = self.generate_builder(json, 'input').build_input(json['input']) + else: + connection_output = self.generate_builder(json, 'output').build_midi_output(json['output']) + connection_input = self.generate_builder(json, 'input').build_midi_input(json['input']) - return Connection(connection_output, connection_input) + return connection_output, connection_input def generate_builder(self, json, audio_port): """ diff --git a/setup.py b/setup.py index 7716bd8..59a4238 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ def readme(): setup( name='PedalPi-PluginsManager', - version='0.5.1', + version='0.6.0', description='Pythonic management of LV2 audio plugins with mod-host.', long_description=readme(), @@ -62,7 +62,7 @@ def readme(): ], test_suite='test', - tests_require=['JACK-Client'], + tests_require=['JACK-Client', 'pytest', 'pytest-cov'], classifiers=[ 'Development Status :: 3 - Alpha', diff --git a/test/banks_manager_test.py b/test/banks_manager_test.py index cbf6240..ac58e6f 100644 --- a/test/banks_manager_test.py +++ b/test/banks_manager_test.py @@ -51,25 +51,25 @@ def test_observers_calls(self): pedalboard.append(reverb2) observer.on_effect_updated.assert_called_with(reverb2, UpdateType.CREATED, index=reverb2.index, origin=pedalboard) - reverb.outputs[0].connect(filter.inputs[0]) + pedalboard.connect(reverb.outputs[0], filter.inputs[0]) observer.on_connection_updated.assert_called_with( Connection(reverb.outputs[0], filter.inputs[0]), UpdateType.CREATED, pedalboard=pedalboard ) - reverb.outputs[1].connect(filter.inputs[0]) + pedalboard.connect(reverb.outputs[1], filter.inputs[0]) observer.on_connection_updated.assert_called_with( Connection(reverb.outputs[1], filter.inputs[0]), UpdateType.CREATED, pedalboard=pedalboard ) - filter.outputs[0].connect(reverb2.inputs[0]) + pedalboard.connect(filter.outputs[0], reverb2.inputs[0]) observer.on_connection_updated.assert_called_with( Connection(filter.outputs[0], reverb2.inputs[0]), UpdateType.CREATED, pedalboard=pedalboard ) - reverb.outputs[0].connect(reverb2.inputs[0]) + pedalboard.connect(reverb.outputs[0], reverb2.inputs[0]) observer.on_connection_updated.assert_called_with( Connection(reverb.outputs[0], reverb2.inputs[0]), UpdateType.CREATED, @@ -139,3 +139,13 @@ def test_observers(self): manager.register(observer2) self.assertListEqual([observer1, observer2], manager.observers) + + def test_unregister(self): + observer = MagicMock() + + manager = BanksManager() + manager.register(observer) + + self.assertIn(observer, manager.observers) + manager.unregister(observer) + self.assertNotIn(observer, manager.observers) diff --git a/test/jack/__init__.py b/test/jack/__init__.py index e69de29..75323bb 100644 --- a/test/jack/__init__.py +++ b/test/jack/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/test/mod_host/mod_host_test.py b/test/mod_host/mod_host_test.py index 6e08575..add25f6 100644 --- a/test/mod_host/mod_host_test.py +++ b/test/mod_host/mod_host_test.py @@ -100,15 +100,15 @@ def test_observers_mock(self): pedalboard.append(filter) pedalboard.append(reverb2) - reverb.outputs[0].connect(filter.inputs[0]) - reverb.outputs[1].connect(filter.inputs[0]) - filter.outputs[0].connect(reverb2.inputs[0]) - reverb.outputs[0].connect(reverb2.inputs[0]) + pedalboard.connect(reverb.outputs[0], filter.inputs[0]) + pedalboard.connect(reverb.outputs[1], filter.inputs[0]) + pedalboard.connect(filter.outputs[0], reverb2.inputs[0]) + pedalboard.connect(reverb.outputs[0], reverb2.inputs[0]) filter.toggle() filter.params[0].value = (filter.params[0].maximum - filter.params[0].minimum) / 2 - filter.outputs[0].disconnect(reverb2.inputs[0]) + pedalboard.disconnect(filter.outputs[0], reverb2.inputs[0]) filter.toggle() pedalboard.effects.remove(filter) @@ -217,4 +217,47 @@ def test_connection_not_current_pedalboard(self): mod_host.pedalboard = pedalboard - pedalboard2.effects[0].outputs[0].connect(pedalboard2.effects[1].inputs[0]) + pedalboard2.connect(pedalboard2.effects[0].outputs[0], pedalboard2.effects[1].inputs[0]) + + @unittest.skip + def test_system_midi_port(self): + from pluginsmanager.observer.mod_host.mod_host import ModHost + from pluginsmanager.model.pedalboard import Pedalboard + from pluginsmanager.model.lv2.lv2_effect_builder import Lv2EffectBuilder + from pluginsmanager.model.system.system_effect import SystemEffect + + jack_system = SystemEffect( + 'system', + [], # audio inputs + ['playback_1', 'playback_2'], # audio output + [], # midi inputs + [] # midi outputs + ) + jack_ttymidi = SystemEffect( + 'ttymidi', + [], # audio inputs + [], # audio output + ['MIDI_in'], # midi inputs + ['MIDI_out'] # midi outputs + ) + + modhost = ModHost('localhost') + modhost.connect() + + pedalboard = Pedalboard('MDA-EP') + builder = Lv2EffectBuilder() + ep = builder.build('http://guitarix.sourceforge.net/plugins/gx_oc_2_#_oc_2_') + + pedalboard.append(ep) + + # REMEMBER: FIRST OUTPUT, SECOND INPUT + # EPiano contains two audio output ports and one midi input port + pedalboard.connect(ep.outputs[0], jack_system.inputs[0]) + pedalboard.connect(jack_ttymidi.midi_outputs[0], ep.midi_inputs[0]) + + # If not using banks manager, the changes will not be applied automatically + # then, is necessary changes the pedalboard at the end + modhost.pedalboard = pedalboard + + # Safe close + modhost.close() \ No newline at end of file diff --git a/test/model/bank_test.py b/test/model/bank_test.py index 585e8e8..0a54dec 100644 --- a/test/model/bank_test.py +++ b/test/model/bank_test.py @@ -105,12 +105,12 @@ def test_json(self): pedalboard.append(filter) pedalboard.append(reverb2) - system_effect.outputs[0].connect(reverb.inputs[0]) - reverb.outputs[0].connect(filter.inputs[0]) - reverb.outputs[1].connect(filter.inputs[0]) - filter.outputs[0].connect(reverb2.inputs[0]) - reverb.outputs[0].connect(reverb2.inputs[0]) - reverb.outputs[0].connect(system_effect.inputs[0]) + pedalboard.connect(system_effect.outputs[0], reverb.inputs[0]) + pedalboard.connect(reverb.outputs[0], filter.inputs[0]) + pedalboard.connect(reverb.outputs[1], filter.inputs[0]) + pedalboard.connect(filter.outputs[0], reverb2.inputs[0]) + pedalboard.connect(reverb.outputs[0], reverb2.inputs[0]) + pedalboard.connect(reverb.outputs[0], system_effect.inputs[0]) bank.append(pedalboard) diff --git a/test/model/connection_test.py b/test/model/connection_test.py index 41b0025..974a4ab 100644 --- a/test/model/connection_test.py +++ b/test/model/connection_test.py @@ -14,6 +14,7 @@ import unittest from pluginsmanager.model.connection import Connection, ConnectionError +from pluginsmanager.model.midi_connection import MidiConnection from pluginsmanager.model.lv2.lv2_effect_builder import Lv2EffectBuilder from pluginsmanager.model.system.system_effect import SystemEffect @@ -62,3 +63,14 @@ def test_connect_effect_itself_successful(self): self.assertEqual(connection.input, system_effect.inputs[0]) self.assertEqual(connection.output, system_effect.outputs[0]) + + def test_connection_midi_ports(self): + cctonode1 = self.builder.build('http://gareus.org/oss/lv2/midifilter#cctonote') + cctonode2 = self.builder.build('http://gareus.org/oss/lv2/midifilter#cctonote') + + system_effect = SystemEffect('system', ('capture_1', 'capture_2'), ('playback_1', 'playback_2')) + + with self.assertRaises(ConnectionError): + Connection(cctonode1.midi_outputs[0], cctonode2.midi_inputs[0]) + with self.assertRaises(ConnectionError): + MidiConnection(system_effect.outputs[0], system_effect.inputs[0]) diff --git a/test/model/effect_test.py b/test/model/effect_test.py index d26840b..143d511 100644 --- a/test/model/effect_test.py +++ b/test/model/effect_test.py @@ -71,10 +71,10 @@ def test_connections_effect_remove_your_connections(self): pedalboard.append(filter) pedalboard.append(reverb2) - reverb.outputs[0].connect(filter.inputs[0]) - reverb.outputs[1].connect(filter.inputs[0]) - filter.outputs[0].connect(reverb2.inputs[0]) - reverb.outputs[0].connect(reverb2.inputs[0]) + pedalboard.connect(reverb.outputs[0], filter.inputs[0]) + pedalboard.connect(reverb.outputs[1], filter.inputs[0]) + pedalboard.connect(filter.outputs[0], reverb2.inputs[0]) + pedalboard.connect(reverb.outputs[0], reverb2.inputs[0]) reverb_connections = ( Connection(reverb.outputs[0], filter.inputs[0]), diff --git a/test/model/lv2/__init__.py b/test/model/lv2/__init__.py new file mode 100644 index 0000000..75323bb --- /dev/null +++ b/test/model/lv2/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/test/model/lv2/lv2_effect_builder_test.py b/test/model/lv2/lv2_effect_builder_test.py new file mode 100644 index 0000000..9b7718e --- /dev/null +++ b/test/model/lv2/lv2_effect_builder_test.py @@ -0,0 +1,26 @@ +# 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. + +import unittest + +from pluginsmanager.model.lv2.lv2_effect_builder import Lv2EffectBuilder +from pluginsmanager.model.lv2.lv2_effect_builder import Lv2EffectBuilderError + + +class Lv2EffecBuilderTest(unittest.TestCase): + + def test_load_nonexistent_effect(self): + builder = Lv2EffectBuilder() + with self.assertRaises(Lv2EffectBuilderError): + builder.build('nonexistent_effect') diff --git a/test/model/midi_output_test.py b/test/model/midi_output_test.py new file mode 100644 index 0000000..5287b5d --- /dev/null +++ b/test/model/midi_output_test.py @@ -0,0 +1,129 @@ +# 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. + +import unittest +from unittest.mock import MagicMock + +from pluginsmanager.model.lv2.lv2_effect_builder import Lv2EffectBuilder +from pluginsmanager.model.pedalboard import Pedalboard +from pluginsmanager.model.system.system_effect import SystemEffect +from pluginsmanager.observer.update_type import UpdateType + + +class MidiOutputTest(unittest.TestCase): + builder = None + + @classmethod + def setUpClass(cls): + cls.builder = Lv2EffectBuilder() + + def test_connect(self): + pedalboard = Pedalboard('Pedalboard name') + pedalboard.observer = MagicMock() + + builder = MidiOutputTest.builder + cctonode = builder.build('http://gareus.org/oss/lv2/midifilter#cctonote') + cctonode2 = builder.build('http://gareus.org/oss/lv2/midifilter#cctonote') + + pedalboard.append(cctonode) + pedalboard.append(cctonode2) + + self.assertEqual(0, len(pedalboard.connections)) + pedalboard.connect(cctonode.midi_outputs[0], cctonode2.midi_inputs[0]) + self.assertEqual(1, len(pedalboard.connections)) + + new_connection = pedalboard.connections[0] + pedalboard.observer.on_connection_updated.assert_called_with(new_connection, UpdateType.CREATED, pedalboard=pedalboard) + + pedalboard.connect(cctonode.midi_outputs[0], cctonode2.midi_inputs[0]) + self.assertEqual(2, len(pedalboard.connections)) + + new_connection = pedalboard.connections[-1] + pedalboard.observer.on_connection_updated.assert_called_with(new_connection, UpdateType.CREATED, pedalboard=pedalboard) + + def test_disconnect(self): + pedalboard = Pedalboard('Pedalboard name') + + builder = MidiOutputTest.builder + cctonode = builder.build('http://gareus.org/oss/lv2/midifilter#cctonote') + cctonode2 = builder.build('http://gareus.org/oss/lv2/midifilter#cctonote') + + pedalboard.append(cctonode) + pedalboard.append(cctonode2) + + pedalboard.connect(cctonode.midi_outputs[0], cctonode2.midi_inputs[0]) + self.assertEqual(1, len(pedalboard.connections)) + + pedalboard.observer = MagicMock() + + disconnected = pedalboard.connections[-1] + pedalboard.disconnect(cctonode.midi_outputs[0], cctonode2.midi_inputs[0]) + self.assertEqual(0, len(pedalboard.connections)) + pedalboard.observer.on_connection_updated.assert_called_with(disconnected, UpdateType.DELETED, pedalboard=pedalboard) + + def test_disconnect_connection_not_created(self): + pedalboard = Pedalboard('Pedalboard name') + + builder = MidiOutputTest.builder + cctonode = builder.build('http://gareus.org/oss/lv2/midifilter#cctonote') + cctonode2 = builder.build('http://gareus.org/oss/lv2/midifilter#cctonote') + + pedalboard.append(cctonode) + pedalboard.append(cctonode2) + + pedalboard.observer = MagicMock() + + with self.assertRaises(ValueError): + pedalboard.disconnect(cctonode.midi_outputs[0], cctonode2.midi_inputs[0]) + + pedalboard.observer.on_connection_updated.assert_not_called() + + def test_system_effect_connections(self): + pedalboard = Pedalboard('A pedalboard') + sys_effect = SystemEffect( + 'system', + ['capture_1'], + ['playback_1', 'playback_2'], + ['midi_capture_1'], + ['midi_playback_1'] + ) + + effect_output = sys_effect.midi_outputs[0] + effect_input = sys_effect.midi_inputs[0] + + pedalboard.connect(effect_output, effect_input) + self.assertEqual(len(pedalboard.connections), 1) + + pedalboard.disconnect(effect_output, effect_input) + self.assertEqual(len(pedalboard.connections), 0) + + def test_port_symbol(self): + builder = MidiOutputTest.builder + cctonode = builder.build('http://gareus.org/oss/lv2/midifilter#cctonote') + sys_effect = SystemEffect( + 'system', + ['capture_1'], + ['playback_1', 'playback_2'], + ['midi_capture_1'], + ['midi_playback_1'] + ) + + self.port_symbol_test(cctonode) + self.port_symbol_test(sys_effect) + + def port_symbol_test(self, effect): + port = effect.midi_outputs[0] + self.assertEqual(effect.midi_outputs[port.index], effect.midi_outputs[port.symbol]) + port = effect.midi_inputs[0] + self.assertEqual(effect.midi_inputs[port.index], effect.midi_inputs[port.symbol]) diff --git a/test/model/output_test.py b/test/model/output_test.py index f91164c..8617760 100644 --- a/test/model/output_test.py +++ b/test/model/output_test.py @@ -41,13 +41,13 @@ def test_connect(self): pedalboard.append(reverb2) self.assertEqual(0, len(pedalboard.connections)) - reverb.outputs[0].connect(reverb2.inputs[0]) + pedalboard.connect(reverb.outputs[0], reverb2.inputs[0]) self.assertEqual(1, len(pedalboard.connections)) new_connection = pedalboard.connections[0] pedalboard.observer.on_connection_updated.assert_called_with(new_connection, UpdateType.CREATED, pedalboard=pedalboard) - reverb.outputs[1].connect(reverb2.inputs[1]) + pedalboard.connect(reverb.outputs[1], reverb2.inputs[1]) self.assertEqual(2, len(pedalboard.connections)) new_connection = pedalboard.connections[-1] @@ -63,19 +63,19 @@ def test_disconnect(self): pedalboard.append(reverb) pedalboard.append(reverb2) - reverb.outputs[0].connect(reverb2.inputs[0]) - reverb.outputs[1].connect(reverb2.inputs[0]) + pedalboard.connect(reverb.outputs[0], reverb2.inputs[0]) + pedalboard.connect(reverb.outputs[1], reverb2.inputs[0]) self.assertEqual(2, len(pedalboard.connections)) pedalboard.observer = MagicMock() disconnected = pedalboard.connections[-1] - reverb.outputs[1].disconnect(reverb2.inputs[0]) + pedalboard.disconnect(reverb.outputs[1], reverb2.inputs[0]) self.assertEqual(1, len(pedalboard.connections)) pedalboard.observer.on_connection_updated.assert_called_with(disconnected, UpdateType.DELETED, pedalboard=pedalboard) disconnected = pedalboard.connections[-1] - reverb.outputs[0].disconnect(reverb2.inputs[0]) + pedalboard.disconnect(reverb.outputs[0], reverb2.inputs[0]) self.assertEqual(0, len(pedalboard.connections)) pedalboard.observer.on_connection_updated.assert_called_with(disconnected, UpdateType.DELETED, pedalboard=pedalboard) @@ -92,24 +92,19 @@ def test_disconnect_connection_not_created(self): pedalboard.observer = MagicMock() with self.assertRaises(ValueError): - reverb.outputs[1].disconnect(reverb2.inputs[0]) + pedalboard.disconnect(reverb.outputs[1], reverb2.inputs[0]) pedalboard.observer.on_connection_updated.assert_not_called() - def test_connect_system_effect(self): + def test_system_effect_connections(self): + pedalboard = Pedalboard('A pedalboard') sys_effect = SystemEffect('system', ['capture_1'], ['playback_1', 'playback_2']) effect_output = sys_effect.outputs[0] effect_input = sys_effect.inputs[0] - with self.assertRaises(ConnectionError): - effect_output.connect(effect_input) + pedalboard.connect(effect_output, effect_input) + self.assertEqual(len(pedalboard.connections), 1) - def test_disconnect_system_effect(self): - sys_effect = SystemEffect('system', ['capture_1'], ['playback_1', 'playback_2']) - - effect_output = sys_effect.outputs[0] - effect_input = sys_effect.inputs[0] - - with self.assertRaises(ConnectionError): - effect_output.disconnect(effect_input) + pedalboard.disconnect(effect_output, effect_input) + self.assertEqual(len(pedalboard.connections), 0) diff --git a/test/model/pedalboard_test.py b/test/model/pedalboard_test.py index f67a027..67af093 100644 --- a/test/model/pedalboard_test.py +++ b/test/model/pedalboard_test.py @@ -17,17 +17,24 @@ from pluginsmanager.model.bank import Bank from pluginsmanager.model.lv2.lv2_effect_builder import Lv2EffectBuilder -from pluginsmanager.model.pedalboard import Pedalboard +from pluginsmanager.model.pedalboard import Pedalboard, PedalboardError +from pluginsmanager.model.system.system_effect import SystemEffect from pluginsmanager.observer.update_type import UpdateType class PedalboardTest(unittest.TestCase): + @property + def mock_effect(self): + effect = MagicMock() + effect.is_unique_for_all_pedalboards = False + return effect + def test_add_effect_by_effects(self): pedalboard = Pedalboard('Pedalboard 1') - effect1 = MagicMock() - effect2 = MagicMock() + effect1 = self.mock_effect + effect2 = self.mock_effect pedalboard.observer = MagicMock() @@ -44,8 +51,8 @@ def test_add_effect_by_effects(self): def test_add_effect(self): pedalboard = Pedalboard('Pedalboard 1') - effect1 = MagicMock() - effect2 = MagicMock() + effect1 = self.mock_effect + effect2 = self.mock_effect pedalboard.observer = MagicMock() @@ -62,8 +69,8 @@ def test_add_effect(self): def test_update_effect(self): pedalboard = Pedalboard('Pedalboard 1') - effect1 = MagicMock() - effect2 = MagicMock() + effect1 = self.mock_effect + effect2 = self.mock_effect pedalboard.append(effect1) @@ -77,8 +84,8 @@ def test_update_effect(self): def test_delete_effect(self): pedalboard = Pedalboard('Bank 1') - effect = MagicMock() - effect2 = MagicMock() + effect = self.mock_effect + effect2 = self.mock_effect pedalboard.append(effect) pedalboard.append(effect2) @@ -152,15 +159,14 @@ def test_delete_effect_remove_your_connections(self): pedalboard.append(filter) pedalboard.append(reverb2) - reverb.outputs[0].connect(filter.inputs[0]) - reverb.outputs[1].connect(filter.inputs[0]) - filter.outputs[0].connect(reverb2.inputs[0]) - reverb.outputs[0].connect(reverb2.inputs[0]) + pedalboard.connect(reverb.outputs[0], filter.inputs[0]) + pedalboard.connect(reverb.outputs[1], filter.inputs[0]) + pedalboard.connect(filter.outputs[0], reverb2.inputs[0]) + pedalboard.connect(reverb.outputs[0], reverb2.inputs[0]) self.assertEqual(4, len(pedalboard.connections)) pedalboard.observer = MagicMock() - fuzz_connections = filter.connections pedalboard.effects.remove(filter) @@ -198,3 +204,8 @@ def test_index(self): with self.assertRaises(IndexError): pedalboard2.index + + def test_add_system_effect(self): + pedalboard = Pedalboard('test_add_system_effect') + with self.assertRaises(PedalboardError): + pedalboard.append(SystemEffect('System Effect', (), ())) diff --git a/test/model/system/system_effect_builder_test.py b/test/model/system/system_effect_builder_test.py index d450277..2535788 100644 --- a/test/model/system/system_effect_builder_test.py +++ b/test/model/system/system_effect_builder_test.py @@ -29,3 +29,4 @@ def test_mock_system_effect_builder(self): self.assertEqual((), system_effect.inputs) self.assertTupleEqual((), system_effect.outputs) + self.assertEqual('', system_effect.version) diff --git a/test/observer/autosaver_test.py b/test/observer/autosaver_test.py index 8a55e21..ae30f56 100644 --- a/test/observer/autosaver_test.py +++ b/test/observer/autosaver_test.py @@ -26,7 +26,7 @@ class AutoSaverTest(unittest.TestCase): def autosaver(self, auto_save=True): - return Autosaver('../data/autosaver_data/', auto_save=auto_save) + return Autosaver('test/autosaver_data/', auto_save=auto_save) def test_observers(self): save_mock = MagicMock() @@ -64,16 +64,16 @@ def test_observers(self): save_mock.assert_called_with(bank) save_mock.reset_mock() - reverb.outputs[0].connect(filter.inputs[0]) + pedalboard.connect(reverb.outputs[0], filter.inputs[0]) save_mock.assert_called_with(bank) save_mock.reset_mock() - reverb.outputs[1].connect(filter.inputs[0]) + pedalboard.connect(reverb.outputs[1], filter.inputs[0]) save_mock.assert_called_with(bank) save_mock.reset_mock() - filter.outputs[0].connect(reverb2.inputs[0]) + pedalboard.connect(filter.outputs[0], reverb2.inputs[0]) save_mock.assert_called_with(bank) save_mock.reset_mock() - reverb.outputs[0].connect(reverb2.inputs[0]) + pedalboard.connect(reverb.outputs[0], reverb2.inputs[0]) save_mock.assert_called_with(bank) save_mock.reset_mock() @@ -198,8 +198,8 @@ def test_connections_effect_removed(self): bank.append(pedalboard) pedalboard.append(reverb) - reverb.outputs[0].connect(system_effect.inputs[0]) - reverb.outputs[0].connect(system_effect.inputs[1]) + pedalboard.connect(reverb.outputs[0], system_effect.inputs[0]) + pedalboard.connect(reverb.outputs[0], system_effect.inputs[1]) pedalboard.effects.remove(reverb) diff --git a/test/util/observable_list_test.py b/test/observer/observable_list_test.py similarity index 100% rename from test/util/observable_list_test.py rename to test/observer/observable_list_test.py diff --git a/test/util/data/EPiano.pedalboard/EPiano.ttl b/test/util/data/EPiano.pedalboard/EPiano.ttl new file mode 100644 index 0000000..fc6664c --- /dev/null +++ b/test/util/data/EPiano.pedalboard/EPiano.ttl @@ -0,0 +1,1056 @@ +@prefix atom: . +@prefix doap: . +@prefix ingen: . +@prefix lv2: . +@prefix midi: . +@prefix mod: . +@prefix pedal: . +@prefix rdfs: . + +_:b1 + ingen:tail ; + ingen:head . + +_:b2 + ingen:tail ; + ingen:head . + +_:b3 + ingen:tail ; + ingen:head . + +_:b4 + ingen:tail ; + ingen:head . + +_:b5 + ingen:tail ; + ingen:head . + +_:b6 + ingen:tail ; + ingen:head . + +_:b7 + ingen:tail ; + ingen:head . + +_:b8 + ingen:tail ; + ingen:head . + +_:b9 + ingen:tail ; + ingen:head . + +_:b10 + ingen:tail ; + ingen:head . + +_:b11 + ingen:tail ; + ingen:head . + +_:b12 + ingen:tail ; + ingen:head . + +_:b13 + ingen:tail ; + ingen:head . + +_:b14 + ingen:tail ; + ingen:head . + +_:b15 + ingen:tail ; + ingen:head . + +_:b16 + ingen:tail ; + ingen:head . + +_:b17 + ingen:tail ; + ingen:head . + +_:b18 + ingen:tail ; + ingen:head . + +_:b19 + ingen:tail ; + ingen:head . + +_:b20 + ingen:tail ; + ingen:head . + +_:b21 + ingen:tail ; + ingen:head . + + + ingen:canvasX 261.0 ; + ingen:canvasY 997.0 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 3 ; + lv2:minorVersion 0 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + a atom:AtomPort , + lv2:InputPort . + + + ingen:value 23.285000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 50.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 1.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 100.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 7.300000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 50.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:canvasX 3287.0 ; + ingen:canvasY 301.0 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 2 ; + lv2:minorVersion 0 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + , + , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + ingen:value 234.449997 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value -16.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 4.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 758.500000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:canvasX 2681.0 ; + ingen:canvasY 1142.0 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 6 ; + lv2:minorVersion 2 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + ingen:value 186.015747 ; + midi:binding [ + midi:channel 0 ; + midi:controllerNumber 78 ; + lv2:minimum 6.000000 ; + lv2:maximum 1000.000000 ; + a midi:Controller ; + ] ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 5.511811 ; + midi:binding [ + midi:channel 0 ; + midi:controllerNumber 76 ; + lv2:minimum 0.000000 ; + lv2:maximum 100.000000 ; + a midi:Controller ; + ] ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 21.259842 ; + midi:binding [ + midi:channel 0 ; + midi:controllerNumber 5 ; + lv2:minimum 0.000000 ; + lv2:maximum 100.000000 ; + a midi:Controller ; + ] ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 1.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 7500.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 20.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 120.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 1.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 16.535433 ; + midi:binding [ + midi:channel 0 ; + midi:controllerNumber 77 ; + lv2:minimum 0.000000 ; + lv2:maximum 100.000000 ; + a midi:Controller ; + ] ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 1.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + a lv2:ControlPort , + lv2:OutputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:canvasX 2433.0 ; + ingen:canvasY 122.0 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 0 ; + lv2:minorVersion 0 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + ingen:value 31.000000 ; + midi:binding [ + midi:channel 0 ; + midi:controllerNumber 94 ; + lv2:minimum 0.000000 ; + lv2:maximum 127.000000 ; + a midi:Controller ; + ] ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 64.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 18.000000 ; + midi:binding [ + midi:channel 0 ; + midi:controllerNumber 93 ; + lv2:minimum 0.000000 ; + lv2:maximum 127.000000 ; + a midi:Controller ; + ] ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 85.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 118.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 109.000000 ; + midi:binding [ + midi:channel 0 ; + midi:controllerNumber 92 ; + lv2:minimum 1.000000 ; + lv2:maximum 600.000000 ; + a midi:Controller ; + ] ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 16.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:canvasX 1026.4 ; + ingen:canvasY 1005.3 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 0 ; + lv2:minorVersion 2 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + ingen:value 0.582677 ; + midi:binding [ + midi:channel 0 ; + midi:controllerNumber 75 ; + lv2:minimum 0.000000 ; + lv2:maximum 1.000000 ; + a midi:Controller ; + ] ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.500000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.637795 ; + midi:binding [ + midi:channel 0 ; + midi:controllerNumber 91 ; + lv2:minimum 0.000000 ; + lv2:maximum 1.000000 ; + a midi:Controller ; + ] ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.503937 ; + midi:binding [ + midi:channel 0 ; + midi:controllerNumber 72 ; + lv2:minimum 0.000000 ; + lv2:maximum 1.000000 ; + a midi:Controller ; + ] ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 1.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.645669 ; + midi:binding [ + midi:channel 0 ; + midi:controllerNumber 73 ; + lv2:minimum 0.000000 ; + lv2:maximum 1.000000 ; + a midi:Controller ; + ] ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:canvasX 700.0 ; + ingen:canvasY 1049.0 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 0 ; + lv2:minorVersion 770 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + ingen:value -16.473213 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 1.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + a lv2:ControlPort , + lv2:OutputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:canvasX 438.4 ; + ingen:canvasY 158.2 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 0 ; + lv2:minorVersion 2 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + ingen:value 0.236220 ; + midi:binding [ + midi:channel 0 ; + midi:controllerNumber 65 ; + lv2:minimum 0.000000 ; + lv2:maximum 1.000000 ; + a midi:Controller ; + ] ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.346457 ; + midi:binding [ + midi:channel 0 ; + midi:controllerNumber 74 ; + lv2:minimum 0.000000 ; + lv2:maximum 1.000000 ; + a midi:Controller ; + ] ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.527559 ; + midi:binding [ + midi:channel 0 ; + midi:controllerNumber 71 ; + lv2:minimum 0.000000 ; + lv2:maximum 1.000000 ; + a midi:Controller ; + ] ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:canvasX 3764.0 ; + ingen:canvasY 679.0 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 0 ; + lv2:minorVersion 770 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + ingen:value -2.991071 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 1.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + a lv2:ControlPort , + lv2:OutputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:canvasX 1866.0 ; + ingen:canvasY 150.0 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 0 ; + lv2:minorVersion 520 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + , + , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value -0.748031 ; + midi:binding [ + midi:channel 0 ; + midi:controllerNumber 95 ; + lv2:minimum -1.000000 ; + lv2:maximum 1.000000 ; + a midi:Controller ; + ] ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + +<:bpb> + ingen:value 4.000000 ; + lv2:index 0 ; + a lv2:ControlPort , + lv2:InputPort . + +<:bpm> + ingen:value 120.000000 ; + lv2:index 1 ; + a lv2:ControlPort , + lv2:InputPort . + +<:rolling> + ingen:value 0 ; + lv2:index 2 ; + a lv2:ControlPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + lv2:index 3 ; + lv2:name "Control In" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "control_in" ; + 4096 ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + lv2:index 4 ; + lv2:name "Control Out" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "control_out" ; + 4096 ; + a atom:AtomPort , + lv2:OutputPort . + + + lv2:index 5 ; + lv2:name "Playback 1" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "playback_1" ; + a lv2:AudioPort , + lv2:OutputPort . + + + lv2:index 6 ; + lv2:name "Playback 2" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "playback_2" ; + a lv2:AudioPort , + lv2:OutputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 7 ; + lv2:name "pisound MIDI PS 19FP3ND" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "midi_capture_1" ; + 4096 ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 8 ; + lv2:name "pisound MIDI PS 19FP3ND" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "midi_playback_1" ; + 4096 ; + a atom:AtomPort , + lv2:OutputPort . + +<> + doap:name "EPiano" ; + pedal:width 0 ; + pedal:height 0 ; + pedal:addressings ; + pedal:screenshot ; + pedal:thumbnail ; + ingen:polyphony 1 ; + ingen:arc _:b1 , + _:b2 , + _:b3 , + _:b4 , + _:b5 , + _:b6 , + _:b7 , + _:b8 , + _:b9 , + _:b10 , + _:b11 , + _:b12 , + _:b13 , + _:b14 , + _:b15 , + _:b16 , + _:b17 , + _:b18 , + _:b19 , + _:b20 , + _:b21 ; + ingen:block , + , + , + , + , + , + , + , + ; + lv2:port <:bpb> , + <:bpm> , + <:rolling> , + , + , + , + , + , + ; + lv2:extensionData ; + a lv2:Plugin , + ingen:Graph , + pedal:Pedalboard . diff --git a/test/util/data/EPiano.pedalboard/addressings.json b/test/util/data/EPiano.pedalboard/addressings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/test/util/data/EPiano.pedalboard/addressings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/util/data/EPiano.pedalboard/manifest.ttl b/test/util/data/EPiano.pedalboard/manifest.ttl new file mode 100644 index 0000000..4592f7d --- /dev/null +++ b/test/util/data/EPiano.pedalboard/manifest.ttl @@ -0,0 +1,11 @@ +@prefix ingen: . +@prefix lv2: . +@prefix pedal: . +@prefix rdfs: . + + + lv2:prototype ingen:GraphPrototype ; + a lv2:Plugin , + ingen:Graph , + pedal:Pedalboard ; + rdfs:seeAlso . diff --git a/test/util/data/EPiano.pedalboard/screenshot.png b/test/util/data/EPiano.pedalboard/screenshot.png new file mode 100644 index 0000000..4889752 Binary files /dev/null and b/test/util/data/EPiano.pedalboard/screenshot.png differ diff --git a/test/util/data/EPiano.pedalboard/thumbnail.png b/test/util/data/EPiano.pedalboard/thumbnail.png new file mode 100644 index 0000000..ae9653b Binary files /dev/null and b/test/util/data/EPiano.pedalboard/thumbnail.png differ diff --git a/test/util/data/EPiano_simple.pedalboard/EPiano_simple.ttl b/test/util/data/EPiano_simple.pedalboard/EPiano_simple.ttl new file mode 100644 index 0000000..de2ad81 --- /dev/null +++ b/test/util/data/EPiano_simple.pedalboard/EPiano_simple.ttl @@ -0,0 +1,235 @@ +@prefix atom: . +@prefix doap: . +@prefix ingen: . +@prefix lv2: . +@prefix midi: . +@prefix mod: . +@prefix pedal: . +@prefix rdfs: . + +_:b1 + ingen:tail ; + ingen:head . + +_:b2 + ingen:tail ; + ingen:head . + +_:b3 + ingen:tail ; + ingen:head . + +_:b4 + ingen:tail ; + ingen:head . + +_:b5 + ingen:tail ; + ingen:head . + + + ingen:canvasX 983.2 ; + ingen:canvasY 376.3 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 3 ; + lv2:minorVersion 0 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + a atom:AtomPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 50.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 100.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 23.285000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 7.300000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 1.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 50.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + lv2:index 0 ; + lv2:name "Control In" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "control_in" ; + 4096 ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + lv2:index 1 ; + lv2:name "Control Out" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "control_out" ; + 4096 ; + a atom:AtomPort , + lv2:OutputPort . + + + lv2:index 2 ; + lv2:name "Playback 1" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "playback_1" ; + a lv2:AudioPort , + lv2:OutputPort . + + + lv2:index 3 ; + lv2:name "Playback 2" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "playback_2" ; + a lv2:AudioPort , + lv2:OutputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 4 ; + lv2:name "USB MIDI MIDI 1" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "midi_capture_1" ; + 4096 ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 5 ; + lv2:name "USB MIDI MIDI 1" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "midi_playback_1" ; + 4096 ; + a atom:AtomPort , + lv2:OutputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 6 ; + lv2:name "Serial MIDI In" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "serial_midi_in" ; + 4096 ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 7 ; + lv2:name "Serial MIDI In" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "serial_midi_out" ; + 4096 ; + a atom:AtomPort , + lv2:OutputPort . + +<> + doap:name "EPiano_simple" ; + pedal:width 0 ; + pedal:height 0 ; + pedal:addressings ; + pedal:screenshot ; + pedal:thumbnail ; + ingen:polyphony 1 ; + ingen:arc _:b1 , + _:b2 , + _:b3 , + _:b4 , + _:b5 ; + ingen:block ; + lv2:port , + , + , + , + , + , + , + ; + lv2:extensionData ; + a lv2:Plugin , + ingen:Graph , + pedal:Pedalboard . diff --git a/test/util/data/EPiano_simple.pedalboard/addressings.json b/test/util/data/EPiano_simple.pedalboard/addressings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/test/util/data/EPiano_simple.pedalboard/addressings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/util/data/EPiano_simple.pedalboard/manifest.ttl b/test/util/data/EPiano_simple.pedalboard/manifest.ttl new file mode 100644 index 0000000..6f032a1 --- /dev/null +++ b/test/util/data/EPiano_simple.pedalboard/manifest.ttl @@ -0,0 +1,11 @@ +@prefix ingen: . +@prefix lv2: . +@prefix pedal: . +@prefix rdfs: . + + + lv2:prototype ingen:GraphPrototype ; + a lv2:Plugin , + ingen:Graph , + pedal:Pedalboard ; + rdfs:seeAlso . diff --git a/test/util/data/EPiano_simple.pedalboard/screenshot.png b/test/util/data/EPiano_simple.pedalboard/screenshot.png new file mode 100644 index 0000000..e4f12a7 Binary files /dev/null and b/test/util/data/EPiano_simple.pedalboard/screenshot.png differ diff --git a/test/util/data/EPiano_simple.pedalboard/thumbnail.png b/test/util/data/EPiano_simple.pedalboard/thumbnail.png new file mode 100644 index 0000000..1720d81 Binary files /dev/null and b/test/util/data/EPiano_simple.pedalboard/thumbnail.png differ diff --git a/test/util/data/EPiano_simple_tt.pedalboard/EPiano_simple_tt.ttl b/test/util/data/EPiano_simple_tt.pedalboard/EPiano_simple_tt.ttl new file mode 100644 index 0000000..315e0a2 --- /dev/null +++ b/test/util/data/EPiano_simple_tt.pedalboard/EPiano_simple_tt.ttl @@ -0,0 +1,246 @@ +@prefix atom: . +@prefix doap: . +@prefix ingen: . +@prefix lv2: . +@prefix midi: . +@prefix mod: . +@prefix pedal: . +@prefix rdfs: . + +_:b1 + ingen:tail ; + ingen:head . + +_:b2 + ingen:tail ; + ingen:head . + +_:b3 + ingen:tail ; + ingen:head . + + + ingen:canvasX 1310.8 ; + ingen:canvasY 550.9 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 3 ; + lv2:minorVersion 0 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + a atom:AtomPort , + lv2:InputPort . + + + ingen:value 100.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 50.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 7.300000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 1.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 23.285000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 50.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + +<:bpb> + ingen:value 4.000000 ; + lv2:index 0 ; + a lv2:ControlPort , + lv2:InputPort . + +<:bpm> + ingen:value 120.000000 ; + lv2:index 1 ; + a lv2:ControlPort , + lv2:InputPort . + +<:rolling> + ingen:value 0 ; + lv2:index 2 ; + a lv2:ControlPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + lv2:index 3 ; + lv2:name "Control In" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "control_in" ; + 4096 ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + lv2:index 4 ; + lv2:name "Control Out" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "control_out" ; + 4096 ; + a atom:AtomPort , + lv2:OutputPort . + + + lv2:index 5 ; + lv2:name "Playback 1" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "playback_1" ; + a lv2:AudioPort , + lv2:OutputPort . + + + lv2:index 6 ; + lv2:name "Playback 2" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "playback_2" ; + a lv2:AudioPort , + lv2:OutputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 7 ; + lv2:name "pisound MIDI PS 19FP3ND" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "midi_capture_1" ; + 4096 ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 8 ; + lv2:name "pisound MIDI PS 19FP3ND" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "midi_playback_1" ; + 4096 ; + a atom:AtomPort , + lv2:OutputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 9 ; + lv2:name "Serial MIDI In" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "serial_midi_in" ; + 4096 ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 10 ; + lv2:name "Serial MIDI In" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "serial_midi_out" ; + 4096 ; + a atom:AtomPort , + lv2:OutputPort . + +<> + doap:name "EPiano-simple-ttymdidi" ; + pedal:width 0 ; + pedal:height 0 ; + pedal:addressings ; + pedal:screenshot ; + pedal:thumbnail ; + ingen:polyphony 1 ; + ingen:arc _:b1 , + _:b2 , + _:b3 ; + ingen:block ; + lv2:port <:bpb> , + <:bpm> , + <:rolling> , + , + , + , + , + , + , + , + ; + lv2:extensionData ; + a lv2:Plugin , + ingen:Graph , + pedal:Pedalboard . diff --git a/test/util/data/EPiano_simple_tt.pedalboard/addressings.json b/test/util/data/EPiano_simple_tt.pedalboard/addressings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/test/util/data/EPiano_simple_tt.pedalboard/addressings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/util/data/EPiano_simple_tt.pedalboard/manifest.ttl b/test/util/data/EPiano_simple_tt.pedalboard/manifest.ttl new file mode 100644 index 0000000..f37ce31 --- /dev/null +++ b/test/util/data/EPiano_simple_tt.pedalboard/manifest.ttl @@ -0,0 +1,11 @@ +@prefix ingen: . +@prefix lv2: . +@prefix pedal: . +@prefix rdfs: . + + + lv2:prototype ingen:GraphPrototype ; + a lv2:Plugin , + ingen:Graph , + pedal:Pedalboard ; + rdfs:seeAlso . diff --git a/test/util/data/EPiano_simple_tt.pedalboard/screenshot.png b/test/util/data/EPiano_simple_tt.pedalboard/screenshot.png new file mode 100644 index 0000000..75146a3 Binary files /dev/null and b/test/util/data/EPiano_simple_tt.pedalboard/screenshot.png differ diff --git a/test/util/data/EPiano_simple_tt.pedalboard/thumbnail.png b/test/util/data/EPiano_simple_tt.pedalboard/thumbnail.png new file mode 100644 index 0000000..7b1526e Binary files /dev/null and b/test/util/data/EPiano_simple_tt.pedalboard/thumbnail.png differ diff --git a/test/util/data/setBfree_simple.pedalboard/addressings.json b/test/util/data/setBfree_simple.pedalboard/addressings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/test/util/data/setBfree_simple.pedalboard/addressings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/util/data/setBfree_simple.pedalboard/manifest.ttl b/test/util/data/setBfree_simple.pedalboard/manifest.ttl new file mode 100644 index 0000000..f2e3ed8 --- /dev/null +++ b/test/util/data/setBfree_simple.pedalboard/manifest.ttl @@ -0,0 +1,11 @@ +@prefix ingen: . +@prefix lv2: . +@prefix pedal: . +@prefix rdfs: . + + + lv2:prototype ingen:GraphPrototype ; + a lv2:Plugin , + ingen:Graph , + pedal:Pedalboard ; + rdfs:seeAlso . diff --git a/test/util/data/setBfree_simple.pedalboard/screenshot.png b/test/util/data/setBfree_simple.pedalboard/screenshot.png new file mode 100644 index 0000000..2611d82 Binary files /dev/null and b/test/util/data/setBfree_simple.pedalboard/screenshot.png differ diff --git a/test/util/data/setBfree_simple.pedalboard/setBfree_simple.ttl b/test/util/data/setBfree_simple.pedalboard/setBfree_simple.ttl new file mode 100644 index 0000000..cdd96b2 --- /dev/null +++ b/test/util/data/setBfree_simple.pedalboard/setBfree_simple.ttl @@ -0,0 +1,151 @@ +@prefix atom: . +@prefix doap: . +@prefix ingen: . +@prefix lv2: . +@prefix midi: . +@prefix mod: . +@prefix pedal: . +@prefix rdfs: . + +_:b1 + ingen:tail ; + ingen:head . + +_:b2 + ingen:tail ; + ingen:head . + +_:b3 + ingen:tail ; + ingen:head . + +_:b4 + ingen:tail ; + ingen:head . + +_:b5 + ingen:tail ; + ingen:head . + +_:b6 + ingen:tail ; + ingen:head . + + + ingen:canvasX 956.4 ; + ingen:canvasY 294.9 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 9 ; + lv2:minorVersion 2059 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + a atom:AtomPort , + lv2:OutputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + lv2:index 0 ; + lv2:name "Control In" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "control_in" ; + 4096 ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + lv2:index 1 ; + lv2:name "Control Out" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "control_out" ; + 4096 ; + a atom:AtomPort , + lv2:OutputPort . + + + lv2:index 2 ; + lv2:name "Playback 1" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "playback_1" ; + a lv2:AudioPort , + lv2:OutputPort . + + + lv2:index 3 ; + lv2:name "Playback 2" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "playback_2" ; + a lv2:AudioPort , + lv2:OutputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 4 ; + lv2:name "Serial MIDI In" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "serial_midi_in" ; + 4096 ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 5 ; + lv2:name "Serial MIDI In" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "serial_midi_out" ; + 4096 ; + a atom:AtomPort , + lv2:OutputPort . + +<> + doap:name "setBfree_simple" ; + pedal:width 0 ; + pedal:height 0 ; + pedal:addressings ; + pedal:screenshot ; + pedal:thumbnail ; + ingen:polyphony 1 ; + ingen:arc _:b1 , + _:b2 , + _:b3 , + _:b4 , + _:b5 , + _:b6 ; + ingen:block ; + lv2:port , + , + , + , + , + ; + lv2:extensionData ; + a lv2:Plugin , + ingen:Graph , + pedal:Pedalboard . diff --git a/test/util/data/setBfree_simple.pedalboard/thumbnail.png b/test/util/data/setBfree_simple.pedalboard/thumbnail.png new file mode 100644 index 0000000..de00de7 Binary files /dev/null and b/test/util/data/setBfree_simple.pedalboard/thumbnail.png differ diff --git a/test/util/data/setBfree_ttymidi.pedalboard/addressings.json b/test/util/data/setBfree_ttymidi.pedalboard/addressings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/test/util/data/setBfree_ttymidi.pedalboard/addressings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/util/data/setBfree_ttymidi.pedalboard/manifest.ttl b/test/util/data/setBfree_ttymidi.pedalboard/manifest.ttl new file mode 100644 index 0000000..f1baf83 --- /dev/null +++ b/test/util/data/setBfree_ttymidi.pedalboard/manifest.ttl @@ -0,0 +1,11 @@ +@prefix ingen: . +@prefix lv2: . +@prefix pedal: . +@prefix rdfs: . + + + lv2:prototype ingen:GraphPrototype ; + a lv2:Plugin , + ingen:Graph , + pedal:Pedalboard ; + rdfs:seeAlso . diff --git a/test/util/data/setBfree_ttymidi.pedalboard/screenshot.png b/test/util/data/setBfree_ttymidi.pedalboard/screenshot.png new file mode 100644 index 0000000..17a3f2d Binary files /dev/null and b/test/util/data/setBfree_ttymidi.pedalboard/screenshot.png differ diff --git a/test/util/data/setBfree_ttymidi.pedalboard/setBfree_ttymidi.ttl b/test/util/data/setBfree_ttymidi.pedalboard/setBfree_ttymidi.ttl new file mode 100644 index 0000000..75665d0 --- /dev/null +++ b/test/util/data/setBfree_ttymidi.pedalboard/setBfree_ttymidi.ttl @@ -0,0 +1,186 @@ +@prefix atom: . +@prefix doap: . +@prefix ingen: . +@prefix lv2: . +@prefix midi: . +@prefix mod: . +@prefix pedal: . +@prefix rdfs: . + +_:b1 + ingen:tail ; + ingen:head . + +_:b2 + ingen:tail ; + ingen:head . + +_:b3 + ingen:tail ; + ingen:head . + +_:b4 + ingen:tail ; + ingen:head . + + + ingen:canvasX 1635.3 ; + ingen:canvasY 520.9 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 9 ; + lv2:minorVersion 2059 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + a atom:AtomPort , + lv2:OutputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + +<:bpb> + ingen:value 4.000000 ; + lv2:index 0 ; + a lv2:ControlPort , + lv2:InputPort . + +<:bpm> + ingen:value 120.000000 ; + lv2:index 1 ; + a lv2:ControlPort , + lv2:InputPort . + +<:rolling> + ingen:value 0 ; + lv2:index 2 ; + a lv2:ControlPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + lv2:index 3 ; + lv2:name "Control In" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "control_in" ; + 4096 ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + lv2:index 4 ; + lv2:name "Control Out" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "control_out" ; + 4096 ; + a atom:AtomPort , + lv2:OutputPort . + + + lv2:index 5 ; + lv2:name "Playback 1" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "playback_1" ; + a lv2:AudioPort , + lv2:OutputPort . + + + lv2:index 6 ; + lv2:name "Playback 2" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "playback_2" ; + a lv2:AudioPort , + lv2:OutputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 7 ; + lv2:name "pisound MIDI PS 19FP3ND" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "midi_capture_1" ; + 4096 ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 8 ; + lv2:name "pisound MIDI PS 19FP3ND" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "midi_playback_1" ; + 4096 ; + a atom:AtomPort , + lv2:OutputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 9 ; + lv2:name "Serial MIDI In" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "serial_midi_in" ; + 4096 ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 10 ; + lv2:name "Serial MIDI In" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "serial_midi_out" ; + 4096 ; + a atom:AtomPort , + lv2:OutputPort . + +<> + doap:name "setBfree-ttymidi-in-out" ; + pedal:width 0 ; + pedal:height 0 ; + pedal:addressings ; + pedal:screenshot ; + pedal:thumbnail ; + ingen:polyphony 1 ; + ingen:arc _:b1 , + _:b2 , + _:b3 , + _:b4 ; + ingen:block ; + lv2:port <:bpb> , + <:bpm> , + <:rolling> , + , + , + , + , + , + , + , + ; + lv2:extensionData ; + a lv2:Plugin , + ingen:Graph , + pedal:Pedalboard . diff --git a/test/util/data/setBfree_ttymidi.pedalboard/thumbnail.png b/test/util/data/setBfree_ttymidi.pedalboard/thumbnail.png new file mode 100644 index 0000000..c7108fa Binary files /dev/null and b/test/util/data/setBfree_ttymidi.pedalboard/thumbnail.png differ diff --git a/test/util/data/teste.pedalboard/addressings.json b/test/util/data/teste.pedalboard/addressings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/test/util/data/teste.pedalboard/addressings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/util/data/teste.pedalboard/manifest.ttl b/test/util/data/teste.pedalboard/manifest.ttl new file mode 100644 index 0000000..4df670e --- /dev/null +++ b/test/util/data/teste.pedalboard/manifest.ttl @@ -0,0 +1,11 @@ +@prefix ingen: . +@prefix lv2: . +@prefix pedal: . +@prefix rdfs: . + + + lv2:prototype ingen:GraphPrototype ; + a lv2:Plugin , + ingen:Graph , + pedal:Pedalboard ; + rdfs:seeAlso . diff --git a/test/util/data/teste.pedalboard/teste.ttl b/test/util/data/teste.pedalboard/teste.ttl new file mode 100644 index 0000000..2db5889 --- /dev/null +++ b/test/util/data/teste.pedalboard/teste.ttl @@ -0,0 +1,398 @@ +@prefix atom: . +@prefix doap: . +@prefix ingen: . +@prefix lv2: . +@prefix midi: . +@prefix mod: . +@prefix pedal: . +@prefix rdfs: . + +_:b1 + ingen:tail ; + ingen:head . + +_:b2 + ingen:tail ; + ingen:head . + +_:b3 + ingen:tail ; + ingen:head . + +_:b4 + ingen:tail ; + ingen:head . + +_:b5 + ingen:tail ; + ingen:head . + +_:b6 + ingen:tail ; + ingen:head . + +_:b7 + ingen:tail ; + ingen:head . + +_:b8 + ingen:tail ; + ingen:head . + +_:b9 + ingen:tail ; + ingen:head . + + + ingen:canvasX 1585.0 ; + ingen:canvasY 1.0 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 0 ; + lv2:minorVersion 34 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 1.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + a lv2:ControlPort , + lv2:OutputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:canvasX 743.3 ; + ingen:canvasY 1143.7 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 3 ; + lv2:minorVersion 1039 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + a atom:AtomPort , + lv2:OutputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + a lv2:ControlPort , + lv2:OutputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:canvasX 1911.5 ; + ingen:canvasY 1137.5 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 3 ; + lv2:minorVersion 1039 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + , + , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + a atom:AtomPort , + lv2:OutputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 0.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 1.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:value 48.000000 ; + a lv2:ControlPort , + lv2:InputPort . + + + a lv2:ControlPort , + lv2:OutputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:canvasX 2543.1 ; + ingen:canvasY 490.9 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 0 ; + lv2:minorVersion 0 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + a atom:AtomPort , + lv2:InputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + + + ingen:canvasX 1519.3 ; + ingen:canvasY 320.6 ; + ingen:enabled true ; + ingen:polyphonic false ; + lv2:microVersion 0 ; + lv2:minorVersion 0 ; + mod:builderVersion 0 ; + mod:releaseNumber 0 ; + lv2:port , + , + , + ; + lv2:prototype ; + pedal:preset <> ; + a ingen:Block . + + + a lv2:AudioPort , + lv2:InputPort . + + + a lv2:AudioPort , + lv2:OutputPort . + + + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + a atom:AtomPort , + lv2:InputPort . + + + ingen:value 0 ; + a lv2:ControlPort , + lv2:InputPort . + +<:bpb> + ingen:value 4.000000 ; + lv2:index 0 ; + a lv2:ControlPort , + lv2:InputPort . + +<:bpm> + ingen:value 120.000000 ; + lv2:index 1 ; + a lv2:ControlPort , + lv2:InputPort . + +<:rolling> + ingen:value 0 ; + lv2:index 2 ; + a lv2:ControlPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + lv2:index 3 ; + lv2:name "Control In" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "control_in" ; + 4096 ; + a atom:AtomPort , + lv2:InputPort . + + + atom:bufferType atom:Sequence ; + lv2:index 4 ; + lv2:name "Control Out" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "control_out" ; + 4096 ; + a atom:AtomPort , + lv2:OutputPort . + + + lv2:index 5 ; + lv2:name "Capture 1" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "capture_1" ; + a lv2:AudioPort , + lv2:InputPort . + + + lv2:index 6 ; + lv2:name "Capture 2" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "capture_2" ; + a lv2:AudioPort , + lv2:InputPort . + + + lv2:index 7 ; + lv2:name "Playback 1" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "playback_1" ; + a lv2:AudioPort , + lv2:OutputPort . + + + lv2:index 8 ; + lv2:name "Playback 2" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "playback_2" ; + a lv2:AudioPort , + lv2:OutputPort . + +<> + doap:name "teste" ; + pedal:width 1342 ; + pedal:height 1176 ; + pedal:addressings ; + pedal:screenshot ; + pedal:thumbnail ; + ingen:polyphony 1 ; + ingen:arc _:b1 , + _:b2 , + _:b3 , + _:b4 , + _:b5 , + _:b6 , + _:b7 , + _:b8 , + _:b9 ; + ingen:block , + , + , + , + ; + lv2:port <:bpb> , + <:bpm> , + <:rolling> , + , + , + , + , + , + ; + lv2:extensionData ; + a lv2:Plugin , + ingen:Graph , + pedal:Pedalboard . diff --git a/test/util/dict_tuple_test.py b/test/util/dict_tuple_test.py new file mode 100644 index 0000000..c757377 --- /dev/null +++ b/test/util/dict_tuple_test.py @@ -0,0 +1,35 @@ +# 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. + +import unittest + +from pluginsmanager.util.dict_tuple import DictTuple + + +class DictTupleTest(unittest.TestCase): + + def test__get_item__(self): + elements = ('123', '456', '789') + data = DictTuple(elements, lambda e: e) + + for index, element in enumerate(elements): + self.assertEqual(element, data[index]) + self.assertEqual(element, data[element]) + + def test_in(self): + elements = ('abc', 'cde', 'def') + data = DictTuple(elements, lambda e: e.upper()) + + self.assertTrue('ABC' in data) + self.assertFalse('abc' in data) diff --git a/test/util/mod_pedalboard_converter_test.py b/test/util/mod_pedalboard_converter_test.py new file mode 100644 index 0000000..67891c4 --- /dev/null +++ b/test/util/mod_pedalboard_converter_test.py @@ -0,0 +1,118 @@ +# 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. + +import os +import unittest +from pathlib import Path +from unittest.mock import MagicMock + +from pluginsmanager.model.lv2.lv2_effect_builder import Lv2EffectBuilder +from pluginsmanager.model.system.system_effect import SystemEffect +from pluginsmanager.util.mod_pedalboard_converter import ModPedalboardConverter + + +class ModPedalboardConverterTest(unittest.TestCase): + @property + def mod_converter(self): + path = Path('/home/paulo/git/mod/mod_ui/') + #builder = MagicMock() + builder = Lv2EffectBuilder(ignore_unsupported_plugins=False) + # builder.reload(builder.lv2_plugins_data()) + + return ModPedalboardConverter(path, builder, ignore_errors=True) + + @property + def here(self): + return os.path.abspath(os.path.dirname(__file__)) / Path('data') + + @unittest.skipIf('TRAVIS' in os.environ, 'Mod-ui not configured in Travis build') + def test_all(self): + converter = self.mod_converter + pedalboard_path = self.here / Path('teste.pedalboard') + + system_effect = SystemEffect('system', ['capture_1', 'capture_2'], ['playback_1', 'playback_2']) + + print(converter.get_pedalboard_info(pedalboard_path)) + pedalboard = converter.convert(pedalboard_path, system_effect) + print(pedalboard.json) + + @unittest.skipIf('TRAVIS' in os.environ, 'Mod-ui not configured in Travis build') + def test_discover_system_effect(self): + converter = self.mod_converter + pedalboard_path = self.here / Path('teste.pedalboard') + + print(converter.get_pedalboard_info(pedalboard_path)) + pedalboard = converter.convert(pedalboard_path) + print(pedalboard.json) + + #@unittest.skipIf('TRAVIS' in os.environ, 'Mod-ui not configured in Travis build') + @unittest.skip('Raising error: Plugin not installed') + def test_discover_system_effect_midi(self): + converter = self.mod_converter + pedalboard_path = self.here / Path('EPiano.pedalboard') + + print(converter.get_pedalboard_info(pedalboard_path)) + pedalboard = converter.convert(pedalboard_path) + print(pedalboard.json) + + @unittest.skipIf('TRAVIS' in os.environ, 'Mod-ui not configured in Travis build') + #@unittest.skip + def test_discover_system_effect_midi_serial(self): + converter = self.mod_converter + pedalboard_path = self.here / Path('EPiano_simple_tt.pedalboard') + + system_effect = SystemEffect( + 'system', + ['capture_1', 'capture_2'], + ['playback_1', 'playback_2'], + ['midi_playback_1'], + ['midi_capture_1'] + ) + + print(converter.get_pedalboard_info(pedalboard_path)) + pedalboard = converter.convert(pedalboard_path) + print(pedalboard.json) + + @unittest.skipIf('TRAVIS' in os.environ, 'Mod-ui not configured in Travis build') + #@unittest.skip + def test_discover_system_effect_midi_serial_2(self): + converter = self.mod_converter + pedalboard_path = self.here / Path('setBfree_ttymidi.pedalboard') + + print(converter.get_pedalboard_info(pedalboard_path)) + # Serial midi out raises error + pedalboard = converter.convert(pedalboard_path) + print(pedalboard.json) + + @unittest.skipIf('TRAVIS' in os.environ, 'Mod-ui not configured in Travis build') + # @unittest.skip + def test_discover_system_effect_i_dont_know(self): + converter = self.mod_converter + pedalboard_path = self.here / Path('EPiano_simple.pedalboard') + + print(converter.get_pedalboard_info(pedalboard_path)) + # Serial midi out raises error + pedalboard = converter.convert(pedalboard_path) + print(pedalboard.json) + + @unittest.skipIf('TRAVIS' in os.environ, 'Mod-ui not configured in Travis build') + # @unittest.skip + def test_discover_system_effect_i_dont_know_2(self): + converter = self.mod_converter + pedalboard_path = self.here / Path('setBfree_simple.pedalboard') + + print(converter.get_pedalboard_info(pedalboard_path)) + # Serial midi out raises error + pedalboard = converter.convert(pedalboard_path) + print(pedalboard.json) diff --git a/test/util/persistence_decoder_test.py b/test/util/persistence_decoder_test.py index afadab1..6770eef 100644 --- a/test/util/persistence_decoder_test.py +++ b/test/util/persistence_decoder_test.py @@ -32,8 +32,11 @@ def setUpClass(cls): cls.builder = Lv2EffectBuilder() @property - def bank(self): - sys_effect = SystemEffect('system', ('capture_1', 'capture_2'), ('playback_1', 'playback_2')) + def system_effect(self): + return SystemEffect('system', ('capture_1', 'capture_2'), ('playback_1', 'playback_2'), ('midi_out',), ('midi_in',)) + + def bank(self, midi=True): + sys_effect = self.system_effect bank = Bank('Bank 1') pedalboard = Pedalboard('Pedalboard 1') @@ -49,10 +52,16 @@ def bank(self): pedalboard.append(filter) pedalboard.append(reverb2) - reverb.outputs[0].connect(filter.inputs[0]) - reverb.outputs[1].connect(filter.inputs[0]) - filter.outputs[0].connect(reverb2.inputs[0]) - reverb.outputs[0].connect(reverb2.inputs[0]) + pedalboard.connect(reverb.outputs[0], filter.inputs[0]) + pedalboard.connect(reverb.outputs[1], filter.inputs[0]) + pedalboard.connect(filter.outputs[0], reverb2.inputs[0]) + pedalboard.connect(reverb.outputs[0], reverb2.inputs[0]) + + if midi: + cctonode = self.builder.build('http://gareus.org/oss/lv2/midifilter#cctonote') + pedalboard.append(cctonode) + pedalboard.connect(cctonode.midi_outputs[0], sys_effect.midi_inputs[0]) + pedalboard.connect(sys_effect.midi_outputs[0], cctonode.midi_inputs[0]) pedalboard.connections.append(Connection(sys_effect.outputs[0], reverb.inputs[0])) pedalboard.connections.append(Connection(reverb2.outputs[0], sys_effect.inputs[0])) @@ -63,11 +72,9 @@ def bank(self): return bank def test_read(self): - system_effect = SystemEffect('system', ('capture_1', 'capture_2'), ('playback_1', 'playback_2')) - - util = PersistenceDecoder(system_effect) + util = PersistenceDecoder(self.system_effect) - bank = self.bank + bank = self.bank() bank_readed = util.read(bank.json) self.maxDiff = None @@ -106,7 +113,7 @@ def test_read_system_builder(self): util = PersistenceDecoder(system_effect) - bank = self.bank + bank = self.bank(midi=False) bank_readed = util.read(bank.json) self.assertEqual(bank.json, bank_readed.json)