Subject: audio read/write in nonblocking mode
To: None <current-users@netbsd.org>
From: Brook Milligan <brook@biology.nmsu.edu>
List: current-users
Date: 03/07/2002 08:56:33
I am trying to debug a problem with the audio system.  It appears that
when the audio device is set to nonblocking mode the driver does not
support interlaced reading and writing, even though the underlying
hardware supports full duplex operation.  The program below
demonstrates the problem on my system, which is running the head of
the NetBSD/i386 release branch as of 15 Feb 2002.

The program does a bunch of reads and gets the expected sequence of a
full block interspersed with nothing.  As soon as a write is inserted
into the sequence, however, all subsequent reads return nothing.  See
below for the output.

Is this generally a problem with the audio driver in nonblocking mode?

Is this a hardware issue specific to the esa/ac97 driver combination I
am using?

Is this a design limitation of the audio system?

How can it be fixed?

I would really appreciate it if people could run this little program,
or otherwise try to determine the answers to these questions.

Thanks very much for your help.

Cheers,
Brook

===========================================================================
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/audioio.h>
#include <sys/time.h>
#include <unistd.h>

typedef enum {
	DEV_PCMU, /* mu-law (8 bits) */
        DEV_PCMA, /*  a-law (8 bits) */
	DEV_S8,   /* signed 8 bits   */
        DEV_U8,   /* unsigned 8 bits */
	DEV_S16   /* signed 16 bits  */
} deve_e;

typedef struct s_audio_format {
  deve_e encoding;
  int    sample_rate; 		/* Should be one of 8000, 16000, 24000, 32000, 48000 	*/
  int    bits_per_sample;	/* Should be 8 or 16 					*/
  int    channels;  		/* Should be 1 or 2  					*/
  int    bytes_per_block;       /* size of unit we will read/write in 			*/
} audio_format;

static audio_format ifmt = { DEV_S16, 8000, 16, 1, 320 };
static audio_format ofmt = { DEV_S16, 8000, 16, 1, 320 };

static char audio_path[MAXPATHLEN] = "/dev/audio"; /* path to audio device */
static int            audio_fd = -1;		   /* active audio file descriptor */

/*
 * Try to open the audio device.
 * Return: valid file descriptor if ok, -1 otherwise.
 */

int
netbsd_audio_open(audio_format *ifmt, audio_format *ofmt)
{
	int fd;
	audio_info_t dev_info;
	int audio_props;
	int full_duplex = 1;

	fd = open(audio_path, O_RDWR | O_NONBLOCK);

	if (fd < 0) {
		/* 
		 * Because we opened the device with O_NONBLOCK, the wait
		 * flag was not updated so update it manually.
		 */
		fd = open(audio_path, O_WRONLY);
		if (fd < 0) {
			AUDIO_INITINFO(&dev_info);
			dev_info.play.waiting = 1;
			(void)ioctl(fd, AUDIO_SETINFO, &dev_info);
			close(fd);
		}
		return -1;
	}

	if (ioctl(fd, AUDIO_GETPROPS, &audio_props) < 0) {
		/* uh oh, this shouldn't happen */
		perror("netbsd_audio_open: getting properties");
		close(fd);
		return -1;
	}

	AUDIO_INITINFO(&dev_info);
	dev_info.monitor_gain = 0;
	dev_info.blocksize = ifmt->bytes_per_block;

	/* input port setup */
	dev_info.record.sample_rate = ifmt->sample_rate;
	dev_info.record.channels    = ifmt->channels;
        dev_info.record.precision   = ifmt->bits_per_sample;
	dev_info.record.gain	    = (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN) * 0.75;
	dev_info.record.port	    = AUDIO_MICROPHONE;
	dev_info.record.balance     = AUDIO_MID_BALANCE;
	switch (ifmt->encoding) {
	case DEV_PCMU:
		dev_info.record.encoding = AUDIO_ENCODING_ULAW;
		break;
	case DEV_PCMA:
		dev_info.record.encoding = AUDIO_ENCODING_ALAW;
		break;
	case DEV_S8:
		assert(ifmt->bits_per_sample == 8);
		dev_info.record.encoding = AUDIO_ENCODING_SLINEAR;
		break;
	case DEV_U8:
		assert(ifmt->bits_per_sample == 8);
		dev_info.record.encoding = AUDIO_ENCODING_ULINEAR;
		break;
	case DEV_S16:
		assert(ifmt->bits_per_sample == 16);
		dev_info.record.encoding = AUDIO_ENCODING_LINEAR;
		break;
	default:
		printf("ERROR: Unknown audio encoding in audio_open!\n");
                close(fd);
		return -1;
	}

	/* output port setup */
	dev_info.play.sample_rate = ofmt->sample_rate;
	dev_info.play.channels    = ofmt->channels;
        dev_info.play.precision   = ofmt->bits_per_sample;
	dev_info.play.gain	  = (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN) * 0.75;
	dev_info.play.balance     = AUDIO_MID_BALANCE;
	switch (ofmt->encoding) {
	case DEV_PCMU:
		assert(ofmt->bits_per_sample == 8);
		dev_info.play.encoding = AUDIO_ENCODING_ULAW;
		break;
	case DEV_PCMA:
		assert(ofmt->bits_per_sample == 8);
		dev_info.play.encoding = AUDIO_ENCODING_ALAW;
		break;
	case DEV_S8:
		assert(ofmt->bits_per_sample == 8);
		dev_info.play.encoding = AUDIO_ENCODING_SLINEAR;
		break;
	case DEV_U8:
		assert(ofmt->bits_per_sample == 8);
		dev_info.play.encoding = AUDIO_ENCODING_ULINEAR;
		break;
	case DEV_S16:
		assert(ofmt->bits_per_sample == 16);
		dev_info.play.encoding = AUDIO_ENCODING_LINEAR;
		break;
	default:
		printf("ERROR: Unknown audio encoding in audio_open!\n");
                close(fd);
		return -1;
	}

	if (ioctl(fd, AUDIO_SETINFO, &dev_info) < 0) {
		perror("netbsd_audio_open: setting parameters");
                close(fd);
		return -1;
	}

	/* set full duplex */
	if (ioctl(fd, AUDIO_SETFD, &full_duplex) < 0) {
		perror("netbsd_audio_open: setting full duplex");
                close(fd);
		return -1;
	}

	if (ioctl(fd, AUDIO_GETINFO, &dev_info) < 0) {
		perror("netbsd_audio_open: getting parameters");
                close(fd);
		return -1;
	}
	printf("blocksize=%u\n", dev_info.blocksize);
	printf("play.buffer_size=%u\n", dev_info.play.buffer_size);
	printf("record.buffer_size=%u\n", dev_info.record.buffer_size);

	audio_fd = fd;
	return audio_fd;
}

/*
 * Set options on audio device to be non-blocking.
 */

void
netbsd_audio_non_block()
{
	int on = 1;

	warnx("netbsd_audio_non_block()");
	if (audio_fd < 0)
		return;

	if (ioctl(audio_fd, FIONBIO, &on) < 0)
		perror("netbsd_audio_non_block");
	return;
}

int
main ()
{
  int i, j, k;
  int n = 10;

#define BUFFERLEN (4096)
  u_char buffer[BUFFERLEN];

  netbsd_audio_open(&ifmt, &ofmt);
  netbsd_audio_non_block();

  for (i = 0; i < 2; ++i) {
    for (j = 0; j < n; ++j) {
      for (k = 0; k < n; ++k) {
	printf("read(%d) = %d\n", BUFFERLEN, read(audio_fd, buffer, BUFFERLEN));
	usleep(60 * 1000);
      }
      printf("=====================\n");
      if (i > 0)
	printf("write(%d) = %d\n", BUFFERLEN, write(audio_fd, buffer, BUFFERLEN));
    }
  }
  close(audio_fd);
  return (0);
}

===========================================================================
audio: netbsd_audio_non_block()
blocksize=4096
play.buffer_size=65536
record.buffer_size=65536
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
=====================
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
=====================
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = 4096
=====================
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
=====================
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
=====================
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
=====================
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = 4096
=====================
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
=====================
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
=====================
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
=====================
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = 4096
=====================
write(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
=====================
write(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
=====================
write(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
=====================
write(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
=====================
write(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
=====================
write(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
=====================
write(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
=====================
write(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
=====================
write(4096) = 4096
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
read(4096) = -1
=====================
write(4096) = 4096