#ifndef NO_SOUND

#define __RAWKS_SOUND__
#define DBG 0

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <signal.h>
#include <malloc.h>
#include <unistd.h>
#include <errno.h>
#include <sys/prctl.h>
#include <invent.h>

#include "defines.h"
#include "protos.h"
#include "globals.h"
#include "audio.h"
#include "aiff.h"

/* we're going to nail down the value of infinity for the IEEE conversion
 * routine below: math.h yields to local definition of HUGE_VAL */
union {
  unsigned long n[2];		/* initialized in main */
  double d;
} _inf_union;

#define HUGE_VAL _inf_union.d;
#include <math.h>

typedef union {
  unsigned char b[2];
  short s;
} align_short_t;

typedef union {
  unsigned char b[4];
  long l;
} align_long_t;

#define D_TIME		(2.0/((float)CLK_TCK))

#define	MAX_EXP_DEPTH	5
#define	MAX_EXP_ANGLE	3
#define EXP_DELTA_ANGLE	( M_PI / 4.0 )
#define	EXP_DELTA_DEPTH	250.0
#define	MAX_AUDIO_PORTS	3

static char *myname = "rawks_audio";

static unsigned long orig_left_gain;
static unsigned long orig_right_gain;
static unsigned long orig_output_rate;
static unsigned long curr_left_gain;
static unsigned long curr_right_gain;
static long sound_size[SFX_N_SOUNDS];
static short *sound_data[SFX_N_SOUNDS];
static long explosion_size;
static short *explosion_data[MAX_EXP_DEPTH][MAX_EXP_ANGLE];
static int sound_enabled = 0;
static int has_sound_hw = 0;
static char *filename;		/* input file name */
static int bytes_per_samp;	/* sample width */
static int samps_per_frame;	/* frame size */
static int frames_per_sec;	/* sample rate */
static ALport audio_port[MAX_AUDIO_PORTS + 1];	/* foreground audio ports */
static unsigned long gain_settings[]={0,2,3,5,9,14,16,50,91,156,255};
static int gain_index;

int pause(void);
/* BEGIN PROTOTYPES -S sound.c */
static void sigint_handler(int sig,int code,struct sigcontext *sc);
static double ConvertFromIeeeExtended(char *bytes);
static char *init_audio(int fd, audio_params_t * audio_params,
                        ssnd_chunk_t * ssnd_data, comm_chunk_t * comm_data,
                        long *size);
static ALport init_audio_port(int all);
static void init_explosion_data(short *src_data, long src_size);
static char *load_aiff_file(char *dir, char *name, long *size);
static char *read_aiff_file(int fd, long *size);
static int read_chunk_header(int fd, chunk_header_t * chunk_header);
static void read_comm_chunk(int fd, comm_chunk_t * comm_data,
                            audio_params_t * audio_params);
static void read_form_chunk(int fd, chunk_header_t * chunk_header);
static void read_ssnd_chunk(int fd, chunk_header_t * chunk_header,
                            ssnd_chunk_t * ssnd_data);
static void reset_audio_hw(void);
static void set_gain(float l, float r);
static void set_gain_index(unsigned long gain);
static void skip_chunk(int fd, chunk_header_t * chunk_header);
static void sound_handler(void *arg);
static void wake_up(int sig, int code, struct sigcontext *sc);
/* END PROTOTYPES -S sound.c */


void snd_init_sound(void)
{
  int i;
  char *audio_dir;

  if(audio_port[0]) {
    reset_audio_hw();
    audio_port[0]=NULL;
  }
  if ((has_sound_hw = check_for_audio_hw())) {
    if ((audio_port[0] = init_audio_port(1)) == NULL) {
      fprintf(stderr, "%s: no audio ports available\n", myname);
      return;
    }
    if ((audio_port[1] = init_audio_port(0)) != NULL) {
      audio_port[2] = init_audio_port(0);
    }
    audio_port[3] = NULL;

    if ((audio_dir = getenv("RAWK_SOUNDDIR")) == NULL) {
      audio_dir = "./.data/rawks/Sound";
    }
    for (i = 0; i < SFX_N_SOUNDS; i++) {
      sound_data[i] = (short *)load_aiff_file(audio_dir, Sfx_info[i].filename,
					      &sound_size[i]);
    }

/*
    if (sound_data[SFX_EXPLOSION]) {
      init_explosion_data(sound_data[SFX_EXPLOSION],
			  sound_size[SFX_EXPLOSION]);
    } else {
    */
      explosion_size = 0;
/*    } */

    sigset(SIGINT,sigint_handler);
    if ((Sound_child = sproc(sound_handler, PR_SALL, audio_port)) < 0) {
      fprintf(stderr, "%s: could not sproc sound handler: %s\n",
	      myname, strerror(errno));
      return;
    } else {
      sound_enabled = 1;
      sginap(50);
    }
  } else {
    explosion_size = 0;
    sound_enabled = 0;
    return;
  }

  DEBUG("Allocated sound channels");
  return;
}

void snd_set_volume(int level)
{
  if (level >= 0 &&
      level < sizeof(gain_settings) / sizeof(gain_settings[0])) {
    gain_index = level;
    curr_left_gain = curr_right_gain = gain_settings[gain_index];
  }
}

int snd_toggle_sound(void)
{
  if (has_sound_hw && Sound_child) sound_enabled = !sound_enabled;

  return (sound_enabled);
}

static void sigint_handler(int sig,int code,struct sigcontext *sc)
{
  snd_end_sound();
  exit(0);
}

void snd_end_sound(void)
{
  if (Sound_child > 0) {
    kill(Sound_child, SIGKILL);
  }
  if (has_sound_hw)
    reset_audio_hw();
}

static void reset_audio_hw(void)
{
  long pvbuf[6];
  long buflen;

  pvbuf[0] = AL_LEFT_SPEAKER_GAIN;
  pvbuf[1] = orig_left_gain;
  pvbuf[2] = AL_RIGHT_SPEAKER_GAIN;
  pvbuf[3] = orig_right_gain;
  pvbuf[4] = AL_OUTPUT_RATE;
  pvbuf[5] = orig_output_rate;
  buflen = 6;

  ALsetparams(AL_DEFAULT_DEVICE, pvbuf, buflen);
}

static void set_gain( float l, float r)
{
  long pvbuf[6];
  long buflen;

  pvbuf[0] = AL_LEFT_SPEAKER_GAIN;
  pvbuf[1] = l * curr_left_gain;
  pvbuf[2] = AL_RIGHT_SPEAKER_GAIN;
  pvbuf[3] = r * curr_right_gain;
  pvbuf[4] = AL_OUTPUT_RATE;
  pvbuf[5] = AL_RATE_16000;
  buflen = 6;

  ALsetparams(AL_DEFAULT_DEVICE, pvbuf, buflen);
}

static void sound_handler( void *arg)
{
  ALport *ap = (ALport *) arg;
  int i;
  int qnum,ssnd,esnd;
  int empty;
  int next_port = 0;
  int n_audio_ports;
  long samp_count[SFX_N_SOUNDS];
  long max_samps_per_frame;
  int sound_num[MAX_AUDIO_PORTS];
  long samp_pri[MAX_AUDIO_PORTS];
  long samp_repeat[MAX_AUDIO_PORTS];
  long samps_to_play[MAX_AUDIO_PORTS];
  long samps_played[MAX_AUDIO_PORTS];
  short *sample[MAX_AUDIO_PORTS];

  max_samps_per_frame = D_TIME * 16000.;

  n_audio_ports = 0;
  while (ap[n_audio_ports] != NULL) {
    sample[n_audio_ports] = 0;
    samps_to_play[n_audio_ports] = 0;
    n_audio_ports++;
  }
  for(i=0;i<SFX_N_SOUNDS;i++) samp_count[i] = 0;

  while (1)
  {
    sginap(1); /* Wait for 1/CLK_TCK seconds */

    for(i=0;i<START_SND_QUEUESIZE;i++) {
      if(Start_sound[i]!=-1) {
	ssnd = Start_sound[i];
	Start_sound[i] = -1;
	break;
      }
    }
    esnd = End_sound;   End_sound = -1;

    if(esnd>=0 || Kill_all_sounds) {
      DEBUG("Attempting to kill sound");

      for(i=0;i<n_audio_ports;i++) { /* Find and kill sample */
#if DBG
	printf("sound_num[%d] = %d\n",i,sound_num[i]);
	printf("ssnd = %d\n",ssnd);
#endif
	if(sound_num[i] == esnd || Kill_all_sounds) {
	  samps_to_play[i] = 0;
	  samp_repeat[i] = 0;
	  sample[i] = 0;
	  if(samp_count[esnd]>0) samp_count[esnd]--;
	  DEBUG("Killing sound");
	}
      }
      Kill_all_sounds = 0;
      if(esnd>=0) {
	esnd = -1;
	DEBUG("Deliberate sound kill complete");
	continue;
      }
    }
    if (ssnd>=0) {

      samp_count[ssnd]++;
      sound_num[next_port] = ssnd;
      samp_repeat[next_port] = (Sfx_info[ssnd].repeat) ? 
	sound_size[ssnd] : 0;
      samp_pri[next_port] = Sfx_info[ssnd].pri;
      sample[next_port] = sound_data[ssnd];
      samps_to_play[next_port] = sound_size[ssnd];
      samps_played[next_port] = 0;
      ssnd = -1;
      i = 0;
      do if((++next_port) >= n_audio_ports) next_port = 0;
      while(i++ < n_audio_ports && 
	(samp_repeat[next_port] || samp_pri[next_port]>=SFX_PRI_BACKGROUND));
    }
    empty = 0;
    for (i = 0; i < n_audio_ports; i++) {
      if(ALgetfilled(ap[i]) >= (max_samps_per_frame<<1)) continue;
      if (samps_to_play[i] && samps_to_play[i] <= max_samps_per_frame) {
	set_gain(1., 1.);
	ALwritesamps(ap[i], sample[i] + samps_played[i],
		     samps_to_play[i]);
	samps_played[i] = 0;
	if(!(samps_to_play[i] = samp_repeat[i]))
	  samp_count[sound_num[i]]--;
      }
      if (samps_to_play[i] > max_samps_per_frame) {
	set_gain(1., 1.);
	ALwritesamps(ap[i], sample[i] + samps_played[i],
		     max_samps_per_frame);
	samps_played[i] += max_samps_per_frame;
	samps_to_play[i] -= max_samps_per_frame;
	empty = 1;
      }
    }
  }
}

static ALport init_audio_port( int all)
{
  long pvbuf[6];
  long buflen;
  ALconfig audio_port_config;
  ALport ap;

  if (all) {
    pvbuf[0] = AL_LEFT_SPEAKER_GAIN;
    pvbuf[2] = AL_RIGHT_SPEAKER_GAIN;
    pvbuf[4] = AL_OUTPUT_RATE;
    buflen = 6;
    ALgetparams(AL_DEFAULT_DEVICE, pvbuf, buflen);
    curr_left_gain = orig_left_gain = pvbuf[1];
    curr_right_gain = orig_right_gain = pvbuf[3];
    orig_output_rate = pvbuf[5];

    set_gain_index((orig_left_gain + orig_right_gain) / 2);
  }
  /* configure and open audio port */
  audio_port_config = ALnewconfig();
  ALsetwidth(audio_port_config, AL_SAMPLE_16);
  ALsetchannels(audio_port_config, AL_MONO);

  /* make the ring buffer large enough to hold 0.1 sec of audio samples */
  ALsetqueuesize(audio_port_config, 16000 * 0.1);
  ap = ALopenport(myname, "w", audio_port_config);

  ALfreeconfig(audio_port_config);

  return (ap);
}

static char *load_aiff_file( char *dir, char *name, long *size)
{
  int fd;
  char *data;

  filename = malloc(strlen(name) + strlen(dir) + 2);
  sprintf(filename, "%s/%s", dir, name);

  if ((fd = open(filename, O_RDONLY)) < 0) {
    fprintf(stderr, "%s: can't open %s: %s\n", myname, filename,
	    strerror(errno));
    free(filename);
    return (NULL);
  }
  data = read_aiff_file(fd, size);

  free(filename);

  close(fd);

  return (data);
}

static char *read_aiff_file( int fd, long *size)
{
  int n;
  chunk_header_t chunk_header;
  comm_chunk_t comm_data;
  ssnd_chunk_t ssnd_data;
  audio_params_t audio_params;

  if ((n = read_chunk_header(fd, &chunk_header)) != CHUNK_HEADER) {
/*     fprintf(stderr, "%s: failed to read FORM chunk header\n", myname); */
    return (NULL);
  }
  if (strncmp(chunk_header.id, "FORM", 4)) {
    fprintf(stderr, "%s: couldn't find FORM chunk id\n", myname);
    return (NULL);
  }
  read_form_chunk(fd, &chunk_header);

  /* loop on the local chunks */
  while ((n = read_chunk_header(fd, &chunk_header))) {
    if (n != CHUNK_HEADER) {
      fprintf(stderr, "%s: failed to read a chunk header\n", myname);
      return (NULL);
    }
    /* common */
    if (!strncmp(chunk_header.id, "COMM", 4)) {
      read_comm_chunk(fd, &comm_data, &audio_params);
    }
    /* sound data */
    else if (!strncmp(chunk_header.id, "SSND", 4)) {
      read_ssnd_chunk(fd, &chunk_header, &ssnd_data);
    } else if ((!strncmp(chunk_header.id, "MARK", 4))	/* marker */
	       ||(!strncmp(chunk_header.id, "INST", 4))	/* instrument */
	       ||(!strncmp(chunk_header.id, "APPL", 4))	/* appl specific */
	       ||(!strncmp(chunk_header.id, "MIDI", 4))	/* midi data */
	       ||(!strncmp(chunk_header.id, "AESD", 4))	/* audio  rec */
	       ||(!strncmp(chunk_header.id, "COMT", 4))	/* comments */
	       ||(!strncmp(chunk_header.id, "NAME", 4))	/* text */
	       ||(!strncmp(chunk_header.id, "AUTH", 4))	/* text */
	       ||(!strncmp(chunk_header.id, "(c) ", 4))	/* text */
	       ||(!strncmp(chunk_header.id, "ANNO", 4))	/* text */
	       ||(!strncmp(chunk_header.id, "NONE", 4))	/* ??? */
	       ||(!strncmp(chunk_header.id, "FVER", 4))	/* ??? */
      ) {
      skip_chunk(fd, &chunk_header);
    } else {
      fprintf(stderr, "%s: Skipping unknown chunk id  %c%c%c%c\n",
	      myname, chunk_header.id[0], chunk_header.id[1],
	      chunk_header.id[2], chunk_header.id[3]);
      skip_chunk(fd, &chunk_header);
    }
  }				/* while */

  return (init_audio(fd, &audio_params, &ssnd_data, &comm_data, size));
}

/* R E A D _ C H U N K _ H E A D E R */
static int read_chunk_header( int fd, chunk_header_t * chunk_header)
{
  align_long_t align_long;
  char buf[CHUNK_HEADER];
  int i;
  int n;

  if ((n = read(fd, buf, CHUNK_HEADER)) != CHUNK_HEADER) {
    return (n);
  }
  for (i = 0; i < 4; i++) {
    chunk_header->id[i] = buf[i];
  }

  for (i = 0; i < 4; i++) {
    align_long.b[i] = buf[i + 4];
  }
  chunk_header->size = align_long.l;

  return (CHUNK_HEADER);
}

static void read_form_chunk( int fd, chunk_header_t * chunk_header)
{
  char buf[FORM_CHUNK_DATA];

  if (chunk_header->size < 0) {
    fprintf(stderr, "%s: invalid FORM chunk data size %ld\n", myname,
	    chunk_header->size);
    exit(1);
  } else if (chunk_header->size == 0) {
    fprintf(stderr, "%s: FORM chunk data size = 0\n", myname);
    exit(0);
  }
  if (read(fd, buf, FORM_CHUNK_DATA) != FORM_CHUNK_DATA) {
    fprintf(stderr, "%s: couldn't read AIFF identifier from %s\n",
	    myname, filename);
    exit(1);
  }
  if (strncmp(buf, "AIFF", 4) && strncmp(buf,"AIFC",4)) {
    fprintf(stderr, "%s: %s does not have an AIFF identifier\n",
	    myname, filename);
    exit(1);
  }
}

/* R E A D _ C O M M _ C H U N K */
static void read_comm_chunk( int fd, comm_chunk_t * comm_data,
		audio_params_t * audio_params)
{
  int n;
  char *buf, *bufp;
  int i;
  align_short_t align_short;
  align_long_t align_long;
  double tmpdouble;

  buf = malloc(COMM_CHUNK_DATA + 1);	/* leave an extra loc at the end */

  if ((n = read(fd, buf, COMM_CHUNK_DATA)) != COMM_CHUNK_DATA) {
    fprintf(stderr,
	  "%s: failed to read COMM chunk data. Expected %d bytes, got %d.\n",
	    myname, COMM_CHUNK_DATA, n);
    exit(1);
  }
  bufp = buf;
  for (i = 0; i < 2; i++) {
    align_short.b[i] = *bufp++;
  }
  comm_data->nchannels = align_short.s;
  for (i = 0; i < 4; i++) {
    align_long.b[i] = *bufp++;
  }
  comm_data->nsampframes = align_long.l;
  for (i = 0; i < 2; i++) {
    align_short.b[i] = *bufp++;
  }
  comm_data->sampwidth = align_short.s;

  /* the sample rate value from the common chunk is an 80-bit IEEE extended
   * floating point number: [s bit] [15 exp bits (bias=16383)] [64 mant bits
   * (leading 1 not hidden)]
   * 
   */
  tmpdouble = ConvertFromIeeeExtended(bufp);
  bufp += 10;
  comm_data->samprate = (long)tmpdouble;

  switch (comm_data->samprate) {
    case 48000:
      audio_params->samprate = AL_RATE_48000;
      break;
    case 44100:
      audio_params->samprate = AL_RATE_44100;
      break;
    case 32000:
      audio_params->samprate = AL_RATE_32000;
      break;
    case 22050:
      audio_params->samprate = AL_RATE_22050;
      break;
    case 16000:
      audio_params->samprate = AL_RATE_16000;
      break;
    case 11025:
      audio_params->samprate = AL_RATE_11025;
      break;
    case 8000:
      audio_params->samprate = AL_RATE_8000;
      break;
    default:
      fprintf(stderr, "%s: can't set output sample rate to %ld\n",
	      myname, comm_data->samprate);
      audio_params->samprate = AL_RATE_48000;
  }

  switch (comm_data->nchannels) {
    case 1:
      audio_params->nchannels = AL_MONO;
      break;
    case 2:
      audio_params->nchannels = AL_STEREO;
      break;
    default:
      fprintf(stderr, "%s: can't handle %d channels per frame\n",
	      myname, comm_data->nchannels);
      audio_params->nchannels = AL_STEREO;
  }
  switch (comm_data->sampwidth) {
    case 8:
      audio_params->sampwidth = AL_SAMPLE_8;
      break;
    case 16:
      audio_params->sampwidth = AL_SAMPLE_16;
      break;
    case 24:
      audio_params->sampwidth = AL_SAMPLE_24;
      break;
    default:
      fprintf(stderr, "%s: unsupported sample width %d bits\n",
	      myname, comm_data->nchannels);
      audio_params->sampwidth = AL_SAMPLE_16;
  }
  free(buf);
}

static void read_ssnd_chunk( int fd, chunk_header_t * chunk_header,
			ssnd_chunk_t * ssnd_data)
{
  char buf[SSND_CHUNK_DATA];
  int i;
  align_long_t align_long;

  read(fd, buf, SSND_CHUNK_DATA);

  for (i = 0; i < 4; i++) {
    align_long.b[i] = buf[i];
  }
  ssnd_data->offset = align_long.l;
  for (i = 0; i < 4; i++) {
    align_long.b[i] = buf[i + 4];
  }
  ssnd_data->blocksize = align_long.l;
  /* store the offset to the beginning of the audio sample data so that we can
   * come back and play it later */
  ssnd_data->file_position = lseek(fd, 0, SEEK_CUR);
  ssnd_data->sample_area_bytes = chunk_header->size - 2 * sizeof(long);
  if (chunk_header->size % 2 == 1) {
    ssnd_data->sample_area_bytes++;
  }
  /* move the fileptr to the end of the chunk */
  lseek(fd, ssnd_data->sample_area_bytes, SEEK_CUR);
}

/* S K I P _ C H U N K */
static void skip_chunk( int fd, chunk_header_t * chunk_header)
{
  char id[5];
  int s;

  strncpy(id, chunk_header->id, 4);
  id[4] = '\0';

  /* skip the pad byte, if necessary */
  s = ((chunk_header->size % 2) == 1) ? chunk_header->size + 1 : chunk_header->size;
  lseek(fd, s, SEEK_CUR);
}

/* C O N V E R T   F R O M   I E E E   E X T E N D E D  */

/* Copyright (C) 1988-1991 Apple Computer, Inc. All rights reserved.
 * 
 * Warranty Information Even though Apple has reviewed this software, Apple makes
 * no warranty or representation, either express or implied, with respect to
 * this software, its quality, accuracy, merchantability, or fitness for a
 * particular purpose.  As a result, this software is provided "as is," and
 * you, its user, are assuming the entire risk as to its quality and accuracy.
 * 
 * This code may be used and freely distributed as long as it includes this
 * copyright notice and the above warranty information.
 * 
 * Machine-independent I/O routines for IEEE floating-point numbers.
 * 
 * NaN's and infinities are converted to HUGE_VAL or HUGE, which happens to be
 * infinity on IEEE machines.  Unfortunately, it is impossible to preserve
 * NaN's in a machine-independent way. Infinities are, however, preserved on
 * IEEE machines.
 * 
 * These routines have been tested on the following machines: Apple Macintosh,
 * MPW 3.1 C compiler Apple Macintosh, THINK C compiler Silicon Graphics IRIS,
 * MIPS compiler Cray X/MP and Y/MP Digital Equipment VAX
 * 
 * 
 * Implemented by Malcolm Slaney and Ken Turkowski.
 * 
 * Malcolm Slaney contributions during 1988-1990 include big- and little- endian
 * file I/O, conversion to and from Motorola's extended 80-bit floating-point
 * format, and conversions to and from IEEE single- precision floating-point
 * format.
 * 
 * In 1991, Ken Turkowski implemented the conversions to and from IEEE
 * double-precision format, added more precision to the extended conversions,
 * and accommodated conversions involving +/- infinity, NaN's, and
 * denormalized numbers. */

#ifndef HUGE_VAL
#define HUGE_VAL HUGE
#endif /* HUGE_VAL */

#define UnsignedToFloat(u)    \
     (((double)((long)(u - 2147483647L - 1))) + 2147483648.0)

/****************************************************************
 * Extended precision IEEE floating-point conversion routine.
 ****************************************************************/

static double ConvertFromIeeeExtended( char *bytes)
{
  double f;
  long expon;
  unsigned long hiMant;
  unsigned long loMant;

  DEBUG("Converting from IEEE floating point extended");
  expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF);
  hiMant = ((unsigned long)(bytes[2] & 0xFF) << 24)
    | ((unsigned long)(bytes[3] & 0xFF) << 16)
    | ((unsigned long)(bytes[4] & 0xFF) << 8)
    | ((unsigned long)(bytes[5] & 0xFF));
  loMant = ((unsigned long)(bytes[6] & 0xFF) << 24)
    | ((unsigned long)(bytes[7] & 0xFF) << 16)
    | ((unsigned long)(bytes[8] & 0xFF) << 8)
    | ((unsigned long)(bytes[9] & 0xFF));

  if (expon == 0 && hiMant == 0 && loMant == 0) {
    f = 0;
  } else {
    if (expon == 0x7FFF) {	/* Infinity or NaN */
      f = HUGE_VAL;
    } else {
      expon -= 16383;
      f = ldexp(UnsignedToFloat(hiMant), expon -= 31);
      f += ldexp(UnsignedToFloat(loMant), expon -= 32);
    }
  }

  if (bytes[0] & 0x80)
    return (-f);
  else
    return (f);
}

/* I N I T _ A U D I O */
static char *init_audio( int fd, audio_params_t * audio_params,
			 ssnd_chunk_t * ssnd_data, comm_chunk_t * comm_data,
			 long *size)
{
  int i,j;
  char *buf;
  int bytes_read;
  int do_8_to_16=0;

  /* decide what size blocks of samples we should read from the AIFF file and
   * pass to ALwritesamps */
  switch (audio_params->sampwidth) {
    default:
    case AL_SAMPLE_8:
      fprintf(stderr, "%s: sound file %s is not 16 bytes/sample\n",
	      myname, filename);
      return (NULL);

/*       bytes_per_samp = 1; */
/*       do_8_to_16 = 1; */
      break;

    case AL_SAMPLE_16:
      bytes_per_samp = 2;
      break;
  }

  switch (audio_params->nchannels) {
    default:
    case AL_MONO:
      samps_per_frame = 1;
      break;

    case AL_STEREO:
      fprintf(stderr, "%s: sound file %s is in stereo (not supported)\n",
	      myname, filename);
      return (NULL);
      break;
  }

  switch (audio_params->samprate) {
    default:
    case AL_RATE_48000:
    case AL_RATE_44100:
    case AL_RATE_32000:
    case AL_RATE_22050:
    case AL_RATE_11025:
    case AL_RATE_8000:
      fprintf(stderr, "%s: sound file %s is not sampled at 16KHz\n",
	      myname, filename);
      return (NULL);
      break;

    case AL_RATE_16000:
      frames_per_sec = 16000;
      break;
  }

  *size = comm_data->nsampframes * samps_per_frame * bytes_per_samp;

  buf = (char *)malloc(*size);

/*   if(!do_8_to_16) buf = (char *)malloc(*size); */
/*   else buf = (char *)malloc(*size * 2); */

  lseek(fd, ssnd_data->file_position, SEEK_SET);
  if ((bytes_read = read(fd, buf, *size)) < *size) {
    fprintf(stderr, "%s: short read on %s\n", myname, filename);
    *size = bytes_read;
  }
  if(samps_per_frame > 1) *size /= samps_per_frame;
  (*size) >>= 1;

/*   if (do_8_to_16) { */
/*       for(i=(((*size))-1), j=(((*size)<<1) - 1) ; */
/* 	  i>=0 && j>=1 ; */
/* 	  i--, j-=2) */
/*       { */
/* 	  buf[j] = buf[i]; */
/* 	  buf[j-1]=buf[i]; */
/*       } */
/*       (*size) <<= 1; */
/*   } */

  return (buf);
}

int check_for_audio_hw(void)
{
  int n;

  if((n=open("/dev/hdsp/hdsp0master",O_RDWR)) < 0)
  {
/*    fprintf(stderr,"Can't play audio data on this hardware platform\n"); */
    return 0;
  }
  close(n);
  return 1;
}

int snd_volume_change( int direction)
{
  if (direction == 1) {
    if (gain_index <
	sizeof(gain_settings) / sizeof(gain_settings[0]) - 1) {
      gain_index++;
    }
  } else {
    if (gain_index > 0) {
      gain_index--;
    }
  }
  curr_left_gain = curr_right_gain = gain_settings[gain_index];

  set_gain(1., 1.);

  return (gain_index);
}

static void set_gain_index( unsigned long gain)
{
  int i;
  long min = 256;
  long diff;

  for (i = 0; i < sizeof(gain_settings) / sizeof(gain_settings[0]);
       i++) {
    diff = gain - gain_settings[i];
    if (diff < 0)
      diff = -diff;
    if (diff < min) {
      min = diff;
      gain_index = i;
    }
  }
}

static void init_explosion_data( short *src_data, long src_size)
{
  int d;
  int a;
  int i;
  int st = 0;
  double angle;
  double dist;
  double s;
  double x;
  double y;
  double l;
  double r;

  for (d = 0; d < MAX_EXP_DEPTH && !st; d++) {
    for (a = 0; a < MAX_EXP_ANGLE && !st; a++) {
      if ((explosion_data[d][a] = (short *)malloc(
			      (src_size * 2 + 1) * sizeof(short))) == NULL) {
	st = 1;
      }
    }
  }

  if (st) {
    for (d = 0; d < MAX_EXP_DEPTH; d++) {
      for (a = 0; a < MAX_EXP_ANGLE; a++) {
	if (explosion_data[d][a])
	  free(explosion_data[d][a]);
      }
    }
    explosion_size = 0;
    return;
  }
  for (d = 0; d < MAX_EXP_DEPTH; d++) {
    for (a = 0; a < MAX_EXP_ANGLE; a++) {
      angle = a * EXP_DELTA_ANGLE;
      dist = d * EXP_DELTA_DEPTH;
      x = cos(angle);
      s = 0.5 * (x * ABS(x));
      dist = 1. - (dist / 1250.);
      dist *= dist;
      r = sqrt((0.5 - s) * dist);
      l = sqrt((0.5 + s) * dist);
      for (i = 0; i < 2 * src_size; i += 2) {
	explosion_data[d][a][i + 0] = r * src_data[i + 0];
	explosion_data[d][a][i + 1] = l * src_data[i + 1];
      }
      explosion_data[d][a][2 * src_size] = 0;
    }
  }
  explosion_size = src_size;
}
#endif /* !NO_SOUND */
