plietar / librespot

Open Source Spotify client library
MIT License
1.14k stars 184 forks source link

Output PCM to fifo pipe #88

Closed 1337sup3rh4x0r closed 7 years ago

1337sup3rh4x0r commented 8 years ago

Hello, I am hoping to run librespot on a headless FreeBSD server that doesn't offer any of the supported audio backends. My goal is to feed its output to the Snapcast Server (https://github.com/badaix/snapcast) to fire my multi room audio system. Currently the only way to acchieve this would be to use joerg-krause's fork with the alsa backend and configure the asound.conf to output to a filesink:

pcm.!default {
    type plug
    slave.pcm rate48000Hz
}

pcm.rate48000Hz {
    type rate
    slave {
        pcm writeFile # Direct to the plugin which will write to a file
        format S16_LE
        rate 48000
    }
}

pcm.writeFile {
    type file
    slave.pcm null
    file "/tmp/snapfifo"
    format "raw"
}

But as I understand, pcm chunks are already available inside librespot. So sending it back and forth to alsa would actually be a detour, wouldn't it?

Could this be output directly to the fifo somehow?

plietar commented 8 years ago

This would definitely be possible.

It would need a new Sink implementation. The sink receives some i16s, which it could then write to the fifo. Will look into it some time.

Two issues I can think of though :

On a side note, I have written spotsync as an experiment, which is multiroom directly embedded in librespot. This has the advantage that each player can buffer the audio in advance independently, rather than having it streamed live across the network. Very unstable though.

1337sup3rh4x0r commented 8 years ago

Spotsync looks interesting! The advantage of using snapcast for me however is that the same clients can be used for mpd based radio playback and other stuff like text to speech output.

Thanks for looking into this!

snizzleorg commented 7 years ago

This sounds like a very good idea. I would love to option to output to a pipe. I'm using forked-daapd to stream to multiple airplay devices. So this would be most welcome....

badaix commented 7 years ago

Hi, the Snapcast guy here. Since yesterday I have a Spotify premium account, and I'm massively impressed about librespot and @plietar's reverse engineering skills :+1: I had to integrate it prototypical into Snapcast as fast as possible to have Spotify "multi roomed". I forked librespot and added a simply stdout-backend: 60ad12033aab2712682bfbd8f48538394cc3c04b that writes the PCM data to stdout. I hope there is no other stuff written to stdout :wink: Using the current Snapcast master, I manually start librespot with the stdout backend and pipe the output into a Snapcast fifo (> /tmp/snapcastspot). Now I will add next to the existing "pipe reader input module" a new "process launcher and stdout reader input module", so that Snapcast can launch librespot and read from it's stdout.

sashahilton00 commented 7 years ago

@badaix sounds like great news. Will test it out this evening, Spotify multiroom would be awesome :)

plietar commented 7 years ago

Awesome. librespot already uses stdout to print some debug output, so that might sound a bit funky :grinning: Audio backends can now take an optional string argument, so you could use that as the output file.

badaix commented 7 years ago

@sashahilton00 two things:

  1. build with --features stdout-backend
  2. Start the Snapserver first, so that the server can create a fifo. Otherwise the data is redirected into a regular file.

@plietar It seems that the debug data is written to stderr. I'm using it for the last 6 hours without any unwanted funk 😄 . But good and valid point about the string argument!

airdrummingfool commented 7 years ago

@plietar

Audio backends can now take an optional string argument, so you could use that as the output file.

How do I pass a path string to the pipe backend?

plietar commented 7 years ago

Use the --device flag

snizzleorg commented 7 years ago

I tried:

./librespot --device /home/pi/spotify

and I get:

INFO:librespot: librespot 740bee5 (2016-12-30). Built on 2017-01-28.
INFO:librespot::session: Connecting to AP lon3-accesspoint-a27.ap.spotify.com:4070
INFO:librespot::session: Authenticated !
INFO:librespot::audio_backend::alsa: Using alsa sink
INFO:librespot::player: Loading track "Let's Have a Ball Tonight"
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM /home/pi/spotify
thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', /buildslave/rust-buildbot/slave/stable-dist-rustc-cross-host-linux/build/src/libcore/option.rs:323
note: Run with `RUST_BACKTRACE=1` for a backtrace.
^Cthread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: "PoisonError { inner: .. }"', /buildslave/rust-buildbot/slave/stable-dist-rustc-cross-host-linux/build/src/libcore/result.rs:837

how exactly is the syntax for outputting to a named pipe?

snizzleorg commented 7 years ago

Ok I get that it has to be built with stdout first. But it seems that the feature has not been merged yet?

pi@automator:~/src/librespot$ cargo build --features stdout-backend                                                                                  
    Updating registry `https://github.com/rust-lang/crates.io-index`
    Updating git repository `https://github.com/plietar/rust-tremor`
    Updating git repository `https://github.com/hyperium/hyper`
    Updating git repository `https://github.com/plietar/rust-alsa`
    Updating git repository `https://github.com/mvdnes/portaudio-rs`
    Updating git repository `https://github.com/plietar/rust-mdns`
    Updating git repository `https://github.com/astro/libpulse-sys`
    Updating git repository `https://github.com/plietar/rust-protobuf-macros`
    Updating git repository `https://github.com/plietar/dns-parser`
error: Package `librespot v0.1.0 (file:///home/pi/src/librespot)` does not have these features: `stdout-backend`
snizzleorg commented 7 years ago

Ok. I got it sorted out:

mkfifo /home/pi/music/spotify
./librespot --backend pipe --device /home/pi/music/spotify -u username -p password -n pipe

works as expected.

sorry for the bothering, I hope this helps someone else