# 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.effects_list import EffectsList
from pluginsmanager.model.connections_list import ConnectionsList
from pluginsmanager.observer.update_type import UpdateType, CustomChange
from unittest.mock import MagicMock
[docs]class Pedalboard(object):
"""
Pedalboard is a patch representation: your structure contains
:class:`.Effect` and :class:`~pluginsmanager.model.connection.Connection`::
>>> pedalboard = Pedalboard('Rocksmith')
>>> bank.append(pedalboard)
>>> builder = Lv2EffectBuilder()
>>> pedalboard.effects
[]
>>> reverb = builder.build('http://calf.sourceforge.net/plugins/Reverb')
>>> pedalboard.append(reverb)
>>> pedalboard.effects
[<Lv2Effect object as 'Calf Reverb' active at 0x7f60effb09e8>]
>>> fuzz = builder.build('http://guitarix.sourceforge.net/plugins/gx_fuzzfacefm_#_fuzzfacefm_')
>>> pedalboard.effects.append(fuzz)
>>> pedalboard.connections
[]
>>> 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
>>> pedalboard.connect(reverb.outputs[1], sys_effect.inputs[0])
>>> pedalboard.connections
[<Connection object as 'system.capture_1 -> GxFuzzFaceFullerMod.In' at 0x7f60f45f3f60>, <Connection object as 'GxFuzzFaceFullerMod.Out -> Calf Reverb.In L' at 0x7f60f45f57f0>, <Connection object as 'Calf Reverb.Out R -> system.playback_1' at 0x7f60f45dacc0>]
>>> pedalboard.data
{}
>>> pedalboard.data = {'my-awesome-component': True}
>>> pedalboard.data
{'my-awesome-component': True}
For load the pedalboard for play the songs with it::
>>> mod_host.pedalboard = pedalboard
All changes¹ in the pedalboard will be reproduced in mod-host.
¹ Except in data attribute, changes in this does not interfere with anything.
:param string name: Pedalboard name
"""
[docs] def __init__(self, name):
self._name = name
self._effects = EffectsList()
self._connections = ConnectionsList(self)
self.effects.observer = self._effects_observer
self.connections.observer = self._connections_observer
self._observer = MagicMock()
self.bank = None
self._data = {}
@property
def observer(self):
return self._observer
@observer.setter
def observer(self, observer):
self._observer = observer
for effect in self.effects:
effect.observer = observer
def _effects_observer(self, update_type, effect, index, **kwargs):
kwargs['index'] = index
kwargs['origin'] = self
if update_type == UpdateType.CREATED:
self._init_effect(effect)
elif update_type == UpdateType.UPDATED:
self._init_effect(effect)
old_effect = kwargs['old']
if old_effect not in self.effects:
self._clear_effect(old_effect)
elif update_type == UpdateType.DELETED:
self._clear_effect(effect)
self.observer.on_effect_updated(effect, update_type, index=index, origin=self)
def _init_effect(self, effect):
effect.pedalboard = self
effect.observer = self.observer
def _clear_effect(self, effect):
for connection in effect.connections:
self.connections.remove_silently(connection)
effect.pedalboard = None
effect.observer = MagicMock()
def _connections_observer(self, update_type, connection, index, **kwargs):
self.observer.on_connection_updated(connection, update_type, pedalboard=self, **kwargs)
@property
def json(self):
"""
Get a json decodable representation of this pedalboard
:return dict: json representation
"""
return self.__dict__
@property
def name(self):
"""
Pedalboard name
:getter: Pedalboard name
:setter: Set the pedalboard name
:type: string
"""
return self._name
@name.setter
def name(self, new_value):
if self._name == new_value:
return
self._name = new_value
self.observer.on_custom_change(CustomChange.PEDALBOARD_NAME, UpdateType.UPDATED, pedalboard=self)
@property
def data(self):
"""
Custom information about pedalboard that is necessary to be persisted.
Example is effects disposition in a visual modelling pedalboard.
.. note::
This operation only will notifies changes if the setter is called::
>>> data = {'level': 50}
>>> # This call the observer on_custom_change(CustomChange.PEDALBOARD_DATA, UpdateType.UPDATED, pedalboard=pedalboard)
>>> pedalboard.data = data
>>> # This doesn't call the observer on_custom_change(...)
>>> data['level'] = 80
>>> # But this call, even though the object is the same.
>>> pedalboard.data = data
:getter: Data
:setter: Set the data
:type: dict
"""
return self._data
@data.setter
def data(self, new_value):
self._data = new_value
self.observer.on_custom_change(CustomChange.PEDALBOARD_DATA, UpdateType.UPDATED, pedalboard=self)
@property
def __dict__(self):
return {
'name': self.name,
'effects': [effect.json for effect in self.effects],
'connections': [connection.json for connection in self.connections],
'data': self.data
}
[docs] def append(self, effect):
"""
Add a :class:`.Effect` in this pedalboard
This works same as::
>>> pedalboard.effects.append(effect)
or::
>>> pedalboard.effects.insert(len(pedalboard.effects), effect)
:param Effect effect: Effect that will be added
"""
self.effects.append(effect)
@property
def effects(self):
"""
Return the effects presents in the pedalboard
.. note::
Because the effects is an :class:`.ObservableList`, it isn't settable.
For replace, del the effects unnecessary and add the necessary
effects
"""
return self._effects
@property
def connections(self):
"""
Return the pedalboard connections list
.. note::
Because the connections is an :class:`.ObservableList`, it isn't settable.
For replace, del the connections unnecessary and add the necessary
connections
"""
return self._connections
@property
def index(self):
"""
Returns the first occurrence of the pedalboard in your bank
"""
if self.bank is None:
raise IndexError('Pedalboard not contains a bank')
return self.bank.pedalboards.index(self)
[docs] 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))
[docs] 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))