Re: Making OSSO default JACK option

From: Florian Walpen <dev_at_submerge.ch>
Date: Sun, 25 Aug 2024 18:24:21 UTC
On Tuesday, August 20, 2024 7:42:37 PM CEST Goran Mekić wrote:
> On 8/20/24 18:33, Florian Walpen wrote:
> > One of the achievements in the new JACK sosso backend is the decoupling of
> > JACK period from the OSS processing interval. The mmap'ed IO and a
> > rigorous
> > progress measurement let JACK and OSS process at their own pace, without a
> > huge buffer that hurts latency. This way it also gets rid of the timing
> > dependencies between playback and recording.
> > 
> > With some assistance from the kernel it should be possible to simplify the
> > progress measurements and implement the decoupling method in other sound
> > servers and applications on FreeBSD. We'll see how that goes.
> 
> I do intend to learn how mmaped IO is done, at least just enough to
> understand what you did for SOSSO. I guess before that I'm not in a
> position to understand it all.

In principle it's easy - you get some shared memory from mmap() where you can 
read recorded audio data from or write playback audio data to. The shared 
memory corresponds to the cyclic OSS buffer (bs) in the kernel. From time to 
time you check the _GETIPTR and _GETOPTR ioctls to get the current recording / 
playback position of the OSS buffer.
Keep track and compare that position with the position the application last 
read from / wrote to, and read the newly available recording data or write 
more playback data into the newly available space.

It's getting difficult when you want to measure the progress of the mmap'ed 
recording and playback position in time, accurately enough to manage latency 
within +/- 1ms. There's no wakeup signals from poll(), the application has to 
set timers for wakeup. As I explained to Christos in a review:

Time-based approach from simple (OSSv4 example) to sophisticated (sosso):

 - Let the application wake up every e.g. 4ms, and process all audio data 
available. Works with non-blocking read() / write() or mmap'ed io.

 - 4ms is too coarse to control the latency within +/- 1ms, wake up at 0.3ms 
intervals to synchronize with the driver writes / reads from the buffer.

 - 0.3ms is too many wakeups, only synchronize at certain intervals (Jack 
period, e.g. 4ms). Requires one wakeup before the driver writes / reads, one 
wakeup 0.3ms afterwards.

 - Make synchronizing more efficient (less wakeups) by predicting the next chunk 
of driver write / read.

 - Require multiple successful synchronizations at start, after under- and 
overruns, and other irregularities like USB data loss.

If implemented correctly, this method achieves very precise synchronization 
between the driver and system time, depending on the wakeup precision of the 
system scheduler. This is measured separately for recording and playback, and 
then one of them (usually recording) can be used as the master to correct 
drift on the other.

As mentioned, the progress measurement could be done in the kernel, just tag 
the OSS buffer progress with some timestamp and make that available to the 
application. It would greatly simplify the sosso approach described here.

> 
> > Good luck and success with your album recording!
> 
> We do intend to make it CC licensed (just like the first album), so it
> might be good showcase for FreeBSD in the future. We'll see.

Make sure to send me a link when it's ready.

Florian