/*

    Sound interface for Speak Freely for Unix

    Designed and implemented in July of 1990 by John Walker

*/

#include "speakfree.h"
#include <stdio.h>
#include <fcntl.h>
#ifdef AUDIO_DEVICE_FILE

#include <errno.h>
#include <sys/ioctl.h>
#define AUDIO_MIN_GAIN 0
#define AUDIO_MAX_GAIN 255

static int abuf_size;

#ifdef IN_AUDIO_DEV
#define SoundFileIn IN_AUDIO_DEV
#else
#define SoundFileIn     "/dev/audio"
#endif

#ifdef OUT_AUDIO_DEV
#define SoundFileOut OUT_AUDIO_DEV
#else
#define SoundFileOut    "/dev/audio"
#endif

#ifdef sun
#define AUDIO_CTLDEV    "/dev/audioctl"
#else
#define AUDIO_CTLDEV    "/dev/mixer"
#endif

char *devAudioInput = SoundFileIn,    /* Audio device files to open. */
     *devAudioOutput = SoundFileOut,  /* These can overridden by the -y */
     *devAudioControl = AUDIO_CTLDEV; /* option on sfmike and sfspeaker. */

#define MAX_GAIN    100

struct sound_buf {
    struct sound_buf *snext;          /* Next sound buffer */
    int sblen;                /* Length of this sound buffer */
    unsigned char sbtext[2];          /* Actual sampled sound */
};

/*  Local variables  */
static int paudiof = -1;        /* For playing   */
static int raudiof = -1;        /* For recording */
static int mode    = -1;
static int volume  = 100;

static char WriteFile[256];     /* For the template */
static char ReadFile[256];      /* For the template */

static short pbuf[BUFL];        /* Buffer for playing */
static short rbuf[BUFL];
static int audiof = -1;           /* Audio device file descriptor */
static int Audio_fd;              /* Audio control port */
#ifdef sun
static audio_info_t Audio_info;       /* Current configuration info */
#endif
struct sound_buf *sbchain = NULL,     /* Sound buffer chain links */
         *sbtail = NULL;
static int sbtotal = 0;           /* Total sample bytes in memory */
static int playing = FALSE;       /* Replay in progress ? */
/* static int playqsize;  */            /* Output queue size */
static int playlen = 0;           /* Length left to play */
static unsigned char *playbuf= NULL;  /* Current play pointer */
static int squelch = 0;           /* Squelch value */
extern int neverRelease = FALSE;     /* Never release audio device.  This
                     is set if we inherited our open
                     audio device file descriptor from
                     a parent process.  If we release
                                         it, there's no way we can get it
                     back, so we glom onto it until
                     we exit. */

/* Convert local gain into device parameters */

static unsigned scale_gain(g)
  unsigned g;
{
    return (AUDIO_MIN_GAIN + (unsigned)
    ((int) ((((double) (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN)) *
    ((double) g / (double) MAX_GAIN)) + 0.5)));
}

#ifdef Solaris

/*  SETAUBUFSIZE  --  Preset size of internal /dev/audio buffer segments
              Must be called before soundinit()  */

static int aubufsize = 2048;          /* Default */

void setaubufsize(size)
  int size;
{
    aubufsize = size;
}
#endif


/*  SOUNDINIT  --  Open the sound peripheral and initialise for
           access.  Return TRUE if successful, FALSE
           otherwise.  */
 int soundinit(iomode)
   int iomode;
 {
   char *variable;
  int punit   = 0;
   int runit   = 0;

   /* Save mode for soundplayvol() */
   mode = iomode;

   if(iomode == -1) {
     return FALSE;
   }

   /* Find out where to read and play audio */

   variable = getenv("SPEAKFREE_PLAY_UNIT");
   if(variable) {
     punit = strtol(variable, NULL, 10);
   }

   variable = getenv("SPEAKFREE_RECORD_UNIT");
   if(variable) {
     runit = strtol(variable, NULL, 10);
   }

   sprintf((char *) WriteFile,
       "AUDIO:BITS/16/FREQUENCY/8000/CHANNELS/1/TYPE/SIGNED/"
       "VOLUME/%d/BUFFER/8192/UNIT/%d", volume, punit);
   sprintf((char *) ReadFile,
       "AUDIO:BITS/16/FREQUENCY/8000/CHANNELS/1/TYPE/SIGNED/"
       "BUFFER/2048/UNIT/%d", runit);

   if (iomode != O_RDONLY) {
     if ((paudiof = open(WriteFile, O_WRONLY)) < 0) {
       fprintf(stderr, "Audio open: unable to open \"%s\"\n", WriteFile);
       return FALSE;
     }
   }

   if (iomode != O_WRONLY) {
     if ((raudiof = open(ReadFile, O_RDONLY)) < 0) {
       fprintf(stderr, "Audio open: unable to open \"%s\"\n", ReadFile);
       return FALSE;
     }
   }

   return TRUE;
}

/*  SOUNDTERM  --  Close the sound device.  */

 void soundterm()
 {
   if (paudiof >= 0) {
     if (close(paudiof) < 0) {
       perror("closing audio device");
     }
     paudiof = -1;
   }
   if (raudiof >= 0) {
     if (close(raudiof) < 0) {
       perror("closing audio device");
     }
     raudiof = -1;
   }
   mode   = -1;
 }


/*  SOUND_OPEN_FILE_DESCRIPTORS  --  Obtain file descriptors of open
                     audio and audio control device files.  */

void sound_open_file_descriptors(audio_io, audio_ctl)
  int *audio_io, *audio_ctl;
{
    *audio_io = audiof;
    *audio_ctl = Audio_fd;
}

/*  SOUNDPLAY  --  Begin playing a sound.  */

void soundplay(len, buf)
   int len;
  unsigned char *buf;
 {
   int ios;
   int i;

   if(len > BUFL) {
     len = BUFL;
   }

   /* Convert µlaw to 16 bit */
   for (i = 0; i < len; i++) {
     pbuf[i] = audio_u2s(buf[i]);
   }

   while (TRUE) {
     ios = write(paudiof, pbuf, len << 1 );
     if (ios == -1) {
       sf_usleep(100000);
    } else {
       ios >>= 1;
       if (ios < len ) {
         buf += ios;
         len -= ios;
      } else {
         break;
       }
     }
   }
 }


/*  SOUNDPLAYVOL  --  Set playback volume from 0 (silence) to 100 (full on). */

 void soundplayvol(value)
   int value;
 {
   volume = value;
   soundterm();
   soundinit(mode);
 }


/*  SOUNDRECGAIN  --  Set recording gain from 0 (minimum) to 100 (maximum).  */

 void soundrecgain(value)
   int value;
 {
 }

/*  SOUNDDEST  --  Set destination for generated sound.  If "where"
           is 0, sound goes to the built-in speaker; if
           1, to the audio output jack. */


 void sounddest(where)
   int where;
 {
 }

/*  SOUNDGRAB  --  Return audio information in the record queue.  */

int soundgrab(buf, len)
     char *buf;
     int len;
 {
   long read_size;
   int c;
   int i;

   if(len > BUFL) {
     len = BUFL;
   }

   read_size = len;

   while (TRUE) {
     c = read(raudiof, rbuf, read_size << 1);
     if (c < 0) {
       if (errno == EINTR) {
         continue;
       }
     } else {
       c >>= 1;
     }
     break;
   }
   if (c < 0) {
     perror("soundgrab");
   }

   /* Convert 16 bit to µlaw */
   for (i = 0; i < len; i++) {
     buf[i] = audio_s2u(rbuf[i]);
   }

   return c;
 }


/*  SOUNDFLUSH  --  Flush any queued sound.  */

 void soundflush()
 {
 /*
   soundterm();
   soundinit(mode);
 */
 }


#endif /* AUDIO_DEVICE_FILE */
