From 5f091c10c25328fba087a973946d5025c3244a95 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 12 Jul 2014 01:53:43 +0200 Subject: [PATCH] mixer: Inject audio into software mixer Instead of giving all mixers access to the audio actor. --- mopidy/audio/actor.py | 15 ++++++++- mopidy/commands.py | 58 +++++++++++++++++------------------ mopidy/mixer.py | 4 +-- mopidy/softwaremixer/mixer.py | 15 +++++++-- 4 files changed, 57 insertions(+), 35 deletions(-) diff --git a/mopidy/audio/actor.py b/mopidy/audio/actor.py index 25a3a44c..5c8c3c9f 100644 --- a/mopidy/audio/actor.py +++ b/mopidy/audio/actor.py @@ -50,10 +50,11 @@ class Audio(pykka.ThreadingActor): state = PlaybackState.STOPPED _target_state = gst.STATE_NULL - def __init__(self, config): + def __init__(self, config, mixer): super(Audio, self).__init__() self._config = config + self._mixer = mixer self._playbin = None self._signal_ids = {} # {(element, event): signal_id} @@ -68,6 +69,7 @@ class Audio(pykka.ThreadingActor): try: self._setup_playbin() self._setup_output() + self._setup_mixer() self._setup_visualizer() self._setup_message_processor() except gobject.GError as ex: @@ -76,6 +78,7 @@ class Audio(pykka.ThreadingActor): def on_stop(self): self._teardown_message_processor() + self._teardown_mixer() self._teardown_playbin() def _connect(self, element, event, *args): @@ -180,6 +183,16 @@ class Audio(pykka.ThreadingActor): 'Failed to create audio output "%s": %s', output_desc, ex) process.exit_process() + def _setup_mixer(self): + if self._config['audio']['mixer'] != 'software': + return + self._mixer.audio = self.actor_ref.proxy() + + def _teardown_mixer(self): + if self._config['audio']['mixer'] != 'software': + return + self._mixer.audio = None + def _setup_visualizer(self): visualizer_element = self._config['audio']['visualizer'] if not visualizer_element: diff --git a/mopidy/commands.py b/mopidy/commands.py index 87d35056..e43f182e 100644 --- a/mopidy/commands.py +++ b/mopidy/commands.py @@ -266,8 +266,8 @@ class RootCommand(Command): frontend_classes = args.registry['frontend'] try: - audio = self.start_audio(config) - mixer = self.start_mixer(config, mixer_class, audio) + mixer = self.start_mixer(config, mixer_class) + audio = self.start_audio(config, mixer) backends = self.start_backends(config, backend_classes, audio) core = self.start_core(mixer, backends) self.start_frontends(config, frontend_classes, core) @@ -282,9 +282,9 @@ class RootCommand(Command): loop.quit() self.stop_frontends(frontend_classes) self.stop_core() - self.stop_mixer(mixer_class) self.stop_backends(backend_classes) self.stop_audio() + self.stop_mixer(mixer_class) process.stop_remaining_actors() def get_mixer_class(self, config, mixer_classes): @@ -302,9 +302,29 @@ class RootCommand(Command): process.exit_process() return selected_mixers[0] - def start_audio(self, config): + def start_mixer(self, config, mixer_class): + try: + logger.info('Starting Mopidy mixer: %s', mixer_class.__name__) + mixer = mixer_class.start(config=config).proxy() + self.configure_mixer(config, mixer) + return mixer + except exceptions.MixerError as exc: + logger.error( + 'Mixer (%s) initialization error: %s', + mixer_class.__name__, exc.message) + raise + + def configure_mixer(self, config, mixer): + volume = config['audio']['mixer_volume'] + if volume is not None: + mixer.set_volume(volume) + logger.info('Mixer volume set to %d', volume) + else: + logger.debug('Mixer volume left unchanged') + + def start_audio(self, config, mixer): logger.info('Starting Mopidy audio') - return Audio.start(config=config).proxy() + return Audio.start(config=config, mixer=mixer).proxy() def start_backends(self, config, backend_classes, audio): logger.info( @@ -325,26 +345,6 @@ class RootCommand(Command): return backends - def start_mixer(self, config, mixer_class, audio): - try: - logger.info('Starting Mopidy mixer: %s', mixer_class.__name__) - mixer = mixer_class.start(config=config, audio=audio).proxy() - self.configure_mixer(config, mixer) - return mixer - except exceptions.MixerError as exc: - logger.error( - 'Mixer (%s) initialization error: %s', - mixer_class.__name__, exc.message) - raise - - def configure_mixer(self, config, mixer): - volume = config['audio']['mixer_volume'] - if volume is not None: - mixer.set_volume(volume) - logger.info('Mixer volume set to %d', volume) - else: - logger.debug('Mixer volume left unchanged') - def start_core(self, mixer, backends): logger.info('Starting Mopidy core') return Core.start(mixer=mixer, backends=backends).proxy() @@ -372,10 +372,6 @@ class RootCommand(Command): logger.info('Stopping Mopidy core') process.stop_actors_by_class(Core) - def stop_mixer(self, mixer_class): - logger.info('Stopping Mopidy mixer') - process.stop_actors_by_class(mixer_class) - def stop_backends(self, backend_classes): logger.info('Stopping Mopidy backends') for backend_class in backend_classes: @@ -385,6 +381,10 @@ class RootCommand(Command): logger.info('Stopping Mopidy audio') process.stop_actors_by_class(Audio) + def stop_mixer(self, mixer_class): + logger.info('Stopping Mopidy mixer') + process.stop_actors_by_class(mixer_class) + class ConfigCommand(Command): help = 'Show currently active configuration.' diff --git a/mopidy/mixer.py b/mopidy/mixer.py index c000eb41..eaea290d 100644 --- a/mopidy/mixer.py +++ b/mopidy/mixer.py @@ -19,8 +19,6 @@ class Mixer(object): :param config: the entire Mopidy configuration :type config: dict - :param audio: actor proxy for the audio subsystem - :type audio: :class:`pykka.ActorProxy` for :class:`mopidy.audio.Audio` """ name = None @@ -122,7 +120,7 @@ class MixerListener(listener.Listener): @staticmethod def send(event, **kwargs): - """Helper to allow calling of audio listener events""" + """Helper to allow calling of mixer listener events""" listener.send_async(MixerListener, event, **kwargs) def volume_changed(self, volume): diff --git a/mopidy/softwaremixer/mixer.py b/mopidy/softwaremixer/mixer.py index 3da90007..c50166c5 100644 --- a/mopidy/softwaremixer/mixer.py +++ b/mopidy/softwaremixer/mixer.py @@ -14,22 +14,33 @@ class SoftwareMixer(pykka.ThreadingActor, mixer.Mixer): name = 'software' - def __init__(self, config, audio): + def __init__(self, config): super(SoftwareMixer, self).__init__() - self.audio = audio + + self.audio = None logger.info('Mixing using GStreamer software mixing') def get_volume(self): + if self.audio is None: + return None return self.audio.get_volume().get() def set_volume(self, volume): + if self.audio is None: + return False self.audio.set_volume(volume) self.trigger_volume_changed(volume) + return True def get_mute(self): + if self.audio is None: + return None return self.audio.get_mute().get() def set_mute(self, muted): + if self.audio is None: + return False self.audio.set_mute(muted) self.trigger_mute_changed(muted) + return True