Re: Question on sndio 1.8.1: SIO_PLAY and SIO_REC

From: Alexandre Ratchov <alex_at_caoua.org>
Date: Thu, 19 May 2022 09:50:48 +0200
On Thu, May 19, 2022 at 07:43:22AM +0200, Tru Hobbyist wrote:
> Hi all,
> 
> I was tinkering with sndio on a fresh OpenBSD 7.1 install and tried to use the
> synchronous mode by using a simple application that opens a handle (sio_open())
> with both SIO_PLAY and SIO_REC modes set (mode = SIO_PLAY | SIO_REC).
> 
> >From the documentation: it should now be possible to record from the
> microphone and get this recording immediately output through my
> speakers, pseudo-simultaneously.
> 
> Problem is, it does not work. The symptoms are: it freezes at sio_psleep() and
> nothing happens.
> 
> The same setup (OS, microphone, speakers) works correctly when used in either
> SIO_PLAY or SIO_REC mode. I have also tried to record first and then playback
> the recorded data (using two sio_open(), sio_start(), sio_read/write(), ...
> sio_stop(), sio_close() sequences) in the same program. Works flawlessly.
> 
> What am I doing wrong?
> 

Hi,

I'm can't tell for sure without looking at the code, but most probably
there's a deadlock: recording direction waits play direction to be
ready to start, but play direction can't start as there are no
recorded samples to play (yet).

Playback and recording always start simultaneously and stay
synchronized. But in order to start, the device's play buffer must be
full (there must be something to play to start playback).

This pseudo-code is deadlock-free:

	hdl = sio_open(..., SIO_PLAY | SIO_REC, 0);

	sio_initpar(&par);
	...
	sio_setpar(hdl, &par);
	...
	sio_getpar(hdl, &par);

	/*
	 * send a full buffer of silence for device to start
	 */
	sio_write(hdl, silence, par.bufsz * par.pchan * par.bps);

	while (1) {
		/*
		 * record a single block, we need the loop because
		 * sio_read() use Unix read() semantics and may
		 * return short reads
		 */
		todo = par.round * par.rchan * par.bps;
		data = rec_buf;
		while (todo > 0)
			n = sio_read(hdl, data, todo);
			data += n;
			todo -= n;
		}

		/*
		 * do some processing (ex. just copy rec->play
		 * assuming the same number of channels)
		 */
		memcpy(play_buf, rec_buf, par.round * par.rchan * par.bps);

		/*
		 * sio_write() blocks until full block is sent
		 */
		sio_write(hdl, play_buf, par.round * par.pchan * par.bps);
	}

This is how a programs to apply effects would work. The latency is:

	par.bufsz / par.rate

which lower bound is a fixed system parameter (170ms in current
defaults). If you need to lower latency further, use sndiod -b option
to set it to a value suitable for your system.

Last note: play and rec direction always stay in sync and recover
after underruns. The synchronous mode (aka SIO_SYNC) is not useful
here and may create problems.

HTH,

-- Alexandre
Received on Thu May 19 2022 - 09:50:48 CEST

This archive was generated by hypermail 2.3.0 : Fri May 20 2022 - 01:33:47 CEST