Actorify mixers

This commit is contained in:
Stein Magnus Jodal 2011-03-07 22:03:11 +01:00
parent 3a1de6578d
commit b88d8d5d8a
8 changed files with 54 additions and 59 deletions

View File

@ -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():

View File

@ -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

View File

@ -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.

View File

@ -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()

View File

@ -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):

View File

@ -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()

View File

@ -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)

View File

@ -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