[alsa-devel] Alsa timing question
I'm working on an application where I'd like to have relatively fixed timing between calls to snd_pcm_writei. As an example, of this I used the sample pcm.c code from < http://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_8c-example.html#a6.... I placed a simple function to write out the time (from clock_getttime) before each call to snd_pcm_writei in write_loop. With a sample rate of 48kHz and a buffer time of 128000 and period time of 42667, what I see is that most calls to snd_pcm_writei are spaced about 50 ms apart. This makes some sense, but I was expecting the calls to be about 42.67 ms apart. However, about every 6th call, the time is just 200 us or so. Why is that?
Is there any simple way to make the calls spaced more closely to 42.67 ms apart? Do I need to do something more complicated to achieve this?
Ray
Raymond Toy wrote:
I'm working on an application where I'd like to have relatively fixed timing between calls to snd_pcm_writei.
Why?
[...] I see is that most calls to snd_pcm_writei are spaced about 50 ms apart. This makes some sense, but I was expecting the calls to be about 42.67 ms apart. However, about every 6th call, the time is just 200 us or so. Why is that?
In theory, when the device is configured for a specific period size, its interrupts (and therefore the application wakeups) will arrive spaced at this interval (modulo any scheduling delays).
However, if you're using the dmix plugin, the period size of your application's device will not necessarily be identical with the period size of the hardware device. To see the actual period size, look into /proc/asound/card?/pcm0p/sub0/hw_params.
Regards, Clemens
On Thu, Aug 25, 2011 at 12:15 AM, Clemens Ladisch clemens@ladisch.dewrote:
Raymond Toy wrote:
I'm working on an application where I'd like to have relatively fixed
timing
between calls to snd_pcm_writei.
Why?
It's a bit complicated. A different process generates the audio and places the data in a shared memory area. My process sends signals the other process to generate more data, I read the shared memory area (for the previously generated data). Sometime later, the other process fills the shared area with new samples. Thus, if the timing between calls is not fairly regular, I'll either reread the same data or miss the new data. (I'm not in control of how this works. I just have to deal with what I'm given.)
[...] I see is that most calls to snd_pcm_writei are spaced about 50 ms apart. This makes some sense, but I was expecting the calls to be about 42.67 ms apart. However, about every 6th call, the time is just 200 us or so. Why is that?
In theory, when the device is configured for a specific period size, its interrupts (and therefore the application wakeups) will arrive spaced at this interval (modulo any scheduling delays).
However, if you're using the dmix plugin, the period size of your application's device will not necessarily be identical with the period size of the hardware device. To see the actual period size, look into /proc/asound/card?/pcm0p/sub0/hw_params.
It says:
access: MMAP_INTERLEAVED format: S16_LE subformat: STD channels: 2 rate: 44100 (44100/1) period_size: 44096 buffer_size: 88192
Ray
Raymond Toy wrote:
On Thu, Aug 25, 2011 at 12:15 AM, Clemens Ladisch clemens@ladisch.dewrote:
Raymond Toy wrote:
I'm working on an application where I'd like to have relatively fixed timing between calls to snd_pcm_writei.
Why?
It's a bit complicated. A different process generates the audio and places the data in a shared memory area. My process sends signals the other process to generate more data, I read the shared memory area (for the previously generated data). Sometime later, the other process fills the shared area with new samples. Thus, if the timing between calls is not fairly regular, I'll either reread the same data or miss the new data.
The obvious way to resolve this problem would be to add a signal from the other process to your process. Even a serial number in the shared memory area would help detecting problems.
(I'm not in control of how this works. I just have to deal with what I'm given.)
"It is a vessel of fertilizer, and none may abide its strength."
[...] I see is that most calls to snd_pcm_writei are spaced about 50 ms apart. This makes some sense, but I was expecting the calls to be about 42.67 ms apart. However, about every 6th call, the time is just 200 us or so. Why is that?
In theory, when the device is configured for a specific period size, its interrupts (and therefore the application wakeups) will arrive spaced at this interval (modulo any scheduling delays).
However, if you're using the dmix plugin,
Your other mail shows you're using the PulseAudio plugin.
the period size of your application's device will not necessarily be identical with the period size of the hardware device.
PulseAudio doesn't use period interrupts, it uses its own timer that, apparently, runs at 50 ms.
In theory, you should use a period size that is an exact multiple of 50 ms, but in practice, this is not possible because the device's sample clock is not synchronized with the computer's timer.
You could try to use, e.g., 500 ms periods so that the inaccuracies of PA's timer result in smaller relative jitter in your own timing, but this will increase latency.
Try asking on the PA list how to get accurate period timing.
Regards, Clemens
Hi,
Raymond Toy wrote:
I'm working on an application where I'd like to have relatively fixed
timing
between calls to snd_pcm_writei.
Why?
It's a bit complicated. A different process generates the audio and places the data in a shared memory area. My process sends signals the other process to generate more data, I read the shared memory area (for the previously generated data). Sometime later, the other process fills the shared area with new samples. Thus, if the timing between calls is not fairly regular, I'll either reread the same data or miss the new data. (I'm not in control of how this works. I just have to deal with what I'm given.)
IMO, it's not a good idea to try to synchronize perfectly two processes by way of an interrupt. If the generating process has to remain untouched, you should probably bufferize its data in your own process before sending it to writei. It would add some latency, but make the data flow safer. Here is how it could work: 1- Your process writes a "magic" pattern at the end of the shared area 2- It sends a signal to the other process to make it generate data 3- It checks whether the magic pattern is still here 4- When changed, it means the data have been calculated. It saves them to an internal circular buffer 5- If the circular buffer is not full, it restarts at 1- 6-In parallel, when data can be written to the device through writei, they are taken from the circular buffer.
Hope this helps, Olivier
On Fri, Aug 26, 2011 at 1:29 AM, Olivier Guillion - Myriad < olivier@myriad-online.com> wrote:
Hi,
Raymond Toy wrote:
I'm working on an application where I'd like to have relatively fixed
timing
between calls to snd_pcm_writei.
Why?
It's a bit complicated. A different process generates the audio and
places the
data in a shared memory area. My process sends signals the other process
to
generate more data, I read the shared memory area (for the previously
generated
data). Sometime later, the other process fills the shared area with new samples. Thus, if the timing between calls is not fairly regular, I'll
either
reread the same data or miss the new data. (I'm not in control of how
this
works. I just have to deal with what I'm given.)
IMO, it's not a good idea to try to synchronize perfectly two processes by way of an interrupt. If the generating process has to remain untouched, you should probably bufferize its data in your own process before sending it to writei. It would add some latency, but make the data flow safer. Here is how it could work: 1- Your process writes a "magic" pattern at the end of the shared area 2- It sends a signal to the other process to make it generate data 3- It checks whether the magic pattern is still here 4- When changed, it means the data have been calculated. It saves them to an internal circular buffer
A nice idea, but I'm not sure it will work in my scenario. The "magic" pattern could potentially be real data (the buffer contains audio samples), so that would cause funny, hard-to-reproduce glitches. Plus, I think that when I request new data, something is always written.
Clemens idea of a counter at the beginning of the buffer would work nicely, but requires changes in the generating process.
Thanks,
Ray
Hi,
Raymond Toy rtoy@google.com wrote :
IMO, it's not a good idea to try to synchronize perfectly two processes by way of an interrupt. If the generating process has to remain untouched, you should probably bufferize its data in your own process before sending it to writei. It would add some latency, but make the data flow safer. Here is how it could work: 1- Your process writes a "magic" pattern at the end of the shared area 2- It sends a signal to the other process to make it generate data 3- It checks whether the magic pattern is still here 4- When changed, it means the data have been calculated. It saves them to an internal circular buffer
A nice idea, but I'm not sure it will work in my scenario. The "magic" pattern could potentially be real data (the buffer contains audio samples), so that would cause funny, hard-to-reproduce glitches.
You are theorically right, even if I doubt that much genuine audio buffer could end with, let's say : 0x8000 0x7FFF followed by "This is magic!!!" string :)
Plus, I think that when I request new data, something is always written.
I thought it was asynchronous, and you just send a signal to your generating process to ask it generate data.
Clemens idea of a counter at the beginning of the buffer would work nicely, but requires changes in the generating process.
Right. But if you can modify the generating process and recompile it, there is no more problem. Can you?
Olivier
On Sat, Aug 27, 2011 at 1:38 PM, Olivier Guillion - Myriad olivier@myriad-online.com wrote:
Raymond Toy rtoy@google.com wrote :
IMO, it's not a good idea to try to synchronize perfectly two processes by way of an interrupt. If the generating process has to remain untouched, you should probably bufferize its data in your own process before sending it to writei. It would add some latency, but make the data flow safer. Here is how it could work: 1- Your process writes a "magic" pattern at the end of the shared area 2- It sends a signal to the other process to make it generate data 3- It checks whether the magic pattern is still here 4- When changed, it means the data have been calculated. It saves them to an internal circular buffer
A nice idea, but I'm not sure it will work in my scenario. The "magic" pattern could potentially be real data (the buffer contains audio samples), so that would cause funny, hard-to-reproduce glitches.
You are theorically right, even if I doubt that much genuine audio buffer could end with, let's say : 0x8000 0x7FFF followed by "This is magic!!!" string :)
It can be made simpler. If anything like that, I tend to agree with Clemens idea if he meant each chunk (particular size of data written by the producer process and read by consumer process) starts with a, say 32bits, serial number. After reading, the reader resets the serial to 0, for the writer to get the message that the data has been consumed.
On Sat, Aug 27, 2011 at 1:08 AM, Olivier Guillion - Myriad < olivier@myriad-online.com> wrote:
Hi,
Raymond Toy rtoy@google.com wrote :
IMO, it's not a good idea to try to synchronize perfectly two processes
by way
of an interrupt. If the generating process has to remain untouched, you
should
probably bufferize its data in your own process before sending it to
writei.
It would add some latency, but make the data flow safer. Here is how it
could
work: 1- Your process writes a "magic" pattern at the end of the shared
area
2- It sends a signal to the other process to make it generate data 3-
It
checks whether the magic pattern is still here 4- When changed, it
means the
data have been calculated. It saves them to an internal circular buffer
A nice idea, but I'm not sure it will work in my scenario. The "magic" pattern could potentially be real data (the buffer contains audio
samples), so
that would cause funny, hard-to-reproduce glitches.
You are theorically right, even if I doubt that much genuine audio buffer could end with, let's say : 0x8000 0x7FFF followed by "This is magic!!!" string :)
Agreed.
Plus, I think that when I request new data, something is always written.
I thought it was asynchronous, and you just send a signal to your generating process to ask it generate data.
I'm not 100% sure, but the signal is really request to generate the next set of data and the "current" set is returned. If we call faster than necessary, we always get some data, plus calls to generate new data to be read next time. I suspect the new data is just written into a buffer, overwriting whatever was there. Hence, the problem I have if the callback requests are not regularly spaced according to the period size.
Clemens idea of a counter at the beginning of the buffer would work
nicely, but
requires changes in the generating process.
Right. But if you can modify the generating process and recompile it, there is no more problem. Can you?
Sure, but once I have to modify both, there's not point in a "magic" string. I can do what Clemens suggested.
Ray
In theory, when the device is configured for a specific period size, its
interrupts (and therefore the application wakeups) will arrive spaced at this interval (modulo any scheduling delays).
However, if you're using the dmix plugin, the period size of your application's device will not necessarily be identical with the period size of the hardware device. To see the actual period size, look into /proc/asound/card?/pcm0p/sub0/hw_params.
Would dmix allow me to control this better than pulse audio? (I'm still looking into how to get pulse audio to do what I want.)
Ray
Raymond Toy wrote:
However, if you're using the dmix plugin, the period size of your application's device will not necessarily be identical with the period size of the hardware device.
Would dmix allow me to control this better than pulse audio?
These parameters must be chosen by dmix before the first dmix device is used, so applications cannot control them directly.
The defaults for dmix's period_size, period_time, and periods are 1024, -1, and 16. They can be overridden by setting the configuration values defaults.dmix.DRIVER.period_size/period_time/periods, where DRIVER is the internal driver name (the name between the colon and the hyphen in /proc/asound/cards, or the file name in /usr/share/alsa/cards/).
Regards, Clemens
On Tue, Aug 30, 2011 at 12:49 AM, Clemens Ladisch clemens@ladisch.dewrote:
Raymond Toy wrote:
However, if you're using the dmix plugin, the period size of your application's device will not necessarily be identical with the period size of the hardware device.
Would dmix allow me to control this better than pulse audio?
These parameters must be chosen by dmix before the first dmix device is used, so applications cannot control them directly.
The defaults for dmix's period_size, period_time, and periods are 1024, -1, and 16. They can be overridden by setting the configuration values defaults.dmix.DRIVER.period_size/period_time/periods, where DRIVER is the internal driver name (the name between the colon and the hyphen in /proc/asound/cards, or the file name in /usr/share/alsa/cards/).
Thanks for the info. Can this be done programmatically? I've done some tests using dmix by setting up my .asoundrc appropriately, and dmix works very nicely once I got the dmix period size and buffer size to agree with my application. However for the application, I don't want to have to tell the user to set up dmix. (Yes, I know this is not nice, but we're doing this for testing right now, to make it easier for users to test this.)
Thanks,
Ray
Raymond Toy wrote:
Clemens Ladisch clemens@ladisch.de wrote: The defaults for dmix's period_size, period_time, and periods are 1024, -1, and 16. They can be overridden by setting the configuration values defaults.dmix.DRIVER.period_size/period_time/periods, where DRIVER is the internal driver name (the name between the colon and the hyphen in /proc/asound/cards, or the file name in /usr/share/alsa/cards/).
Thanks for the info. Can this be done programmatically?
Not really. You could define your own device, but it wouldn't work together with any open default dmix device.
However for the application, I don't want to have to tell the user to set up dmix.
If you know that you're using dmix, and which hardware device is actually used, you could use a PCM slave timer to get notifications of period interrupts. (The dmix plugin also uses this mechanism, but then rounds the events to the application's period size.)
For PulseAudio, there seems to be no choice but to use a period size that is somehow aligned to PA's internal timer granularity.
Regards, Clemens
participants (4)
-
Clemens Ladisch
-
Jassi Brar
-
Olivier Guillion - Myriad
-
Raymond Toy