From 9da716935cd7b1701037c504145eb83d04c221b2 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Tue, 8 Jul 2014 01:28:02 +0200 Subject: [PATCH] audio: Only expose GStreamer's software mixer --- mopidy/audio/actor.py | 177 +++----------------------------------- tests/audio/test_actor.py | 36 +------- 2 files changed, 18 insertions(+), 195 deletions(-) diff --git a/mopidy/audio/actor.py b/mopidy/audio/actor.py index 08c634e9..25a3a44c 100644 --- a/mopidy/audio/actor.py +++ b/mopidy/audio/actor.py @@ -10,7 +10,7 @@ import gst # noqa import pykka -from mopidy.audio import mixers, playlists, utils +from mopidy.audio import playlists, utils from mopidy.audio.constants import PlaybackState from mopidy.audio.listener import AudioListener from mopidy.utils import process @@ -18,8 +18,6 @@ from mopidy.utils import process logger = logging.getLogger(__name__) -mixers.register_mixers() - playlists.register_typefinders() playlists.register_elements() @@ -60,12 +58,6 @@ class Audio(pykka.ThreadingActor): self._playbin = None self._signal_ids = {} # {(element, event): signal_id} - self._mixer = None - self._mixer_track = None - self._mixer_scale = None - self._software_mixing = False - self._volume_set = None - self._appsrc = None self._appsrc_caps = None self._appsrc_need_data_callback = None @@ -77,7 +69,6 @@ class Audio(pykka.ThreadingActor): self._setup_playbin() self._setup_output() self._setup_visualizer() - self._setup_mixer() self._setup_message_processor() except gobject.GError as ex: logger.exception(ex) @@ -85,7 +76,6 @@ class Audio(pykka.ThreadingActor): def on_stop(self): self._teardown_message_processor() - self._teardown_mixer() self._teardown_playbin() def _connect(self, element, event, *args): @@ -204,86 +194,6 @@ class Audio(pykka.ThreadingActor): 'Failed to create audio visualizer "%s": %s', visualizer_element, ex) - def _setup_mixer(self): - mixer_desc = self._config['audio']['mixer'] - track_desc = self._config['audio']['mixer_track'] - volume = self._config['audio']['mixer_volume'] - - if mixer_desc is None: - logger.info('Not setting up audio mixer') - return - - if mixer_desc == 'software': - self._software_mixing = True - logger.info('Audio mixer is using software mixing') - if volume is not None: - self.set_volume(volume) - logger.info('Audio mixer volume set to %d', volume) - return - - try: - mixerbin = gst.parse_bin_from_description( - mixer_desc, ghost_unconnected_pads=False) - except gobject.GError as ex: - logger.warning( - 'Failed to create audio mixer "%s": %s', mixer_desc, ex) - return - - # We assume that the bin will contain a single mixer. - mixer = mixerbin.get_by_interface(b'GstMixer') - if not mixer: - logger.warning( - 'Did not find any audio mixers in "%s"', mixer_desc) - return - - if mixerbin.set_state(gst.STATE_READY) != gst.STATE_CHANGE_SUCCESS: - logger.warning( - 'Setting audio mixer "%s" to READY failed', mixer_desc) - return - - track = self._select_mixer_track(mixer, track_desc) - if not track: - logger.warning('Could not find usable audio mixer track') - return - - self._mixer = mixer - self._mixer_track = track - self._mixer_scale = ( - self._mixer_track.min_volume, self._mixer_track.max_volume) - - logger.info( - 'Audio mixer set to "%s" using track "%s"', - str(mixer.get_factory().get_name()).decode('utf-8'), - str(track.label).decode('utf-8')) - - if volume is not None: - self.set_volume(volume) - logger.info('Audio mixer volume set to %d', volume) - - def _select_mixer_track(self, mixer, track_label): - # Ignore tracks without volumes, then look for track with - # label equal to the audio/mixer_track config value, otherwise fallback - # to first usable track hoping the mixer gave them to us in a sensible - # order. - - usable_tracks = [] - for track in mixer.list_tracks(): - if not mixer.get_volume(track): - continue - - if track_label and track.label == track_label: - return track - elif track.flags & (gst.interfaces.MIXER_TRACK_MASTER | - gst.interfaces.MIXER_TRACK_OUTPUT): - usable_tracks.append(track) - - if usable_tracks: - return usable_tracks[0] - - def _teardown_mixer(self): - if self._mixer is not None: - self._mixer.set_state(gst.STATE_NULL) - def _setup_message_processor(self): bus = self._playbin.get_bus() bus.add_signal_watch() @@ -514,108 +424,49 @@ class Audio(pykka.ThreadingActor): def get_volume(self): """ - Get volume level of the installed mixer. + Get volume level of the software mixer. Example values: 0: - Muted. + Minimum volume. 100: - Max volume for given system. - :class:`None`: - No mixer present, so the volume is unknown. + Max volume. - :rtype: int in range [0..100] or :class:`None` + :rtype: int in range [0..100] """ - if self._software_mixing: - return int(round(self._playbin.get_property('volume') * 100)) - - if self._mixer is None: - return None - - volumes = self._mixer.get_volume(self._mixer_track) - avg_volume = float(sum(volumes)) / len(volumes) - - internal_scale = (0, 100) - - if self._volume_set is not None: - volume_set_on_mixer_scale = self._rescale( - self._volume_set, old=internal_scale, new=self._mixer_scale) - else: - volume_set_on_mixer_scale = None - - if volume_set_on_mixer_scale == avg_volume: - return self._volume_set - else: - return self._rescale( - avg_volume, old=self._mixer_scale, new=internal_scale) + return int(round(self._playbin.get_property('volume') * 100)) def set_volume(self, volume): """ - Set volume level of the installed mixer. + Set volume level of the software mixer. :param volume: the volume in the range [0..100] :type volume: int :rtype: :class:`True` if successful, else :class:`False` """ - if self._software_mixing: - self._playbin.set_property('volume', volume / 100.0) - return True - - if self._mixer is None: - return False - - self._volume_set = volume - - internal_scale = (0, 100) - - volume = self._rescale( - volume, old=internal_scale, new=self._mixer_scale) - - volumes = (volume,) * self._mixer_track.num_channels - self._mixer.set_volume(self._mixer_track, volumes) - - return self._mixer.get_volume(self._mixer_track) == volumes - - def _rescale(self, value, old=None, new=None): - """Convert value between scales.""" - new_min, new_max = new - old_min, old_max = old - if old_min == old_max: - return old_max - scaling = float(new_max - new_min) / (old_max - old_min) - return int(round(scaling * (value - old_min) + new_min)) + self._playbin.set_property('volume', volume / 100.0) + return True def get_mute(self): """ - Get mute status of the installed mixer. + Get mute status of the software mixer. :rtype: :class:`True` if muted, :class:`False` if unmuted, :class:`None` if no mixer is installed. """ - if self._software_mixing: - return self._playbin.get_property('mute') - - if self._mixer_track is None: - return None - - return bool(self._mixer_track.flags & gst.interfaces.MIXER_TRACK_MUTE) + return self._playbin.get_property('mute') def set_mute(self, mute): """ - Mute or unmute of the installed mixer. + Mute or unmute of the software mixer. :param mute: Wether to mute the mixer or not. :type mute: bool :rtype: :class:`True` if successful, else :class:`False` """ - if self._software_mixing: - return self._playbin.set_property('mute', bool(mute)) - - if self._mixer_track is None: - return False - - return self._mixer.set_mute(self._mixer_track, bool(mute)) + self._playbin.set_property('mute', bool(mute)) + return True def set_metadata(self, track): """ diff --git a/tests/audio/test_actor.py b/tests/audio/test_actor.py index 1df4ff18..bcb7ca2b 100644 --- a/tests/audio/test_actor.py +++ b/tests/audio/test_actor.py @@ -23,8 +23,7 @@ class AudioTest(unittest.TestCase): def setUp(self): config = { 'audio': { - 'mixer': 'fakemixer track_max_volume=65536', - 'mixer_track': None, + 'mixer': 'software', 'mixer_volume': None, 'output': 'fakesink', 'visualizer': None, @@ -74,38 +73,11 @@ class AudioTest(unittest.TestCase): self.assertTrue(self.audio.set_volume(value).get()) self.assertEqual(value, self.audio.get_volume().get()) - def test_set_volume_with_mixer_max_below_100(self): - config = { - 'audio': { - 'mixer': 'fakemixer track_max_volume=40', - 'mixer_track': None, - 'mixer_volume': None, - 'output': 'fakesink', - 'visualizer': None, - } - } - self.audio = audio.Audio.start(config=config).proxy() - - for value in range(0, 101): - self.assertTrue(self.audio.set_volume(value).get()) - self.assertEqual(value, self.audio.get_volume().get()) - - def test_set_volume_with_mixer_min_equal_max(self): - config = { - 'audio': { - 'mixer': 'fakemixer track_max_volume=0', - 'mixer_track': None, - 'mixer_volume': None, - 'output': 'fakesink', - 'visualizer': None, - } - } - self.audio = audio.Audio.start(config=config).proxy() - self.assertEqual(0, self.audio.get_volume().get()) - @unittest.SkipTest def test_set_mute(self): - pass # TODO Probably needs a fakemixer with a mixer track + for value in (True, False): + self.assertTrue(self.audio.set_mute(value).get()) + self.assertEqual(value, self.audio.get_mute().get()) @unittest.SkipTest def test_set_state_encapsulation(self):