text: /* sndread.c -- read sound files */
  
  /* CHANGELOG
   *
   * 29Jun95  RBD  ULAW fixed problems with signed chars
   * 28Apr03  dm   explicitly declare sndread_file_open_count as int
   * 24Jul08  RBD & Judy Hawkins -- replace snd with PortAudio and libsndfile
   */
  
  #include "switches.h"
  #include "stdio.h"
  #include "string.h"
  #ifdef UNIX
  #include "sys/file.h"
  #else
  /* #include <unistd.h> */
  #ifdef WINDOWS
  #include <sys/stat.h>
  #include "io.h"
  #else
  #include <stat.h>
  #endif
  #define L_SET SEEK_SET
  #define L_INCR SEEK_CUR
  #define PROTECTION 
  #endif
  #ifndef mips
  #include "stdlib.h"
  #endif
  #include "sndfile.h"
  #include "xlisp.h"
  #include "sound.h"
  #include "sndfmt.h"
  #include "falloc.h"
  #include "sndread.h"
  #include "multiread.h"
  
  /* file.h doesn't define O_RDONLY under RS6K AIX */
  #ifndef O_RDONLY
  #define O_RDONLY 0
  #endif
  
  static int sndread_file_open_count = 0;
  
  void read__fetch(susp, snd_list)
    register read_susp_type susp;
    snd_list_type snd_list;
  {
      long n; /* jlh Changed type to long, trying to make move_samples_... work */
      sample_block_type out;
      register sample_block_values_type out_ptr;
      /* allow up to 4 bytes/sample:  jlh -- does this need to be 8? */
      /* FIX -- why 8? for doubles? Maybe it should be sizeof(sample). I think
         this buffer was here to allow you to input any format and convert to
         float. The assumption was no sample would be longer than 4 bytes and
         after conversion, samples would be 4 byte floats. 
      */
      long in_count; /* jlh Trying to make move_samples_... work */
  
      falloc_sample_block(out, "read__fetch");
      out_ptr = out->samples;
      snd_list->block = out;
  
      in_count = sf_readf_float(susp->sndfile, out_ptr, max_sample_block_len);
  
      n = in_count;
  
      /* don't read too many */
      if (n > (susp->cnt - susp->susp.current)) {
          n = susp->cnt - susp->susp.current;
      }
  
      snd_list->block_len = n;
      susp->susp.current += n;
  
      if (n == 0) {
          /* we didn't read anything, but can't return length zero, so
             convert snd_list to pointer to zero block */
          snd_list_terminate(snd_list);
      } else if (n < max_sample_block_len) {
          /* this should close file and free susp */
          snd_list_unref(snd_list->u.next);
          /* if something is in buffer, terminate by pointing to zero block */
          snd_list->u.next = zero_snd_list;
      }
  } /* read__fetch */
  
  
  void read_free(read_susp_type susp)
  {
      sf_close(susp->sndfile);
      sndread_file_open_count--;
      ffree_generic(susp, sizeof(read_susp_node), "read_free");
  }
  
  
  void read_print_tree(read_susp_type susp, int n)
  {
  }
  
  
  LVAL snd_make_read(
    unsigned char *filename, 	/* file to read */
    time_type offset, 	/* offset to skip (in seconds) */
    time_type t0,		/* start time of resulting sound */
    long *format,		/* AIFF, IRCAM, NeXT, etc. */
    long *channels,	/* number of channels */
    long *mode, 		/* sample format: PCM, ALAW, etc. */
    long *bits,		/* BPS: bits per sample */
    long *swap,           /* swap bytes */
    double *srate,	/* srate: sample rate */
    double *dur,		/* duration (in seconds) to read */
    long *flags,		/* which parameters have been set */
    long *byte_offset)	/* byte offset in file of first sample */
  {
      register read_susp_type susp;
      /* srate specified as input parameter */
      sample_type scale_factor = 1.0F;
      sf_count_t frames;
      double actual_dur;
  
      falloc_generic(susp, read_susp_node, "snd_make_read");
      memset(&(susp->sf_info), 0, sizeof(SF_INFO));
  
      susp->sf_info.samplerate = ROUND(*srate);
      susp->sf_info.channels = *channels;
  
      switch (*mode) {
      case SND_MODE_ADPCM:
          susp->sf_info.format = SF_FORMAT_IMA_ADPCM;
          break;
      case SND_MODE_PCM:
          if (*bits == 8) susp->sf_info.format = SF_FORMAT_PCM_S8;
          else if (*bits == 16) susp->sf_info.format = SF_FORMAT_PCM_16;
          else if (*bits == 24) susp->sf_info.format = SF_FORMAT_PCM_24;
          else if (*bits == 32) susp->sf_info.format = SF_FORMAT_PCM_32;
          else {
              susp->sf_info.format = SF_FORMAT_PCM_16;
              *bits = 16;
          }
          break;
      case SND_MODE_ULAW:
          susp->sf_info.format = SF_FORMAT_ULAW;
          break;
      case SND_MODE_ALAW:
          susp->sf_info.format = SF_FORMAT_ALAW;
          break;
      case SND_MODE_FLOAT:
          susp->sf_info.format = SF_FORMAT_FLOAT;
          break;
      case SND_MODE_UPCM:
          susp->sf_info.format = SF_FORMAT_PCM_U8;
          *bits = 8;
          break;
      }
  
      if (*format == SND_HEAD_RAW) susp->sf_info.format |= SF_FORMAT_RAW;
  
      if (*swap) {
          /* set format to perform a byte swap (change from cpu endian-ness) */
          /* write the code so it will only compile if one and only one 
             ENDIAN setting is defined */
  #ifdef XL_LITTLE_ENDIAN
          long format = SF_ENDIAN_BIG;
  #endif
  #ifdef XL_BIG_ENDIAN
          long format = SF_ENDIAN_LITTLE;
  #endif
          susp->sf_info.format |= format;
      }
  
      susp->sndfile = sf_open((const char *) filename, SFM_READ, 
                              &(susp->sf_info));
  
      if (!susp->sndfile) {
          char error[240];
          sprintf(error, "SND-READ: Cannot open file '%s'", filename);
          xlfail(error);
      }
      if (susp->sf_info.channels < 1) {
          sf_close(susp->sndfile);
          xlfail("Must specify 1 or more channels");
      }
  
      /* report samplerate from file, but if user provided a double
       * as sample rate, don't replace it with an integer.
       */
      if ((susp->sf_info.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) {
          *srate = susp->sf_info.samplerate;
      }
      /* compute dur */
      frames = sf_seek(susp->sndfile, 0, SEEK_END);
      actual_dur = ((double) frames) / *srate;
      if (offset < 0) offset = 0;
      /* round offset to an integer frame count */
      frames = (sf_count_t) (offset * *srate + 0.5);
      offset = ((double) frames) / *srate;
      actual_dur -= offset;
      if (actual_dur < 0) {
          sf_close(susp->sndfile);
          xlfail("SND-READ: offset is beyond end of file");
      }
      if (actual_dur < *dur) *dur = actual_dur;
  
      sf_seek(susp->sndfile, frames, SEEK_SET); /* return to read loc in file */
  
      /* initialize susp state */
      susp->susp.sr = *srate;
      susp->susp.t0 = t0;
      susp->susp.mark = NULL;
      susp->susp.print_tree = read_print_tree; /*jlh empty function... */
      susp->susp.current = 0;
      susp->susp.log_stop_cnt = UNKNOWN;
      /* watch for overflow */
      if (*dur * *srate + 0.5 > (unsigned long) 0xFFFFFFFF) {
          susp->cnt = 0x7FFFFFFF;
      } else {
          susp->cnt = ROUND((*dur) * *srate);
      }
  
      switch (susp->sf_info.format & SF_FORMAT_TYPEMASK) {
      case SF_FORMAT_WAV: *format = SND_HEAD_WAVE; break;
      case SF_FORMAT_AIFF: *format = SND_HEAD_AIFF; break;
      case SF_FORMAT_AU: *format = SND_HEAD_NEXT; break;
      case SF_FORMAT_RAW: *format = SND_HEAD_RAW; break;
      case SF_FORMAT_PAF: *format = SND_HEAD_PAF; break;
      case SF_FORMAT_SVX: *format = SND_HEAD_SVX; break;
      case SF_FORMAT_NIST: *format = SND_HEAD_NIST; break;
      case SF_FORMAT_VOC: *format = SND_HEAD_VOC; break;
      case SF_FORMAT_W64: *format = SND_HEAD_W64; break;
      case SF_FORMAT_MAT4: *format = SND_HEAD_MAT4; break;
      case SF_FORMAT_MAT5: *format = SND_HEAD_MAT5; break;
      case SF_FORMAT_PVF: *format = SND_HEAD_PVF; break;
      case SF_FORMAT_XI: *format = SND_HEAD_XI; break;
      case SF_FORMAT_HTK: *mode = SND_HEAD_HTK; break;
      case SF_FORMAT_SDS: *mode = SND_HEAD_SDS; break;
      case SF_FORMAT_AVR: *mode = SND_HEAD_AVR; break;
      case SF_FORMAT_WAVEX: *format = SND_HEAD_WAVE; break;
      case SF_FORMAT_SD2: *format = SND_HEAD_SD2; break;
      case SF_FORMAT_FLAC: *format = SND_HEAD_FLAC; break;
      case SF_FORMAT_CAF: *format = SND_HEAD_CAF; break;
      default: *format = SND_HEAD_NONE; break;
      }
      *channels = susp->sf_info.channels;
      switch (susp->sf_info.format & SF_FORMAT_SUBMASK) {
      case SF_FORMAT_PCM_S8: *bits = 8; *mode = SND_MODE_PCM; break;
      case SF_FORMAT_PCM_16: *bits = 16; *mode = SND_MODE_PCM; break;
      case SF_FORMAT_PCM_24: *bits = 24; *mode = SND_MODE_PCM; break;
      case SF_FORMAT_PCM_32: *bits = 32; *mode = SND_MODE_PCM; break;
      case SF_FORMAT_PCM_U8: *bits = 8; *mode = SND_MODE_UPCM; break;
      case SF_FORMAT_FLOAT: *bits = 32; *mode = SND_MODE_FLOAT; break;
      case SF_FORMAT_DOUBLE: *bits = 64; *mode = SND_MODE_DOUBLE; break;
      case SF_FORMAT_ULAW: *bits = 8; *mode = SND_MODE_ULAW; break;
      case SF_FORMAT_ALAW: *bits = 8; *mode = SND_MODE_ALAW; break;
      case SF_FORMAT_IMA_ADPCM: *bits = 16; *mode = SND_MODE_ADPCM; break;
      case SF_FORMAT_MS_ADPCM: *bits = 16; *mode = SND_MODE_ADPCM; break;
      case SF_FORMAT_GSM610: *bits = 16; *mode = SND_MODE_GSM610; break;
      case SF_FORMAT_VOX_ADPCM: *bits = 16; *mode = SND_MODE_ADPCM; break;
      case SF_FORMAT_G721_32: *bits = 16; *mode = SND_MODE_ADPCM; break;
      case SF_FORMAT_G723_24: *bits = 16; *mode = SND_MODE_ADPCM; break;
      case SF_FORMAT_G723_40: *bits = 16; *mode = SND_MODE_ADPCM; break;
      case SF_FORMAT_DWVW_12: *bits = 12; *mode = SND_MODE_DWVW; break;
      case SF_FORMAT_DWVW_16: *bits = 16; *mode = SND_MODE_DWVW; break;
      case SF_FORMAT_DWVW_24: *bits = 24; *mode = SND_MODE_DWVW; break;
      case SF_FORMAT_DWVW_N: *bits = 32; *mode = SND_MODE_DWVW; break;
      case SF_FORMAT_DPCM_8: *bits = 8; *mode = SND_MODE_DPCM; break;
      case SF_FORMAT_DPCM_16: *bits = 16; *mode = SND_MODE_DPCM; break;
      default: *mode = SND_MODE_UNKNOWN; break;
      }
      sndread_file_open_count++;
  #ifdef MACINTOSH
      if (sndread_file_open_count > 24) {
          nyquist_printf("Warning: more than 24 sound files are now open\n");
      }
  #endif
      /* report info back to caller */
      if ((susp->sf_info.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) {
          *flags = SND_HEAD_CHANNELS | SND_HEAD_MODE | SND_HEAD_BITS |
                   SND_HEAD_SRATE | SND_HEAD_LEN | SND_HEAD_TYPE;
      }    
      if (susp->sf_info.channels == 1) {
          susp->susp.fetch = read__fetch;
          susp->susp.free = read_free;
          susp->susp.name = "read";
          return cvsound(sound_create((snd_susp_type)susp, t0, *srate, 
                                      scale_factor));
      } else {
          susp->susp.fetch = multiread_fetch;
          susp->susp.free = multiread_free;
          susp->susp.name = "multiread";
          return multiread_create(susp);
      }
  }
  
  
  
  
  