[alsa-devel] idea: a reserve alsa plugin
Just had an idea which I'll write down here before I forget it again...and I'm not saying I'll implement this anytime soon either, but here goes:
There is a device reserve protocol between PulseAudio and JACK2 - when JACK needs the sound card, it'll send a dbus message to PulseAudio and grab a name in D-Bus.
However, there are plenty of applications who like to access ALSA directly, without going through JACK2 or PulseAudio. By making a "reserve" plugin, we could have this functionality for those apps too.
In practice, if the app usually opens "plughw:0" or "hw:0", it could instead open "reserve:plughw:0" or "reserve:hw:0" to also reserve the device from PulseAudio usage while the device is open. Meanwhile, PulseAudio is free to use other audio devices (which is not the case when using e g pasuspender).
How does that sound?
On Thu, 2013-05-02 at 12:55 +0200, David Henningsson wrote:
Just had an idea which I'll write down here before I forget it again...and I'm not saying I'll implement this anytime soon either, but here goes:
There is a device reserve protocol between PulseAudio and JACK2 - when JACK needs the sound card, it'll send a dbus message to PulseAudio and grab a name in D-Bus.
However, there are plenty of applications who like to access ALSA directly, without going through JACK2 or PulseAudio. By making a "reserve" plugin, we could have this functionality for those apps too.
In practice, if the app usually opens "plughw:0" or "hw:0", it could instead open "reserve:plughw:0" or "reserve:hw:0" to also reserve the device from PulseAudio usage while the device is open. Meanwhile, PulseAudio is free to use other audio devices (which is not the case when using e g pasuspender).
How does that sound?
Might be neat to do have desktops set up to do this whenever an ALSA device is opened (that is do it unconditionally when hw:X or plughw:X is opened).
-- Arun
On 05/02/2013 02:37 PM, Arun Raghavan wrote:
On Thu, 2013-05-02 at 12:55 +0200, David Henningsson wrote:
Just had an idea which I'll write down here before I forget it again...and I'm not saying I'll implement this anytime soon either, but here goes:
There is a device reserve protocol between PulseAudio and JACK2 - when JACK needs the sound card, it'll send a dbus message to PulseAudio and grab a name in D-Bus.
However, there are plenty of applications who like to access ALSA directly, without going through JACK2 or PulseAudio. By making a "reserve" plugin, we could have this functionality for those apps too.
In practice, if the app usually opens "plughw:0" or "hw:0", it could instead open "reserve:plughw:0" or "reserve:hw:0" to also reserve the device from PulseAudio usage while the device is open. Meanwhile, PulseAudio is free to use other audio devices (which is not the case when using e g pasuspender).
How does that sound?
Might be neat to do have desktops set up to do this whenever an ALSA device is opened (that is do it unconditionally when hw:X or plughw:X is opened).
Including when PA opens it? :P
I would prefer to do it explicitly, due to the possible overhead of talking to D-Bus.
One could possibly imagine it being done automatically with plughw, but definitely not with hw only because that's meant to be as low as you can possibly get.
Some codec registers need negative values. For example, sound/soc/codecs/tlv320aic32x4.c specifies this:
75 static DECLARE_TLV_DB_SCALE(tlv_step_0_5, 0, 50, 0);
78 SOC_DOUBLE_R_TLV("PCM Playback Volume", AIC32X4_LDACVOL, 79 AIC32X4_RDACVOL, 0, 0x30, 0, tlv_step_0_5),
It's incomplete. The actual range is from -63.5dB to +24dB in 0.5 dB steps. The 8-bit value is interpreted as a signed 8-bit integer, so to get -3dB the register value must be -6 or 0xFA.
For some reason, the author seemed to think that no one would ever want to lower the volume of the output, so he just limited the range to 0..24dB. Or, more likely, like me he did not have a clue how to configure this.
There are quite a few more registers like this, usually the range is limited in both negative and positive ranges.
How do I explain that to Alsa?
(and on a side note "amixer" doesn't accept negative values either)
Mike Looijmans wrote:
Some codec registers need negative values. For example, sound/soc/codecs/tlv320aic32x4.c specifies this:
78 SOC_DOUBLE_R_TLV("PCM Playback Volume", AIC32X4_LDACVOL, 79 AIC32X4_RDACVOL, 0, 0x30, 0, tlv_step_0_5),
It's incomplete. The actual range is from -63.5dB to +24dB in 0.5 dB steps. The 8-bit value is interpreted as a signed 8-bit integer, so to get -3dB the register value must be -6 or 0xFA.
How do I explain that to Alsa?
There are some ASoC helper macros that handle signed register fields, such as SOC_DOUBLE_S8_TLV and SOC_SINGLE_XR_SX. If those don't do what you want, you have to write your own.
(and on a side note "amixer" doesn't accept negative values either)
A workaround would be to set the control value first to 0 and then to the desired negative value ... but this bug needs fixing.
Regards, Clemens
On 05/06/2013 03:22 PM, Clemens Ladisch wrote:
Mike Looijmans wrote:
Some codec registers need negative values. For example, sound/soc/codecs/tlv320aic32x4.c specifies this:
78 SOC_DOUBLE_R_TLV("PCM Playback Volume", AIC32X4_LDACVOL, 79 AIC32X4_RDACVOL, 0, 0x30, 0, tlv_step_0_5),
It's incomplete. The actual range is from -63.5dB to +24dB in 0.5 dB steps. The 8-bit value is interpreted as a signed 8-bit integer, so to get -3dB the register value must be -6 or 0xFA.
How do I explain that to Alsa?
There are some ASoC helper macros that handle signed register fields, such as SOC_DOUBLE_S8_TLV and SOC_SINGLE_XR_SX. If those don't do what you want, you have to write your own.
SOC_DOUBLE_S8_TLV seems to be a misnamed hardware-specific macro. It provides no way to specify the location of the left/right bits so the macro name is misleading.
SOC_SINGLE_XR_SX is too new for my kernel. And from what I gather, I cannot use it anyway for my purposes. So the solution turns out to be "write my own". The SOC_DOUBLE_S8_TLV macro did provide a very nice starting point though.
Thanks for the pointer though, it turned out to be the key to a solution.
(and on a side note "amixer" doesn't accept negative values either)
A workaround would be to set the control value first to 0 and then to the desired negative value ... but this bug needs fixing.
I have no idea what you meant here.
Kind regards, Mike.
On Tue, May 28, 2013 at 01:46:15PM +0200, Mike Looijmans wrote:
On 05/06/2013 03:22 PM, Clemens Ladisch wrote:
Mike Looijmans wrote:
*ALWAYS* CC maintainers on mails and don't drop people from CCs either...
There are some ASoC helper macros that handle signed register fields, such as SOC_DOUBLE_S8_TLV and SOC_SINGLE_XR_SX. If those don't do what you want, you have to write your own.
SOC_DOUBLE_S8_TLV seems to be a misnamed hardware-specific macro. It provides no way to specify the location of the left/right bits so the macro name is misleading.
That's trivially fixable...
SOC_SINGLE_XR_SX is too new for my kernel. And from what I gather, I cannot use it anyway for my purposes. So the solution turns out to
What makes you gather this?
On 05/29/2013 09:42 PM, Mark Brown wrote:
On Tue, May 28, 2013 at 01:46:15PM +0200, Mike Looijmans wrote:
On 05/06/2013 03:22 PM, Clemens Ladisch wrote:
Mike Looijmans wrote:
*ALWAYS* CC maintainers on mails and don't drop people from CCs either...
I hereby promise to improve myself in that respect :)
There are some ASoC helper macros that handle signed register fields, such as SOC_DOUBLE_S8_TLV and SOC_SINGLE_XR_SX. If those don't do what you want, you have to write your own.
SOC_DOUBLE_S8_TLV seems to be a misnamed hardware-specific macro. It provides no way to specify the location of the left/right bits so the macro name is misleading.
That's trivially fixable...
SOC_SINGLE_XR_SX is too new for my kernel. And from what I gather, I cannot use it anyway for my purposes. So the solution turns out to
What makes you gather this?
What I actually needed was a SOC_DOUBLE_... thingy. Otherwise, the macro was pretty close, but I'm using a 2.6.37 kernel so just backporting the snd_soc_*_xr_sx methods would be more work than simply writing new info/set/get methods. I also considered the fact that only one driver is using the SOC_SINGLE_XR_SX macro a bit of a code smell, so I feared a similar experience as with SOC_DOUBLE_S8_TLV which was also introduced just for a single driver.
Mike.
The sort form of my question:
If I enable the "jiffies check", what is my driver doing wrong if I get this complaint on the kernel log while capturing:
PCM: hw_ptr skipping! (pos=13677, delta=876, period=6400, jdelta=0/17/0, hw_ptr=1241601/1241601)
The context:
I notice that this message originates at pcm_lib.c. I've been looking at that code for a while, but cannot figure out what its purpose is.
if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) ...
If that evaluates to "true", the logging as shown above is being shown.
But I can't grasp the meaning here. What's wrong then? I'd suspect it checks on the number of samples reported and the time elapsed, but I cannot figure out what the criteria is.
Not I did a bit of adding my own logging, so that I could also see whether things were at the IRQ or at "user" side of things, using a "[Q]" to indicate interrupt routine handling.
A normal overrun looks like this:
hwptr log: pcmC6D0c:0 [ ]: j=4294875206, pos=997/6400/25600, hwptr=8166401/8166400 hwptr log: pcmC6D0c:0 [Q]: j=4294875311, pos=6401/6400/25600, hwptr=8167397/8166400 hwptr log: pcmC6D0c:0 [ ]: j=4294875331, pos=7414/6400/25600, hwptr=8172801/8166400 hwptr log: pcmC6D0c:0 [Q]: j=4294875436, pos=12801/6400/25600, hwptr=8173814/8166400 hwptr log: pcmC6D0c:0 [ ]: j=4294875550, pos=18633/6400/25600, hwptr=8179201/8166400 hwptr log: pcmC6D0c:0 [Q]: j=4294875561, pos=19200/6400/25600, hwptr=8185033/8166400 hwptr log: pcmC6D0c:0 [Q]: j=4294875686, pos=2/6400/25600, hwptr=8185600/8166400 hwptr log: pcmC6D0c:0 [Q]: j=4294875811, pos=6401/6400/25600, hwptr=8192002/8192000 hwptr log: pcmC6D0c:0 [Q]: j=4294875936, pos=12802/6400/25600, hwptr=8198401/8192000 hwptr log: pcmC6D0c:0 [Q]: j=4294876061, pos=19208/6400/25600, hwptr=8204802/8192000 XRUN: pcmC6D0c:0
In this case, there were 4 periods of 6400 samples in the buffer, and 5 period-completion interrups were issued between userspace reads, so this is a normal case of the user not emptying the buffer quick enough.
The events I'm worried about are these:
hwptr log: pcmC6D0c:0 [ ]: j=4294960399, pos=7646/6400/25600, hwptr=953600/947200 hwptr log: pcmC6D0c:0 [Q]: j=4294960500, pos=12800/6400/25600, hwptr=954846/947200 hwptr log: pcmC6D0c:0 [ ]: j=4294960553, pos=15511/6400/25600, hwptr=960000/947200 hwptr log: pcmC6D0c:0 [Q]: j=4294960625, pos=19200/6400/25600, hwptr=962711/947200 hwptr log: pcmC6D0c:0 [ ]: j=4294960648, pos=20377/6400/25600, hwptr=966400/947200 hwptr log: pcmC6D0c:0 [Q]: j=4294960750, pos=0/6400/25600, hwptr=967577/947200 hwptr log: pcmC6D0c:0 [Q]: j=4294960875, pos=6400/6400/25600, hwptr=972800/972800 hwptr log: pcmC6D0c:0 [Q]: j=4294961000, pos=12800/6400/25600, hwptr=979200/972800 hwptr log: pcmC6D0c:0 [Q]: j=4294961125, pos=19201/6400/25600, hwptr=985600/972800 hwptr log: pcmC6D0c:0 [Q]: j=4294961250, pos=0/6400/25600, hwptr=992001/972800 XRUN: pcmC6D0c:0 hwptr log: pcmC6D0c:0 [Q]: j=9186, pos=6401/6400/25600, hwptr=718334/716800 hwptr log: pcmC6D0c:0 [ ]: j=9206, pos=7382/6400/25600, hwptr=723201/716800 hwptr log: pcmC6D0c:0 [Q]: j=9311, pos=12802/6400/25600, hwptr=724182/716800 hwptr log: pcmC6D0c:0 [ ]: j=9345, pos=14498/6400/25600, hwptr=729602/716800 hwptr log: pcmC6D0c:0 [Q]: j=9436, pos=19200/6400/25600, hwptr=731298/716800 hwptr log: pcmC6D0c:0 [ ]: j=9471, pos=20989/6400/25600, hwptr=736000/716800 hwptr log: pcmC6D0c:0 [Q]: j=9561, pos=0/6400/25600, hwptr=737789/716800 hwptr log: pcmC6D0c:0 [ ]: j=9588, pos=1368/6400/25600, hwptr=742400/742400 hwptr log: pcmC6D0c:0 [Q]: j=9686, pos=6401/6400/25600, hwptr=743768/742400 hwptr log: pcmC6D0c:0 [ ]: j=9686, pos=7271/6400/25600, hwptr=748801/742400 PCM: hw_ptr skipping! (pos=7271, delta=870, period=6400, jdelta=0/16/0, hw_ptr=748801/748801) hwptr log: pcmC6D0c:0 [Q]: j=18810, pos=12801/6400/25600, hwptr=1211784/1203200 hwptr log: pcmC6D0c:0 [ ]: j=18840, pos=14327/6400/25600, hwptr=1216001/1203200 hwptr log: pcmC6D0c:0 [Q]: j=18935, pos=19201/6400/25600, hwptr=1217527/1203200 hwptr log: pcmC6D0c:0 [ ]: j=18967, pos=20807/6400/25600, hwptr=1222401/1203200 hwptr log: pcmC6D0c:0 [Q]: j=19060, pos=0/6400/25600, hwptr=1224007/1203200 hwptr log: pcmC6D0c:0 [ ]: j=19088, pos=1402/6400/25600, hwptr=1228800/1228800 hwptr log: pcmC6D0c:0 [Q]: j=19185, pos=6400/6400/25600, hwptr=1230202/1228800 hwptr log: pcmC6D0c:0 [ ]: j=19213, pos=7801/6400/25600, hwptr=1235200/1228800 hwptr log: pcmC6D0c:0 [Q]: j=19310, pos=12801/6400/25600, hwptr=1236601/1228800 hwptr log: pcmC6D0c:0 [ ]: j=19310, pos=13677/6400/25600, hwptr=1241601/1228800 PCM: hw_ptr skipping! (pos=13677, delta=876, period=6400, jdelta=0/17/0, hw_ptr=1241601/1241601)
Anyone can make heads or tails of this?
Kind regards, Mike.
Mike Looijmans wrote:
If I enable the "jiffies check", what is my driver doing wrong if I get this complaint on the kernel log while capturing:
PCM: hw_ptr skipping! (pos=13677, delta=876, period=6400, jdelta=0/17/0, hw_ptr=1241601/1241601)
"hw_ptr skipping" means that the hw_ptr is skipping. Er, the value returned by the .pointer callback moves too far in a too short amount of time.
hwptr log: pcmC6D0c:0 [Q]: j=19310, pos=12801/6400/25600, hwptr=1236601/1228800 hwptr log: pcmC6D0c:0 [ ]: j=19310, pos=13677/6400/25600, hwptr=1241601/1228800
The interrupt and the userspace check happen at almost the same time (same jiffies value), but there is suddenly a jump of 876 frames.
How does your driver compute the .pointer return value? How does this value change?
Regards, Clemens
Hi David,
On Thursday 02 May 2013 12:55:05 David Henningsson wrote:
Just had an idea which I'll write down here before I forget it again...and I'm not saying I'll implement this anytime soon either, but here goes:
There is a device reserve protocol between PulseAudio and JACK2 - when JACK needs the sound card, it'll send a dbus message to PulseAudio and grab a name in D-Bus.
However, there are plenty of applications who like to access ALSA directly, without going through JACK2 or PulseAudio. By making a "reserve" plugin, we could have this functionality for those apps too.
In practice, if the app usually opens "plughw:0" or "hw:0", it could instead open "reserve:plughw:0" or "reserve:hw:0" to also reserve the device from PulseAudio usage while the device is open. Meanwhile, PulseAudio is free to use other audio devices (which is not the case when using e g pasuspender).
How does that sound?
If I understand correctly you are saying essentially this:
1. There is an existing device control protocol which works over DBus. 2. Your idea is to add a equivalent device control protocol using ALSA PCM plugin (well in fact not add but create a new proxy interface to it).
Is that right? If so, how do you provide this functionality to existing applications without teaching them about the reserve plugin?
And if you have to modify the applications, is the only advantage then that you do not have to add DBus dependency to them? Or a PA dependency to talk directly to the server? If so then this solution feels a bit kludgy.
Perhaps you would want an extension to snd_pcm_open (or whatever, I am going from vague memory here) to have something like "SND_PCM_EXCLUSIVE | SND_PCM_NOTIFY_BUSY_OPEN" mode (if the former is not implied) which would notify the original owner that the second application is attempting to open it. Perhaps using SIGURG or something while returing -EBUSY or something to the caller signalling they should retry. Not sure if this would be doable completely in userspace so I might be leading you toward a generic kernel/glibc solution here. :)
What about the policy control as to which applications are allowed to take over? It sounds sub optimal to allow any ALSA application which knows about this new plugin or other release mechanism to take over just like that. That would create a bit of a mess.
Regards,
Tvrtko
On 05/02/2013 04:28 PM, Tvrtko Ursulin wrote:
Hi David,
On Thursday 02 May 2013 12:55:05 David Henningsson wrote:
Just had an idea which I'll write down here before I forget it again...and I'm not saying I'll implement this anytime soon either, but here goes:
There is a device reserve protocol between PulseAudio and JACK2 - when JACK needs the sound card, it'll send a dbus message to PulseAudio and grab a name in D-Bus.
However, there are plenty of applications who like to access ALSA directly, without going through JACK2 or PulseAudio. By making a "reserve" plugin, we could have this functionality for those apps too.
In practice, if the app usually opens "plughw:0" or "hw:0", it could instead open "reserve:plughw:0" or "reserve:hw:0" to also reserve the device from PulseAudio usage while the device is open. Meanwhile, PulseAudio is free to use other audio devices (which is not the case when using e g pasuspender).
How does that sound?
If I understand correctly you are saying essentially this:
- There is an existing device control protocol which works over DBus.
- Your idea is to add a equivalent device control protocol using ALSA PCM
plugin (well in fact not add but create a new proxy interface to it).
Is that right? If so, how do you provide this functionality to existing applications without teaching them about the reserve plugin?
Many ALSA applications lets you specify the device string on e g the command line or through a configuration interface. So the end user would configure the application to use "reserve:plughw:0" instead of "plughw:0".
The applications that do not follow this will have to be taught, and they have a extremely simple way to implement it - just add "reserve:" do the device string.
And if you have to modify the applications, is the only advantage then that you do not have to add DBus dependency to them? Or a PA dependency to talk directly to the server? If so then this solution feels a bit kludgy.
Perhaps you would want an extension to snd_pcm_open (or whatever, I am going from vague memory here) to have something like "SND_PCM_EXCLUSIVE | SND_PCM_NOTIFY_BUSY_OPEN" mode (if the former is not implied) which would notify the original owner that the second application is attempting to open it. Perhaps using SIGURG or something while returing -EBUSY or something to the caller signalling they should retry. Not sure if this would be doable completely in userspace so I might be leading you toward a generic kernel/glibc solution here. :)
What about the policy control as to which applications are allowed to take over? It sounds sub optimal to allow any ALSA application which knows about this new plugin or other release mechanism to take over just like that. That would create a bit of a mess.
That is a valid point - but so far the only app that can *give away* a sound card is PulseAudio. All of the others (at least in the simplest scenario), can only *take* a sound card. If the card is already used by some app that can't give away its sound card, it's going to be -EBUSY as usual.
On Thursday 02 May 2013 16:37:47 David Henningsson wrote:
On 05/02/2013 04:28 PM, Tvrtko Ursulin wrote:
Hi David,
On Thursday 02 May 2013 12:55:05 David Henningsson wrote:
Just had an idea which I'll write down here before I forget it again...and I'm not saying I'll implement this anytime soon either, but here goes:
There is a device reserve protocol between PulseAudio and JACK2 - when JACK needs the sound card, it'll send a dbus message to PulseAudio and grab a name in D-Bus.
However, there are plenty of applications who like to access ALSA directly, without going through JACK2 or PulseAudio. By making a "reserve" plugin, we could have this functionality for those apps too.
In practice, if the app usually opens "plughw:0" or "hw:0", it could instead open "reserve:plughw:0" or "reserve:hw:0" to also reserve the device from PulseAudio usage while the device is open. Meanwhile, PulseAudio is free to use other audio devices (which is not the case when using e g pasuspender).
How does that sound?
If I understand correctly you are saying essentially this:
- There is an existing device control protocol which works over DBus.
- Your idea is to add a equivalent device control protocol using ALSA PCM
plugin (well in fact not add but create a new proxy interface to it).
Is that right? If so, how do you provide this functionality to existing applications without teaching them about the reserve plugin?
Many ALSA applications lets you specify the device string on e g the command line or through a configuration interface. So the end user would configure the application to use "reserve:plughw:0" instead of "plughw:0".
The applications that do not follow this will have to be taught, and they have a extremely simple way to implement it - just add "reserve:" do the device string.
Ah yes I forgot about this configurability. Which also means the question of central policy is sorted since it is explicit user configuration to steal a device.
It is still a bit kludgy for a PCM plugin to do this, but, it solves a real problem and does it cheaply so I think the result justifies the means in this case.
Regards,
Tvrtko
P.S. I am not active in this field but just dropping in due personal interest in this functionality.
participants (6)
-
Arun Raghavan
-
Clemens Ladisch
-
David Henningsson
-
Mark Brown
-
Mike Looijmans
-
Tvrtko Ursulin