mixer: Inject audio into software mixer

Instead of giving all mixers access to the audio actor.
This commit is contained in:
Stein Magnus Jodal 2014-07-12 01:53:43 +02:00
parent a3dc763b29
commit 5f091c10c2
4 changed files with 57 additions and 35 deletions

View File

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

View File

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

View File

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

View File

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