# Hook before audio playback change

**URL:** <https://discourse.mopidy.com/t/hook-before-audio-playback-change/1973>\
**Category:** Misc\
**Created:** [October 16, 2017, 8:07am UTC](https://discourse.mopidy.com/t/hook-before-audio-playback-change/1973 "2017-10-16T08:07:10Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![pavhofman](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.mopidy.com/pavhofman/32/722_2.png) [@pavhofman](https://discourse.mopidy.com/u/pavhofman)\
**Post date:** [October 16, 2017, 8:07am UTC](https://discourse.mopidy.com/t/hook-before-audio-playback-change/1973/1 "2017-10-16T08:07:10Z")

</div>

I would like to check/switch if needed hardware amplifier inputs BEFORE playback starts to make sure they are switched correctly. I understand I could use my own Audio class which implements the hook. But please is it possible to implement this without modifying the existing extensions which use the built-in Audio class of Mopidy? It would not have to be synchronous, just catching some event about\_to\_start\_playback, similar to the existing call about\_to\_finish.

Or using a modified core.PlaybackController but I would love to keep the mopidy code intact.

Thanks a lot for any hints.

Pavel.

---

<div class="post-metadata">

**Author:** ![pavhofman](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.mopidy.com/pavhofman/32/722_2.png) [@pavhofman](https://discourse.mopidy.com/u/pavhofman)\
**Post date:** [October 16, 2017, 8:33pm UTC](https://discourse.mopidy.com/t/hook-before-audio-playback-change/1973/2 "2017-10-16T20:33:04Z")

</div>

I am studying the source code. Mixer can be configured “mopidy-wide” by any extension with registry “mixer” and used in [https://github.com/mopidy/mopidy/blob/70e510e45985cc5d40a9adecf07980d0f96b3a1f/mopidy/commands.py#L295](https://github.com/mopidy/mopidy/blob/70e510e45985cc5d40a9adecf07980d0f96b3a1f/mopidy/commands.py#L295) . Do you think there is any chance the Audio class could be configured for whole mopidy in the same way, would you accept such patch? Or should I just just monkey patch the method RootCommand.start\_audio at [https://github.com/mopidy/mopidy/blob/70e510e45985cc5d40a9adecf07980d0f96b3a1f/mopidy/commands.py#L372](https://github.com/mopidy/mopidy/blob/70e510e45985cc5d40a9adecf07980d0f96b3a1f/mopidy/commands.py#L372) in my code?

Thanks a lot, Pavel.

---

<div class="post-metadata">

**Author:** ![kingosticks](https://avatars.discourse-cdn.com/v4/letter/k/858c86/32.png) [@kingosticks](https://discourse.mopidy.com/u/kingosticks)\
**Post date:** [October 17, 2017, 9:24am UTC](https://discourse.mopidy.com/t/hook-before-audio-playback-change/1973/3 "2017-10-17T09:24:19Z")

</div>

Are you saying that the existing track\_playback\_started or playback\_state\_changed core events are not useful to you here? Presumably whatever you do to switch inputs is not instantaneous and you can’t block so does it really matter if you do this before or just after?

---

<div class="post-metadata">

**Author:** ![pavhofman](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.mopidy.com/pavhofman/32/722_2.png) [@pavhofman](https://discourse.mopidy.com/u/pavhofman)\
**Post date:** [October 17, 2017, 9:47am UTC](https://discourse.mopidy.com/t/hook-before-audio-playback-change/1973/4 "2017-10-17T09:47:06Z")

</div>

The appliance I am working on is a combination of a PC and integrated amplifier with multiple inputs. The PC soundcard is hooked to one of the inputs. Before starting any playback from gstreamer I have to make sure that particular input is selected first to avoid any clicks/music cuts.

So actually I have to implement a synchronous action. My idea is to extend the existing Audio.set\_uri method to do the check first, and “inject” the modified class to mopidy somehow. I like the way components can inject system-wide mixer facility, I thought the same could work for audio too.

I can always use a forked mopidy but if there was a way to use the latest stock one… you know what I mean.

I very much appreciate your help. Pavel.

---

<div class="post-metadata">

**Author:** ![kingosticks](https://avatars.discourse-cdn.com/v4/letter/k/858c86/32.png) [@kingosticks](https://discourse.mopidy.com/u/kingosticks)\
**Post date:** [October 19, 2017, 2:23pm UTC](https://discourse.mopidy.com/t/hook-before-audio-playback-change/1973/5 "2017-10-19T14:23:56Z")

</div>

I don’t think blocking `in set_uri()` is a good idea as it can potentially break gapless playback, `start_playback()` would probably be a safer place to do something like this. The `Audio` class isn’t really suitable for extending as is, it would probably need to be broken up into an extendable part and a fixed part.

Personally, I think that what you want to do sounds exactly like the sort of thing that should live in a mixer. Maybe we could just extend that API with a `enable_output()` method that’s called from `PlaybackProvider.play()`, or with more work, `Audio.start_playback()`.

---

<div class="post-metadata">

**Author:** ![pavhofman](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.mopidy.com/pavhofman/32/722_2.png) [@pavhofman](https://discourse.mopidy.com/u/pavhofman)\
**Post date:** [October 20, 2017, 4:54pm UTC](https://discourse.mopidy.com/t/hook-before-audio-playback-change/1973/6 "2017-10-20T16:54:20Z")

</div>

Thanks a lot for the warning, start\_playback looks good.

There is already a workaround in Audio.\_setup\_preferences with the TODO comment in commit [https://github.com/mopidy/mopidy/commit/fd9100a5f330db4d98d32abe4d7dbb62330e21f2#diff-41786cf21d61f8d773107bc83320f891R304](https://github.com/mopidy/mopidy/commit/fd9100a5f330db4d98d32abe4d7dbb62330e21f2#diff-41786cf21d61f8d773107bc83320f891R304) . I would prefer being able to insert a piece of code (or extend, or any other way to run custom code before actual playback starts).

However, each backend should have a way to override the default enable\_output behaviour. I will have AnalogInputBackend and SPDIFInputBackend, which will not play anything through gstreamer, just reconfigure the input switch (for AnalogInputBackend) or the soundcard controls (for SPDIFInputBackend). These backends will publish their own playlists with their own uri\_schemes. When one of their tracks is selected to play, the backend should switch the inputs or reconfigure the soundcard accordingly. That way any MPD/web client can control hardware facilities of the appliance.

My idea was to catch the to-be-played uri in my set\_uri(uri) “hacked” method and act as relevant for the actual uri\_scheme. I am not sure the Audio.start\_playback() or potential mixer.enable\_output() would have this key information available. Unless the potential method was overridable in individual backends, that would be perfect solution.

But AFAIU I can provide my own audio class for these nonstandard backends so that should be doable with existing API.

I very much appreciate your opinion.

Pavel.

---

<div class="post-metadata">

**Author:** ![pavhofman](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.mopidy.com/pavhofman/32/722_2.png) [@pavhofman](https://discourse.mopidy.com/u/pavhofman)\
**Post date:** [October 24, 2017, 8:36pm UTC](https://discourse.mopidy.com/t/hook-before-audio-playback-change/1973/7 "2017-10-24T20:36:14Z")

</div>

I tried to modify the audio class for CDBackend to find out the Audio class is initialized system-wide in [https://github.com/mopidy/mopidy/blob/develop/mopidy/commands.py#L374](https://github.com/mopidy/mopidy/blob/develop/mopidy/commands.py#L374) I need to set specific source property (read-speed) in the \_on\_source\_setup handler. I see properties for a specific source are setup e.g. in utils.setup\_proxy [https://github.com/mopidy/mopidy/blob/develop/mopidy/audio/utils.py#L67](https://github.com/mopidy/mopidy/blob/develop/mopidy/audio/utils.py#L67) by checking if the corresponding property exists.

I changed the code of the method to my specific

```
def setup_proxy(element, config):
    if hasattr(element.props, 'proxy') and config.get('hostname'):
        element.set_property('proxy', httpclient.format_proxy(config, auth=False))
        element.set_property('proxy-id', config.get('username'))
        element.set_property('proxy-pw', config.get('password'))

    if hasattr(element.props, 'read-speed'):
        element.set_property('read-speed', 1)

```

Now the CD plays perfectly silent. I am afraid the only way for me will be forking the code and rebasing my own modified version.

---

<div class="post-metadata">

**Author:** ![kingosticks](https://avatars.discourse-cdn.com/v4/letter/k/858c86/32.png) [@kingosticks](https://discourse.mopidy.com/u/kingosticks)\
**Post date:** [October 26, 2017, 10:33am UTC](https://discourse.mopidy.com/t/hook-before-audio-playback-change/1973/8 "2017-10-26T10:33:59Z")

</div>

Given your specialist needs I agree that’s the best course of action for you. All Mopidy Backend playback goes through gstreamer and bypassing that as you want to do is out of scope for us.

What you want to do doesn’t sound too dissimilar to how we juggle multiple audio servers in Pi Musicbox. Here we have Mopidy, Shairport-Sync (Apple Airplay) and librespot (Spotify Connect) all wanting to use the sound device and only Mopidy does so via gstreamer. In theory we could try and so something similar to what you want but since each of those applications already has it’s own client interface, it’s easier to keep everything totally separate. It would be slick to have the status or even control of all three applications under one unified interface, I totally agree. But I think if we did that we’d probably do so via a separate overarching manager-type program, rather than trying to integrate it into Mopidy.

Good luck with your system.

---

<div class="post-metadata">

**Author:** ![pavhofman](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.mopidy.com/pavhofman/32/722_2.png) [@pavhofman](https://discourse.mopidy.com/u/pavhofman)\
**Post date:** [October 26, 2017, 11:32am UTC](https://discourse.mopidy.com/t/hook-before-audio-playback-change/1973/9 "2017-10-26T11:32:25Z")

</div>

Nick, thanks a lot for your important answer. I will go ahead with my project in this direction.

Actually I did not know Pi Musicbox had a separate Spotify web client. I understand the reasons though. For me the MPD API (i.e. all the MPD clients) and the existing web clients such as Iris are still the major attraction to use Mopidy to control other device features.

I thought of providing a “more complete” version of the existing [https://github.com/forscher21/mopidy-cd](https://github.com/forscher21/mopidy-cd) extension with the usability features added (medium detection/recovery, read-speed reduction, etc.), I am afraid that extension will not work without some of the patches/commits I will have to do regarding the existing audio class in my fork of mopidy.

Once more thanks a lot for your help.
