rPi Plexamp release audio device when no file is playing

Hi all,

I’ve got plexamp for the raspberry pi running on a 3b+ with a hifiberry digi+ hat running perfectly fine except for one problem. When there is no file playing plexamp will hold onto the audio device until I go into the rpi plexamp web page and manually change the output to a different device and then manually change it back when I want to use plexamp again.

Needless to say, this is tedious to do every time I want the rpi to output audio from a different source or go back to using plexamp. Is it possible to get the plexamp service to release the device after a certain time of no audio playing?

1 Like

Plexamp releasing the audio device would be an enormous +1.

Now that @elan has graciously and awesomely re-bestowed headless RPI functionality (working great for me), my next hifi project is going to be trying to integrate RPI plexamp with the soon-to-be 64 bit 8.1.0 release of moOde. Essential to that project would be plexamp letting go of the device when it’s not playing.

1 Like

would a long-press of the play/pause button accomplish this? that stops playback, rather than pausing it (but I agree, for the purpose of sharing the audio device with other apps, releasing the device on either pause or stop might be better)

Already tried that and it doesn’t work. Hopefully it’s a feature that can be quickly added.

It should be easy enough to have Plexamp use dmix instead of opening the device for exclusive output. I’m not sure if that’s desirable, however.

Alternatively, we could close the device completely any time the music stops/pauses for a bit. Not sure if that would lead to clicking/artifacts in some cases.

Bottom line—we have ways…

1 Like

Thanks for the reply.
I agree going through dmix would not be desirable at all.
I know that things like the headless AirPlay player and Roon bridge applications for Linux will automatically release the audio device after a certain amount of time of not playing audio so it should be more than possible to get done.

as a temporary measure, would it be possible to query the playing status and change the output device through the command line? If so, we could set up a cron job that runs a script that queries plexamps playing status and changes the output device based on if something is playing or not.
It’s not that elegant but should be a workable hack.

1 Like

This turned out to be more complicated than I thought (w/o using DMIX). However, the way the audio library works, it will open non-exclusively if something is already holding onto the audio device when it starts. This might be helpful for those who prefer something else to “own” ALSA outright.

There may be more flexible solutions in the future, but there’s no simple fix here, and in many use cases, having Plexamp grab the device and hold it for bit-perfect output is what’s desired.

3 Likes

Thanks for looking into it and being so responsive.

my main desire for this is:

  1. my dac has an auto turn on and input switch feature when it seas a valid audio stream on one of its inputs and auto turn off when it has no valid audio stream. the current behavior breaks this functionality.

  2. The ability to use other services such as airplay and Spotify connect quickly and easily would be nice to have.

We’re definitely in a better place now than we have been and you and your team are doing good work, so I’m not going to complain much about it otherwise.

Is it possible to query what plexamp is doing and change the input through bash on the rasberrypi?

it is conceptually possible for a enterprising person to figure this out, yes. i’m going to focus any time spent on trying to figure out a more … universally accessible solution :sweat_smile:

1 Like

hahaha ya, that’s probably a better way to spend your time.

However, I’m just crazy enough to sink some time into it this weekend and see if I can hack something together in the meantime. At the very least to answer my curiosity to see if it can be done.

If anyone has any leads on documentation about what can be done with plex through bash let me know.

If I get really desperate I might open up fiddler and try to mimic the API calls made from the browser to the pi when selecting the audio device. though I hope I don’t have to make it that far. :crazy_face:

It would be very useful and versatile to be able to make plexamp interwork with other audio sources like Airplay or Tidal/Spotify Connect but grabbing the audio device allows to play bit-perfect and this is absolute priority for me so, please, no mixers-in-the-middle :slight_smile:

1 Like

But Plexamp gets jealous of those other sources!

Come on Plexamp, this is a long term relationship, trust me, you will always be the favorite one :smile:

1 Like

That’s exactly what a player would say!

3 Likes

Deprecated native solution has been integrated.

Alright got something hacked up. Turned out to be way easier than I thought it would be. The only caveat is that it relies on tautulli in order to monitor when the pi is streaming and not. If you’re not running it already it’s quite a nice tool to go along with your plex install. I’m not going to go over how to install it here but there are a plethora of tutorials just a google search away. So here’s how I got it working.

  1. get a working tautulli install.

  2. In your tautulli installs config directory(folder) make a subdirectory called scripts. In that folder make a file with the name plexamp-rpi-output-switch.py (It can honestly be called whatever you want it to be but this is what I called it). open that file and copy and paste the following script into it then save and exit from whatever text editor you’re using.
    Warning: It’s never a good idea to blindly copy and paste code people post on the internet so take a minute and look it over until you’re satisfied I’m not trying to do anything funny with your stuff. Or, you know, live on the wild side, I promise this code won’t give you any sort of computer virus. :wink:

import requests
import sys
import time

#replace with the hostname or IP of your raspberry pi.
#if your tautulli install is a docker container you'll probably have to use an IP address
rpi_host = "172.16.0.34" #replace with host name or ip of your raspberry pi
rpi_port = "32500"
rpi_path = "/settings"
rpi_url = "http://" + rpi_host + ":" + rpi_port + rpi_path

rpi_player_name = "rpi audio desk" #<--- this is the name of my player change for yours

#information for tautulli requests
tautulli_host = "172.16.0.32"  #replace with the host name or IP of your tautulli install
tautulli_port = "8181"
tautulli_path = "/api/v2"
api_key = "c996af6732dc45578fbf52dc8d58ccae" #found in settings -> Web Interface
tautulli_url = "http://" + tautulli_host + ":" + tautulli_port + tautulli_path

device = "0,0" #default device
if len(sys.argv) > 1:
    device = sys.argv[1]

make_request = True

if len(sys.argv) > 2 and sys.argv[2] == "start":
    response = requests.get(rpi_url)

    if response.status_code == 200:
        current_device = response.json()["audioDeviceUuid"]
        make_request = device not in current_device #if device is not a sub string of current device set to True

#wait 1 second then make a request to see if playback has actually stopped
elif len(sys.argv) > 2 and sys.argv[2] == "stop":
    time.sleep(1)
    
    params = {"apikey": api_key, "cmd": "get_activity"}
    response = requests.get(tautulli_url, params=params)
    
    if response.status_code == 200:
        sessions = response.json()["response"]["data"]["sessions"]
        
        #if the session still exists break if it doesn't set make_requet to false
        for session in sessions:
            if session["player"] == rpi_player_name and session["state"] == "playing":
                make_request = False
                break

if make_request:
    params = {"name": "audioDeviceUuid", "value": "hw:" + device}
    requests.put(rpi_url, params=params)

  1. go to the tautulli web UI and go to settings → Notification Agents

  2. click on "Add a new notification agent. A list will pop up. Scroll down the list and select “Script”

  3. In the window that pops up click the “Browse” button and navigate to the script directory you created. and then click on the box under “Script File” and select the script you created in step 2. Add a description if you want

  4. Click on the “Triggers” tab. Select “Playback Start” and “Playback Stop”

(this next step is vital to make sure you’re not triggering the script all the time)
7. Click on the “Conditions” tab. In the “Parameter” drop-down select “Player”, In the “Operator” drop-down select “is”, and in the “Value” box type the name of the device as it appears in your app when you are in the “Select Player” menu.

  1. Before we go on in tautulli we’ll need to make a little detour. open a new tab in your web browser and type the following URL in. (replace hostname.local with the IP address or hostname of your device) and you’ll see the following pop up.
http://hostname.local:32500/settings/values?name=audioDeviceUuid

Find the device you want to stream to. and make note of the number next to hw: (In my case 2,0) (note its a comma and not a period) and the number of a device that’s not hooked up to anything (in my case 0,0).

9 Go back to the browser tab that has tautulli in it and click on the “Arguments” tab. Click on “Playback Start” and enter the number of the device you want to stream to followed by a space and the word “start”.

then click on “Playback Stop” and enter the number of the device not hooked up to anything followed by a space and the word “stop”

  1. Click Save and exit the pop-up and you should be done.

I haven’t tested it much so I don’t know if there are any edge cases that’ll make my solution break. While it’s not as good as a native baked-in solution would be, the performance is snappy and so far consistent.

4 Likes

Wow, very cool!

On my end I had an epiphany this afternoon and tried another direction, which seemed to work. Need to do more testing and it might be a little while, but it’s promising for native support.

3 Likes

Deprecated native solution has been integrated.

So for those of you interested I discovered something wrong with the assumptions I made with my previous code. I assumed tautulli would only trigger playback start and stop when the stream was completely stopped. However it looks like these commands are instead triggered every time a song starts and end respectively. meaning the output is changed back and forth rapidly between tracks causing audible artifacts. This as I’m sure you can tell is not desirable behavior.

It’s late where I am and I want to go to bed, so the patch I made is the first solution that came to mind. So it might not be the most elegant solution. The code doesn’t look very clean but it works and doesn’t produce any audible artifacts.

I resolved the issue by adding an extra argument when calling stop that has the code to wait 1 second and then check tautulli to see if anything is playing. If there is, the off switch request is not made.

I can’t think of an easy solution to stop the extra playback start calls without the script keeping track of its own state between executions. Which I’d like to avoid if I can. It also doesn’t seem to cause any problems requesting to change to the currently active device anyway. So I’ll leave that be until I think of a good solution. If anyone has any ideas I wouldn’t mind hearing them.

Anyway here’s the solution.

1 this is the new script. In addition to the rpi hostname change, you’ll need to put the tautulli IP or hostname, port, and API key in the indicated places.

import requests
import sys
import time

#replace with the hostname or IP of your raspberry pi.
#if your tautulli install is a docker container you'll probably have to use an IP address
rpi_host = "IP or hostname here" #replace with host name or ip of your raspberry pi
rpi_port = "32500"
rpi_path = "/settings"
rpi_player_name = "rpi audio desk" #<--- this is the name of my player change for yours

#information for tautulli requests
tautulli_host = "tautulli host name here" #replace with the host name or IP of your tautulli install
tautulli_port = "8181"            #set the port as well if it is different
tautulli_path = "/api/v2"
api_key = "API key here" #found in settings -> Web Interface

device = "0,0" #default device
if len(sys.argv) > 1:
    device = sys.argv[1]

make_request = True

#wait 1 second then make a request to see if playback has actually stopped
if len(sys.argv) > 2 and sys.argv[2] == "stop":
    time.sleep(1)
    url = "http://" + tautulli_host + ":" + tautulli_port + tautulli_path
    params = {
        "apikey": api_key,
        "cmd": "get_activity"
        }
    response = requests.get(url, params=params)
    sessions = response.json()["response"]["data"]["sessions"]

    #if the session still exists break if it doesn't set make_requet to false
    for session in sessions:
        if session["player"] == rpi_player_name and session["state"] == "playing":
            make_request = False
            break

if make_request:
    url = "http://" + rpi_host + ":" + rpi_port + rpi_path
    params = {
        "name": "audioDeviceUuid",
        "value": "hw:" + device
        }
    requests.put(url, params=params)
  1. in Tautulli edit the notification you made before and in the Arguments tab under playback stop type “stop” after the number you gave with a space.

  2. Save, close the window and you’re done

I probably won’t have time tomorrow to improve it any, but if there are any other code inclined individuals who want to improve on this feel free.

1 Like

Deprecated native solution has been integrated.
Had some time to come up with and code a solution to the extra start calls. the script will no longer produce any unnecessary calls to change the output device between tracks.

import requests
import sys
import time

#replace with the hostname or IP of your raspberry pi.
#if your tautulli install is a docker container you'll probably have to use an IP address
rpi_host = "172.16.0.34" #replace with host name or ip of your raspberry pi
rpi_port = "32500"
rpi_path = "/settings"
rpi_url = "http://" + rpi_host + ":" + rpi_port + rpi_path

rpi_player_name = "rpi audio desk" #<--- this is the name of my player change for yours

#information for tautulli requests
tautulli_host = "172.16.0.32"  #replace with the host name or IP of your tautulli install
tautulli_port = "8181"
tautulli_path = "/api/v2"
api_key = "c996af6732dc45578fbf52dc8d58ccae" #found in settings -> Web Interface
tautulli_url = "http://" + tautulli_host + ":" + tautulli_port + tautulli_path

device = "0,0" #default device
if len(sys.argv) > 1:
    device = sys.argv[1]

make_request = True

if len(sys.argv) > 2 and sys.argv[2] == "start":
    response = requests.get(rpi_url)

    if response.status_code == 200:
        current_device = response.json()["audioDeviceUuid"]
        make_request = device not in current_device #if device is not a sub string of current device set to True

#wait 1 second then make a request to see if playback has actually stopped
elif len(sys.argv) > 2 and sys.argv[2] == "stop":
    time.sleep(1)
    
    params = {
        "apikey": api_key,
        "cmd": "get_activity"
        }
    response = requests.get(tautulli_url, params=params)
    
    if response.status_code == 200:
        sessions = response.json()["response"]["data"]["sessions"]
        
        #if the session still exists break if it doesn't set make_requet to false
        for session in sessions:
            if session["player"] == rpi_player_name and session["state"] == "playing":
                make_request = False
                break

if make_request:
    params = {
        "name": "audioDeviceUuid",
        "value": "hw:" + device
        }
    requests.put(rpi_url, params=params)

in addition to the change to the playback stop argument change from the previous point add a space and the word "start the playback start argument.

I’d like to get to the point where the script can get the current playing status from the rpi directly instead of having to make a get request to tautulli but I’ll leave that for another day.

Give this a try ~ Plexamp on the Raspberry Pi - #11 by elan

3 Likes

Seems to be working great. Thanks for getting a native solution out so fast.

Even though it only needed to be used for a day or two it’s been years since I sniffed an applications API and programed something for my own use. It felt good to get back into doing stuff like this.

On a side note: Now that this is working, I honestly think plexamp is a couple of features away from being a significantly better alternative to Roon. You honestly already have a significantly better radio and autoplay feature. At least for my needs if you’re ever able to get Qobuz integrated I’d drop my Roon subscription immediately.

2 Likes