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

Reply via email to