On Wed, 28 Nov 2001, Paul Davis wrote:
> >non-continous transfers. The right loop, based on the period_size
> >transfers, should be like this:
> >
> > poll();
> > if ((pfd->revents & POLLIN) {
> > while (1) {
> > if (snd_pcm_avail_update(pcm) < period_size)
> > break;
> > count = period_size;
> > // transfer whole period (can be composed from more
> > // non-continuous parts)
>
> right, i understand that on some hardware, this is true. but there is
> no reason i know of for the trident h/w (perhaps for any hardware) to
> have a whole period composed of 2 non-continuous parts of size 1 and
> (period_size-1) frames. i can't believe that its an honest reflection
> of the state of the hardware.
There can be two reasons:
1) the interrupt acknowledge is a bit delayed
2) the task wakeup is a bit delayed
It makes perfectly sense, that the stream is some frames ahead when
task is woken up.
> the one complication that i can see that might cause this is the
> extent to which capture+playback hw pointers are in sync. what might
> be happening is that there really is a continuous period_size's worth
> of frames available on one stream, but in the other stream, the
> pointer doesn't quite reflect this, so we end up with the silly
> situation of having (period_size-1) + 1 frames. you'll note that in the
> code below, i poll only one descriptor.
Yes, that's right. Some hardware has trouble to pass the same pointers
when the streams are running in sync, but the difference should be only
a few frames.
> do you think this is possible/likely? if it is, do you think that its
> the job of the low level driver(s) to handle this? otherwise, its hard
> for me to see how an application (or library) can handle this
> efficiently in any full-duplex situations.
Yes, I think that only lowlevel driver code knows which streams are broken
and we can have only one interrupt acknowledge source.
> i say this because even if we poll on both stream descriptors, if one
> of them is out-of-step by 1 (or lets just say some very small number
> of frames), we have to go back into poll immediately, which will
> return immediately, and we end up busy waiting, which is not good at all.
> i don't see any other way to handle that situation. do you?
Nope.
> i'm going to add some debugging to check on the "pointers-not-in-sync
> theory", and i'll let you know what i find. since very few programs do
> full duplex operation, this may be a rather hidden problem.
>
> > while (avail > 0) {
Oops. There should be 'while (count > 0) {'.
> > frames = count;
> > snd_pcm_mmap_begin(pcm, areas, &offset, &frames
> >);
> > ....
> > snd_pcm_mmap_commit(pcm, offset, frames);
> > count -= frames;
> > }
> > }
> > }
>
> is there any difference between that and the actual loop (a few
> non-essentials removed to make it clearer):
Yes, there are differences:
> if (poll (&driver->pfd, 1, 1000) < 0) {
> .... error ...
> }
>
> if (driver->pfd.revents & POLLERR) {
> .... error ....
> }
>
> if (driver->pfd.revents == 0) {
> ... timeout ....
> }
>
> if ((capture_avail = snd_pcm_avail_update (driver->capture_handle)) < 0) {
> ... detect xruns ...
> }
>
> if ((playback_avail = snd_pcm_avail_update (driver->playback_handle)) < 0) {
> ... detect xruns ...
> }
>
> ... handle xruns ...
>
> avail = capture_avail < playback_avail ?
> capture_avail : playback_avail;
>
> while (avail) {
You are trying to process all available frames. It's not bad, but you can
assume (if poll returned a bit later) that there will be extra frames
available in the ring buffer. It's your problem with the sigle extra
frame, which started this discussion.
> /* driver->frames_per_cycle === period_size */
>
> capture_avail = (avail > driver->frames_per_cycle) ?
> driver->frames_per_cycle : avail;
> playback_avail = (avail > driver->frames_per_cycle) ?
> driver->frames_per_cycle : avail;
Note that my algorithm has two loops: One handles that only frames for the
whole period will be processed, the second is for non-contiguous frames
in one period. The second loop can be called maximally twice.
> /* THIS CALLS snd_pcm_mmap_begin() FOR BOTH STREAMS */
>
> if (alsa_driver_get_channel_addresses
> (driver,
> (snd_pcm_uframes_t *) &capture_avail,
> (snd_pcm_uframes_t *) &playback_avail,
> &capture_offset, &playback_offset) < 0) {
> return -1;
> }
>
> contiguous = capture_avail < playback_avail ?
> capture_avail : playback_avail;
>
> .... do interesting stuff with `contiguous' frames ...
>
> snd_pcm_mmap_commit (driver->capture_handle,
> capture_offset, contiguous);
> snd_pcm_mmap_commit (driver->playback_handle,
> playback_offset, contiguous);
>
> avail -= contiguous;
> }
I suggest to do this loop:
if (poll (&driver->pfd, 1, 1000) < 0) {
.... error ...
}
if (driver->pfd.revents & POLLERR) {
.... error ....
}
if (driver->pfd.revents == 0) {
... timeout ....
}
while (1) {
if ((capture_avail = snd_pcm_avail_update(driver->capture_handle)) <
0) {
... detect xruns ...
}
if ((playback_avail = snd_pcm_avail_update(driver->playback_handle)) <
0) {
... detect xruns ...
}
... handle xruns ...
avail = capture_avail < playback_avail ?
capture_avail : playback_avail;
/* here is very bad assumption, that all drivers are able */
/* todo full duplex with same period sizes, it would be better */
/* to choose smaller value from playback->frames_per_cycle */
/* and capture->frames_per_cycle */
if (avail < driver->frames_per_cycle)
break;
avail = driver->frames_per_cycle;
while (avail) {
/* THIS CALLS snd_pcm_mmap_begin() FOR BOTH STREAMS */
capture_avail = avail;
playback_avail = avail;
if (alsa_driver_get_channel_addresses(driver,
(snd_pcm_uframes_t *) &capture_avail,
(snd_pcm_uframes_t *) &playback_avail,
&capture_offset, &playback_offset) < 0) {
return -1;
contiguous = capture_avail < playback_avail ?
capture_avail : playback_avail;
.... do interesting stuff with `contiguous' frames ...
// well, it violates the current mmap_commit
// description, because contiguous can be
// less that playback_avail or capture_avail
// but ok, here is perfectly useable, so I change
// the mmap_commit description
snd_pcm_mmap_commit (driver->capture_handle,
capture_offset, contiguous);
snd_pcm_mmap_commit (driver->playback_handle,
playback_offset, contiguous);
avail -= contiguous;
}
}
Jaroslav
-----
Jaroslav Kysela <[EMAIL PROTECTED]>
SuSE Linux http://www.suse.com
ALSA Project http://www.alsa-project.org
_______________________________________________
Alsa-devel mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/alsa-devel