St?phane Letz wrote:
>>>>
>>>
>>> I understand the need for 2 threads in "general" case, but here the
>>> requeriments are somewhat restricted. JACK server runs usually in
>>> *duplex* mode, using a card and using input and output devices of
>>> the *same* card. That is we assume both devices runs completely
>>> synchronized (otherwise we are not going to try having a system
>>> meant for completely synchronous execution to work reliably....)
>>
>> Duplex works fine with multiple threads. You can have two file
>> descriptors, one used by the reading thread (opened O_RDONLY) and one
>> used by the playback thread (opened O_WRONLY). You don't have to use
>> only a single file descriptor to make this work.
>>
>> You should *not* assume that the two sides (read vs. write) are
>> synchronized. On a single device, they might have different
>> fragmentation boundaries, and might use completely independent
>> clocks! (This is not as uncommon as you might think.)
>
> Do you mean with an input and output rate that would not stay in
> synch? What is the application supposed to do (for example in a input
> ==> some processing ==> output case) Resample?
In general, the sample rates *should* be the same, but there could be
some clock drift. The *fragment sizes* might also not be the same,
even if the clock rates are. Resampling shouldn't be necessary, but
tying the input quantum to the output quantum *might* be a mistake.
>
>>
>> I think the root of your troubles here are "assumptions". Well
>> written code won't have any such assumptions and will work well
>> regardless of the details of the device driver, or even the
>> underlying implementation.
>>
>>>
>>> That said, the system is supposed to consume and produce frames at
>>> the right time! Said in another way, if the execution of the client
>>> graph takes toom much time, then we have an "Xrun
>>> (overrun/underrun)" which is perfectly normal... What else could we
>>> do? We can possible smooth "occasionnal" CPU spike a bit by
>>> prefilling the output buffer, but in the general case, the audio
>>> cycle : Read / Process/ Write is not supposed to take more time than
>>> represented but the "buffer size" used in the graph processing.
>>
>> *That* is a bad assumption. You're trying to get a promise/guarantee
>> from the kernel that simply doesn't exist. And, you don't actually
>> need the promise if your code is written properly.
>>
>> To prevent overruns or underruns, you need to either restrict your
>> writes or reads to the size that can be performed without blocking
>> (which you can learn with the SNDCTL_DSP_GETOSPACE/ISPACE ioctls), or
>> use non-blocking IO. If you want to do this for both read and write
>> in a single thread, you'll need to also use poll().
>>
>> All the other approaches you're thinking of are hacks that *might*
>> work for some of the devices some of the time, but which are
>> technically "incorrect" and will almost certainly fail for *some*
>> devices, at least some of the time.
>
> Do we at least agree on the fact that a duplex processing application
> *cannot* use more than the time slice represented by the buffer-size
> value the application processing part is using, *on average* ?
>
> (I agree that for a *given* cycle this may be more, if the output
> buffer has some frames in advance to compensate...)
Yes, I think I would agree with that.
>
>>
>>>
>>> So to summarize, we prefill output a bit to avoid underruns, then
>>> the Read / Process/ Write is supposed to work at the right speed.
>>> And this one thread model simplify others parts: we don't need to
>>> use additonal "ring-buffers" or "queues" to move data between the
>>> thread that calls the graph "Process" and the one that would call
>>> Read/Write.
>>
>> Okay, one thread will work for you, but you need to use the poll or
>> ioctl or nonblocking approaches I've already pointed out then. My
>> sample code should help you out there.
>>
>> Actually, with your crazy processing considerations, probably what we
>> should do is look into getting you mmap(2) support, where you can
>> just run completely disconnected from the kernel code, and use
>> whatever speed and rate is most comfortable for your server.
>> Unfortunately, the mmap(2) syscall isn't supported by Boomer Phase I
>> (it will be in Phase II), and in any case it isn't portable to all
>> drivers. (4Front's code only supports it on Linux, and only for some
>> devices ... e.g. USB audio probably doesn't support it because USB
>> devices don't use memory mapped IO.)
>
> Yes I know mmap would be much better (used in JACK ALSA backend in
> Linux)
Hopefully you'll be able to use it in a couple of months when we have
Boomer Phase II ready for test.
-- Garrett
>
> Stephane Letz