moode-player / moode

moOde sources and configs
GNU General Public License v3.0
994 stars 166 forks source link

load/unload i2s audio overlays without reboot #584

Closed TheOldPresbyope closed 5 months ago

TheOldPresbyope commented 1 year ago

From the Raspberry Pi Docs

dtoverlay is a command line utility that loads and removes overlays while the system is running, as well as listing the available overlays and displaying their help information.

This is cool but it cannot manipulate---or even list---overlays loaded during the pre-OS booting process.

To see if this command could be used to add/remove an i2s audio device from moOde without rebooting, I did the following quick-n-dirty test on a moOde 8.2.2 player already configured for my HiFiBerry DAC+ card (so I don't have to mess with the PHP code to change values in moode-sqlite3.db).

The player boots and plays audio via the HiFiBerry DAC without issue. The dtparam audio command works because the audio parameter is in the base dtb which is loaded regardless.

From this quick test, I infer that it should be possible to retool moOde to allow user switching among audio devices without rebooting, although I don't claim to know there's no hidden gotcha ahead. I'll try to work my way through moOde's current php code but holiday activities take precedence.

NOTES

moodeaudio commented 1 year ago

Thats a great find :-)

I'll have a look at the code to see whats needed to integrate it. It prolly won't make it into upcoming 8.2.3 but definitely will #1 on the TODO list for Q1 2023.

AFAIK its only HFB that tried EEPROM overlays and only certain HFB boards have EEPROM overlay bugs. Thats why its best to just disable the whole auto-load EEPROM overlay feature.

TheOldPresbyope commented 1 year ago

The dynamically loaded overlays go in a last-in/first-out stack. The docs describe somewhat imprecisely how to remove one below the top, if that's necessary.

moodeaudio commented 1 year ago

Does this process also add a dtoverlay line to config.txt or does it replace that scheme?

TheOldPresbyope commented 1 year ago

The dtoverlay command doesn't modify /boot/config.txt.

To my way of thinking, we'd use the dynamic loading/unloading capability mostly for i2s HAT overlays. From a fresh image, a user could configure moOde for, say, the HFB DAC+ Pro and be done. Then, the onboard audio can be switched on and off, the HFB dtoverlay loaded/unloaded, yada yada yada, while moOde/Linux are running, using the dtoverlay command. I haven't thought through what the use cases should look like to the user.

Instead of modifying /boot/config.txt, we would have to add some startup code to assert whatever dtoverlay commands are needed, based on what's been stored in /var/local/www/db/moode-sqlite3.db (maybe...I'm just blueskying here)

There are a few gotchas while running---for example you can't unload an overlay that's in use, say because MPD is playing a track.

Whatever is in /boot/config.txt is acted on every time the GPU begins the boot process, before the CPU and Linux are initialized. I don't have any examples of overlays I know would have to go there.

I'm thinking, but haven't tested, that one could disable WiFi and BT via the dtoverlay command while LInux is running, similarly to switching onboard audio on/off. This requires that the WiFi and BT parameters are defined in the base DTB and---full disclosure---I haven't checked to be sure this is so. It also may be that doing this after the system has booted is a bad idea for some reason.

ETA - My bad, disable-wifi and disable-bt are names of actual overlays, not of parameters. So, yes, should be possible to load/unload but still may be a bad idea.

moodeaudio commented 1 year ago

I like the idea of being able to dynamically switch among audio devices whether they are Pi integrated HDMI/Headphone, USB or I2S HAT's because it would make it easier for the user.

The challenge is that Linux/ALSA does not maintain a static list of the audio interfaces on the board, rather its a dynamic list. For example when an I2S audio device is configured its the only one that shows up in aplay -l. There is no way to know whether there is also HDMI, Headphone or USB interfaces present.

TheOldPresbyope commented 1 year ago

Grrr. There's so much I don't know about ALSA.

Maybe, when a user says "I want to switch away from the i2s device", unload the dtoverlay, reenable the "audio" parameter, reread the devices present, and present the choices? and if the user "cancels" then reverse the steps to continue using the same i2s device.

I'll have to play with this a bit.

TheOldPresbyope commented 1 year ago

I've run into a roadblock. It seems that the dtparam command is impotent with respect to the base DTB parameters. Invoking sudo dtparam audio=on or sudo dtparam audio=off doesn't appear to affect the onboard audio subsystem. If the subsystem was enabled by the bootloader because dtparam=audio=on is present in /boot/config.txt, then it stays enabled no matter what; similarly, if it is disabled (the default condition) during boot, it stays disabled no matter what.

Loading and unloading an i2s overlay, eg, hifiberry-dacplus, works fine. Here's what aplay -l reports after booting with just dtparam=audio=on in /boot/config.txt

$aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: b1 [bcm2835 HDMI 1], device 0: bcm2835 HDMI 1 [bcm2835 HDMI 1]
  Subdevices: 4/4
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
card 1: Headphones [bcm2835 Headphones], device 0: bcm2835 Headphones [bcm2835 Headphones]
  Subdevices: 4/4
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3

and here's what it reports after running

$ sudo dtparam audio=off
$ sudo dtoverlay hifiberry-dacplus
$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: b1 [bcm2835 HDMI 1], device 0: bcm2835 HDMI 1 [bcm2835 HDMI 1]
  Subdevices: 4/4
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
card 1: Headphones [bcm2835 Headphones], device 0: bcm2835 Headphones [bcm2835 Headphones]
  Subdevices: 4/4
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
card 2: sndrpihifiberry [snd_rpi_hifiberry_dacplus], device 0: HiFiBerry DAC+ HiFi pcm512x-hifi-0 [HiFiBerry DAC+ HiFi pcm512x-hifi-0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

This even though both dt-commands succeeded

$ dtoverlay -l
Overlays (in load order):
0:  dtparam  audio=off
1:  hifiberry-dacplus

Using dtoverlay to unload hifiberry-dacplus returns me to the original state.

It would seem to me that to use this feature as it currently exists, we'd have to undo moOde's taking advantage of an i2s DAC always being Card 0.

NOTE- Quoting from the Raspberry Pi docs

The loading of overlays at runtime is a recent addition to the kernel, and so far there is no accepted way to do this from userspace. By hiding the details of this mechanism behind commands the aim is to insulate users from changes in the event that a different kernel interface becomes standardised.

We live in interesting times.

moodeaudio commented 1 year ago

Did your results show the following?

  1. dtparam=audio=on can be left in config.txt
  2. I2S overlay can be dynamically loaded and is assigned next card number
  3. Any of the devices (HDMI, Headphone, I2S device) can be played. This can be tested by examining the output from cat /proc/asound/cardN/pcm0p/sub0/hw_params for each card number N.

If thats the case then it's goodness but would as you mentioned require modifying the current scheme for managing ALSA cards/devices.

TheOldPresbyope commented 1 year ago

Affirmative.

  1. dtparam=audio-on must be present in /boot/config.txt to enable the onboard audio subsystem
  2. This is true on my RPi4B. Here, for example, I have attached a Tone1 USB DAC and a HiFiBerry DAC Plus to it and booted.
    $ aplay -l
    **** List of PLAYBACK Hardware Devices ****
    card 0: b1 [bcm2835 HDMI 1], device 0: bcm2835 HDMI 1 [bcm2835 HDMI 1]
    Subdevices: 4/4
    Subdevice #0: subdevice #0
    Subdevice #1: subdevice #1
    Subdevice #2: subdevice #2
    Subdevice #3: subdevice #3
    card 1: Headphones [bcm2835 Headphones], device 0: bcm2835 Headphones [bcm2835 Headphones]
    Subdevices: 4/4
    Subdevice #0: subdevice #0
    Subdevice #1: subdevice #1
    Subdevice #2: subdevice #2
    Subdevice #3: subdevice #3
    card 2: Tone1 [Tone1], device 0: USB Audio [USB Audio]
    Subdevices: 1/1
    Subdevice #0: subdevice #0

Then I executed sudo dtoverlay hifiberry-dacplus to pick up the i2s DAC

$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: b1 [bcm2835 HDMI 1], device 0: bcm2835 HDMI 1 [bcm2835 HDMI 1]
  Subdevices: 4/4
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
card 1: Headphones [bcm2835 Headphones], device 0: bcm2835 Headphones [bcm2835 Headphones]
  Subdevices: 4/4
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
card 2: Tone1 [Tone1], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 3: sndrpihifiberry [snd_rpi_hifiberry_dacplus], device 0: HiFiBerry DAC+ HiFi pcm512x-hifi-0 [HiFiBerry DAC+ HiFi pcm512x-hifi-0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
  1. Using aplay to play a WAV file to each of these cards in turn, I see the content of hw_params file for that card only turn from closed to, e.g.,
$ more card2/pcm0p/sub0/hw_params
access: MMAP_INTERLEAVED
format: S32_LE
subformat: STD
channels: 2
rate: 192000 (192000/1)
period_size: 24000
buffer_size: 96000
moodeaudio commented 1 year ago

Ok cool.

Will investigate for Q1 2023

tomaxsas commented 1 year ago

Really looking to this 👍

moodeaudio commented 5 months ago

A quick test on moode 9 pre and it looks like the overlay, after loading is not listed in aplay -l output and thus cannot be use for audio output.

I think best to close this and if new info comes up then reopen.