arkq / bluez-alsa

Bluetooth Audio ALSA Backend
MIT License
854 stars 188 forks source link

How to pipe bluealsa PCM to fifo? #703

Closed amigthea closed 4 months ago

amigthea commented 4 months ago

I've been able to install, configure and start bluez-alsa on my headless server. I can see my BT devices and BT PCMs with bluealsa-aplay -L and bluealsa-aplay -l but when I try to pipe an output to /tmp/snapfifo (snapcast) I do not hear anything, why it could be?

aplay -D bluealsa:DEV=RA:ND:OM:MA:CA:DD,PROFILE=a2dp < /tmp/snapfifo

PS: I can successfully use the same pipe with mopidy

graham8 commented 4 months ago

Shouldn't that < be a >?

borine commented 4 months ago

Please can you clarify (I am not familiar with snapcast): are you trying to send audio from a phone to play it through snapcast, or are you trying to use snapcast to play audio from a file to a bluetooth speaker?

amigthea commented 4 months ago

thank you for joining me @graham8 @borine, I'm sorry for missing this infos:

aplay -l **** List of PLAYBACK Hardware Devices ****

aplay -L

null
    Discard all samples (playback) or generate zero samples (capture)
lavrate
    Rate Converter Plugin Using Libav/FFmpeg Library
samplerate
    Rate Converter Plugin Using Samplerate Library
speexrate
    Rate Converter Plugin Using Speex Resampler
jack
    JACK Audio Connection Kit
oss
    Open Sound System
pulse
    PulseAudio Sound Server
speex
    Plugin using Speex DSP (resample, agc, denoise, echo, dereverb)
upmix
    Plugin for channel upmix (4,6,8)
vdownmix
    Plugin for channel downmix (stereo) with a simple spacialization
front:CARD=Intel
    Front output / input
surround21:CARD=Intel
    2.1 Surround output to Front and Subwoofer speakers
surround40:CARD=Intel
    4.0 Surround output to Front and Rear speakers
surround41:CARD=Intel
    4.1 Surround output to Front, Rear and Subwoofer speakers
surround50:CARD=Intel
    5.0 Surround output to Front, Center and Rear speakers
surround51:CARD=Intel
    5.1 Surround output to Front, Center, Rear and Subwoofer speakers
surround71:CARD=Intel
    7.1 Surround output to Front, Center, Side, Rear and Woofer speakers
usbstream:CARD=Intel
    HDA Intel
    USB Stream Output

bluealsa-play -l

**** List of PLAYBACK Bluetooth Devices ****
**** List of CAPTURE Bluetooth Devices ****
hci0: RA:ND:OM:MA:CA:DD [Phone], trusted phone
  A2DP (SBC): S16_LE 2 channels 44100 Hz

bluealsa-play -L

bluealsa:DEV=RA:ND:OM:MA:CA:D2,PROFILE=a2dp,SRV=org.bluealsa
    Phone, trusted phone, capture
    A2DP (SBC): S16_LE 2 channels 44100 Hz

For now I'm trying the fifo way, but maybe the alsa one is cleaner? right now there's no configured /etc/asound.conf, maybe I'm missing something there?

borine commented 4 months ago

To capture audio from a phone, you need arecord not aplay, just as you would if you were capturing from a sound card. You will also need to take care of the sample rate, sample format and channel count, just as you would if capturing from a sound card.

amigthea commented 4 months ago

that makes sense, I'll play around that way, thank you so much. After that how can I costantly play, non interactively, from the PCM to a fifo?

borine commented 4 months ago

I think this is wandering off-topic a little now. You are actually asking "How do I play audio from an ALSA capture device using snapcast" ( because a BlueALSA device is just like any other ALSA device ); and so I think the best place to ask is on a snapcast forum (or perhaps the answer is already in the snapcast documentation).

amigthea commented 4 months ago

I expressed myself poorly, I was looking for a way obtain the same effect of arecord through an /etc/asound.conf file maybe, I've see that it can output to a file (fifo), right? how that works under bluealsa?

amigthea commented 4 months ago

I tried with this in asound.conf but to no avail

 pcm.!default {
     type file
     slave {
         pcm "bluealsa"
     }
     file "/tmp/snapfifo"
     format "raw"
 }

but I got a little victory with the interactive way and it now works perfectly!

arecord -D bluealsa -f s16_le -c 2 -r 48000 > /tmp/snapfifo

let me know if this is still off topic and thank you so much for the support

borine commented 4 months ago

OK, so as you seem reluctant to ask for help from the snapcast project, I've had a quick look at the snapcast docs, and installed snapserver and snapclient on my own machine.

I defined the default source in the file /etc/snapserver.conf as:

source = alsa://?name=default&device=bluealsa

I added the _snapserver user to the audio group and then reloaded the dbus config so that the snap sever has permission to use bluealsa:

sudo systemctl reload dbus

I restarted the snapserver service:

sudo systemctl restart snapserver.service

I started the snapclient service:

sudo systemctl start snapclient.service

I then connected my 'phone, started music playback, and immediately the music played through my computer speakers. No need for /etc/asoundrc or any applications other than snapserver and snapclient. Easy.

amigthea commented 4 months ago
source = alsa://?name=default&device=bluealsa

that bit helped me greatly to sort my problem out without the overhead of the fifo source (I'm running in a containerized env), thank you!

amigthea commented 4 months ago

Unfortunately, while the stream is playing, I´m facing continuous dropouts, would raising the PCM buffer help to solve this?

 pcm.buffered {
     type plug
     slave.pcm {
         type bluealsa
         device "XX:XX:XX:XX:XX:XX"
         profile "a2dp"
         buffer_size 256000
     }
 }

source = alsa://?name=default&device=buffered

would that even work? I'm trying to wrap my head around the audio flow when working with PCMs

@borine have you encountered this kind of issue related to PCM direct play (without pulseaudio or pipewire)? I excluded external interference like wifi and tried to setup without containers and the problem still there

amigthea commented 4 months ago

after a night of testing, I was able to boil the problem down a bit:

I enabled the loopback modprobe snd-aloop

Used bluealsa-aplay to play audio from the bluetooth device to the loopback out pcm device bluealsa-aplay --pcm=hw:1,0

Now I the dropouts are far less and in between and when those happens the bluealsa-aplay command gives me this errors

bluealsa-aplay: [663105] D: aplay.c:540: Starting IO loop
bluealsa-aplay: [663105] D: aplay.c:664: Opening ALSA playback PCM: name=hw:1,0 channels=2 rate=44100
bluealsa-aplay: [663105] D: aplay.c:679: Opening ALSA mixer: name=default elem=Master index=0
bluealsa-aplay: [663105] W: aplay.c:683: Couldn't open ALSA mixer: Mixer element not found
bluealsa-aplay: [663105] D: aplay.c:578: BT device marked as inactive: XX:XX:XX:XX:XX:XX
bluealsa-aplay: [663105] D: aplay.c:664: Opening ALSA playback PCM: name=hw:1,0 channels=2 rate=44100
bluealsa-aplay: [663105] D: aplay.c:679: Opening ALSA mixer: name=default elem=Master index=0
bluealsa-aplay: [663105] W: aplay.c:683: Couldn't open ALSA mixer: Mixer element not found
bluealsa-aplay: [663105] D: aplay.c:578: BT device marked as inactive: XX:XX:XX:XX:XX:XX

what it does mean?

ps: should I open another issue for this?

arkq commented 4 months ago

D: aplay.c:578: BT device marked as inactive: XX:XX:XX:XX:XX:XX

It means that the audio has stopped coming from the remote BT device for more or equal to 500ms. In most cases it's long enough to generate PCM underrun anyway. So, in such case the output PCM is closed and bluealsa-aplay waits for incoming PCM samples from the remote BT device. If your ALSA PCM output device buffer is bigger than 500ms, then you can try to increase this timeout value: https://github.com/arkq/bluez-alsa/blob/87e55cdb2feca6c9136f0344de2d49859b468abf/utils/aplay/aplay.c#L745

amigthea commented 4 months ago

thank you for that insight, I will inspect deeper that way.

I have one last doubt, I have two bluetooth devices and right now I'm streaming them each in a separate PCM loopback-in subdevice with a script, so I can reroute their PCM loopback-out to snapcast.

This is working

/usr/bin/bluealsa-aplay --pcm=hw:1,0,0 YY:XX:XX:XX:XX:XX &
/usr/bin/bluealsa-aplay --pcm=hw:1,0,1 XX:YY:XX:XX:XX:XX

but I would like to replicate this non interactively by creating PCMs devices through asound.conf. Bluealsa creates the pcm device on bluetooth connection so asound.conf would fail at boot if bluetooth device are not connected right? there's a cleaner way to achieve that?

thank you for your time

borine commented 4 months ago

would that even work?

No, the bluelalsa plugin type does not accept "buffer_size" as a parameter. See the bluealsa-plugins manual page for details of the BlueALSA PCM plugin.

have you encountered this kind of issue related to PCM direct play

I have never used snapcast before, so can't say what to expect here. Occasional dropouts are not uncommon when reading from one PCM device and "live streaming" to another PCM device on a different sound card. That is because the timers on the two devices are never exactly synchronized, there is always some small drift between them. Some applications contain code to adjust the stream to compensate for this, others do not. Often increasing buffer size and/or start threshold can fix this well enough to play for a few hours without dropouts occuring.

However "continuous dropouts" is rarely due to timer drift because most modern timers are reasonably accurate. I have seen this when using the ALSA rate plugin, especially in combination with the ALSA dmix plugin. see the bluealsa-aplay manual page. I see that your phone is playing audio sampled at 44100; so the ALSA rate plugin is being used here if snapserver is configured to read its source at rate 48000

After playing with snapcast a little this morning, I see that it does not like alsa sources that do not guarantee a continuous stream. That is not uncommon, and at present there is no workaround for the bluealsa plugin when used as a source. So perhaps my suggestion of using the snapserver alsa source was not the best after all.

One alternaive is to use bluealsa-aplay with the pipe source. I tested that successfully (no droputs) with the following setup:

/etc/asound.conf

pcm.snapserver {
    type plug
    slave {
        rate 48000
        format S16_LE
        channels 2
        pcm {
            type file
            file "/tmp/snapfifo"
            format raw
            slave.pcm null
        }
    }   
}

/etc/snapserver.conf

source = pipe:///tmp/snapfifo?name=default&mode=create&sampleformat=48000:16:2&codec=pcm

bluealsa-aplay command line:

bluealsa-aplay --pcm=snapserver

Note that my phone is sending stereo, S16_LE sampled at 48000.

I will try the loopback solution later in the week and report back here. Meanwhile please can you post your /etc/snapserver.conf source = ... entry that you are using with the loopback solution (and also any /etc/asound.conf entry associated with that).

amigthea commented 4 months ago

that was a really great compendium and reading, thank you for that.

as for the alternatives to continuous streaming, I found out that using the loopback device instead of the fifo is more reliable in terms of latency, and to me (correct me if I'm guessing wrong) looks more of a streamlined approach using PCMs only. Beside that I'm puzzling over why using the loopback as a middle device solved all the dropouts issue altogether, it is because it add some kind of buffer? Have some patience please, this is my first serious approach to the audio driver world

my actual (working) snapserver.conf

source = alsa://?name=phone1&device=hw:1,1,0&sampleformat=44100:16:2&codec=pcm
source = alsa://?name=phone2&device=hw:1,1,1&sampleformat=44100:16:2&codec=pcm

My target is to have multiple snapcast group to separate bluetooth source devices; ideally this let me connect to the bluetooth adapter with multiple devices so I can choose on which snapclient each bt client will play

borine commented 4 months ago

I'm puzzling over why using the loopback as a middle device solved all the dropouts

I can only guess that it is because you have explicitly set &sampleformat=44100:16:2 and used the hw alsa plugin. That means there is no rate conversion anywhere in the chain.

I've now tried bluealsa-aplay with both alsa Loopback and fifo sources and I seem to have very similar latency and no dropouts with either. So I would recommend to go with whichever works best for you.

amigthea commented 4 months ago

beautiful, thank you all and keep up with this amazing project!