/* sndread.c -- read sound files */

/* CHANGELOG
 *
 * 29Jun95  RBD  ULAW fixed problems with signed chars
 */

#include "stdio.h"
#include "sys/file.h"
#ifndef mips
#include "stdlib.h"
#endif
#include "sndheader.h"
#include "xlisp.h"
#include "sound.h"
#include "falloc.h"
#include "sndread.h"
#include "multiread.h"

int ulaw_table[256] = {
    -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
    -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
    -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
    -11900, -11388, -10876, -10364,  -9852,  -9340,  -8828,  -8316,
     -7932,  -7676,  -7420,  -7164,  -6908,  -6652,  -6396,  -6140,
     -5884,  -5628,  -5372,  -5116,  -4860,  -4604,  -4348,  -4092,
     -3900,  -3772,  -3644,  -3516,  -3388,  -3260,  -3132,  -3004,
     -2876,  -2748,  -2620,  -2492,  -2364,  -2236,  -2108,  -1980,
     -1884,  -1820,  -1756,  -1692,  -1628,  -1564,  -1500,  -1436,
     -1372,  -1308,  -1244,  -1180,  -1116,  -1052,   -988,   -924,
      -876,   -844,   -812,   -780,   -748,   -716,   -684,   -652,
      -620,   -588,   -556,   -524,   -492,   -460,   -428,   -396,
      -372,   -356,   -340,   -324,   -308,   -292,   -276,   -260,
      -244,   -228,   -212,   -196,   -180,   -164,   -148,   -132,
      -120,   -112,   -104,    -96,    -88,    -80,    -72,    -64,
       -56,    -48,    -40,    -32,    -24,    -16,     -8,      0,
     32124,  31100,  30076,  29052,  28028,  27004,  25980,  24956,
     23932,  22908,  21884,  20860,  19836,  18812,  17788,  16764,
     15996,  15484,  14972,  14460,  13948,  13436,  12924,  12412,
     11900,  11388,  10876,  10364,   9852,   9340,   8828,   8316,
      7932,   7676,   7420,   7164,   6908,   6652,   6396,   6140,
      5884,   5628,   5372,   5116,   4860,   4604,   4348,   4092,
      3900,   3772,   3644,   3516,   3388,   3260,   3132,   3004,
      2876,   2748,   2620,   2492,   2364,   2236,   2108,   1980,
      1884,   1820,   1756,   1692,   1628,   1564,   1500,   1436,
      1372,   1308,   1244,   1180,   1116,   1052,    988,    924,
       876,    844,    812,    780,    748,    716,    684,    652,
       620,    588,    556,    524,    492,    460,    428,    396,
       372,    356,    340,    324,    308,    292,    276,    260,
       244,    228,    212,    196,    180,    164,    148,    132,
       120,    112,    104,     96,     88,     80,     72,     64,
	56,     48,     40,     32,     24,     16,      8,      0 };


void read_free();

/* file.h doesn't define O_RDONLY under RS6K AIX */
#ifndef O_RDONLY
#define O_RDONLY 0
#endif

void read__fetch(susp, snd_list)
  register read_susp_type susp;
  snd_list_type snd_list;
{
    double scale_factor;
    int i;
    int n;
    sample_block_type out;
    register sample_block_values_type out_ptr;
    /* allow up to 4 bytes/sample: */
    char input_buffer[max_sample_block_len * 4];
    unsigned char *byte_buffer;
    short *int16_buffer;
    long *int32_buffer;
    float *float_buffer;
    int in_count;

    falloc_sample_block(out, "read__fetch");
    out_ptr = out->samples;
    snd_list->block = out;

    in_count = read(susp->inf, input_buffer,
		    max_sample_block_len * susp->bytes_per_sample);
    n = in_count / susp->bytes_per_sample;

    /* don't read too many */
    if (n > (susp->cnt - susp->susp.current)) {
	n = susp->cnt - susp->susp.current;
    }

    switch (susp->bytes_per_sample) {
      case 1:
	if (susp->mode == MODE_ULAW) {
	    unsigned char *bytes = (unsigned char *) input_buffer;
	    scale_factor = 1.0 / SCALE_FACTOR_TO_SHORT;
	    for (i = 0; i < n; i++) {
		*out_ptr++ = st_ulaw_to_linear(bytes[i]) * scale_factor;
	    }
	} else if (susp->mode == MODE_UPCM) {
	    unsigned char *bytes = (unsigned char *) input_buffer;
	    scale_factor = 1.0 / SCALE_FACTOR_TO_BYTE;
	    for (i = 0; i < n; i++) {
		*out_ptr++ = (((int) bytes[i]) - 127) * scale_factor;
	    }
	} else {
	    signed char *bytes = (signed char *) input_buffer;
	    scale_factor = 1.0 / SCALE_FACTOR_TO_BYTE;
	    for (i = 0; i < n; i++) {
		*out_ptr++ = bytes[i] * scale_factor;
	    }
	}
	break;
      case 2:
	scale_factor = 1.0 / SCALE_FACTOR_TO_SHORT;        
	int16_buffer = (short *) input_buffer;
	for (i = 0; i < n; i++) {
	    *out_ptr++ = int16_buffer[i] * scale_factor;
	}
	break;
      case 4:
	if (susp->mode == MODE_FLOAT) {
	    float_buffer = (float *) input_buffer;
	    for (i = 0; i < n; i++) {
		*out_ptr++ = float_buffer[i];
	    }
	} else {
	    scale_factor = 1.0 / SCALE_FACTOR_TO_LONG;
	    int32_buffer = (long *) input_buffer;
	    for (i = 0; i < n; i++) {
		*out_ptr++ = int32_buffer[i] * scale_factor;
	    }
	}
	break;
      default:
	printf("error: cannot handle %d bytes per sample\n",
		 susp->bytes_per_sample);
	break;
    }
    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)
{
    (void) close(susp->inf);
    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 */
  double *srate,	/* srate: sample rate */
  double *dur,		/* duration (in seconds) to read */
  long *flags)		/* which parameters have been set */
{
    register read_susp_type susp;
    /* srate specified as input parameter */
    sample_type scale_factor = 1.0;
    int inf;
    long length = 1000000000; /* 1 gig */
    long offset_in_bytes;

    inf = open_snd_file((char *) filename, format, channels, mode, bits, 
			    srate, &length, flags);
    if (inf < 0) {
	char error[100];
	sprintf(error, "SND-READ: Cannot open file '%s'", filename);
	xlfail(error);
    }
    if (*channels < 1) xlfail("Must specify 1 or more channels");
    offset_in_bytes = ((long) (offset * *srate + 0.5)) *
		     ((*bits + 7) >> 3) * *channels;
    if (offset_in_bytes >= length) return NIL;
    length -= offset_in_bytes;
    if (offset < 0) xlfail("Negative offset");
    if (offset > 0) lseek(inf, offset_in_bytes, L_INCR);

    /* see if file is shorter than requested duration and adjust */
    if ((*flags & SND_HEAD_LEN) &&
	(((long) (*dur * *srate + 0.5)) * ((*bits + 7) >> 3) * *channels >
	 length)) {
	*dur = (length / (((*bits + 7) >> 3) * *channels)) / *srate;
    }

    falloc_generic(susp, read_susp_node, "snd_make_read");

    /* initialize susp state */
    susp->susp.sr = *srate;
    susp->susp.t0 = t0;
    susp->nchans = *channels;
    susp->mode = *mode;
    susp->bytes_per_sample = (*bits + 7) >> 3;
    susp->susp.mark = NULL;
    susp->susp.print_tree = read_print_tree;
    susp->susp.current = 0;
    susp->inf = inf;
    susp->cnt = (*dur * *srate) + 0.5;

    if (*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);
    }
}


