/**********************************************************************
audio play & record (Linux, Solaris, IRIX)

$Id: playrec.c,v 1.31 2003/01/19 23:17:09 purnhage Exp $

Source file: playrec.c

(c) Heiko Purnhagen 1998-2003
e-mail: purnhage@tnt.uni-hannover.de
WWW:    http://www.tnt.uni-hannover.de/~purnhage/

This module is available under the terms of the GNU Lesser General
Public License. See file COPYING-LGPL for further details. All usual
disclaimers apply!

Authors:
HP    Heiko Purnhagen, Uni Hannover <purnhage@tnt.uni-hannover.de>
NM    Nikolaus Meine, Uni Hannover (c/o Heiko Purnhagen)
BE    Bernd Edler, Uni Hannover <edler@tnt.uni-hannover.de>

Changes:
16-sep-98   HP/NM   born, based on auplay.c and aurec.c
            HP/BE   ... and play_list.c
17-sep-98   HP      Solaris & IRIX
07-jan-99   HP      warning if no audio device support
08-jan-99   HP      audio device selection, fixed Solaris PROpenRead()
26-jan-99   HP      SGI: AL_DIGITAL_INPUT_RATE
18-feb-99   HP      write 0 before closing play device
23-feb-99   HP      fill up to n*numChan before closing play device
                    (prevents lock on Linux)
11-nov-99   HP      fixed debug output in PROpenWrite: device=
14-nov-99   HP      removed fileno(), using open() instead fopen()
19-nov-99   HP      added PRWriteBuffer(), PRWriteFilled(), PRNap()
21-nov-99   HP      fixed bug in PRNap() / work-around for alsa-0.3.0 startup
27-nov-99   HP      fixed PRWriteFilled for old OSS/ES1688 (2.0.35)
28-dec-99   HP      fixed PRReadBuffer() return value (Linux/Solaris)
15-feb-00   HP      PRReadData returns 0 on error
                    added read/write error warnings
07-jun-00   HP      fixed PRWriteFilled for buffer underrun (SB128PCI)
27-Jul-2001 HP      fixed SGI CLK_TCK
11-Oct-2001 CF      added Win32 support
05-Nov-2001 HP      added SNDCTL_DSP_GETODELAY support in PRWriteFilled()
                    (disabled by -DNO_GETODELAY)
19-Dec-2001 HP      added SNDCTL_DSP_CHANNELS support (Linux > 2 ch)
                    PRnoWarnings (debugMode==-1)
04-Jan-2002 HP      added strerror(errno)
                    PRReadData()/PRWriteData() now return error flag
                    fixed handling of read()==0, write()==0 (Linux/Solaris)
01-Feb-2002 HP      added PROpenReadFrag(), PROpenWriteFrag()
20-Mar-2002 HP      fixed bug/handling of read()==-1, write()==-1
31-May-2002 HP      PROpenXxxFrag() use playrec defaults, not driver defaults
**********************************************************************/

/* Audio play & record for Linux, Solaris, IRIX. */
/* Sample format: 16 bit twos complement, uniform quantisation */

/* Multi channel data is interleaved: l0 r0 l1 r1 ... */


#ifdef WIN32
#include <windows.h>
#include <mmsystem.h>
#define NREAD_WAVEHDR 10
#define WAVE_MAPPER_ID "-1";
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "playrec.h"
#include "common.h"

#ifdef Linux
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/soundcard.h>
#endif

#ifdef Solaris
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/audioio.h>
#endif

#ifdef IRIX
#include <unistd.h>
#include <dmedia/audio.h>
#endif


/* ---------- declarations ---------- */

#ifndef MSVC
#ifndef WIN32
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
#endif
#endif

#define IRIXSIZE (2.0)		/* IRIX audio buffer size (sec) */


/* ---------- declarations (structures) ---------- */

struct PRStreamStruct		/* play/rec stream handle */
{
#ifdef Linux
  int fd;
  long maxCinfoBytes;
#endif

#ifdef Solaris
  int fd;
#endif

#ifdef IRIX
  ALconfig rt_config;
  ALport rt_audio_p;
#endif

#ifdef WIN32
  WAVEHDR **waveHdr; /* WAVEHDR structure defines the header */
  /* used to identify waveform-audio buffer */
  HWAVEIN  hWaveIn;
  HWAVEOUT hWaveOut;
  WAVEFORMATEX waveFormat;
  /*  The WAVEFORMATEX structure defines the
      format of waveform-audio data :
    
      typedef struct { 
      unsigned short int wFormatTag;
      unsigned short int nChannels; 
      unsigned int       nSamplesPerSec; 
      unsigned int       nAvgBytesPerSec; 
      unsigned short int nBlockAlign; 
      unsigned short int wBitsPerSample; 
      unsigned short int cbSize;	
      } WAVEFORMATEX;  */

  WAVEOUTCAPS waveOutDevCaps;
  /* The WAVEOUTCAPS structure describes
     the capabilities of a waveform-audio
     output device:
   
     typedef struct {
     unsigned short int   wMid;
     unsigned short int   wPid; 
     unsigned int	     vDriverVersion;
     char		     szPname[MAXPNAMELEN]; 
     unsigned int         dwFormats;     
     unsigned short int   wChannels;
     unsigned short int   wReserved1; 
     unsigned int         dwSupport; 
     } WAVEOUTCAPS;  */
 
  WAVEINCAPS waveInDevCaps;
  /* The WAVEINCAPS structure describes 
     the capabilities of a waveform-audio 
     input device:

     typedef struct { 
     unsigned short int   wMid;
     unsigned short int   wPid; 
     unsigned int	     vDriverVersion;
     char		     szPname[MAXPNAMELEN]; 
     unsigned int         dwFormats;     
     unsigned short int   wChannels;
     unsigned short int   wReserved1; 
     }  WAVEINCAPS; */
 
  int reqDeviceID;
  /* unsigned int nBufCount,nWaveHdr; */
  int nBufCount,nWaveHdr;
#endif

  long currentSample;		/* number of samples read/written */
  int write;			/* write (not read) flag */
  int numChannel;		/* number of channels */
  float fSample;		/* sampling frequency [Hz] */
  long bufNumSample;		/* play buffer size */
};


/* ---------- variables ---------- */

static int PRdebugLevel = 0;	/* debug level */
static int PRnoWarnings = 0;	/* be silent ... */


/* ---------- functions ---------- */


#ifdef WIN32
void W32GetWaveCaps(unsigned long dwFormats, float *fSample, int *numChannel) {
  switch (dwFormats) {
  case WAVE_FORMAT_1M08: /* 11.025 kHz, mono, 8-bit   */
    *fSample = 11025;
    *numChannel = 1;
    break;
  case WAVE_FORMAT_1M16: /* 11.025 kHz, mono, 16-bit  */
    *fSample = 11025;
    *numChannel = 1;
    break;
  case WAVE_FORMAT_1S08: /* 11.025 kHz, stereo, 8-bit */
    *fSample = 11025;
    *numChannel = 2;
    break;
  case WAVE_FORMAT_1S16: /* 11.025 kHz, stereo, 16-bit*/
    *fSample = 11025;
    *numChannel = 2;
    break;
  case WAVE_FORMAT_2M08: /* 22.05 kHz, mono, 8-bit  */
    *fSample = 22050;
    *numChannel = 1;
    break;
  case WAVE_FORMAT_2M16: /* 22.05 kHz, mono, 16-bit */
    *fSample = 22050;
    *numChannel = 1;
    break;
  case WAVE_FORMAT_2S08: /* 22.05 kHz, stereo, 8-bit */
    *fSample = 22050;
    *numChannel = 2;
    break;
  case WAVE_FORMAT_2S16: /* 22.05 kHz, stereo, 16-bit */
    *fSample = 22050;
    *numChannel = 2;
    break;
  case WAVE_FORMAT_4M08: /* 44.1 kHz, mono, 8-bit */
    *fSample = 44100;
    *numChannel = 1;
    break;
  case WAVE_FORMAT_4M16: /* 44.1 kHz, mono, 16-bit */
    *fSample = 44100;
    *numChannel = 1;
    break;
  case WAVE_FORMAT_4S08: /* 44.1 kHz, stereo, 8-bit */
    *fSample = 44100;
    *numChannel = 2;
    break;
  case WAVE_FORMAT_4S16: /* 44.1 kHz, stereo, 16-bit */
    *fSample = 44100;
    *numChannel = 2;
    break;
  }
}
#endif


/* PRInit() */
/* Init audio play & record. */

void PRInit (
  int debugLevel)		/* in: debug level */
				/*     -1=no warnings 0=off  1=basic  2=full */
{
  if(debugLevel==-1) {
    PRdebugLevel = 0;
    PRnoWarnings = 1;
  }
  else {
    PRnoWarnings = 0;
    PRdebugLevel = debugLevel;
  }
  if (PRdebugLevel >= 1) {
    printf("PRInit: debugLevel=%d\n",PRdebugLevel);
  }
}


/* PRNap() */
/* nap for ms milliseconds */

void PRNap (
  int ms)			/* in: time to nap [ms] */
{
#ifdef Linux
  struct timeval tv;

  tv.tv_sec = 0;
  tv.tv_usec = min(999999,max(1,ms)*1000);
  select(0,NULL,NULL,NULL,&tv);
#endif  

#ifdef Solaris
  struct timeval tv;

  tv.tv_sec = 0;
  tv.tv_usec = min(999999,max(1,ms)*1000);
  select(0,NULL,NULL,NULL,&tv);
#endif  

#ifdef IRIX
  sginap(max(1,(ms*CLK_TCK)/1000));	/* CLK_TCK = 100 ticks/sec */
#endif

#ifdef WIN32
  Sleep(ms);
#endif
}


/* PROpenRead() */
/* Open play/rec stream for reading. */

PRStream *PROpenRead (
  char *device,			/* in: device name (NULL = default device) */
  int *numChannel,		/* in/out: number of channels */
  float *fSample)		/* in/out: sampling frequency [Hz] */
				/*  numChannel & fSample: */
				/*  in: requested configuration */
				/*      (SGI: 0 = use audio panel settings) */
				/*  out: selected configuration */
				/*       (set according to audio */
				/*        device capabilities) */
				/* returns: */
				/*  play/rec stream (handle) */
				/*  or NULL if error */
{
  return PROpenReadFrag(device,numChannel,fSample,NULL,NULL);
}


/* PROpenWrite() */
/* Open play/rec stream for writing. */

PRStream *PROpenWrite (
  char *device,			/* in: device name (NULL = default device) */
  int *numChannel,		/* in/out: number of channels */
  float *fSample)		/* in/out: sampling frequency [Hz] */
				/*  numChannel & fSample: */
				/*  in: requested configuration */
				/*      (SGI: 0 = use audio panel settings) */
				/*  out: selected configuration */
				/*       (set according to audio */
				/*        device capabilities) */
				/* returns: */
				/*  play/rec stream (handle) */
				/*  or NULL if error */

{
  return PROpenWriteFrag(device,numChannel,fSample,NULL,NULL);
}


/* PROpenReadFrag() */
/* Open play/rec stream for reading. */

PRStream *PROpenReadFrag (
  char *device,			/* in: device name (NULL = default device) */
  int *numChannel,		/* in/out: number of channels */
  float *fSample,		/* in/out: sampling frequency [Hz] */
  int *fragsize,		/* in/out: fragment size (samples) */
  int *fragstotal)		/* in/out: number of fragments */
				/*  numChannel & fSample: */
				/*  in: requested configuration */
				/*      (SGI: 0 = use audio panel settings) */
				/*  out: selected configuration */
				/*       (set according to audio */
				/*        device capabilities) */
				/*  frags: NULL or 0 = dflt/unkown */
				/* returns: */
				/*  play/rec stream (handle) */
				/*  or NULL if error */
{
  PRStream *s;

#ifdef Linux
  int err,tmp,tmpf,tmperr;
  audio_buf_info ainfo;
#endif  

#ifdef Solaris
  audio_info_t ainfo;
  int err;
#endif  

#ifdef IRIX
  long sr[2];
  long size;
  int err;
#endif  

#ifdef WIN32
  int err;
  int i,nDevice;	/* total # of devices available */
  char errStr[1024];
#endif

  if (PRdebugLevel)
    printf("PROpenRead: device=\"%s\" numChannel=%d fSample=%.1f\n",
	   device?device:"(default)",*numChannel,*fSample);

  if ((s=(PRStream*)malloc(sizeof(PRStream)))==NULL)
    CommonExit(-1,"PROpenRead: Can not allocate memory");
  s->currentSample = 0;
  s->write = 0;

#ifndef Linux
  /* set frags to unkown for non-Linux systems ... */
  if (fragsize)
    *fragsize = 0;
  if (fragstotal)
    *fragstotal = 0;
#endif

#ifdef Linux
  if (!device)
    device = "/dev/dsp";
  if ((s->fd=open(device,O_RDONLY,0))==-1) {
    if (!PRnoWarnings)
      CommonWarning("PROpenRead: Can not open \"%s\"",device);
    free(s);
    return NULL;
  }

  if (!fragsize || *fragsize==0) {
    /* defaults: min 512 bytes / 40 ms */
    tmp = 9;
    tmpf = (int)(0.040*(*fSample)+.5)*2*(*numChannel);
  }
  else {
    tmp = 4;
    tmpf = *fragsize*2;
  }
  if (tmpf<1)
    tmpf = 1;
  while (1<<tmp < tmpf)
    tmp++;
  if (fragstotal && *fragstotal>0) {
    if (*fragstotal>0x7fff)
      *fragstotal = 0x7fff;
    tmp |= *fragstotal<<16; 
  }
  else
    tmp |= 0x7fff0000;
  err = 0;
  /* HP20020531 use playrec defaults, not driver defaults */
  /* if (fragsize && *fragsize>0 || fragstotal && *fragstotal>0) */
  err = err || ioctl(s->fd,SNDCTL_DSP_SETFRAGMENT,&tmp);

  tmp = AFMT_S16_LE;			/* aka 16 */
  err = err || ioctl(s->fd,SNDCTL_DSP_SAMPLESIZE,&tmp);
  if (tmp != AFMT_S16_LE) {
    if (!PRnoWarnings)
      CommonWarning("PROpenRead: 16 bit audio not supported");
    free(s);
    return NULL;
  }
  tmperr = 1;
  if (*numChannel<=2) {
    /* try SNDCTL_DSP_STEREO first */
    tmp = (*numChannel>1)?1:0;
    tmperr = ioctl(s->fd,SNDCTL_DSP_STEREO,&tmp);
    if (!tmperr)
      *numChannel = tmp+1;
  }
  if (tmperr) {
    tmp = *numChannel;
    err = err || ioctl(s->fd,SNDCTL_DSP_CHANNELS,&tmp);
    *numChannel = tmp;
  }
  tmp = (int)(*fSample+.5);
  err = err || ioctl (s->fd,SNDCTL_DSP_SPEED,&tmp);
  *fSample = tmp;
  if (err) {
    if (!PRnoWarnings)
      CommonWarning("PROpenRead: Unable to init dsp");
    free(s);
    return NULL;
  }

  ioctl(s->fd,SNDCTL_DSP_GETISPACE,&ainfo);

  if (PRdebugLevel)
    printf("PROpenRead: fragstotal=%d fragsize=%d\n",
	   ainfo.fragstotal,ainfo.fragsize);

  if (fragsize)
    *fragsize = ainfo.fragsize/2;
  if (fragstotal)
    *fragstotal = ainfo.fragstotal;

#endif

#ifdef Solaris
  if (!device)
    device = "/dev/audio";
  if ((s->fd=open(device,O_RDONLY,0))==-1) {
    if (!PRnoWarnings)
      CommonWarning("PROpenRead: Can not open \"%s\"",device);
    free(s);
    return NULL;
  }

  AUDIO_INITINFO(&ainfo);

  ainfo.record.sample_rate = (long)(*fSample+.5);
  ainfo.record.channels = *numChannel;
  ainfo.record.precision = 16;
  ainfo.record.encoding	= AUDIO_ENCODING_LINEAR;
  
  err = ioctl(s->fd,AUDIO_SETINFO,&ainfo);
  err = err || ioctl(s->fd,AUDIO_GETINFO,&ainfo);
  *fSample = ainfo.record.sample_rate;
  *numChannel = ainfo.record.channels;
  if (err) {
    if (!PRnoWarnings)
      CommonWarning("PROpenRead: Unable to init audio");
    free(s);
    return NULL;
  }
  if (ainfo.record.precision != 16 ||
      ainfo.record.encoding != AUDIO_ENCODING_LINEAR) {
    if (!PRnoWarnings)
      CommonWarning("PROpenRead: 16 bit audio not supported");
    free(s);
    return NULL;
  }
#endif

#ifdef IRIX
  sr[0] = AL_INPUT_RATE;
  sr[1] = (long)(*fSample+.5);
  err = 0;
  if (*fSample>0)
    err = err || ALsetparams(AL_DEFAULT_DEVICE,sr,2);
  err = err || ALgetparams(AL_DEFAULT_DEVICE,sr,2);
  *fSample = sr[1];
  if (*fSample == AL_RATE_AES_1) {
    sr[0] = AL_DIGITAL_INPUT_RATE;
    err = err || ALgetparams(AL_DEFAULT_DEVICE,sr,2);
    *fSample = sr[1];
  }
  s->rt_config = ALnewconfig();
  err = err || (!s->rt_config);
  err = err || ALsetsampfmt(s->rt_config,AL_SAMPFMT_TWOSCOMP);
  err = err || ALsetwidth(s->rt_config,AL_SAMPLE_16); 
  if (*numChannel>0)
    err = err || ALsetchannels(s->rt_config,*numChannel);
  err = err || ((*numChannel=ALgetchannels(s->rt_config))<0);
  size = (long)(*numChannel**fSample*IRIXSIZE);
  if (PRdebugLevel)
    printf("PROpenRead: got: numChannel=%d fSample=%.1f size=%ld\n",
	   *numChannel,*fSample,size);
  err = err || ALsetqueuesize(s->rt_config,size);
  if (err || !(s->rt_audio_p = ALopenport("adc","r",s->rt_config))) {
    if (!PRnoWarnings)
      CommonWarning("PROpenRead: Unable to init adc");
    free(s);
    return NULL;
  }
#endif

#ifdef WIN32
  /* init variables */
  s->waveHdr = NULL; /* WAVEHDR structure defines the header */
  s->hWaveIn = NULL; /* handle to audio input device */
  s->hWaveOut = NULL;/* handle to audio output device */
  memset((char*)&s->waveFormat,0,sizeof(WAVEFORMATEX));
  memset((char*)&s->waveOutDevCaps,0,sizeof(WAVEOUTCAPS));
  memset((char*)&s->waveInDevCaps,0,sizeof(WAVEINCAPS));
  s->reqDeviceID = 0;
  s->nBufCount = 0;
  s->nWaveHdr = 0;

  /* check if ANY device present */
  if(!(nDevice = waveInGetNumDevs()))  { 
    if (!PRnoWarnings)
      CommonWarning("PROpenRead: NO audio-device present!\n");
    free(s);
    return(FALSE);
  }
  if (!device) /* set default device ID to WAVE_MAPPER (-1) */
    device = WAVE_MAPPER_ID;
  sscanf(device,"%d",&s->reqDeviceID); /* convert device-STRING into INT */

  /* specify PCM format */ 
  s->waveFormat.wFormatTag = WAVE_FORMAT_PCM;
  s->waveFormat.nChannels = (unsigned short) *numChannel;
  /* sampling frequency */
  s->waveFormat.nSamplesPerSec = (unsigned long) *fSample;
  s->waveFormat.wBitsPerSample = 16;
  s->waveFormat.cbSize = 0; /* MUST be 0 for WAVE_FORMAT_PCM */
  /*  nBlockAlign should be equal to the product of 
      nChannels and wBitsPerSample(16) divided by 8 (bits per byte).  */
  s->waveFormat.nBlockAlign = *numChannel * 2;
  /*  nAvgBytesPerSec should be equal to the product of 
      nSamplesPerSec and nBlockAlign */
  s->waveFormat.nAvgBytesPerSec = (unsigned long)(*fSample * s->waveFormat.nBlockAlign);

  /* try to open requested device without conversions on audio data */ 
  err= waveInOpen( &s->hWaveIn, s->reqDeviceID, &s->waveFormat, 0L, 0L, WAVE_FORMAT_DIRECT );

  while (err != 0) { 
    switch (err) {
    case WAVERR_BADFORMAT:  /* Attempted to open with unsupported audio format. */ 
      waveInGetDevCaps( s->reqDeviceID, &(s->waveInDevCaps), sizeof(s->waveInDevCaps));
      W32GetWaveCaps(s->waveInDevCaps.dwFormats, fSample, numChannel);
      if (!PRnoWarnings)
        CommonWarning("PROpenRead: data mapped -> ID %d", s->reqDeviceID);
      /* open default device, data might be converted  */ 
      err = waveInOpen( &(s->hWaveIn), s->reqDeviceID, &(s->waveFormat), 0L, 0L, WAVE_MAPPED );
      break;
    case MMSYSERR_ALLOCATED: 
      /*  Specified resource is already allocated. */
      if (!PRnoWarnings)
        CommonWarning("PROpenRead: specified device ID %d is already allocated", s->reqDeviceID);
      free(s);
      return(FALSE);
    case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
      /* check if specified device ID is valid */
      if (!PRnoWarnings)
        CommonWarning("PROpenRead: requested invalid device %d\n", s->reqDeviceID);
      fprintf(stdout,"valid device IDs are:   ID\tName\t\t\tVersion\n");
      for(i=-1 ; i<nDevice; ++i) {
        waveInGetDevCaps( i, &(s->waveInDevCaps), sizeof(s->waveInDevCaps));
        fprintf(stdout,"\t\t\t%d\t%s\t%d.%d\n", i, s->waveInDevCaps.szPname,
                                     HIBYTE(s->waveInDevCaps.vDriverVersion),
                                     LOBYTE(s->waveInDevCaps.vDriverVersion)); 
      }
      fprintf(stdout,"\n");    
      free(s);
      return(FALSE);
    case MMSYSERR_NODRIVER: /* No device driver is present. */
      if (!PRnoWarnings)
        CommonWarning("PROpenRead: no audio input device present");
      free(s);
      return(FALSE);
    case MMSYSERR_NOMEM:    /* Unable to allocate or lock memory. */
      if (!PRnoWarnings)
        CommonWarning("PROpenRead: unable to allocate or lock memory");
      free(s);
      return(FALSE);
    default:
      waveInGetErrorText(err, errStr, sizeof(errStr));
      if (!PRnoWarnings)
        CommonWarning("PROpenRead: %s", errStr);
      free(s);
      return(FALSE);
    }	/* END switch() */
  }
  if (PRdebugLevel) {
    waveInGetDevCaps( s->reqDeviceID, &(s->waveInDevCaps), sizeof(s->waveInDevCaps));
    printf("PROpenRead: device ID %d\t%s\t%d.%d\n", s->reqDeviceID, s->waveInDevCaps.szPname,
                                   HIBYTE(s->waveInDevCaps.vDriverVersion),
                                   LOBYTE(s->waveInDevCaps.vDriverVersion)); 
  }
#endif

#if !(defined Linux || defined Solaris || defined IRIX || defined WIN32)
  if (!PRnoWarnings)
    CommonWarning("PROpenRead: no audio device support");
  free(s);
  return NULL;
#endif

  s->numChannel = *numChannel;
  s->fSample = *fSample;

  if (PRdebugLevel)
    printf("PROpenRead: set to: numChannel=%d fSample=%.1f\n",
	   *numChannel,*fSample);

  return s;
}


/* PROpenWriteFrag() */
/* Open play/rec stream for writing. */

PRStream *PROpenWriteFrag (
  char *device,			/* in: device name (NULL = default device) */
  int *numChannel,		/* in/out: number of channels */
  float *fSample,		/* in/out: sampling frequency [Hz] */
  int *fragsize,		/* in/out: fragment size (samples) */
  int *fragstotal)		/* in/out: number of fragments */
				/*  numChannel & fSample: */
				/*  in: requested configuration */
				/*      (SGI: 0 = use audio panel settings) */
				/*  out: selected configuration */
				/*       (set according to audio */
				/*        device capabilities) */
				/*  frags: NULL or 0 = dflt/unkown */
				/* returns: */
				/*  play/rec stream (handle) */
				/*  or NULL if error */
{
  PRStream *s;

#ifdef Linux
  int err,tmp,tmpf,tmperr;
  audio_buf_info ainfo;
#endif  

#ifdef Solaris
  audio_info_t ainfo;
  int err;
#endif  

#ifdef IRIX
  long sr[2];
  long size;
  int err;
#endif  

#ifdef WIN32
  int err;
  int i;
  int nDevice;	/* total # of devices available */
  char errStr[1024];
#endif
  
  if (PRdebugLevel)
    printf("PROpenWrite: device=\"%s\" numChannel=%d fSample=%.1f\n",
	   device?device:"(default)",*numChannel,*fSample);

  if ((s=(PRStream*)malloc(sizeof(PRStream)))==NULL)
    CommonExit(-1,"PROpenWrite: Can not allocate memory");
  s->currentSample = 0;
  s->write = 1;
  s->bufNumSample = 0;

#ifndef Linux
  /* set frags to unkown for non-Linux systems ... */
  if (fragsize)
    *fragsize = 0;
  if (fragstotal)
    *fragstotal = 0;
#endif

#ifdef Linux
  if (!device)
    device = "/dev/dsp";
  if ((s->fd=open(device,O_WRONLY,0))==-1) {
    if (!PRnoWarnings)
      CommonWarning("PROpenWrite: Can not open \"%s\"",device);
    free(s);
    return NULL;
  }

#if 0
  /* use 20 ms .. 40 ms fragement size */
  tmp = 9;				/* min 512 bytes/fragment */
  while (1<<tmp < (int)(0.020*(*fSample)+.5)*2*(*numChannel))
    tmp++;
#endif
  if (!fragsize || *fragsize==0) {
    /* defaults: min 512 bytes / 40 ms */
    tmp = 9;
    tmpf = (int)(0.040*(*fSample)+.5)*2*(*numChannel);
  }
  else {
    tmp = 4;
    tmpf = *fragsize*2;
  }
  if (tmpf<1)
    tmpf = 1;
  while (1<<tmp < tmpf)
    tmp++;
  if (fragstotal && *fragstotal>0) {
    if (*fragstotal>0x7fff)
      *fragstotal = 0x7fff;
    tmp |= *fragstotal<<16; 
  }
  else
    tmp |= 0x7fff0000;
  err = 0;
  /* HP20020531 use playrec defaults, not driver defaults */
  /* if (fragsize && *fragsize>0 || fragstotal && *fragstotal>0) */
    err = err || ioctl(s->fd,SNDCTL_DSP_SETFRAGMENT,&tmp);

  tmp = AFMT_S16_LE;			/* aka 16 */
  err = err || ioctl(s->fd,SNDCTL_DSP_SAMPLESIZE,&tmp);
  if (tmp != AFMT_S16_LE) {
    if (!PRnoWarnings)
      CommonWarning("PROpenWrite: 16 bit audio not supported");
    free(s);
    return NULL;
  }
  tmperr = 1;
  if (*numChannel<=2) {
    /* try SNDCTL_DSP_STEREO first */
    tmp = (*numChannel>1)?1:0;
    tmperr = ioctl(s->fd,SNDCTL_DSP_STEREO,&tmp);
    if (!tmperr)
      *numChannel = tmp+1;
  }
  if (tmperr) {
    tmp = *numChannel;
    err = err || ioctl(s->fd,SNDCTL_DSP_CHANNELS,&tmp);
    *numChannel = tmp;
  }
  tmp = (int)(*fSample+.5);
  err = err || ioctl (s->fd,SNDCTL_DSP_SPEED,&tmp);
  *fSample = tmp;
  if (err) {
    if (!PRnoWarnings)
      CommonWarning("PROpenWrite: Unable to init dsp");
    free(s);
    return NULL;
  }

  s->maxCinfoBytes = 0;
  ioctl(s->fd,SNDCTL_DSP_GETOSPACE,&ainfo);

  if (PRdebugLevel)
    printf("PROpenWrite: fragstotal=%d fragsize=%d\n",
	   ainfo.fragstotal,ainfo.fragsize);

  if (fragsize)
    *fragsize = ainfo.fragsize/2;
  if (fragstotal)
    *fragstotal = ainfo.fragstotal;
#endif

#ifdef Solaris
  if (!device)
    device = "/dev/audio";
  if ((s->fd=open(device,O_WRONLY,0))==-1) {
    if (!PRnoWarnings)
      CommonWarning("PROpenWrite: Can not open \"%s\"",device);
    free(s);
    return NULL;
  }

  AUDIO_INITINFO(&ainfo);

  ainfo.play.sample_rate = (long)(*fSample+.5);
  ainfo.play.channels = *numChannel;
  ainfo.play.precision	= 16;
  ainfo.play.encoding	= AUDIO_ENCODING_LINEAR;
  
  err = ioctl(s->fd,AUDIO_SETINFO,&ainfo);
  err = err || ioctl(s->fd,AUDIO_GETINFO,&ainfo);
  *fSample = ainfo.play.sample_rate;
  *numChannel = ainfo.play.channels;
  if (err) {
    if (!PRnoWarnings)
      CommonWarning("PROpenWrite: Unable to init audio");
    free(s);
    return NULL;
  }
  if (ainfo.play.precision != 16 ||
      ainfo.play.encoding != AUDIO_ENCODING_LINEAR) {
    if (!PRnoWarnings)
      CommonWarning("PROpenWrite: 16 bit audio not supported");
    free(s);
    return NULL;
  }

  if (PRdebugLevel)
    printf("PROpenWrite: buffer_size=%d\n",ainfo.play.buffer_size);
#endif

#ifdef IRIX
  sr[0]=AL_OUTPUT_RATE;
  sr[1]=(long)(*fSample+.5);
  err = 0;
  if (*fSample>0)
    err = err || ALsetparams(AL_DEFAULT_DEVICE,sr,2);
  err = err || ALgetparams(AL_DEFAULT_DEVICE,sr,2);
  *fSample = sr[1];
  s->rt_config = ALnewconfig();
  err = err || (!s->rt_config);
  err = err || ALsetsampfmt(s->rt_config,AL_SAMPFMT_TWOSCOMP);
  err = err || ALsetwidth(s->rt_config,AL_SAMPLE_16); 
  if (*numChannel>0)
    err = err || ALsetchannels(s->rt_config,*numChannel);
  err = err || ((*numChannel=ALgetchannels(s->rt_config))<0);
  size = (long)(*numChannel**fSample*IRIXSIZE);
  if (PRdebugLevel)
    printf("PROpenWrite: size=%ld\n",size);
  err = err || ALsetqueuesize(s->rt_config,size);
  if (err || !(s->rt_audio_p = ALopenport("dac","w",s->rt_config))) {
    if (!PRnoWarnings)
      CommonWarning("PROpenWrite: Unable to init dac");
    free(s);
    return NULL;
  }
#endif

#ifdef WIN32
  /* init variables */
  s->waveHdr = NULL; /* WAVEHDR structure defines the header */
  s->hWaveIn = NULL; /* handle to audio input device */
  s->hWaveOut = NULL;/* handle to audio output device */
  memset((char*)&s->waveFormat,0,sizeof(WAVEFORMATEX));
  memset((char*)&s->waveOutDevCaps,0,sizeof(WAVEOUTCAPS));
  memset((char*)&s->waveInDevCaps,0,sizeof(WAVEINCAPS));
  s->reqDeviceID = 0;
  s->nBufCount = 0;
  s->nWaveHdr = 0;

  /* check if ANY device present */
  if(!(nDevice = waveOutGetNumDevs()))  {
    if (!PRnoWarnings)
      CommonWarning("PROpenWrite: NO audio-device present!\n");
    free(s);
    return (NULL);
  }
  if (!device)  /* set default device ID to WAVE_MAPPER (-1) */
    device = WAVE_MAPPER_ID;
  sscanf(device,"%2d",&s->reqDeviceID); /* convert device-STRING into INT */

  /* specify PCM format */ 
  s->waveFormat.wFormatTag = WAVE_FORMAT_PCM;
  s->waveFormat.nChannels = (unsigned short) *numChannel;
  /* sampling frequency */
  s->waveFormat.nSamplesPerSec = (unsigned long) *fSample;
  s->waveFormat.wBitsPerSample = 16;
  s->waveFormat.cbSize = 0; /* MUST be 0 for WAVE_FORMAT_PCM */
  /*  nBlockAlign should be equal to the product of 
      nChannels and wBitsPerSample(16) divided by 8 (bits per byte).  */
  s->waveFormat.nBlockAlign = *numChannel * 2;
  /*  nAvgBytesPerSec should be equal to the product of 
      nSamplesPerSec and nBlockAlign */
  s->waveFormat.nAvgBytesPerSec = (unsigned long)(*fSample * s->waveFormat.nBlockAlign);
   
  /* sndPlaySound(NULL, 0); */   /* shut any sound off */
  /* try to open requested device without conversions on audio data */ 
  err = waveOutOpen( &s->hWaveOut, s->reqDeviceID, &s->waveFormat, 0L, 0L, 
                     WAVE_FORMAT_DIRECT);
  while(err != 0) {
    switch (err) {
    case WAVERR_BADFORMAT:  /* Attempted to open with unsupported audio format. */ 
      waveOutGetDevCaps( s->reqDeviceID, &(s->waveOutDevCaps), sizeof(s->waveOutDevCaps));
      W32GetWaveCaps(s->waveOutDevCaps.dwFormats, fSample, numChannel);
      if (!PRnoWarnings)
        CommonWarning("PROpenRead: data mapped -> ID %d", s->reqDeviceID);
      /* open default device, data might be converted  */ 
      err = waveOutOpen( &(s->hWaveOut), s->reqDeviceID, &(s->waveFormat), 0L, 0L,
                         WAVE_MAPPED );
      break;
    case WAVERR_SYNC:
      /* open device in synchronous mode */ 
      err = waveOutOpen( &s->hWaveOut, s->reqDeviceID, &s->waveFormat, 0L, 0L,
                         WAVE_FORMAT_DIRECT & WAVE_ALLOWSYNC);
      break;
    case MMSYSERR_ALLOCATED: 
      /*  Specified resource is already allocated. */
      if (!PRnoWarnings)
        CommonWarning("PROpenWrite: specified device ID %d is already allocated", s->reqDeviceID);
      free(s);
      return(FALSE);
    case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
       /* check if specified device ID is valid */
      if (!PRnoWarnings)
        CommonWarning("PROpenWrite: Requested invalid device %d\n", s->reqDeviceID);
      fprintf(stdout,"valid device IDs are:   ID\tName\t\t\tVersion\n");
      for(i=-1 ; i<nDevice; ++i) {
        waveOutGetDevCaps( i, &(s->waveOutDevCaps), sizeof(s->waveOutDevCaps));
        fprintf(stdout,"\t\t\t%d\t%s\t%d.%d\n", i, s->waveOutDevCaps.szPname,
                                     HIBYTE(s->waveOutDevCaps.vDriverVersion),
                                     LOBYTE(s->waveOutDevCaps.vDriverVersion)); 
      }
      fprintf(stdout,"\n");    
      free(s);
      return(FALSE);
    case MMSYSERR_NODRIVER: /* No device driver is present. */
      if (!PRnoWarnings)
        CommonWarning("PROpenWrite: no audio input device present");
      free(s);
      return(FALSE);
    case MMSYSERR_NOMEM:    /* Unable to allocate or lock memory. */
      if (!PRnoWarnings)
        CommonWarning("PROpenWrite: unable to allocate or lock memory");
      free(s);
      return(FALSE);
    default:
      waveOutGetErrorText(err, errStr, sizeof(errStr));
      if (!PRnoWarnings)
        CommonWarning("PROpenWrite: %s", errStr);
      free(s);
      return(FALSE);
    }	/* END switch() */
  }

  if (PRdebugLevel) {
    waveOutGetDevCaps( s->reqDeviceID, &(s->waveOutDevCaps), sizeof(s->waveOutDevCaps));
    printf("PROpenWrite: device ID %d\t%s\t%d.%d\n", s->reqDeviceID, s->waveOutDevCaps.szPname,
                                   HIBYTE(s->waveOutDevCaps.vDriverVersion),
                                   LOBYTE(s->waveOutDevCaps.vDriverVersion)); 
  }
  waveOutSetVolume(0,0xffffffff);  /* Set volume to max. */
  waveOutPause(s->hWaveOut); /* pause until buffer filled */	
#endif

#if !(defined Linux || defined Solaris || defined IRIX || defined WIN32)
  if (!PRnoWarnings)
    CommonWarning("PROpenWrite: no audio device support");
  free(s);
  return NULL;
#endif

  s->numChannel = *numChannel;
  s->fSample = *fSample;

  if (PRdebugLevel)
    printf("PROpenWrite: set to: numChannel=%d fSample=%.1f\n",
	   *numChannel,*fSample);

  return s;
}


/* PRReadData() */
/* Read data from play/rec stream. */

int PRReadData (
  PRStream *stream,		/* in: play/rec stream (handle) */
  short *data,			/* out: data[] */
  long numSample)		/* in: number of samples to be read */
				/* returns: */
				/*  0=OK 1=ERROR */
{
  int retval;

#ifdef Linux
  long n,i,t;
#endif

#ifdef Solaris
  long n,i,t;
#endif

#ifdef WIN32
  static int j;
  unsigned int bytes;
#endif

  retval = 0;

  if (PRdebugLevel > 1)
    printf("PRReadData: numSample=%ld\n",numSample);

  if (stream->write)
    CommonExit(1,"PRReadData: device not in read mode");

#ifdef Linux
  n = sizeof(short)*numSample;
  i = 0;
  while (i<n) {
    t = read(stream->fd,((char*)data)+i,n-i);
    /* fprintf(stderr,"p=%08x n-i=%08x n=%08x i=%08x t=%08x\n",((char*)data)+i,n-i,n,i,t); */
    if (t<0) {
      if (!PRnoWarnings)
	CommonWarning("PRReadData: Can not read from device (%s)",
		      strerror(errno));
      retval = 1;
      i = n;
      t = 0;
    }
    else if (t==0 && PRdebugLevel>1)
      CommonWarning("PRReadData: read() returned 0");
    i += t;
  }
#endif

#ifdef Solaris
  n = sizeof(short)*numSample;
  i = 0;
  while (i<n) {
    t = read(stream->fd,((char*)data)+i,n-i);
    if (t<0) {
      if (!PRnoWarnings)
	CommonWarning("PRReadData: Can not read from device (%s)",
		      strerror(errno));
      retval = 1;
      i = n;
      t = 0;
    }
    else if (t==0 && PRdebugLevel>1)
      CommonWarning("PRReadData: read() returned 0");
    i += t;
  }
#endif

#ifdef IRIX
  if (ALreadsamps(stream->rt_audio_p,data,numSample)) {
    if (!PRnoWarnings)
      CommonWarning("PRReadData: Can not read from device");
    retval = 1;
  }
#endif

#ifdef WIN32
  
  if(stream->nBufCount==0) {
    stream->nWaveHdr = NREAD_WAVEHDR;
    stream->waveHdr = (WAVEHDR **)malloc(stream->nWaveHdr*sizeof(WAVEHDR *));
    for (j=0 ; j<stream->nWaveHdr ; ++j) {
      stream->waveHdr[j] = (WAVEHDR *)malloc(sizeof(WAVEHDR));
      stream->waveHdr[j]->dwUser = 0;
      stream->waveHdr[j]->dwFlags = 0;
      stream->waveHdr[j]->dwLoops = 1;
      stream->waveHdr[j]->lpNext = NULL;
      stream->waveHdr[j]->reserved = 0;
      stream->waveHdr[j]->dwBytesRecorded = 0;
      stream->waveHdr[j]->dwBufferLength = numSample*sizeof(short);
      stream->waveHdr[j]->lpData = (char *)malloc(stream->waveHdr[j]->dwBufferLength);
      waveInPrepareHeader(stream->hWaveIn, stream->waveHdr[j],sizeof(WAVEHDR));

      if(waveInAddBuffer( stream->hWaveIn, stream->waveHdr[j], sizeof(WAVEHDR)))
	if (!PRnoWarnings)
          CommonWarning("PRReadData: Can not read from device");
    }
    waveInStart(stream->hWaveIn);  /* now start the audio input-device */     
    j=0;
  }
  else{
    if(++j==stream->nWaveHdr)
      j=0;
  }

  /* wait till current buffer is filled */
  while(!(stream->waveHdr[j]->dwFlags & WHDR_DONE))
    PRNap(1);				/* 1 ms */
  /* if buffer is filled copy data */
  /* if(stream->waveHdr[j]->dwFlags & WHDR_DONE) */ /* Copy data into buffer */
  memcpy((char *)data, stream->waveHdr[j]->lpData, stream->waveHdr[j]->dwBufferLength);

  /* Setup and prepare current buffer for next cycle */
  bytes = numSample*sizeof(short);
  if(stream->waveHdr[j]->dwBufferLength != bytes) {
    waveInUnprepareHeader(stream->hWaveIn, stream->waveHdr[j],sizeof(WAVEHDR));
    stream->waveHdr[j]->lpData = (char *)realloc(stream->waveHdr[j]->lpData, bytes);
    stream->waveHdr[j]->dwBufferLength = bytes;
    waveInPrepareHeader(stream->hWaveIn, stream->waveHdr[j],sizeof(WAVEHDR));
  }

  if(waveInAddBuffer( stream->hWaveIn, stream->waveHdr[j], sizeof(WAVEHDR)))
    if (!PRnoWarnings)
      CommonWarning("PRReadData: Can not read from device");

  stream->nBufCount++;
#endif	

  if (!retval)
    stream->currentSample += numSample;
  return retval;
}


/* PRWriteBuffer() */
/* Set play buffer size. */
/* PRWriteData() waits until less than bufNumSample´s are left in play */
/* buffer before writing the new samples. */

void PRWriteBuffer (
  PRStream *stream,		/* in: play/rec stream (handle) */
  long bufNumSample)		/* in: number of samples in play buffer */
				/*     (0 to use max. available buffer) */
{
  if (PRdebugLevel > 1)
    printf("PRWriteBuffer: bufNumSample=%ld\n",bufNumSample);

  if (!stream->write)
    CommonExit(1,"PRWriteBuffer: device not in write mode");

  stream->bufNumSample = bufNumSample;
}


/* PRWriteFilled() */
/* Get number of samples currently left in play buffer, */
/* i.e. written by PRWriteData() but not yet played. */

long PRWriteFilled (
  PRStream *stream)		/* in: play/rec stream (handle) */
				/* returns: */
				/*  number of samples left in play buffer */
{
  long numSampleLeft;

#ifdef Linux
  int err;
  int odelay;
  count_info cinfo;
  audio_buf_info ainfo;
  long tmp;
#endif

#ifdef Solaris
  audio_info_t ainfo;
#endif

#ifdef WIN32
  MMTIME mmt;
#endif

  if (!stream->write)
    CommonExit(1,"PRWriteFilled: device not in write mode");

#ifdef Linux
#ifdef NO_GETODELAY
  err = 1;
#else
  err = ioctl(stream->fd,SNDCTL_DSP_GETODELAY,&odelay);
  numSampleLeft = odelay/2;
  if (err && (PRdebugLevel > 1))
    printf("PRWriteFilled: SNDCTL_DSP_GETODELAY ioctl error\n");
#endif
  if (err) {
    /* do it the old way ... */
    err = ioctl(stream->fd,SNDCTL_DSP_GETOPTR,&cinfo);
    err = err || ioctl(stream->fd,SNDCTL_DSP_GETOSPACE,&ainfo);
    if (stream->maxCinfoBytes!=-1 && stream->maxCinfoBytes <= cinfo.bytes) {
      numSampleLeft = stream->currentSample-cinfo.bytes/2;
      stream->maxCinfoBytes = cinfo.bytes;
    }
    else {   /* handle SB128PCI buffer-underrun   HP 20000607 */
      numSampleLeft = 0;
      stream->maxCinfoBytes = -1;
    }
    /* work-around for old OSS/ES1688 (2.0.35) -- (fragLeft-1)*fragSize */
    tmp = ((ainfo.fragstotal-1)*ainfo.fragsize-ainfo.bytes)/2;
    if (PRdebugLevel > 1)
      printf("bts=%d bks=%d ptr=%d "
	     "fgm=%d (%d*%d) bts=%d "
	     "cur=%ld lft=%ld/%ld\n",
	     cinfo.bytes,cinfo.blocks,cinfo.ptr,ainfo.fragments,
	     ainfo.fragstotal,ainfo.fragsize,ainfo.bytes,
	     stream->currentSample,numSampleLeft,tmp);
    numSampleLeft = max(0,numSampleLeft);
    tmp = max(0,tmp);
    numSampleLeft = max(numSampleLeft,tmp);
    /* make sure that play starts ... */
    if (numSampleLeft < ainfo.fragsize/2)
      numSampleLeft = 0;
  }
  if (err) {
    if (!PRnoWarnings)
      CommonWarning("PRWriteFilled: ioctl error");
    numSampleLeft = 0;
  }
#endif

#ifdef Solaris
  ioctl(stream->fd,AUDIO_GETINFO,&ainfo);
  numSampleLeft = stream->currentSample-ainfo.play.samples*stream->numChannel;
#endif

#ifdef IRIX
  numSampleLeft = ALgetfilled(stream->rt_audio_p);
#endif

#ifdef WIN32
  mmt.wType = TIME_SAMPLES;
  waveOutGetPosition( stream->hWaveOut, &mmt, sizeof(mmt) );
  numSampleLeft = stream->currentSample-mmt.u.sample*stream->numChannel;
#endif

  if (PRdebugLevel > 1)
    printf("PRWriteFilled: numSampleLeft=%ld\n",numSampleLeft);

  return numSampleLeft;
}


/* PRWriteData() */
/* Write data to play/rec stream. */

int PRWriteData (
  PRStream *stream,		/* in: play/rec stream (handle) */
  short *data,			/* in: data[] */
  long numSample)		/* in: number of samples to be written */
				/* returns: */
				/*  0=OK 1=ERROR */
{
  int retval;

#ifdef Linux
  long n,i,t;
#endif

#ifdef Solaris
  long n,i,t;
#endif

#ifdef WIN32
  int i;
  static int j;
  unsigned int bytes;
#endif

  retval = 0;

  if (PRdebugLevel > 1)
    printf("PRWriteData: numSample=%ld\n",numSample);

  if (!stream->write)
    CommonExit(1,"PRWriteData: device not in write mode");

#ifdef WIN32
  /*   Initialise wave-header structures */  
  if(stream->nBufCount==0){
    stream->nWaveHdr = max(stream->bufNumSample/numSample,2);
    stream->waveHdr = (WAVEHDR **)malloc(stream->nWaveHdr*sizeof(WAVEHDR *));
    for (i=0 ; i<stream->nWaveHdr ; ++i) {
      stream->waveHdr[i] = (WAVEHDR *)malloc(sizeof(WAVEHDR));
      stream->waveHdr[i]->dwUser = 0;
      stream->waveHdr[i]->dwFlags = 0;
      stream->waveHdr[i]->dwLoops = 1;
      stream->waveHdr[i]->lpNext = NULL;
      stream->waveHdr[i]->reserved = 0;
      stream->waveHdr[i]->dwBytesRecorded = 0;
      stream->waveHdr[i]->dwBufferLength = numSample*sizeof(short);
      stream->waveHdr[i]->lpData = (char *)malloc(stream->waveHdr[i]->dwBufferLength);
      waveOutPrepareHeader(stream->hWaveOut, stream->waveHdr[i],sizeof(WAVEHDR));
    }
    j=0;
  } 
  else {
    if(++j==stream->nWaveHdr)
      j=0;
    if(stream->nBufCount >= stream->nWaveHdr)
      while(!(stream->waveHdr[j]->dwFlags & WHDR_DONE))
	      PRNap(1);				/* 1 ms */
  }
#else
  if (stream->bufNumSample)
    while (PRWriteFilled(stream)>stream->bufNumSample)
      PRNap(10);				/* 10 ms */
#endif

#ifdef Linux
  n = sizeof(short)*numSample;
  i = 0;
  while (i<n) {
    t = write(stream->fd,((char*)data)+i,n-i);
    /* fprintf(stderr,"p=%08x n-i=%08x n=%08x i=%08x t=%08x\n",((char*)data)+i,n-i,n,i,t); */
    if (t<0) {
      if (!PRnoWarnings)
	CommonWarning("PRWriteData: Can not write to device (%s)",
		      strerror(errno));
      retval = 1;
      i = n;
      t = 0;
    }
    else if (t==0 && PRdebugLevel>1)
      CommonWarning("PRWriteData: write() returned 0");
    i += t;
  }
  
#ifdef NO_GETODELAY
  /* work-around for alsa-0.3.0 -- wait until playback started */
  if (stream->bufNumSample &&
      stream->currentSample <= stream->bufNumSample &&
      stream->currentSample+numSample > stream->bufNumSample) {
    count_info cinfo;
    audio_buf_info ainfo;
    ioctl(stream->fd,SNDCTL_DSP_GETOPTR,&cinfo);
    ioctl(stream->fd,SNDCTL_DSP_GETOSPACE,&ainfo);
    if (numSample*2>=ainfo.fragsize)
      while (cinfo.ptr==0x20000) {
	PRNap(1);
	ioctl(stream->fd,SNDCTL_DSP_GETOPTR,&cinfo);
      }
    if (PRdebugLevel > 1)
      printf("alsa started: bts=%d blks=%d ptr=%d / "
	     "fgmts=%d ftot=%d frgsz=%d bts=%d\n",
	     cinfo.bytes,cinfo.blocks,cinfo.ptr,ainfo.fragments,
	     ainfo.fragstotal,ainfo.fragsize,ainfo.bytes);
  }
#endif
#endif

#ifdef Solaris
  n = sizeof(short)*numSample;
  i = 0;
  while (i<n) {
    t = write(stream->fd,((char*)data)+i,n-i);
    if (t<0) {
      if (!PRnoWarnings)
	CommonWarning("PRWriteData: Can not write to device (%s)",
		      strerror(errno));
      retval = 1;
      i = n;
      t = 0;
    }
    else if (t==0 && PRdebugLevel>1)
      CommonWarning("PRWriteData: write() returned 0");
    i += t;
  }
#endif

#ifdef IRIX
  if (ALwritesamps(stream->rt_audio_p,data,numSample)) {
    if (!PRnoWarnings)
      CommonWarning("PRWriteData: Can not write to device");
    retval = 1;
  }
#endif

#ifdef WIN32
  /* Setup and prepare current buffer */
  bytes = numSample*sizeof(short);
  if(stream->waveHdr[j]->dwBufferLength != bytes) {
    waveOutUnprepareHeader(stream->hWaveOut, stream->waveHdr[j],sizeof(WAVEHDR));
    stream->waveHdr[j]->lpData = (char *)realloc(stream->waveHdr[j]->lpData, bytes);
    stream->waveHdr[j]->dwBufferLength = bytes;
    waveOutPrepareHeader(stream->hWaveOut, stream->waveHdr[j],sizeof(WAVEHDR));
  }
  /* Copy data into buffer */
  memcpy(stream->waveHdr[j]->lpData, (char *)data, stream->waveHdr[j]->dwBufferLength);

  if(waveOutWrite( stream->hWaveOut, stream->waveHdr[j], sizeof(WAVEHDR)))
    if (!PRnoWarnings)
      CommonWarning("PRWriteData: Can not write to device");

  if (stream->nBufCount==stream->nWaveHdr-1)
    /* if (stream->nBufCount==2) */
    waveOutRestart(stream->hWaveOut);  /* now unpause the audio device */     

  stream->nBufCount++;
#endif

  if (!retval)
    stream->currentSample += numSample;
  return retval;
}


/* PRClose() */
/* Close play/rec stream.*/

void PRClose (
  PRStream *stream)		/* in: play/rec stream (handle) */
{
  short data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  int n;
#ifdef WIN32
  int j,i; 
  int err;
  char errStr[1024];
#endif

  if (PRdebugLevel)
    printf("PRClose: currentSample=%ld\n",stream->currentSample);

  n = stream->currentSample%stream->numChannel;
  if (n)
    if (!PRnoWarnings)
      CommonWarning("PRClose: odd number of samples (smps%%chan=%d chan=%d)",
		    n,stream->numChannel);

  if (stream->write) {
    if (n)
      n = stream->numChannel-n;		/* missing samples */
    n += stream->numChannel;		/* silence */
    n = min(16,n);			/* ugly, but ... */

#ifdef Linux
    if (write(stream->fd,data,sizeof(short)*n) < 0)
      if (!PRnoWarnings)
        CommonWarning("PRClose: Can not write to device (%s)",
		      strerror(errno));
#endif

#ifdef Solaris
    if (write(stream->fd,data,sizeof(short)*n) < 0)
      if (!PRnoWarnings)
        CommonWarning("PRClose: Can not write to device (%s)",
		      strerror(errno));
#endif

#ifdef IRIX
    if (ALwritesamps(stream->rt_audio_p,data,n))
      if (!PRnoWarnings)
        CommonWarning("PRClose: Can not write to device (%s)",
		      strerror(errno));
#endif

#ifdef WIN32
    if (stream->waveHdr!=NULL) {
      j = (stream->nBufCount & (stream->nWaveHdr-1));
      while (stream->waveHdr[j]->dwFlags & WHDR_INQUEUE)
        PRNap(10);				/* 10 ms */
  
      stream->waveHdr[j]->lpData = (char *)realloc(stream->waveHdr[j]->lpData, n*sizeof(short));
      stream->waveHdr[j]->dwBufferLength = n*sizeof(short);

      /* Copy data into buffer */
      memcpy(stream->waveHdr[j]->lpData, (char *)data, stream->waveHdr[j]->dwBufferLength);
     /* prepare buffer for playing */
      waveOutPrepareHeader(stream->hWaveOut, stream->waveHdr[j],sizeof(WAVEHDR));
      err = waveOutWrite(stream->hWaveOut, stream->waveHdr[j], sizeof(WAVEHDR));
      if(err != MMSYSERR_NOERROR) {
        waveOutGetErrorText(err, errStr, sizeof(errStr));
	if (!PRnoWarnings) {
          CommonWarning("PRClose: Can not write to device\n");
	  CommonWarning("waveOutWrite: %s \n", errStr);	
	}
      }
    }
#endif
  }

#ifdef Linux
  ioctl(stream->fd,SNDCTL_DSP_SYNC,0);
  /* ioctl(stream->fd,SNDCTL_DSP_RESET,0); */
  close(stream->fd);
#endif

#ifdef Solaris
  close(stream->fd);
#endif

#ifdef IRIX
  if (stream->write)
    while (ALgetfilled(stream->rt_audio_p) > 0)
      sginap(1);			/* typ. 10 ms (CLK_TCK = 100) */
  ALcloseport(stream->rt_audio_p);
  ALfreeconfig(stream->rt_config);
#endif

#ifdef WIN32
  if (stream->write) {
    if (stream->waveHdr!=NULL) {
      while (stream->waveHdr[j]->dwFlags & WHDR_INQUEUE)
        PRNap(10);				/* 10 ms */
    }
    waveOutReset(stream->hWaveOut);
    err = waveOutClose (stream->hWaveOut);
    if(err != MMSYSERR_NOERROR) {
      waveOutGetErrorText(err, errStr, sizeof(errStr));
      if (!PRnoWarnings) {
        CommonWarning("PRClose: Can not write to device\n");
	CommonWarning("waveOutClose: %s \n", errStr);	
      }
    }
    if (stream->waveHdr!=NULL) {
      for(i=0 ; i<stream->nWaveHdr ; i++) {
        waveOutUnprepareHeader (stream->hWaveOut, stream->waveHdr[i], sizeof (WAVEHDR));
        free(stream->waveHdr[i]->lpData);
        free(stream->waveHdr[i]);
      }
      free(stream->waveHdr);
    }
    stream->hWaveOut = NULL;
  }
  else {
    waveInReset(stream->hWaveIn);
    err = waveInClose (stream->hWaveIn);
    if(err != MMSYSERR_NOERROR) {
      waveInGetErrorText(err, errStr, sizeof(errStr));
      if (!PRnoWarnings) {
        CommonWarning("PRClose: Can not read from device\n");
	CommonWarning("waveInClose: %s \n", errStr);	
      }
    }
    if (stream->waveHdr!=NULL) {
      for(i=0 ; i<stream->nWaveHdr ; i++) {
        err = waveInUnprepareHeader (stream->hWaveIn, stream->waveHdr[i], sizeof (WAVEHDR));
        free(stream->waveHdr[i]->lpData);
        free(stream->waveHdr[i]);
      }
      free(stream->waveHdr);
    }
    stream->hWaveIn = NULL;
  }
#endif

  free(stream);
}


/* end of austream.c */

