On Wed, Oct 06, 2010 at 10:48:59PM +0200, Alexandre Ratchov wrote: > On Wed, Oct 06, 2010 at 04:35:14PM +0200, Remco wrote: > > >> ... endless clicking noise intermixed with music ... > > > > > sounds like the mp "delayed interrupt notification" issue that wreaks > > > havoc on audio. > > > > > > > I don't know if I'm telling anything new and whether this is useful. > > (my coding skills may be lacking as well) > > > > I seem to be able to reproduce the following reliably: > > > > When I create a producer/consumer loop using circular buffers where the > > producer (WAVE file) is much faster than the consumer (audio device), using > > polling to see whether there is work to do for either the producer or > > consumer: > > Note that you can't poll() a file. It always returns POLLIN. So > there's no way to implement a true non-blocking program with a > poll()-based event loop; the program will always block for typically > around 50-100ms. This is not a problem as long as audio buffers are > large enough. > > > - in blocking mode everything seems fine: > > > > The producer fills the buffers, if it overruns it's paused. The consumer > > takes the buffers one at the time and when it's about to underrun it > > resumes the producer. This seems to produce uninterrupted sound just fine. > > > > fine, this works because the producer manages to fill the buffer fast > enough, i.e. before the device buffer underruns. This is part of the > ``my producer is fast'' hypothesis. > > > - in non-blocking mode: > > > > This basically follows the same procedure as with blocking mode. However, it > > appears that the audio device becomes available prematurely. (within a few > > ms independent of the amount of data that was written) > > e.g.: when I try to send the audio data in ~20ms chunks, I hear a lot of > > clicks, and the audio appears to be sped up somewhat. > > Increasing the length of the audio chunks (~2s) makes it sound like it is in > > fast forward. Just a few ms of each 2s buffer appear to be played before > > advancing to the next buffer skipping over the song really fast, playing > > only tiny fragments of the song. > > sounds that fifo pointers are not correct, may by the return value of > write() (or sio_write() if you're using libsndio) are not taken into > account correctly. > > > It doesn't necessarily seem to be related to sndio/aucat. I seem to get the > > same results with audio(4). > > > > I was expecting the writes to the audio device to fail (EAGAIN) if the > > previous buffer isn't done playing yet. Basically I don't expect POLLOUT to > > be set until all software buffers have been drained and the data has been > > written to the device completely. > > write(2) returns EAGAIN if the device buffer is full (no space > available) and POLLOUT is set as soon as there's some space available. > This behaviour is desirables. If all the data was processed by the > device it would underrun, and the sound would stutter.
to expand ... write() doesn't actually send anything to the device. it puts the data in a kernel buffer. the hardware has been configured to know where the buffer is, and it reads the data there, constantly moving through the buffer. write() isn't instantaneous, and neither is poll(). so there must always be some new data available in the buffer, or the device would be reading invalid data while poll() wakes up and new data is being written to the buffer, if poll() only returned POLLOUT when the buffer was "empty". > What you describe is surprising since using a poll() and non-blocking > write() is somewhat equivalent to a blocking write. somehwat, or rather, can be. really depends on how the writing loops are written. audio is subject to flow control, so the following from write(2) applies: When using non-blocking I/O on objects such as sockets that are subject to flow control, write() and writev() may write fewer bytes than requested; the return value must be noted, and the remainder of the operation should be retried when possible. now, the "write a full buffer, wait until I can write again, write a full buffer, wait, etc" model is simple, and can be achieved. however, as explained above, the "full buffer" that you will write cannot be the full kernel audio buffer. the kernel audio buffer is split into two or more blocks. the buffer is processed one block at a time*. so, if your "full buffer" is exactly the size of a block, and you always write a "full buffer", when poll() returns POLLOUT, you will be able to write another "full buffer" ... but you should still check the return value of write(), to be sure the write didn't get interrupted or returned short for some other unexpected reason. * I said earlier, that the hardware is continuously moving through the buffer, and now I'm saying the buffer is processed block at a time, which seem to contradict. the hardware *is* constantly moving through the buffer. the hardware is also configured to interrupt every time a block boundary is crossed. this is an efficient way for audio(4) to be able to track the current position. otherwise the audio layer would have to constantly query the hardware's position, which could eat a lot of cycles. this makes it appear that the buffer is processed one block (one "full buffer") at a time ... -- jake...@sdf.lonestar.org SDF Public Access UNIX System - http://sdf.lonestar.org