owntone / owntone-server

Linux/FreeBSD DAAP (iTunes) and MPD audio server with support for AirPlay 1 and 2 speakers (multiroom), Apple Remote (and compatibles), Chromecast, Spotify and internet radio.
https://owntone.github.io/owntone-server
GNU General Public License v2.0
2.04k stars 234 forks source link

Spotify Connect #295

Open snizzleorg opened 7 years ago

snizzleorg commented 7 years ago

Would be awesome if spotify-connect could be supported so that one could stream from spotify to forked-daapd and then to all the speakers in the house:

https://github.com/plietar/librespot

It's probably possible to do this even now via pipe (the same was as mentioned here: https://github.com/ejurgensen/forked-daapd/issues/278)

See also this discussion: https://github.com/plietar/librespot/issues/88

aleszczynskig commented 3 years ago

For those interested, I have done some testing with the devs of libspotify-java which is the core of spocon and we have now correctly implemented the metadata pipe to send volume events and to enable a fixed volume output similar to the capability offered by shairport-sync. This is available in the latest dev branch. The issue is 317 for those interested. It now enables direct volume control of the selected airplay speakers direct from your Spotify client which is an awesome feature. Other than ~4 sec for playback commands to be reflected in the output, this is now a very good interim solution for integrating spotify and forked-daapd until forked-daapd can support Spotify-connect directly. In case it's not clear, volume changes are implemented instantly.

@ejurgensen - are there commands via the metadata pipe which can be used to flush the stream and implement playback commands immediately?

ejurgensen commented 3 years ago

@aleszczynskig no, there isn't support for that currently. I've made a quick attempt at supporting it here that you could give a try.

aleszczynskig commented 3 years ago

Great. I'll take a look and feedback. Thanks for taking a look at this.

floodwayprintco commented 3 years ago

Really appreciate all the work going into this.

aleszczynskig commented 3 years ago

@ejurgensen - I've never compiled forked-daapd from source before, I normally use the deb you provide. I have successful compiled this branch from source however I do not want to run the make install as this is on the "production" system. I was hoping to just run the compiled binary from the src folder (after having stopped the system version) however I am running into this error.

[2021-03-08 12:54:13] [ LOG] db: Could not load SQLite extension: /usr/lib/forked-daapd/forked-daapd-sqlext.so.so: cannot open shared object file: No such file or directory

At present I am not sure how to resolve this. Do you have any tips on how to resolve this?

Thanks

ejurgensen commented 3 years ago

Not sure what the best way of solving that is. I can offer you a test deb that you can try. It will of course overwrite your current forked-daapd, but it should be easy enough to go back again with apt (I think you can specify a version to install).

aleszczynskig commented 3 years ago

Thank you. The test deb makes it easier to test :)

aleszczynskig commented 3 years ago

So I've been testing this. It seems to cause a crash. When I send the 'pfls' command via the metadata pipe, the output instantly pauses which seems to be the desired effect but then forked-daapd crashes (I think) and is restarted by systemd.

Debug log is attached though I can't see any obvious error with my untrained eyes. crash.log

ejurgensen commented 3 years ago

Indeed, looks like a crash. I better test myself. Any hints on how to trigger the 'pfls'?

aleszczynskig commented 3 years ago

I use echo "<item><type>73736e63</type><code>70666C73</code><length>0</length></item>" > /media/usb-disk2/Media/Music/aux.metadata

ejurgensen commented 3 years ago

Here is another attempt. I tested with the command you shared, and it seems to work with that. I haven't made any further tests than that, so hope you can check if it does what you are looking for.

aleszczynskig commented 3 years ago

Thank you. I'll give it ago.

Update. Yes - it seems to work exactly as I would expect it too. Thank you for implementing this.

aleszczynskig commented 3 years ago

FYI - I have asked the developer of librespot-java to implement a pfls event for playback commands. He has done this via https://github.com/librespot-org/librespot-java/commit/6a2679d8f2e26b31bbb85419fa524913d64ffe86. I have tested in combination with the deb here by @ejurgensen and the controls are now super slick and very responsive. It works better than I had hoped.

The only small caveat to this is you get approximate 0.2 seconds of the previous track play at the beginning of the next track. You can mitigate this with a sleep command or similar if scripting.

floodwayprintco commented 3 years ago

Would you mind sharing even a basic rundown on how to make it happen? I'm a little lost on how to update librespot and forked-daapd, is that what needs to be done?

--

Cory Beal

He/HimProduction Manager @ Floodway Print Company LTD.

https://floodwayprintco.com/

On Thu, Mar 11, 2021 at 3:13 PM aleszczynskig @.***> wrote:

FYI - I have asked the developer of librespot-java to implement a pfls event for playback commands. He has done this via @.*** https://github.com/librespot-org/librespot-java/commit/6a2679d8f2e26b31bbb85419fa524913d64ffe86. I have tested in combination with the deb here by @ejurgensen https://github.com/ejurgensen and the controls are now super slick and very responsive. It works better than I had hoped.

The only small caveat to this is you get approximate 0.2 seconds of the previous track play at the beginning of the next track. You can mitigate this with a sleep command or similar if scripting.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ejurgensen/forked-daapd/issues/295#issuecomment-797052704, or unsubscribe https://github.com/notifications/unsubscribe-auth/AM3B6M25LYUB3D2XLSHGSWDTDEXAJANCNFSM4CTJMIFA .

aleszczynskig commented 3 years ago

@floodwayprintco - sure. I can put together a little write-up to cover it. I can only provide instructions for use on a raspberry pi though, if you are using other distributions you will need to alter as necessary.

FYI - at present the capabilities are not in the formal releases so you will need to install either a test deb for forked-daapd or compile from source. For librespot-Java you will need to compile from source and make a small modification to the spocon systemd configuration. Depending on your technical capabilities you may want to wait until these changes are rolled into a full release for everything involved.

floodwayprintco commented 3 years ago

My capability is right around the 'just enough to be dangerous' area, although I'm willing to learn and could revert to a regular setup pretty easily I think. Compiling from source, and the test deb are both the tricky parts for me. I am on a raspberry pi though and am comfortable with a bit of configuration. Let me put it this way, if you were to make a writeup I could at least test it if that would help, but I am happy to wait for a full release as well. So don't sweat it if you unless you think it'd be 'worth it' to test it too.

--

Cory Beal

He/HimProduction Manager @ Floodway Print Company LTD.

https://floodwayprintco.com/

On Fri, Mar 12, 2021 at 8:16 AM aleszczynskig @.***> wrote:

@floodwayprintco https://github.com/floodwayprintco - sure. I can put together a little write-up to cover it. I can only provide my instructions for use in a raspberry pi though, if you are using other distributions you will need to alter as necessary.

FYI - at present the capabilities are not in the formal releases so you will need to install either a test deb for forked-daapd or compile from source. For librespot-Java you will need to compile from source and make a small modification to the spocon systemd configuration. Depending on you technical capability you may want to wait until these changes are rolled into a full release for everything involved.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ejurgensen/forked-daapd/issues/295#issuecomment-797517091, or unsubscribe https://github.com/notifications/unsubscribe-auth/AM3B6M6Y4WK4FXHFAYPI3UDTDIO5FANCNFSM4CTJMIFA .

st0nec0ld commented 3 years ago

Hi, I use forked-daapd the same way @floodwayprintco does. Until today using the pipe capability of raspotify was my approach too. Now I encountered a Java based connector called spocon. Additionally to the functionalities of raspotify it supports a metadata pipe that works fine with forked-daapd!

After following the installation instructions of spocon I created two files (spotify and spotify.metadata) and changed the configuration in the [player]-Section:

# Audio output device (MIXER, PIPE, STDOUT)
output = "PIPE"
# Output raw (signed) PCM to this file (`player.output` must be PIPE)
pipe = "/home/pi/music/spotify" 
# Output metadata in Shairport Sync format (https://github.com/mikebrady/shairport-sync-metadata-reader)
metadataPipe = "/home/pi/music/spotify.metadata"

After restarting spocon and forked-daapd I finally have metadata with artwork in my UI. I hope this helps everyone who is using forked-daapd with Spotify this way. Thank you for your great work on this topic!

Hi, how you create the two files? I made it with "touch".

touch spotify
touch spotify.metadata

Then i edit spocon config.toml

metadataPipe = "/home/pi/spotify.metadata"
output = "PIPE"
pipe = "/home/pi/spotify

After Restart i cannot connect to spocon. What i doing wrong? `

floodwayprintco commented 3 years ago

I'm not 100% on this, but for the sake of reply maybe this will help. Pretty sure I had to make my pipes using a 'mkfifo' command.

--

Cory Beal

He/HimProduction Manager @ Floodway Print Company LTD.

https://floodwayprintco.com/

On Fri, Mar 19, 2021 at 10:46 AM st0nec0ld @.***> wrote:

Hi, I use forked-daapd the same way @floodwayprintco https://github.com/floodwayprintco does. Until today using the pipe capability of raspotify was my approach too. Now I encountered a Java based connector called spocon https://github.com/spocon/spocon. Additionally to the functionalities of raspotify it supports a metadata pipe that works fine with forked-daapd!

After following the installation instructions of spocon I created two files (spotify and spotify.metadata) and changed the configuration in the [player]-Section:

Audio output device (MIXER, PIPE, STDOUT)

output = "PIPE"

Output raw (signed) PCM to this file (player.output must be PIPE)

pipe = "/home/pi/music/spotify"

Output metadata in Shairport Sync format (https://github.com/mikebrady/shairport-sync-metadata-reader)

metadataPipe = "/home/pi/music/spotify.metadata"

After restarting spocon and forked-daapd I finally have metadata with artwork in my UI. I hope this helps everyone who is using forked-daapd with Spotify this way. Thank you for your great work on this topic!

Hi, how you create the two files? I made it with "touch". touch spotify touch spotify.metadata

Then i edit spocon config.toml `metadataPipe = "/home/pi/spotify.metadata" output = "PIPE" pipe = "/home/pi/spotify"

After Restart i cannot connect to spocon. What i doing wrong? `

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ejurgensen/forked-daapd/issues/295#issuecomment-802928128, or unsubscribe https://github.com/notifications/unsubscribe-auth/AM3B6MYQBQSXRELIZM5KSRDTENWWTANCNFSM4CTJMIFA .

aleszczynskig commented 3 years ago

Hello, apologies I have not found the time to respond to this. @floodwayprintco is correct, you must use mkfifo for both the audio and metadata pipes.

A brief description of what is required is listed here. I am sorry I do not have time to do a more complete write-up at this time.

  1. Install the latest release of forked-daapd to get all of the features. Follow the INSTALL.md instructions. Note the minimum version for full metadata support is 27.3. I don't think there is a deb available for this yet (it may be) but it will be soon. I compiled from source which is not hard. Just follow the instructions provided by @ejurgensen.
  2. Install spocon. Follow instructions.
  3. Download the latest version of master from librespot-java and compile.
  4. Copy the compiled librespot-api-xxx-SNAPSHOT.jar to /opt/spocon where xxx is the latest version number. It should be in the api/target directory after compilation. Note if you are compiling this on a raspberry pi you will need to compile the protobuf library manually and then call the man command mvn clean package -DprotocExecutable=/usr/local/bin/protoc assuming that /usr/local/bin is where protoc is installed to compile librespot-java otherwise you will get an error.
  5. Update /lib/systemd/system/spocon.service to point to the new jar file you copied to /opt/spocon
  6. run sudo systemctl daemon-reload
  7. update your /opt/spocon/config.toml file player section with the following settings. Updating the paths to your pipes as necessary
[player]
        # Whether to apply the Spotify loudness normalisation
        enableNormalisation = true
        # Initial volume (0-65536)
        initialVolume = 5243
        # Release mixer line after set delay (in seconds)
        releaseLineDelay = 20
        # Normalisation pregain in decibels (loud at +6, normal at +3, quiet at -5)
        normalisationPregain = 3.0
        # Output metadata in Shairport Sync format (https://github.com/mikebrady/shairport-sync-metadata-reader)
        metadataPipe = "/media/usb-disk2/Media/Music/spotify.metadata"
        # Autoplay similar songs when your music ends
        autoplayEnabled = true
        # Preferred audio quality (NORMAL, HIGH, VERY_HIGH)
        preferredAudioQuality = "VERY_HIGH"
        # Audio output device (MIXER, PIPE, STDOUT)
        output = "PIPE"
        # Crossfade overlap time (in milliseconds)
        crossfadeDuration = 0
        # Whether the player should retry fetching a chuck if it fails
        retryOnChunkError = true
        # Mixer/backend search keywords (semicolon separated)
        mixerSearchKeywords = ""
        # Output raw (signed) PCM to this file (`player.output` must be PIPE)
        pipe = "/media/usb-disk2/Media/Music/spotify"
        # Log available mixers
        logAvailableMixers = true
        # Number of volume notches
        volumeSteps = 64
        #Bypass spotify volume control (for use with forked-daapd metadata pipe or similar)
        bypassSinkVolume = true
  1. run sudo systemctl restart spocon
  2. Open your spotify client and connect to the spocon device once it is available.
  3. When you play you should now have the outputs coming from whichever devices you have selected in forked-daapd. Changing the volume in the spotify client should reflect immediately in forked-daapd. Current item metadata should also be available. You should find that the controls are very responsive. The only small lag is when you resume playback. I believe this is because of various buffers in the playback stack. It's not a big deal (a second or two).

Note I have recently asked the librespot-java developers to implement configurable scripts to run on a variety of events. These are available in the latest master build.

You will need to add this section to your config.toml if you want this to work. I have for example setup forked-daapd to connect to selected speakers when spocon resumes playback. I am sure there are many uses for these.

[shell]
        # Shell events enabled
        enabled = true
        onContextChanged = ""
        onTrackChanged = ""
        onPlaybackEnded = ""
        onPlaybackPaused = ""
        onPlaybackResumed = "/bin/bash -c /usr/local/sbin/speaker-connect.sh"
        onTrackSeeked = ""
        onMetadataAvailable = ""
        onVolumeChanged = ""
        onInactiveSession = ""
        onPanicState = ""
        onConnectionDropped = ""
        onConnectionEstablished = ""

and speaker-connect.sh looks like this

#!/bin/sh
/usr/bin/curl -s -X PUT "http://localhost:3689/api/outputs/set" --data "{\"outputs\":[\"233717573016223\",\"233797576358078\"]}"

You will need to update the ids for your devices accordingly.

I hope this helps. I am sorry it is brief. I would like to author a proper HOWTO for this and add to the wiki pages if @ejurgensen is willing for me to do that.

Finally none of this would be possible without the wonderful work of all the developers who give their free time to create these excellent examples of open-source software. Thanks to all of those involved.

snoopbird commented 3 years ago

Hello, first of all many thanks for this great project and to all contributors!

I have been able to successfully implement the instructions from aleszczynskig so far. However, the changes in volume are not adopted.

This notice comes in the log. What am I doing wrong?

forked-daapd[1512]: [2021-03-26 08:02:07] [DEBUG]   player: Read Shairport metadata (type=73736e63, code=70766f6c, len=21)
forked-daapd[1512]: [2021-03-26 08:02:07] [DEBUG]   player: Not applying Shairport airplay volume while software volume control is enabled (-18,07,0.00,0.00,0.00)
java[1428]: 2021-03-26 08:02:07,923 INFO  DeviceStateHandler:250 - Put state. {ts: 1616742127767, connId: ODYwY...xMTU4, reason: VOLUME_CHANGED}
forked-daapd[1512]: [2021-03-26 08:02:08] [DEBUG]   player: Read Shairport metadata (type=73736e63, code=70766f6c, len=21)
forked-daapd[1512]: [2021-03-26 08:02:08] [DEBUG]   player: Not applying Shairport airplay volume while software volume control is enabled (-19,68,0.00,0.00,0.00)
forked-daapd[1512]: [2021-03-26 08:02:08] [DEBUG]   player: Read Shairport metadata (type=73736e63, code=70766f6c, len=21)
forked-daapd[1512]: [2021-03-26 08:02:08] [DEBUG]   player: Not applying Shairport airplay volume while software volume control is enabled (-20,00,0.00,0.00,0.00)
java[1428]: 2021-03-26 08:02:08,556 INFO  DeviceStateHandler:250 - Put state. {ts: 1616742128052, connId: ODYwY...xMTU4, reason: VOLUME_CHANGED}
java[1428]: 2021-03-26 08:02:08,683 INFO  DeviceStateHandler:250 - Put state. {ts: 1616742128318, connId: ODYwY...xMTU4, reason: VOLUME_CHANGED}
ejurgensen commented 3 years ago

Check this setting: https://github.com/mikebrady/shairport-sync/blob/master/scripts/shairport-sync.conf#L35

snoopbird commented 3 years ago

I don't use shairport-sync because I use PIPE to transfer the data from Spocon (librespot-java) to forked-daapd. I don't use shairport-sync because I think it is not needed?

ejurgensen commented 3 years ago

Sorry, misunderstood. It looks like the similar setting for Spocon might be: "bypassSinkVolume = true" (see the above)

snoopbird commented 3 years ago

No Problem :) I have set the value "bypassSinkVolume = true" in the configuration and it appears in the log.

Mar 26 11:35:35 raspotify java[32699]: 2021-03-26 11:35:35,795 INFO  DeviceStateHandler:247 - Put state. {ts: 1616754935499, connId: NmZkN...5QkU5, reason: VOLUME_CHANGED, request: device { device_info { can_play: true volume: 35938 name: "SpoCon-Spotify" capabilities { can_be_player: true gaia_eq_connect_id: true supports_logout: true is_observable: true volume_steps: 64 supported_types: "audio/episode" supported_types: "audio/track" command_acks: true supports_playlist_v2: true is_controllable: true supports_transfer_command: true supports_command_request: true supports_gzip_pushes: true } device_software_version: "librespot-java 1.5.6-SNAPSHOT" device_type: SPEAKER spirc_version: "3.2.6" device_id: "6fd7d2d1bb1745e86ce1c2ff05d5a0ff609c5122" } player_state { timestamp: 1616754894111 context_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" context_url: "context://spotify:artist:79xxUllDTqG8bugjRRdmIe" context_restrictions { } play_origin { feature_identifier: "desktop-client-x" feature_version: "1.1.54.592" device_identifier: "c5125b41e3cdd0eb337b02b259234108a1edd8f1" } index { track: 1 } track { uri: "spotify:track:5PLgj89ZyK6kM8z2vl0idE" uid: "toptrack5PLgj89ZyK6kM8z2vl0idE" metadata { key: "album_artist_name" value: "Witchseeker" } metadata { key: "album_artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } metadata { key: "album_disc_number" value: "1" } metadata { key: "album_title" value: "Break Away" } metadata { key: "album_uri" value: "spotify:album:2SckEHxbc2j0GidHxkhJwQ" } metadata { key: "artist_name" value: "Witchseeker" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } metadata { key: "available_file_formats" value: "[\"OGG_VORBIS_320\",\"OGG_VORBIS_160\",\"OGG_VORBIS_96\",\"AAC_24\"]" } metadata { key: "duration" value: "199467" } metadata { key: "image_large_url" value: "spotify:image:ab67616d0000b2730bcb0cc23a5fa72daecad0b0" } metadata { key: "image_small_url" value: "spotify:image:ab67616d000048510bcb0cc23a5fa72daecad0b0" } metadata { key: "image_url" value: "spotify:image:ab67616d00001e020bcb0cc23a5fa72daecad0b0" } metadata { key: "popularity" value: "5" } metadata { key: "title" value: "Break Away" } provider: "context" album_uri: "spotify:album:2SckEHxbc2j0GidHxkhJwQ" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } playback_id: "011833a8ada63c88e404514669719f30" playback_speed: 1.0 position_as_of_timestamp: 0 duration: 199467 is_playing: true options { shuffling_context: false repeating_context: false repeating_track: false } restrictions { } suppressions { } prev_tracks { uri: "spotify:track:1j8DYZpqHzHTpEkkGiNmx4" uid: "toptrack1j8DYZpqHzHTpEkkGiNmx4" metadata { key: "album_uri" value: "spotify:album:0THmDcPTOEaipA41ubPF7B" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } metadata { key: "duration" value: "149250" } provider: "context" album_uri: "spotify:album:0THmDcPTOEaipA41ubPF7B" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:52sCOP1GtDHh9CEGr2mUKk" uid: "toptrack52sCOP1GtDHh9CEGr2mUKk" metadata { key: "album_uri" value: "spotify:album:7KoBPFtaNjPyUKCdDnMRFS" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:7KoBPFtaNjPyUKCdDnMRFS" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:509DytGOFKTDG8nCHXiRuU" uid: "toptrack509DytGOFKTDG8nCHXiRuU" metadata { key: "album_uri" value: "spotify:album:2DXwYTR5YLSM0tUYe68hja" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:2DXwYTR5YLSM0tUYe68hja" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:1Vf556Am126zRcg40XvHZ2" uid: "toptrack1Vf556Am126zRcg40XvHZ2" metadata { key: "album_uri" value: "spotify:album:2DXwYTR5YLSM0tUYe68hja" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:2DXwYTR5YLSM0tUYe68hja" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:2BgiZvgRLvvXPsa6pcfJWX" uid: "toptrack2BgiZvgRLvvXPsa6pcfJWX" metadata { key: "album_uri" value: "spotify:album:2DXwYTR5YLSM0tUYe68hja" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:2DXwYTR5YLSM0tUYe68hja" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:2Jef30hUAx4srflWlB249b" uid: "toptrack2Jef30hUAx4srflWlB249b" metadata { key: "album_uri" value: "spotify:album:2DXwYTR5YLSM0tUYe68hja" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:2DXwYTR5YLSM0tUYe68hja" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:2RMWXXujopOt8irGOEtB8O" uid: "toptrack2RMWXXujopOt8irGOEtB8O" metadata { key: "album_uri" value: "spotify:album:2DXwYTR5YLSM0tUYe68hja" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:2DXwYTR5YLSM0tUYe68hja" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:23JXRZsfkXfrCouMEFNijD" uid: "toptrack23JXRZsfkXfrCouMEFNijD" metadata { key: "album_uri" value: "spotify:album:2DXwYTR5YLSM0tUYe68hja" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:2DXwYTR5YLSM0tUYe68hja" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:0q9zUVaY0S3GMAWmTPVKw2" uid: "toptrack0q9zUVaY0S3GMAWmTPVKw2" metadata { key: "album_uri" value: "spotify:album:2DXwYTR5YLSM0tUYe68hja" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:2DXwYTR5YLSM0tUYe68hja" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } session_id: "p/hs1hgfULYyoaQ/SNa7CA" } } member_type: CONNECT_STATE is_active: true put_state_reason: VOLUME_CHANGED last_command_message_id: 1847233023 started_playing_at: 1616754685775 has_been_playing_for_ms: 43212 client_side_timestamp: 1616754935696}
Mar 26 11:35:36 raspotify java[32699]: 2021-03-26 11:35:36,072 INFO  DeviceStateHandler:247 - Put state. {ts: 1616754935696, connId: NmZkN...5QkU5, reason: VOLUME_CHANGED, request: device { device_info { can_play: true volume: 35938 name: "SpoCon-Spotify" capabilities { can_be_player: true gaia_eq_connect_id: true supports_logout: true is_observable: true volume_steps: 64 supported_types: "audio/episode" supported_types: "audio/track" command_acks: true supports_playlist_v2: true is_controllable: true supports_transfer_command: true supports_command_request: true supports_gzip_pushes: true } device_software_version: "librespot-java 1.5.6-SNAPSHOT" device_type: SPEAKER spirc_version: "3.2.6" device_id: "6fd7d2d1bb1745e86ce1c2ff05d5a0ff609c5122" } player_state { timestamp: 1616754894111 context_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" context_url: "context://spotify:artist:79xxUllDTqG8bugjRRdmIe" context_restrictions { } play_origin { feature_identifier: "desktop-client-x" feature_version: "1.1.54.592" device_identifier: "c5125b41e3cdd0eb337b02b259234108a1edd8f1" } index { track: 1 } track { uri: "spotify:track:5PLgj89ZyK6kM8z2vl0idE" uid: "toptrack5PLgj89ZyK6kM8z2vl0idE" metadata { key: "album_artist_name" value: "Witchseeker" } metadata { key: "album_artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } metadata { key: "album_disc_number" value: "1" } metadata { key: "album_title" value: "Break Away" } metadata { key: "album_uri" value: "spotify:album:2SckEHxbc2j0GidHxkhJwQ" } metadata { key: "artist_name" value: "Witchseeker" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } metadata { key: "available_file_formats" value: "[\"OGG_VORBIS_320\",\"OGG_VORBIS_160\",\"OGG_VORBIS_96\",\"AAC_24\"]" } metadata { key: "duration" value: "199467" } metadata { key: "image_large_url" value: "spotify:image:ab67616d0000b2730bcb0cc23a5fa72daecad0b0" } metadata { key: "image_small_url" value: "spotify:image:ab67616d000048510bcb0cc23a5fa72daecad0b0" } metadata { key: "image_url" value: "spotify:image:ab67616d00001e020bcb0cc23a5fa72daecad0b0" } metadata { key: "popularity" value: "5" } metadata { key: "title" value: "Break Away" } provider: "context" album_uri: "spotify:album:2SckEHxbc2j0GidHxkhJwQ" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } playback_id: "011833a8ada63c88e404514669719f30" playback_speed: 1.0 position_as_of_timestamp: 0 duration: 199467 is_playing: true options { shuffling_context: false repeating_context: false repeating_track: false } restrictions { } suppressions { } prev_tracks { uri: "spotify:track:1j8DYZpqHzHTpEkkGiNmx4" uid: "toptrack1j8DYZpqHzHTpEkkGiNmx4" metadata { key: "album_uri" value: "spotify:album:0THmDcPTOEaipA41ubPF7B" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } metadata { key: "duration" value: "149250" } provider: "context" album_uri: "spotify:album:0THmDcPTOEaipA41ubPF7B" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:52sCOP1GtDHh9CEGr2mUKk" uid: "toptrack52sCOP1GtDHh9CEGr2mUKk" metadata { key: "album_uri" value: "spotify:album:7KoBPFtaNjPyUKCdDnMRFS" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:7KoBPFtaNjPyUKCdDnMRFS" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:509DytGOFKTDG8nCHXiRuU" uid: "toptrack509DytGOFKTDG8nCHXiRuU" metadata { key: "album_uri" value: "spotify:album:2DXwYTR5YLSM0tUYe68hja" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:2DXwYTR5YLSM0tUYe68hja" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:1Vf556Am126zRcg40XvHZ2" uid: "toptrack1Vf556Am126zRcg40XvHZ2" metadata { key: "album_uri" value: "spotify:album:2DXwYTR5YLSM0tUYe68hja" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:2DXwYTR5YLSM0tUYe68hja" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:2BgiZvgRLvvXPsa6pcfJWX" uid: "toptrack2BgiZvgRLvvXPsa6pcfJWX" metadata { key: "album_uri" value: "spotify:album:2DXwYTR5YLSM0tUYe68hja" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:2DXwYTR5YLSM0tUYe68hja" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:2Jef30hUAx4srflWlB249b" uid: "toptrack2Jef30hUAx4srflWlB249b" metadata { key: "album_uri" value: "spotify:album:2DXwYTR5YLSM0tUYe68hja" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:2DXwYTR5YLSM0tUYe68hja" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:2RMWXXujopOt8irGOEtB8O" uid: "toptrack2RMWXXujopOt8irGOEtB8O" metadata { key: "album_uri" value: "spotify:album:2DXwYTR5YLSM0tUYe68hja" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:2DXwYTR5YLSM0tUYe68hja" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:23JXRZsfkXfrCouMEFNijD" uid: "toptrack23JXRZsfkXfrCouMEFNijD" metadata { key: "album_uri" value: "spotify:album:2DXwYTR5YLSM0tUYe68hja" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:2DXwYTR5YLSM0tUYe68hja" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } next_tracks { uri: "spotify:track:0q9zUVaY0S3GMAWmTPVKw2" uid: "toptrack0q9zUVaY0S3GMAWmTPVKw2" metadata { key: "album_uri" value: "spotify:album:2DXwYTR5YLSM0tUYe68hja" } metadata { key: "artist_uri" value: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } provider: "context" album_uri: "spotify:album:2DXwYTR5YLSM0tUYe68hja" artist_uri: "spotify:artist:79xxUllDTqG8bugjRRdmIe" } session_id: "p/hs1hgfULYyoaQ/SNa7CA" } } member_type: CONNECT_STATE is_active: true put_state_reason: VOLUME_CHANGED last_command_message_id: 1847233023 started_playing_at: 1616754685775 has_been_playing_for_ms: 43212 client_side_timestamp: 1616754935696}
aleszczynskig commented 3 years ago

Yes - this setting is required in config.toml for spocon. I can't tell from your post; has enabling this setting worked for you?

snoopbird commented 3 years ago

I think it's set correctly right? Thank you! :)

### Player ###
[player]
    # Whether to apply the Spotify loudness normalisation
    enableNormalisation = false
    # Initial volume (0-65536)
    initialVolume = 5243
    # Whether librespot-java should ignore volume events, sink volume is set to the max
    bypassSinkVolume = true
    # Release mixer line after set delay (in seconds)
    releaseLineDelay = 20
    # Normalisation pregain in decibels (loud at +6, normal at +3, quiet at -5)
    normalisationPregain = 0.0
    # Output metadata in Shairport Sync format (https://github.com/mikebrady/shairport-sync-metadata-reader)
    metadataPipe = "/srv/music/spotify.metadata"
    # Autoplay similar songs when your music ends
    autoplayEnabled = true
    # Preferred audio quality (NORMAL, HIGH, VERY_HIGH)
    preferredAudioQuality = "VERY_HIGH"
    # Audio output device (MIXER, PIPE, STDOUT)
    output = "PIPE"
    # Crossfade overlap time (in milliseconds)
    crossfadeDuration = 0
    # Whether the player should retry fetching a chuck if it fails
    retryOnChunkError = true
    # Mixer/backend search keywords (semicolon separated)
    mixerSearchKeywords = ""
    # Output raw (signed) PCM to this file (`player.output` must be PIPE)
    pipe = "/srv/music/spotify"
    # Log available mixers
    logAvailableMixers = true
    # Number of volume notches
    volumeSteps = 64
aleszczynskig commented 3 years ago

Yes that looks correct.

FYI. You may want to change settings like the number of volume steps, pre-gain and initial volume to suit your needs. The values here are what works best for me.

snoopbird commented 3 years ago

OK thanks! I just pulled and built the current dev branch again. Bug is still there. So then it seems to be 1) error from me when building librespot-java or 2) a bug in the dev branch of librespot-java. Many thanks to your support!

aleszczynskig commented 3 years ago

You can use the librespot-Java master branch. Is there a reason you are using the dev branch?

snoopbird commented 3 years ago

As far as I understand, only this branch (git branch) currently exists there. I probably just don't know my way around git well enough. In a pinch, I'll just wait until there is a release with the changes.

aleszczynskig commented 3 years ago

You are right. That is the correct branch.

jshep321 commented 3 years ago

Hey guys, I installed from the "regular" branch and installed and works great on my rpi4. I used raspotify before and ran out of steam before I ever got metadata working. This works nearly out of the box, so thanks!

@snoopbird you don't need shairport-sync for spotify, but it can be configured to use this device as an airplay endpoint, in case you want to use your iOS device as a source. iphone (airplay audio sender)->shairport-sync(pipeout)->pipefile (file with mkfifo)->forked-daapd -> airplay speakers.

If you enable pipe_autostart in forked_daapd and then play from iOS to the shairport-sync device, it will take over the stream from spocon and pause the spotify session.

snoopbird commented 3 years ago

My goal is to transfer the music above PIPE to the owntone server above SpoCon (librespot-java). This all works wonderfully, except for the quick control of the volume with the new option bypassSinkVolume = true.

Unfortunately I don't know why.

I have again created a current build of librespot-java ("regular" branch / dev branch).

If I set the switch bypassSinkVolume = true, then I can change the volume in Spotify without this having any effect.

jshep321 commented 3 years ago

@snoopbird I have played around with this and after setting my config.toml file to include the bypassSinkVolume line, I get an error (log level is TRACE):

Apr  9 12:31:11 pi4-1 java[19521]: 2021-04-09 12:31:11,545 TRACE FileConfiguration:109 - Removed entry from configuration file: player.bypassSinkVolume

I have the standard install of spocon. Unclear if this option is supported?