[alsa-devel] Need help implementing an external PCM/control plugin pair
Hi,
I'm in the process of putting together a loudness compensation plugin for ALSA. The filtering part is implemented as an external PCM plugin and it is, more or less, ready, meaning that you can set its parameters via configuration options and it'll happily filter away in real time. I'm trying now to create a matching mixer plugin, to allow on-the-fly adjustment of the parameters, and I'm not sure what the best way of accomplishing that would be. My main concerns are:
1. I've figured out how to create a basic external control plugin, that would allow me to expose a set of controls, which the user can fiddle with, using alsamixer et al., but what would be the best way of communicating changes in the values to the PCM plugin? I could imagine using methods external to ALSA, such as a file with stored values, which the ctl plugin updates and which the PCM plugin monitors for changes, or perhaps I could try to open the mixer from inside the PCM plugin, set callbacks for each of its elements and enter a snd_mixer_wait/snd_mixer_handle_events loop, in a separate thread of course. Is there reason to prefer one way over the other? Perhaps there is a better way, I'm not aware of?
2. I'd like to support event subscription/notification, but I'm not sure how to make that work. My understanding (from searching the net and looking at ALSA's source), is that I need to provide some sort of file descriptor, which the subscribing code can poll, so as to block until there's an update. Since I have no obvious candidates for such a file, I've tried creating a pipe, handing one end to the control plugin upon initialization and then writing single bytes to the other end when there's a change in one of the controls. This works but, as far as I can see, the pipe is never actually read from, from the subscribing code, so that I would have to manually read from it somehwere, say in the read_event callback, in order to flush it. In the end, I'll probably use the descriptor from an inotify watch on the file where the plugin stores the control settings, but is this a valid approach overall? Is there some documentation specifying the exact way in which poll descriptors are handled by ALSA (in the case of an extrnal CTL plugin), that I've missed? If not, are my assumptions above (that the descriptor is only polled and that its contents are never actually read) correct?
3. Finally, and although this is not essential, I'd like to provide metadata for some controls, to support dB ranges, as that's the most natural way to set them. I see hints that this is supported for external control plugins (SND_CTL_EXT_ACCESS_TLV_READ/WRITE, the snd_ctl_ext_tlv_rw_t callback), but I'm not sure how to implement it. Is it documented somewhere? If not, could someone describe the basic approach?
Thanks, Dimitris
Hi,
On Feb 16 2018 19:12, Dimitris Papavasiliou wrote:
I'm in the process of putting together a loudness compensation plugin for ALSA. The filtering part is implemented as an external PCM plugin and it is, more or less, ready, meaning that you can set its parameters via configuration options and it'll happily filter away in real time. I'm trying now to create a matching mixer plugin, to allow on-the-fly adjustment of the parameters, and I'm not sure what the best way of accomplishing that would be. My main concerns are:
- I've figured out how to create a basic external control plugin, that
would allow me to expose a set of controls, which the user can fiddle with, using alsamixer et al., but what would be the best way of communicating changes in the values to the PCM plugin? I could imagine using methods external to ALSA, such as a file with stored values, which the ctl plugin updates and which the PCM plugin monitors for changes, or perhaps I could try to open the mixer from inside the PCM plugin, set callbacks for each of its elements and enter a snd_mixer_wait/snd_mixer_handle_events loop, in a separate thread of course. Is there reason to prefer one way over the other? Perhaps there is a better way, I'm not aware of?
- I'd like to support event subscription/notification, but I'm not sure
how to make that work. My understanding (from searching the net and looking at ALSA's source), is that I need to provide some sort of file descriptor, which the subscribing code can poll, so as to block until there's an update. Since I have no obvious candidates for such a file, I've tried creating a pipe, handing one end to the control plugin upon initialization and then writing single bytes to the other end when there's a change in one of the controls. This works but, as far as I can see, the pipe is never actually read from, from the subscribing code, so that I would have to manually read from it somehwere, say in the read_event callback, in order to flush it. In the end, I'll probably use the descriptor from an inotify watch on the file where the plugin stores the control settings, but is this a valid approach overall? Is there some documentation specifying the exact way in which poll descriptors are handled by ALSA (in the case of an extrnal CTL plugin), that I've missed? If not, are my assumptions above (that the descriptor is only polled and that its contents are never actually read) correct?
- Finally, and although this is not essential, I'd like to provide
metadata for some controls, to support dB ranges, as that's the most natural way to set them. I see hints that this is supported for external control plugins (SND_CTL_EXT_ACCESS_TLV_READ/WRITE, the snd_ctl_ext_tlv_rw_t callback), but I'm not sure how to implement it. Is it documented somewhere? If not, could someone describe the basic approach?
ALSA control core has a feature called as 'user-defined control element set'. This will help your work, in my opinion. For example, alsa-lib 'softvol' PCM plugin utilizes this feature to accomplishment configurable volumes by the others[1]. This feature supports metadata by TLV (Type-Length-Value) and eventing to subscribers as well.
The alsa-lib includes a test program for this feature, which I wrote[2]. This may help your work by its actual code, I think[2].
[1] http://git.alsa-project.org/?p=alsa-lib.git;a=blob;f=src/pcm/pcm_softvol.c;h... [2] http://git.alsa-project.org/?p=alsa-lib.git;a=blob;f=test/user-ctl-element-s...
Regards
Takashi Sakamoto
On 02/16/2018 12:38 PM, Takashi Sakamoto wrote:
ALSA control core has a feature called as 'user-defined control element set'. This will help your work, in my opinion. For example, alsa-lib 'softvol' PCM plugin utilizes this feature to accomplishment configurable volumes by the others[1]. This feature supports metadata by TLV (Type-Length-Value) and eventing to subscribers as well.
The alsa-lib includes a test program for this feature, which I wrote[2]. This may help your work by its actual code, I think[2].
Many thanks for your prompt reply Takashi! I'll study your example code and report back, should I need further guidance.
D.
I managed to implement the control aspect of the plugin using user control elements and it seems to work fine, including dB ranges and event metadata. Thanks again for your pointers. A couple further points:
1. I used an async ctl handler (snd_async_add_ctl_handler) to get notified about changes to the controls and it seems to work as intended. Are there any caveats I'm missing? Is there perhaps a more appropriate approach?
2. Is there some way to create a virtual ctl/mixer device, that'll inherit the card's controls and to which I can inject my own elements, without affecting the mixer of the card? There are two reasons why this would be desirable. From a practical aspect, it would make uninstalling the plugin easier, as right now, once the controls are added to the card's mixer and stored by alsactl, it is non-trivial to remove them again. Apart from that, it would make the interface a bit more consistent, as it would allow the plugin to present a separate virtual card of sorts, with its own PCM and mixer, instead of hijacking the card's mixer and injecting controls which are irrelevant, when the card is used on its own.
D.
participants (2)
-
Dimitris Papavasiliou
-
Takashi Sakamoto