/* 

    Functions to play sound on a HP's audio device

    Copyright 1997 Lawrence T. Hoff

*/

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <sys/ioctl.h> 

#include <sys/audio.h>

#include "config.h"
#include "output.h"
#include "controls.h"

static int open_output(void); /* 0=success, 1=warning, -1=fatal error */
static void close_output(void);
static void output_data(int32 *buf, int32 count);
static void flush_output(void);
static void purge_output(void);

/* export the playback mode */

#define dpm hpux_play_mode

PlayMode dpm = {
  DEFAULT_RATE, PE_16BIT|PE_SIGNED,
  -1,
  {0,0,0,0,0}, /* no extra parameters so far */
  "HP audio device", 'd',
  "/dev/audio",
  open_output,
  close_output,
  output_data,
  flush_output,
  purge_output  
};

/*************************************************************************/
/*
   Encoding will be 16-bit linear signed, unless PE_ULAW is set, in
   which case it'll be 8-bit uLaw. I don't think it's worthwhile to
   implement any 8-bit linear modes as the sound quality is
   unrewarding. PE_MONO is honored.  */

static int open_output(void)
{

if (dpm.encoding & PE_ULAW)
  dpm.encoding &= ~PE_16BIT;

if (!(dpm.encoding & PE_16BIT))
  dpm.encoding &= ~PE_BYTESWAP;

dpm.fd = open(dpm.name, O_WRONLY, 0);
if(dpm.fd == -1)
    {
      ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
	   dpm.name, sys_errlist[errno]);
      return -1;
    }

(void) ioctl(dpm.fd, AUDIO_SET_SAMPLE_RATE, dpm.rate);

(void) ioctl(dpm.fd, AUDIO_SET_DATA_FORMAT, (dpm.encoding & PE_16BIT)?
	AUDIO_FORMAT_LINEAR16BIT: AUDIO_FORMAT_ULAW);

(void) ioctl(dpm.fd, AUDIO_SET_CHANNELS, (dpm.encoding & PE_MONO)?1:2);

/* set some reasonable buffer size */
(void) ioctl(dpm.fd, AUDIO_SET_TXBUFSIZE, 8*4096);

/* output to all devices */
(void) ioctl(dpm.fd, AUDIO_SET_OUTPUT, 
	AUDIO_OUT_SPEAKER | AUDIO_OUT_HEADPHONE | AUDIO_OUT_LINE);

return 0;
}

static void output_data(int32 *buf, int32 count)
{
  if (!(dpm.encoding & PE_MONO)) count*=2; /* Stereo samples */
  
  if (dpm.encoding & PE_16BIT)
    {
      if (dpm.encoding & PE_BYTESWAP)
	{
	  if (dpm.encoding & PE_SIGNED)
	    s32tos16x(buf, count);
	  else
	    s32tou16x(buf, count);
	}
      else
	{
	  if (dpm.encoding & PE_SIGNED)
	    s32tos16(buf, count);
	  else 
	    s32tou16(buf, count);
	}

      /* write the data out */
      write(dpm.fd, buf, count * 2);

    }
  else
    {
      if (dpm.encoding & PE_ULAW)
	{
	  s32toulaw(buf, count);
	}
      else
	{
	  if (dpm.encoding & PE_SIGNED)
	    s32tos8(buf, count);
	  else 
	    s32tou8(buf, count);
	}
      
      /* write the data out */
      write(dpm.fd, buf, count);
    }
}

static void close_output(void)
{
  /* free resources */
  close(dpm.fd);
}

static void flush_output(void)
{
(void) ioctl(dpm.fd, AUDIO_DRAIN, RESET_TX_BUF);
}

static void purge_output(void)
{
(void) ioctl(dpm.fd, AUDIO_RESET, RESET_TX_BUF);
}
