diff --git a/mopidy/core.py b/mopidy/core.py index ab457a00..51a0b737 100644 --- a/mopidy/core.py +++ b/mopidy/core.py @@ -20,6 +20,7 @@ def main(): setup_settings() setup_gobject_loop() setup_output() + setup_mixer() setup_backend() setup_frontends() @@ -50,11 +51,13 @@ def setup_gobject_loop(): return gobject_loop def setup_output(): - output = get_class(settings.OUTPUT)() - output.start() - return output + return get_class(settings.OUTPUT).start_proxy() + +def setup_mixer(): + return get_class(settings.MIXER).start_proxy() def setup_backend(): + # XXX Convert backend to one or more actors? return get_class(settings.BACKENDS[0])() def setup_frontends(): diff --git a/mopidy/mixers/alsa.py b/mopidy/mixers/alsa.py index 4aa5952f..658677e5 100644 --- a/mopidy/mixers/alsa.py +++ b/mopidy/mixers/alsa.py @@ -1,12 +1,14 @@ import alsaaudio import logging +from pykka.actor import ThreadingActor + from mopidy import settings from mopidy.mixers.base import BaseMixer logger = logging.getLogger('mopidy.mixers.alsa') -class AlsaMixer(BaseMixer): +class AlsaMixer(ThreadingActor, BaseMixer): """ Mixer which uses the Advanced Linux Sound Architecture (ALSA) to control volume. @@ -20,8 +22,8 @@ class AlsaMixer(BaseMixer): - :attr:`mopidy.settings.MIXER_ALSA_CONTROL` """ - def __init__(self, *args, **kwargs): - super(AlsaMixer, self).__init__(*args, **kwargs) + def __init__(self): + # XXX Do mixer detection after actor starts? self._mixer = alsaaudio.Mixer(self._get_mixer_control()) assert self._mixer is not None diff --git a/mopidy/mixers/base.py b/mopidy/mixers/base.py index f7f9525c..74996cb6 100644 --- a/mopidy/mixers/base.py +++ b/mopidy/mixers/base.py @@ -2,17 +2,12 @@ from mopidy import settings class BaseMixer(object): """ - :param backend: a backend instance - :type backend: :class:`mopidy.backends.base.Backend` - **Settings:** - :attr:`mopidy.settings.MIXER_MAX_VOLUME` """ - def __init__(self, backend, *args, **kwargs): - self.backend = backend - self.amplification_factor = settings.MIXER_MAX_VOLUME / 100.0 + amplification_factor = settings.MIXER_MAX_VOLUME / 100.0 @property def volume(self): @@ -35,9 +30,6 @@ class BaseMixer(object): volume = 100 self._set_volume(volume) - def destroy(self): - pass - def _get_volume(self): """ Return volume as integer in range [0, 100]. :class:`None` if unknown. diff --git a/mopidy/mixers/denon.py b/mopidy/mixers/denon.py index f0712f95..50c321b6 100644 --- a/mopidy/mixers/denon.py +++ b/mopidy/mixers/denon.py @@ -1,12 +1,13 @@ import logging -from threading import Lock + +from pykka.actor import ThreadingActor from mopidy import settings from mopidy.mixers.base import BaseMixer logger = logging.getLogger(u'mopidy.mixers.denon') -class DenonMixer(BaseMixer): +class DenonMixer(ThreadingActor, BaseMixer): """ Mixer for controlling Denon amplifiers and receivers using the RS-232 protocol. @@ -24,12 +25,12 @@ class DenonMixer(BaseMixer): - :attr:`mopidy.settings.MIXER_EXT_PORT` -- Example: ``/dev/ttyUSB0`` """ - def __init__(self, *args, **kwargs): + def __init__(self): """ Connects using the serial specifications from Denon's RS-232 Protocol specification: 9600bps 8N1. """ - super(DenonMixer, self).__init__(*args, **kwargs) + # XXX Do setup after actor starts? device = kwargs.get('device', None) if device: self._device = device @@ -38,14 +39,11 @@ class DenonMixer(BaseMixer): self._device = Serial(port=settings.MIXER_EXT_PORT, timeout=0.2) self._levels = ['99'] + ["%(#)02d" % {'#': v} for v in range(0, 99)] self._volume = 0 - self._lock = Lock() def _get_volume(self): - self._lock.acquire() - self.ensure_open_device() + self._ensure_open_device() self._device.write('MV?\r') vol = str(self._device.readline()[2:4]) - self._lock.release() logger.debug(u'_get_volume() = %s' % vol) return self._levels.index(vol) @@ -53,14 +51,12 @@ class DenonMixer(BaseMixer): # Clamp according to Denon-spec if volume > 99: volume = 99 - self._lock.acquire() - self.ensure_open_device() + self._ensure_open_device() self._device.write('MV%s\r'% self._levels[volume]) vol = self._device.readline()[2:4] - self._lock.release() self._volume = self._levels.index(vol) - def ensure_open_device(self): + def _ensure_open_device(self): if not self._device.isOpen(): logger.debug(u'(re)connecting to Denon device') self._device.open() diff --git a/mopidy/mixers/dummy.py b/mopidy/mixers/dummy.py index 12a8137e..186bc7aa 100644 --- a/mopidy/mixers/dummy.py +++ b/mopidy/mixers/dummy.py @@ -1,10 +1,11 @@ +from pykka.actor import ThreadingActor + from mopidy.mixers.base import BaseMixer -class DummyMixer(BaseMixer): +class DummyMixer(ThreadingActor, BaseMixer): """Mixer which just stores and reports the chosen volume.""" - def __init__(self, *args, **kwargs): - super(DummyMixer, self).__init__(*args, **kwargs) + def __init__(self): self._volume = None def _get_volume(self): diff --git a/mopidy/mixers/gstreamer_software.py b/mopidy/mixers/gstreamer_software.py index 9dca3690..95635794 100644 --- a/mopidy/mixers/gstreamer_software.py +++ b/mopidy/mixers/gstreamer_software.py @@ -1,13 +1,19 @@ +from pykka.actor import ThreadingActor +from pykka.proxy import ActorProxy +from pykka.registry import ActorRegistry + from mopidy.mixers.base import BaseMixer -class GStreamerSoftwareMixer(BaseMixer): +class GStreamerSoftwareMixer(ThreadingActor, BaseMixer): """Mixer which uses GStreamer to control volume in software.""" - def __init__(self, *args, **kwargs): - super(GStreamerSoftwareMixer, self).__init__(*args, **kwargs) + def __init__(self): + # XXX Get reference to output without hardcoding GStreamerOutput + output_refs = ActorRegistry.get_by_class_name('GStreamerOutput') + self.output = ActorProxy(output_refs[0]) def _get_volume(self): - return self.backend.output.get_volume() + return self.output.get_volume().get() def _set_volume(self, volume): - self.backend.output.set_volume(volume) + self.output.set_volume(volume).get() diff --git a/mopidy/mixers/nad.py b/mopidy/mixers/nad.py index 5cf92826..0765882d 100644 --- a/mopidy/mixers/nad.py +++ b/mopidy/mixers/nad.py @@ -2,13 +2,15 @@ import logging from serial import Serial from multiprocessing import Pipe +from pykka.actor import ThreadingActor + from mopidy import settings from mopidy.mixers.base import BaseMixer from mopidy.utils.process import BaseThread logger = logging.getLogger('mopidy.mixers.nad') -class NadMixer(BaseMixer): +class NadMixer(ThreadingActor, BaseMixer): """ Mixer for controlling NAD amplifiers and receivers using the NAD RS-232 protocol. @@ -36,21 +38,19 @@ class NadMixer(BaseMixer): """ - def __init__(self, *args, **kwargs): - super(NadMixer, self).__init__(*args, **kwargs) - self._volume = None - self._pipe, other_end = Pipe() - NadTalker(self.backend.core_queue, pipe=other_end).start() + def __init__(self): + self._volume_cache = None + self._nad_talker = NadTalker.start_proxy() def _get_volume(self): - return self._volume + return self._volume_cache def _set_volume(self, volume): - self._volume = volume - self._pipe.send({'command': 'set_volume', 'volume': volume}) + self._volume_cache = volume + self._nad_talker.set_volume(volume) -class NadTalker(BaseThread): +class NadTalker(ThreadingActor): """ Independent process which does the communication with the NAD device. @@ -72,21 +72,15 @@ class NadTalker(BaseThread): # Volume in range 0..VOLUME_LEVELS. :class:`None` before calibration. _nad_volume = None - def __init__(self, core_queue, pipe=None): - super(NadTalker, self).__init__(core_queue) - self.name = u'NadTalker' - self.pipe = pipe + def __init__(self): self._device = None - def run_inside_try(self): + # XXX Do after actor starts? + self._setup() + + def _setup(self): self._open_connection() self._set_device_to_known_state() - while self.pipe.poll(None): - message = self.pipe.recv() - if message['command'] == 'set_volume': - self._set_volume(message['volume']) - elif message['command'] == 'reset_device': - self._set_device_to_known_state() def _open_connection(self): # Opens serial connection to the device. @@ -164,7 +158,7 @@ class NadTalker(BaseThread): self._nad_volume = 0 logger.info(u'Done calibrating NAD amplifier') - def _set_volume(self, volume): + def set_volume(self, volume): # Increase or decrease the amplifier volume until it matches the given # target volume. logger.debug(u'Setting volume to %d' % volume) diff --git a/mopidy/mixers/osa.py b/mopidy/mixers/osa.py index 2ea04cf2..53983095 100644 --- a/mopidy/mixers/osa.py +++ b/mopidy/mixers/osa.py @@ -1,9 +1,11 @@ from subprocess import Popen, PIPE import time +from pykka.actor import ThreadingActor + from mopidy.mixers.base import BaseMixer -class OsaMixer(BaseMixer): +class OsaMixer(ThreadingActor, BaseMixer): """ Mixer which uses ``osascript`` on OS X to control volume. @@ -14,7 +16,6 @@ class OsaMixer(BaseMixer): **Settings:** - None - """ CACHE_TTL = 30