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