Handling complex matrix mixers in ALSA
Hi,
I'm reverse engineering and implementing support for the RME Digiface USB, which is an ADAT interface with a non-class-compliant interface (probably similar to other RME interfaces like the MADIface, but I don't have any others to test). The basic audio streaming works fine with an entry in quirks-table.h and a format quirk to set the system sample rate in quirks.c. Now I need to figure out how to implement the mixer controls.
Currently I have the snd-usb-audio driver claiming only interface #0 (streaming) and I use a Python script to control the mixer/settings directly with libusb (control transfers and interface #1). This works fine and there's some prior art for this in the firewire world (for example, snd-dice doesn't do any mixer control stuff and you have to use ffado-mixer to control everything from userspace) but I assume it's not really the best way to go?
The problem is that the device has a 66x34 matrix mixer, with up to 2048 cross points enabled at once. Exposing each cross point as an ALSA mixer control (similar to how mixer_scarlett2.c does it) would mean 2244 controls just for the mixer... which seems like a bit too much.
On top of that there is also VU meter feedback for all the inputs/outputs, as well as general fader controls for each output and global output configs and status. I'm not sure about the VU meters, but everything else sounds like it would map fine to normal mixer controls.
Is there some recommended way to expose this kind of matrix mixer interface to userspace? I think for something like this you pretty much have to rely on device-specific tools to make the UX manageable, so maybe hwdep... but at least exposing everything as an ALSA control would have the advantage of supporting save/restore with something like alsactl. So I don't really know what's the way to go here.
System settings/general status/output faders go via control transfers, while interface #1 has an interrupt IN endpoint (streaming state feedback, not very useful) and two bulk endpoints (matrix mixer control out, VU meter data in). There's another pair of bulk endpoints in interface #2 which I'm guessing are for firmware updates (I haven't looked at that part). So in principle it's not crazy to expose all the system controls/output faders as mixer controls in ALSA and leave interface #1 entirely unclaimed so a userspace program can directly configure the matrix mixer and access VU meter levels. There is a global mixer enable bit (controlled via ctl transfer), so if that is exposed as an ALSA control and disabled by default the interface will operate as a 1:1 in/out interface without needing any custom userspace to configure the mixer.
There's one other quirky thing: it also needs a way to set the sample rate as a mixer control, because you need to be able to configure the rate even when the PCM device is not open (since that affects stand-alone mixer operation). I imagine the right logic here would be to have a selector control for the system sample rate, and automatically change it and lock it when the PCM is opened with a given rate?
Any thoughts welcome ^^
~~ Lina
Hi,
I'm a maintainer of both ALSA firewire stack and Linux firewire subsystem. As long as I know, there is neither consensus nor specific userspace API/structure to the issue , therefore each developer selects each way within current implementation of ALSA control core and userspace interface.
On Mon, Jul 1, 2024, at 01:04, Asahi Lina wrote:
The problem is that the device has a 66x34 matrix mixer, with up to 2048 cross points enabled at once. Exposing each cross point as an ALSA mixer control (similar to how mixer_scarlett2.c does it) would mean 2244 controls just for the mixer... which seems like a bit too much.
On top of that there is also VU meter feedback for all the inputs/outputs, as well as general fader controls for each output and global output configs and status. I'm not sure about the VU meters, but everything else sounds like it would map fine to normal mixer controls.
Is there some recommended way to expose this kind of matrix mixer interface to userspace? I think for something like this you pretty much have to rely on device-specific tools to make the UX manageable, so maybe hwdep... but at least exposing everything as an ALSA control would have the advantage of supporting save/restore with something like alsactl. So I don't really know what's the way to go here.
In my opinion, expose of such many control elements would not be necessarily convenient to users, especially to who eager to use GUI for such matrix mixer. Additionally, initialization for all of configurable elements in device would sometimes require conditional operation somehow (e.g. dependency on sampling rate or any mode of digital interfaces).
If you have no need to share one of the USB endpoints for any configurable elemenets between kernel/userspace, it is a simple way to implement such confuguration application as userspace application, as you did with Python 3 and libusb.
Anyway, if you eager to change ALSA control core and its interface to userspace for the purpose, we go for more discussion about it. We share the same interest.
P.S. for devices supported by drivers in ALSA firewire stack, I write some service programs in userspace to utilize ALSA control "user-defined control element set". You can find the implementation of protocol to configure RME Fireface 400/800/UCX/808 in it, as well as any DICE-based models:
* https://github.com/alsa-project/snd-firewire-ctl-services/
Regards
Takashi Sakamoto
Hi Lina,
On Mon, Jul 01, 2024 at 01:04:41AM +0900, Asahi Lina wrote:
Hi,
I'm reverse engineering and implementing support for the RME Digiface USB, which is an ADAT interface with a non-class-compliant interface (probably similar to other RME interfaces like the MADIface, but I don't have any others to test). The basic audio streaming works fine with an entry in quirks-table.h and a format quirk to set the system sample rate in quirks.c. Now I need to figure out how to implement the mixer controls.
Currently I have the snd-usb-audio driver claiming only interface #0 (streaming) and I use a Python script to control the mixer/settings directly with libusb (control transfers and interface #1). This works fine and there's some prior art for this in the firewire world (for example, snd-dice doesn't do any mixer control stuff and you have to use ffado-mixer to control everything from userspace) but I assume it's not really the best way to go?
I'm the developer of the Scarlett2 driver. Doing as much as possible through ALSA mixer controls has worked well in my experience; as you say, being able to do save/restore with alsactl is useful.
The problem is that the device has a 66x34 matrix mixer, with up to 2048 cross points enabled at once. Exposing each cross point as an ALSA mixer control (similar to how mixer_scarlett2.c does it) would mean 2244 controls just for the mixer... which seems like a bit too much.
Note that mixer controls may have more than one value. So I think you could have 66 controls with 34 values or 34 controls with 66 values or 1 control with 2244 values.
On top of that there is also VU meter feedback for all the inputs/outputs, as well as general fader controls for each output and global output configs and status. I'm not sure about the VU meters, but everything else sounds like it would map fine to normal mixer controls.
I handle the VU meter feedback in the Scarlett2 driver with a read-only volatile control that contains multiple values (see scarlett2_meter_ctl).
Regards, Geoffrey.
Is there some recommended way to expose this kind of matrix mixer interface to userspace? I think for something like this you pretty much have to rely on device-specific tools to make the UX manageable, so maybe hwdep... but at least exposing everything as an ALSA control would have the advantage of supporting save/restore with something like alsactl. So I don't really know what's the way to go here.
System settings/general status/output faders go via control transfers, while interface #1 has an interrupt IN endpoint (streaming state feedback, not very useful) and two bulk endpoints (matrix mixer control out, VU meter data in). There's another pair of bulk endpoints in interface #2 which I'm guessing are for firmware updates (I haven't looked at that part). So in principle it's not crazy to expose all the system controls/output faders as mixer controls in ALSA and leave interface #1 entirely unclaimed so a userspace program can directly configure the matrix mixer and access VU meter levels. There is a global mixer enable bit (controlled via ctl transfer), so if that is exposed as an ALSA control and disabled by default the interface will operate as a 1:1 in/out interface without needing any custom userspace to configure the mixer.
There's one other quirky thing: it also needs a way to set the sample rate as a mixer control, because you need to be able to configure the rate even when the PCM device is not open (since that affects stand-alone mixer operation). I imagine the right logic here would be to have a selector control for the system sample rate, and automatically change it and lock it when the PCM is opened with a given rate?
Any thoughts welcome ^^
~~ Lina
On Sun, 30 Jun 2024 18:04:41 +0200, Asahi Lina wrote:
Hi,
I'm reverse engineering and implementing support for the RME Digiface USB, which is an ADAT interface with a non-class-compliant interface (probably similar to other RME interfaces like the MADIface, but I don't have any others to test). The basic audio streaming works fine with an entry in quirks-table.h and a format quirk to set the system sample rate in quirks.c. Now I need to figure out how to implement the mixer controls.
Currently I have the snd-usb-audio driver claiming only interface #0 (streaming) and I use a Python script to control the mixer/settings directly with libusb (control transfers and interface #1). This works fine and there's some prior art for this in the firewire world (for example, snd-dice doesn't do any mixer control stuff and you have to use ffado-mixer to control everything from userspace) but I assume it's not really the best way to go?
The problem is that the device has a 66x34 matrix mixer, with up to 2048 cross points enabled at once. Exposing each cross point as an ALSA mixer control (similar to how mixer_scarlett2.c does it) would mean 2244 controls just for the mixer... which seems like a bit too much.
On top of that there is also VU meter feedback for all the inputs/outputs, as well as general fader controls for each output and global output configs and status. I'm not sure about the VU meters, but everything else sounds like it would map fine to normal mixer controls.
Is there some recommended way to expose this kind of matrix mixer interface to userspace? I think for something like this you pretty much have to rely on device-specific tools to make the UX manageable, so maybe hwdep... but at least exposing everything as an ALSA control would have the advantage of supporting save/restore with something like alsactl. So I don't really know what's the way to go here.
System settings/general status/output faders go via control transfers, while interface #1 has an interrupt IN endpoint (streaming state feedback, not very useful) and two bulk endpoints (matrix mixer control out, VU meter data in). There's another pair of bulk endpoints in interface #2 which I'm guessing are for firmware updates (I haven't looked at that part). So in principle it's not crazy to expose all the system controls/output faders as mixer controls in ALSA and leave interface #1 entirely unclaimed so a userspace program can directly configure the matrix mixer and access VU meter levels. There is a global mixer enable bit (controlled via ctl transfer), so if that is exposed as an ALSA control and disabled by default the interface will operate as a 1:1 in/out interface without needing any custom userspace to configure the mixer.
There's one other quirky thing: it also needs a way to set the sample rate as a mixer control, because you need to be able to configure the rate even when the PCM device is not open (since that affects stand-alone mixer operation). I imagine the right logic here would be to have a selector control for the system sample rate, and automatically change it and lock it when the PCM is opened with a given rate?
Any thoughts welcome ^^
As Geoffrey already suggested, the matrix size can be reduced since each kcontrol element can be an array, so the number of controls can be either column or row size of the matrix, which is well manageable.
The VU meter can be provided as volatile read-only controls, too.
So from the API-wise POV, it'll be a most straightforward implementation.
OTOH, if you need more efficiency (e.g. the control access is way too much overhead), it can be implemented freely via a hwdep device and your own ioctls or mmaps, too. But this is literally h/w dependent, and the API becomes too specific, no other way than using own tool, as a drawback.
Takashi
On Mon, Jul 01, 2024 at 04:17:11PM +0200, Takashi Iwai wrote:
As Geoffrey already suggested, the matrix size can be reduced since each kcontrol element can be an array, so the number of controls can be either column or row size of the matrix, which is well manageable.
Additionally, a snd_kcontrol structure can provide multiple control elements by its 'count' member. I call it 'control element set'. It can reduce allocation in kernel space. If the hardware in this case provides software interface to access to all source coefficients to one destination at once, it is suitable in the case.
For example, assuming the matrix mixer has 34 destination and 66 sources, they can be expressed by 34 control elements with 66 array elements. A single snd_kcontrol structure can provide them, as long as they have the same nature. The control elements are identified by index value.
Once I talked with Philippe Bekaert about the issue, then we found another issue about the way to distinguish both each control elements and the array members. The usage of ALSA control interface heavily relies on the name of control elements, while a single snd_kcontrol structure provides one name and all of the controls provided by it have the same name. We've investigated to use TLV (Type-Length-Array) function of ALSA control core to provide channel information about the sources and destinations, but no further work yet[1].
I think it better to have another care that in this case we have restriction for the size of array; e.g. 128 array elements for integer (long) type of value. The restriction is not the matter in your case.
The VU meter can be provided as volatile read-only controls, too.
So from the API-wise POV, it'll be a most straightforward implementation.
As a side note, the design of software interface for recent hardware requires floating point values for this kind of data, while it is not supported in ALSA control core and its userspace interface.
OTOH, if you need more efficiency (e.g. the control access is way too much overhead), it can be implemented freely via a hwdep device and your own ioctls or mmaps, too. But this is literally h/w dependent, and the API becomes too specific, no other way than using own tool, as a drawback.
[1] https://github.com/PhilippeBekaert/snd-hdspe/issues/13
Regards
Takashi Sakamoto
On Mon, 1 Jul 2024, Asahi Lina wrote:
[...]
The problem is that the device has a 66x34 matrix mixer, with up to 2048 cross points enabled at once. Exposing each cross point as an ALSA mixer control (similar to how mixer_scarlett2.c does it) would mean 2244 controls just for the mixer... which seems like a bit too much.
On top of that there is also VU meter feedback for all the inputs/outputs, as well as general fader controls for each output and global output configs and status. I'm not sure about the VU meters, but everything else sounds like it would map fine to normal mixer controls.
Is there some recommended way to expose this kind of matrix mixer interface to userspace?
I don't know if it's recommended practice, but the echo3g driver may be a prior art as it has a working matrix mixer for the Layla, and the corresponding echomixer in alsa-tools.
I think for something like this you pretty much have to rely on device-specific tools to make the UX manageable
For a full UX, perhaps. But the concept of a matrix mixer itself is general enough.
Perhaps there could be a UI tool which is self contained with one purpose, to control a 'standard' matrix mixer. It could be invoked alongside alsamixer or other mixer tool, and multiple times if there are multiple matrix mixers on a piece of hardware.
On Thu, 4 Jul 2024, at 4:55 AM, Mark Hills wrote: [...]
I think for something like this you pretty much have to rely on device-specific tools to make the UX manageable
For a full UX, perhaps. But the concept of a matrix mixer itself is general enough.
Perhaps there could be a UI tool which is self contained with one purpose, to control a 'standard' matrix mixer. It could be invoked alongside alsamixer or other mixer tool, and multiple times if there are multiple matrix mixers on a piece of hardware.
If this were generalised, it might be possible for the functionality to be exposed via the audio server as well, which allows for things like sandboxing, etc. to work nicely too.
-- Arun
participants (6)
-
Arun Raghavan
-
Asahi Lina
-
Geoffrey D. Bennett
-
Mark Hills
-
Takashi Iwai
-
Takashi Sakamoto