Skip to content

Commit

Permalink
Merge pull request #24 from PedalPi/v0.4.0
Browse files Browse the repository at this point in the history
v0.4.0
  • Loading branch information
SrMouraSilva committed May 17, 2017
2 parents 09620c1 + 1ac1cdb commit 873f7b1
Show file tree
Hide file tree
Showing 16 changed files with 523 additions and 98 deletions.
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ exclude_lines =
def __repr__
pass
def __str__
[run]
omit = pluginsmanager/model/lv2/lilvlib.py
8 changes: 8 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
Version 0.4.0 -- released 05/17/17
==================================
- Improve coverage code
- Remove deprecated files (mod-host auto connect)
- Issue #23 - Add method for secure close in mod-host
- Issue #22 - Fastest load pedalboard
- Issue #19 - x-run callback. Create `pluginsmanager.jack.jack_client.JackClient`

Version 0.3.2 -- released 05/12/17
==================================
- Fix `pluginsmanager.util.builder`: Add folder in pip
Expand Down
10 changes: 7 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Example

This examples uses `Calf`_ and `Guitarix`_ audio plugins

Download and install `mod-host`_. For more information, check the `ModHost` section.
Download and install `mod-host`_. For more information, check the `ModHost section <mod_host.html>`__.

Start audio process

Expand Down Expand Up @@ -115,14 +115,18 @@ Add effects in the pedalboard
# or
# pedalboard.effects.append(reverb2)
For obtains automatically the sound card inputs and outputs, use `SystemEffectBuilder`. It requires `JACK-Client`_.
For obtains automatically the sound card inputs and outputs, use `SystemEffectBuilder`.
It requires a `JackClient` instance, that uses `JACK-Client`_.

.. _JACK-Client: https://jackclient-python.readthedocs.io/

.. code-block:: python
from pluginsmanager.jack.jack_client import JackClient
client = JackClient()
from pluginsmanager.model.system.system_effect_builder import SystemEffectBuilder
sys_effect = SystemEffectBuilder()
sys_effect = SystemEffectBuilder(client)
For manual input and output sound card definition, use:

Expand Down
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Contents:
.. toctree::
:maxdepth: 2

jack
mod_host
model
model_lv2
Expand Down
9 changes: 9 additions & 0 deletions docs/source/jack.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
PedalPi - PluginsManager - Jack
===============================

pluginsmanager.jack.jack_client.JackClient
------------------------------------------

.. autoclass:: pluginsmanager.jack.jack_client.JackClient
:members:
:special-members:
13 changes: 13 additions & 0 deletions pluginsmanager/jack/__init__.py
Original file line number Diff line number Diff line change
@@ -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.
70 changes: 70 additions & 0 deletions pluginsmanager/jack/jack_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# 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 logging
import jack


class JackClient(object):
"""
Based in: http://jackclient-python.readthedocs.io/en/0.4.2/examples.html#chatty-client
Through the :class:`JackClient` it is possible to be notified when `x-run`
occurs and when the Jack server is closed::
>>> client = JackClient()
>>> client.xrun_callback = lambda delay: print('x-run', delay)
>>> client.shutdown_callback = lambda status, reason: print('shutdown: ', status, reason)
When you don't use anymore, close it::
>>> client.close()
:param bool no_start_server: False if starts a new JACK server
True if uses a already started jack (ex: using `jackdump`)
:param name: Jack client name. Default: `JackClient`
"""
def __init__(self, no_start_server=True, name=None):
if name is None:
name = self.__class__.__name__

self.client = jack.Client(name=name, no_start_server=no_start_server)

self.xrun_callback = lambda delay: ...
self.shutdown_callback = lambda status, reason: ...

if self.client.status.server_started:
logging.info('JACK server was started')
else:
logging.info('JACK server was already running')

if self.client.status.name_not_unique:
logging.info('Unique client name generated {}'.format(self.client.name))

@self.client.set_xrun_callback
def xrun(delay):
self.xrun_callback(delay)

@self.client.set_shutdown_callback
def shutdown(status, reason):
self.shutdown_callback(status, reason)

self.client.activate()

def close(self):
"""
Deactive and closes the jack client
"""
self.client.deactivate()
self.client.close()
19 changes: 3 additions & 16 deletions pluginsmanager/mod_host/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,22 +49,6 @@ def remove(self, effect):
"""
self.connection.send(ProtocolParser.remove(effect))

def connect_input_in(self, effect_input):
"""
.. deprecated:: 0.0
Will be removed
"""
self.connection.send(ProtocolParser.connect_input_in(effect_input))

def connect_on_output(self, effect_output, index_out):
"""
.. deprecated:: 0.0
Will be removed
"""
self.connection.send(ProtocolParser.connect_on_output(effect_output, index_out))

def connect(self, connection):
"""
Connect two effect audio ports
Expand Down Expand Up @@ -96,3 +80,6 @@ def set_status(self, effect):
:param Lv2Effect effect: Effect with the status updated
"""
self.connection.send(ProtocolParser.bypass(effect))

def quit(self):
self.connection.send(ProtocolParser.quit())
124 changes: 83 additions & 41 deletions pluginsmanager/mod_host/mod_host.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@
from pluginsmanager.model.updates_observer import UpdatesObserver
from pluginsmanager.model.update_type import UpdateType

from pluginsmanager.model.connection import Connection

from pluginsmanager.mod_host.host import Host
from pluginsmanager.util.pairs_list import PairsList


class ModHost(UpdatesObserver):
Expand Down Expand Up @@ -83,30 +82,15 @@ def __init__(self, address='localhost'):
self.host = None
self._pedalboard = None

self.pairs_list = PairsList(lambda effect: effect.plugin['uri'])

def connect(self):
"""
Connect the object with mod-host with the _address_ parameter informed in
the initialization (``__init__(address)``)
"""
self.host = Host(self.address)

def auto_connect(self):
if self.pedalboard is None or len(self.pedalboard.effects) == 0:
return

first = self.pedalboard.effects[0]
last = self.pedalboard.effects[-1]

self.host.connect_input_in(first.inputs[0])

before = first
for effect in self.pedalboard.effects[1:]:
self.host.connect(Connection(before.outputs[0], effect.inputs[0]))
before = effect

self.host.connect_on_output(last.outputs[0], 1)
self.host.connect_on_output(last.outputs[0], 2)

@property
def pedalboard(self):
"""
Expand All @@ -123,33 +107,27 @@ def pedalboard(self, pedalboard):
self.on_current_pedalboard_changed(pedalboard)

def __del__(self):
if self.pedalboard:
self._remove_pedalboard(self.pedalboard)
self.close()

def close(self):
"""
Remove the audio plugins loaded
"""
self.pedalboard = None
#self.host.quit()

####################################
# Observer
####################################
def on_current_pedalboard_changed(self, pedalboard):
if self.pedalboard is not None:
self._remove_pedalboard(self.pedalboard)

self._pedalboard = pedalboard

# Changes are only updated if self._pedalboard = pedalboard
if pedalboard is not None:
for index, effect in enumerate(pedalboard.effects):
self.on_effect_updated(effect, UpdateType.CREATED, index=index, origin=pedalboard)

for connection in pedalboard.connections:
self.on_connection_updated(connection, UpdateType.CREATED, pedalboard=pedalboard)

def _remove_pedalboard(self, pedalboard):
for index, effect in enumerate(pedalboard.effects):
self.on_effect_updated(effect, UpdateType.DELETED, index=index, origin=pedalboard)
if self.pedalboard is not None and pedalboard is not None:
self._replace_pedalboard(self.pedalboard, pedalboard)
else:
self._change_pedalboard(pedalboard)

def on_bank_updated(self, bank, update_type, **kwargs):
if self.pedalboard is not None \
and bank != self.pedalboard.bank:
if (self.pedalboard is not None
and bank != self.pedalboard.bank):
return
pass

Expand All @@ -172,17 +150,21 @@ def on_effect_updated(self, effect, update_type, index, origin, **kwargs):
self.host.remove(effect)

def _load_params_of(self, effect):
"""
Called only when a effect has created
Param changes calls `self.on_param_value_changed(...)`
"""
for param in effect.params:
if param.value != param.default:
self._set_param_value(param)

def on_effect_status_toggled(self, effect):
def on_effect_status_toggled(self, effect, **kwargs):
if effect.pedalboard != self.pedalboard:
return

self.host.set_status(effect)

def on_param_value_changed(self, param):
def on_param_value_changed(self, param, **kwargs):
if param.effect.pedalboard != self.pedalboard:
return

Expand All @@ -199,3 +181,63 @@ def on_connection_updated(self, connection, update_type, pedalboard, **kwargs):

def _set_param_value(self, param):
self.host.set_param_value(param)

####################################
# Private methods
####################################
def _replace_pedalboard(self, current, pedalboard):
# Replace effects with equal plugins
result = self.pairs_list.calculate(current.effects, pedalboard.effects)

for current_effect, new_effect in result.pairs:
new_effect.instance = current_effect.instance

for parameter_old_effect, parameter_new_effect in zip(current_effect.params, new_effect.params):
if parameter_new_effect.value != parameter_old_effect.value:
self._set_param_value(parameter_new_effect)

# Remove not equal plugins
current_will_remove = result.elements_not_added_a

self._remove_connections_of(current)
self._remove_effects(current_will_remove)

self._pedalboard = pedalboard

# Remove not equal plugins
# Changes are only updated if self._pedalboard = pedalboard
pedalboard_will_add = result.elements_not_added_b

self._add_effects(pedalboard_will_add)
self._add_connections_of(pedalboard)

def _change_pedalboard(self, pedalboard):
if self.pedalboard is not None:
self._remove_pedalboard(self.pedalboard)

self._pedalboard = pedalboard

# Changes are only updated if self._pedalboard = pedalboard
if pedalboard is not None:
self._add_effects(pedalboard.effects)
self._add_connections_of(pedalboard)

def _remove_pedalboard(self, pedalboard):
self._remove_effects(pedalboard.effects)

def _remove_connections_of(self, pedalboard):
for connection in pedalboard.connections:
self.on_connection_updated(connection, UpdateType.DELETED, pedalboard=pedalboard)

def _remove_effects(self, effects):
for effect in effects:
self.on_effect_updated(effect, UpdateType.DELETED, index=None, origin=effect.pedalboard)

def _add_connections_of(self, pedalboard):
for connection in pedalboard.connections:
self.on_connection_updated(connection, UpdateType.CREATED, pedalboard=pedalboard)

def _add_effects(self, effects):
for effect in effects:
self.on_effect_updated(effect, UpdateType.CREATED, index=None, origin=effect.pedalboard)

0 comments on commit 873f7b1

Please sign in to comment.