mikebrady / shairport-sync

AirPlay and AirPlay 2 audio player
Other
7.2k stars 571 forks source link

Multiroom output #384

Closed noelhibbard closed 4 years ago

noelhibbard commented 7 years ago

I have a single 7.1 channel sound card that I plan to use for 5 rooms around the house. I plan to use ALSA to map the channels out as separate output devices and then have multiple instances of shairport-sync running with different names pointing to the correct channels on this soundcard. As a proof of concept I loaded Debian in VirtualBox and used this asound.conf:

pcm.room1
{
    type route;
    slave.pcm default;
    slave.channels 2;
    ttable.0.0 1;
    ttable.1.0 1;
}

pcm.room2
{
    type route;
    slave.pcm default;
    slave.channels 2;
    ttable.0.1 1;
    ttable.1.1 1;
}

pcm.bothrooms
{
    type route;
    slave.pcm default;
    slave.channels 2;
    ttable.0.0 1; # room1
    ttable.1.0 1; # room1
    ttable.0.1 1; # room2
    ttable.1.1 1; # room2
}

In this example I am making the left channel Room1 the right channel Room2 and both channels as "bothrooms". I then have three instances of shairport-sync running and I can AirPlay perfectly to the three instances. What I would really love to be able to do is dynamically alter the asound.conf to change what channels a specific instance of shairport-sync outputs to. For testing I started my shairport instance for "bothrooms" and then started streaming to it. Then I edited asound.conf and commented out the mapping for the channels designated for room1 and then saved the file. The current playing stream is not impacted though. I have to restart that instance of shairport for it to pick up the ALSA changes.

I've been desperately looking for an all Linux way to get AirPlay all through the house. I want to have an instance for each room and then one instance that can tie multiple rooms together dynamically. In the shower this morning I got to thinking... maybe I can accomplish this with ALSA.

So I have two questions.

  1. Would it be possible for shairport-sync to pickup ALSA changes without having to restart the service? I'm guessing no.
  2. Does anyone know of a solution in ALSA that would let you alter routing without interrupting the current playing stream?

Worst case I restart shairport-sync and just reconnect my iDevice after making the change. In the end I want to make a web based front end for altering the ALSA config and then restart shairport-sync. So basically just a list of rooms with toggles and maybe volume sliders for each room. Anytime you make a change in this WebUI it would force shairport-sync to restart. Not perfect but it's better than nothing.

If anyone else has any thoughts please share. I've tried a million AirPlay solutions and none of them are perfect. Currently I'm using TuneAero/TuneBlade and it works really well. The only problem is it's Windows only and it will not let you assign playback to specific channels on a sound card. To work around that I am using VAC but that just adds more layers of crap. The dev says he plans to implement channel selection in the future but who knows when.

It would be really cool if there was a Linux AirPlay client (sender) that could output to multiple targets with sync. I haven't even been able to find a linux client that can output to a single AirPlay target. I see some old solutions but none of them are maintained and all seem to be broken on iOS9+.

mikebrady commented 7 years ago

Wow, that's quite a setup.

Would it be possible for shairport-sync to pickup ALSA changes without having to restart the service? I'm guessing no.

Sadly, you're right – it would be a bit tricky, and to get Shairport Sync to reload its preferences dynamically, (a promising approach, if you could change the configuration file to point to the new outputs), a deprecation that has already been flagged would have to be accelerated.

It would be really cool if there was a Linux AirPlay client (sender) that could output to multiple targets with sync. I haven't even been able to find a linux client that can output to a single AirPlay target. I see some old solutions but none of them are maintained and all seem to be broken on iOS9+.

Maybe I'm misunderstanding you, but doesn't forked-daapd do this?

noelhibbard commented 7 years ago

I don't think forked-daapd works anymore either. Let me give that a shot right now.

What I ultimately want though is: shariport-sync -> stdout -> (some AirPlay Client) -> (Multiple shairport-sync destinations)

It's been a while since I played with forked-daapd. I don't recall if there is a way to pipe data to it.

noelhibbard commented 7 years ago

forked-daapd works. I could have sworn I ran into some problem with it last time I tested. I think I can get AirPlay into it as a radio stream and use shairport-sync piped to something that will serv it up as a radio station. Then just use the iOS Remote app to control the output. Hmm.. this may just work.

noelhibbard commented 7 years ago

Works flawless! Thanks for the help as always Mike.

So here is the setup. I have one 7.1 soundcard with asound.conf setup to pull out pairs of channels and address them as separate output devices for each room. Then I have an instance of shairport-sync for each room. Then one additional instance of shairport-sync named "[Hub1]" (named so it sorts to the top) which is set to pipe to a "/srv/music/AirPlay Hub1". If I want to AirPlay direct to a specific room I can do that. If I want to AirPlay to multiple rooms I simply select [Hub1] as my AirPlay target and then fire up the Apple Remote app and then select "AirPlay Hub1" and then select the rooms I want within the remote app. The volume on my phone works as a global volume which is perfect.

noelhibbard commented 7 years ago

Thinking a little more.. I think I could use on-start to tell forked-daapd to automatically start playing /srv/music/Aireplay Hub1 so that would be one less step for end users.

mikebrady commented 7 years ago

Sounds good!

noelhibbard commented 7 years ago

I scrapped forked-daapd and wrote a NodeJS app using node_airtunes and nodetunes. nodetunes acts as an AirPlay server and node_airtunes works as an AirPlay client with sync support. So I tied the two together to make an AirPlay proxy which has a mobile friendly WebUI that lets you pick which devices you want to output to along with volume sliders. I am still using shairport-sync for the final destinations though.

forked-daapd worked well too but the only functionality I needed out of it was the ability to pipe shairport-sync into it and then back out to other instances of shairport-sync. My new implementation is more streamlined and has less lag. The WebUI is also much more simplistic compared to using the Apple Remote app.

Years I've been trying to throw something together like this. I'm finally satisfied. :)

Thanks again Mike for all your help and time invested in shairport-sync!

mikebrady commented 7 years ago

Thanks for the update on your progress, which does sound impressive!

Dave-Snigier commented 7 years ago

@noelhibbard Sounds epic! Would you consider releasing this? I attempted doing something similar a couple years back using a different libraries and while it did work, was quite buggy.

noelhibbard commented 7 years ago

@LambdaDriver I suspect I will release it at some point. There are still a few things I want to change. I have had guest at the house for the past few weeks so I haven't had much of a chance to blast music throughout the house to test the stability. Once I feel it's reliable I will release.

noelhibbard commented 7 years ago

My guest is gone so I had some time to test this rig of mine. If I only select one destination it seems to play perfect but if I select more than one destination the audio would drop out randomly every few minutes. So now I need to do some digging to see if it is buffering between the phone and nodetunes or if it's buffering between nodetunes and node_airplay or between node_airplay and shairport-sync. Hahaha. I am running this on a very old nettop with an Intel Atom 230 single code 1.6GHz CPU. It could simply be too much for this CPU. I have a NUC on hand with an i5. I will load my stuff on that and see if I get different results.

snizzleorg commented 7 years ago

I'm interested in this too...

ghost commented 7 years ago

@noelhibbard Also very interested in this. Just to get it right: In your latest configuration you don't use shairport-sync, but only node_airtunes and nodetunes in order to "duplicate" the iPhone stream to multiple streams, right? Do you think, it would be possible to stream the music to multiple (let's say Airport Express) Airplay Receivers this way? Could you please share a documentation about what you did to get this work?

Thank you in advance!

noelhibbard commented 7 years ago

@ratio1, correct, I am only using node_airtunes and nodetunes in my project. In my configuration I am still using shairport-sync for the end points but it can also output to an AirPort Express, Apple TV or any other AirPlay endpoint. I will work on hosting my project on GitHub for others to use some time this week. The only hangup is it has only been tested on Debian Linux and Win10 (but doesn't work on Win10). node_airtunes doesn't work under Win10. Someone made a fork which will at least compile but it will not stream correctly. I am sure someone could fix this though and then my project could also work on Win10.

ghost commented 7 years ago

@noelhibbard Thank you for your quick reply! I'm absolutely fine with Debian - it's my favorite Server OS. :-) A perfect world scenario would be to get it running on a Raspberry Pi, where an optional Touchscreen on it shows the Airplay-capable devices found in the network and gives function to de/select target destinations by a single touch.

snizzleorg commented 7 years ago

@noelhibbard @ratio1 No need for Win10 - at least for me. I'm fine with Debian. I will try to get it running on a raspberry Pi as well. preferably in a docker image...

noelhibbard commented 7 years ago

@snizzleorg @LambdaDriver @ratio1

I just created a repo for the project. I was just thinking, I am not currently forwarding the metadata from the original stream over to the endpoints. I will add this later. The only problem is nodetunes seems to have a bug where is only updates the album art when the client first connects. When changing tracks it doesn't update. So for now I will only forward the track name, album, ect. I will not bother with the art for now.

mikebrady commented 4 years ago

Thanks for this interesting contribution. Please open a new issue if necessary.

sedlacekdavid commented 3 years ago

Hi, any change on this topic? Is it still required to run multiple shairport instances?

I'd like to use same setup - have a 7.1 sound card and wiring to speakers in 7 different rooms.

Any ideas, please?