/* gusvoice.c, adapted (slightly) from gusload.c by H. Savolainen, --gl */

#include <stdio.h>
/*#include <linux/ultrasound.h>*/
/*#include <sys/ioctl.h>*/
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>

typedef union {
      unsigned char c[4];
      unsigned long l;
} long_end;
typedef union {
      unsigned char c[2];
      unsigned short s;
} short_end;

typedef struct linfo {
	unsigned char velmin;
	unsigned char velmax;
	unsigned char left_samples;
	unsigned char right_samples;
} linfo;

#pragma pack (1)

struct pat_header
  {
    char            magic[12];
    char            version[10];
    char            description[60];
    unsigned char   instruments;
    unsigned char            voices;
    unsigned char            channels;
    unsigned short  nr_waveforms;
    unsigned short  master_volume;
    unsigned long   data_size;
    char	    sf2magic[7];
    char	    unused1[29];
    short	    instrument_number;
    char	    instrument_name[16];
    int		    instrument_size;
    unsigned char   layer_count;
    unsigned char   velocity_count;
    struct linfo    sf2layer[9];
    char	    unused2[40-37];
    char	    layer_duplicate;
    char	    layer_number;
    int		    layer_size;
    unsigned char   num_samples;
    struct linfo    sf2layer2[10];
  };

#pragma pack (1)

struct sample_header
  {
    char            name[7];
    unsigned char   fractions;
    long            len;
    long            loop_start;
    long            loop_end;
    unsigned short  base_freq;
    long            low_note;
    long            high_note;
    long            base_note;
    short           detune;
    unsigned char   panning;

    unsigned char   envelope_rate[6];
    unsigned char   envelope_offset[6];

    unsigned char   tremolo_sweep;
    unsigned char   tremolo_rate;
    unsigned char   tremolo_depth;

    unsigned char   vibrato_sweep;
    unsigned char   vibrato_rate;
    unsigned char   vibrato_depth;

    char            modes;

    short           scale_frequency;
    unsigned short  scale_factor;
    unsigned short  volume; /* Is this really here?? */

    unsigned char   volume_envelope_delay;
    unsigned char   exclusive_class;
    unsigned char   vibrato_delay;

    unsigned char   mod_envelope_rate[6];
    unsigned char   mod_envelope_offset[6];
    unsigned char   mod_envelope_delay;
    unsigned char   chorus_effect;
    unsigned char   reverb_effect;
    short	    resonance;
    unsigned short  cutoff_frequency;
    unsigned char   modEnvToPitch;
    unsigned char   modEnvToFilterFc;
    unsigned char   modLfoToFilterFc;
    unsigned char   keynumToModEnvHold;
    unsigned char   keynumToModEnvDecay;
    unsigned char   keynumToVolEnvHold;
    unsigned char   keynumToVolEnvDecay;
    unsigned char   true_panning;

    short	    lfo_rate;
    unsigned char   lfo_depth;
    char	    aps_parameter;
  };

static int big_endian = 1;

static long longnum(unsigned char c1, unsigned char c2,
		unsigned char c3, unsigned char c4)
{
    long_end lswitch;
    if (big_endian) {
	lswitch.c[0] = c4;
	lswitch.c[1] = c3;
	lswitch.c[2] = c2;
	lswitch.c[3] = c1;
    } else {
	lswitch.c[0] = c1;
	lswitch.c[1] = c2;
	lswitch.c[2] = c3;
	lswitch.c[3] = c4;
    }
    return(lswitch.l);
}

static short shortnum(unsigned char c1, unsigned char c2)
{
    short_end sswitch;
    if (big_endian) {
	sswitch.c[0] = c2;
	sswitch.c[1] = c1;
    } else {
	sswitch.c[0] = c1;
	sswitch.c[1] = c2;
    }
    return(sswitch.s);
}

static void byteorder()
{   long_end hexx;

    hexx.c[0] = 0x12;
    hexx.c[1] = 0x34;
    hexx.c[2] = 0x56;
    hexx.c[3] = 0x78;
    if (hexx.l == 0x78563412) big_endian = 0;
}


unsigned int freq_table[128] =
{
 8176, 8662, 9177, 9723, 
 10301, 10913, 11562, 12250, 
 12978, 13750, 14568, 15434,
 
 16352, 17324, 18354, 19445,
 20602, 21827, 23125, 24500, 
 25957, 27500, 29135, 30868, 

 32703, 34648, 36708, 38891,
 41203, 43654, 46249, 48999,
 51913, 55000, 58270, 61735,

 65406, 69296, 73416, 77782,
 82407, 87307, 92499, 97999,
 103826, 110000, 116541, 123471,

 130813, 138591, 146832, 155563,
 164814, 174614, 184997, 195998,
 207652, 220000, 233082, 246942,

 261626, 277183, 293665, 311127,
 329628, 349228, 369994, 391995,
 415305, 440000, 466164, 493883,

 523251, 554365, 587330, 622254,
 659255, 698456, 739989, 783991,
 830609, 880000, 932328, 987767,

 1046502, 1108731, 1174659, 1244508,
 1318510, 1396913, 1479978, 1567982,
 1661219, 1760000, 1864655, 1975533,

 2093005, 2217461, 2349318, 2489016,
 2637020, 2793826, 2959955, 3135963,
 3322438, 3520000, 3729310, 3951066,

 4186009, 4434922, 4698636, 4978032,
 5274041, 5587652, 5919911, 6271927,
 6644875, 7040000, 7458620, 7902133,

 8372018, 8869844, 9397273, 9956063, 
 10548082, 11175303, 11839822, 12543854
};


#define MAXSPV 14

#define GUSPATDIR "/usr/local/lib/timidity"
#define GUSPATSLDIR "/usr/local/share/timidity/"

#define WAVE_16_BITS	0x01	/* bit 0 = 8 or 16 bit wave data. */
#define WAVE_UNSIGNED	0x02	/* bit 1 = Signed - Unsigned data. */
#define WAVE_LOOPING	0x04	/* bit 2 = looping enabled-1. */
#define WAVE_BIDIR_LOOP	0x08	/* bit 3 = Set is bidirectional looping. */
#define WAVE_LOOP_BACK	0x10	/* bit 4 = Set is looping backward. */
#define WAVE_SUSTAIN_ON	0x20	/* bit 5 = Turn sustaining on. (Env. pts. 3)*/
#define WAVE_ENVELOPES	0x40	/* bit 6 = Enable envelopes - 1 */
#define WAVE_FAST_RELEASE 0x80	/* bit 7 = Shut off immediately after note off */

static int opt_verbose = 0;
#define MAX_SAMPLE_LEN (256*1024)
static int opt_sample_details = 0;

static void fix_header_read(unsigned char *buf, struct pat_header *header)
{
	header->nr_waveforms = shortnum(buf[85],buf[86]);
	header->master_volume = shortnum(buf[87],buf[88]);
}

static void fix_sample_read(unsigned char *buf, struct sample_header *sample)
{
	sample->len = longnum(buf[8],buf[9],buf[10],buf[11]);
	sample->loop_start = longnum(buf[12],buf[13],buf[14],buf[15]);
	sample->loop_end = longnum(buf[16],buf[17],buf[18],buf[19]);
	sample->base_freq = shortnum(buf[20],buf[21]);
	sample->low_note = longnum(buf[22],buf[23],buf[24],buf[25]);
	sample->high_note = longnum(buf[26],buf[27],buf[28],buf[29]);
	sample->base_note = longnum(buf[30],buf[31],buf[32],buf[33]);
	sample->detune = shortnum(buf[34],buf[35]);
	sample->panning = (unsigned char)buf[36];
	memcpy(sample->envelope_rate, &buf[37], 6);
	memcpy(sample->envelope_offset, &buf[43], 6);
	sample->tremolo_sweep = (unsigned char) buf[49];
	sample->tremolo_rate = (unsigned char) buf[50];
	sample->tremolo_depth = (unsigned char) buf[51];
	sample->vibrato_sweep = (unsigned char) buf[52];
	sample->vibrato_rate = (unsigned char) buf[53];
	sample->vibrato_depth = (unsigned char) buf[54];
	sample->modes = (unsigned char) buf[55];
	sample->scale_frequency = (short)shortnum(buf[56],buf[57]);
	sample->scale_factor = shortnum(buf[58],buf[59]);
	sample->volume = (int)shortnum(buf[60],buf[61]);
	memcpy(sample->mod_envelope_rate, &buf[65], 6);
	memcpy(sample->mod_envelope_offset, &buf[71], 6);
	sample->resonance = (short)shortnum(buf[80],buf[81]);
	sample->cutoff_frequency = (unsigned short)shortnum(buf[82],buf[83]);
	sample->lfo_rate = (short)shortnum(buf[92],buf[93]);
	sample->lfo_depth = (unsigned char) buf[94];
	sample->aps_parameter = (unsigned char) buf[95];
}

extern char *optarg;
extern int optind, opterr, optopt;

main(int argc, char **argv)
{
	int             c, i, j, k, l, n, patfd;
	struct pat_header header;
	struct sample_header sample;
	unsigned char   buf[1024];
	short sample_buf[MAX_SAMPLE_LEN];
	long            offset;
	char		 infile[80], outfile[80];
	int	sf2flag = 0, vlayer_count, stereo_count;
	double resonance;

	while ((c = getopt (argc, argv, "vd")) > 0)
		switch (c) {
			case 'v':
	    			opt_verbose = 1;
	    			break;
			case 'd':
	    			opt_sample_details = 1;
	    			break;
			default:
				fprintf(stderr, "usage: patinfo [-v] filename\n");
				return 1;
		}


	byteorder();

	if (argc - optind != 1) {
		fprintf(stderr, "usage: patinfo file.pat\n");
		exit(1);
	}

	strcpy(outfile, argv[optind]);
	strcpy(infile, GUSPATDIR);
	strcat(infile, argv[optind]);
	if (!strcmp(infile + strlen(infile) - 4, ".pat"))
		outfile[ strlen(outfile) - 4 ] = '\0';
	else strcat(infile, ".pat");

	if ((patfd = open(argv[optind], O_RDONLY, 0)) == -1)
	    if ((patfd = open(infile, O_RDONLY, 0)) == -1) {
		strcpy(infile, GUSPATSLDIR);
		strcat(infile, argv[1]);
		if (strcmp(infile + strlen(infile) - 4, ".pat"))
			strcat(infile, ".pat");
		if ((patfd = open(infile, O_RDONLY, 0)) == -1) {
			perror (infile);
			exit (1);
		}
	    }
	else strcat(infile, argv[optind]);

	if (read (patfd, buf, 239) != 239) {
		fprintf (stderr, "%s: Short file\n", infile);
		exit (1);
	}
	memcpy ((char *) &header, buf, sizeof (header));

	fix_header_read(buf, &header);

	if (strncmp (header.magic, "GF1PATCH110", 12)) {
		fprintf (stderr, "%s: Not a patch file\n", infile);
		exit (1);
	}
	if (strncmp (header.version, "ID#000002", 10)) {
	     fprintf (stderr, "%s: Incompatible patch file version\n", infile);
		exit (-1);
	}

	if (!strncmp (header.sf2magic, "SF2EXT", 7)) {
	     printf ("sf2 extensions detected\n");
	     sf2flag = 1;
	}


	printf("%d sample%s, master volume %d\n",
		 header.nr_waveforms, ((header.nr_waveforms > 1)? "s" : ""),
		 header.master_volume);


	vlayer_count = header.velocity_count;
	if (vlayer_count > 9) vlayer_count = 9;
	if (sf2flag) {
	   for (i = 0; i < vlayer_count; i++) {
		printf("Velocity layer %2d: velmin %3d, velmax %3d, left_samples %3d, right_samples %3d.\n", i+1,
				header.sf2layer[i].velmin,
				header.sf2layer[i].velmax,
				header.sf2layer[i].left_samples,
				header.sf2layer[i].right_samples);
	   }
	   for (i = vlayer_count; i < header.velocity_count; i++) {
		printf("Velocity layer %2d: velmin %3d, velmax %3d, left_samples %3d, right_samples %3d.\n", i+1,
				header.sf2layer2[i-9].velmin,
				header.sf2layer2[i-9].velmax,
				header.sf2layer2[i-9].left_samples,
				header.sf2layer2[i-9].right_samples);
	   }
	   vlayer_count = header.velocity_count;
	}
	else {
		vlayer_count = 1;
		header.sf2layer[0].velmin = 0;
		header.sf2layer[0].velmax = 127;
		header.sf2layer[0].left_samples = header.nr_waveforms;
		header.sf2layer[0].right_samples = 0;
	}

	offset = 239;

 for (l = 0; l < vlayer_count; l++) {

   if (header.sf2layer[l].right_samples) stereo_count = 2;
   else stereo_count = 1;

   for (k = 0; k < stereo_count; k++) {
    int sample_count;

    if (k == 0) sample_count = header.sf2layer[l].left_samples;
    else if (k == 1) sample_count = header.sf2layer[l].right_samples;

    for (i = 0; i < sample_count; i++) {
	int n;
	char stereo_letter, hi_note, lo_note, base_note;
	char *which_channel;

	if (lseek (patfd, offset, 0) == -1) {
		perror (infile);
		exit (1);
	}

	if (read (patfd, &buf, sizeof (sample)) != sizeof (sample)) {
		fprintf (stderr, "%s: Short file\n", infile);
		exit (-1);
	}

	memcpy ((char *) &sample, buf, sizeof (sample));

	/*
	 * Since some fields of the patch record are not 32bit aligned, we must
	 * handle them specially.
	 */
	fix_sample_read(buf, &sample);

	stereo_letter = sample.name[5];
	if (sf2flag) {
		if (!k && stereo_letter == 'L') which_channel = "left";
		else if (!k && stereo_letter == 'M') which_channel = "mono";
		else if (k && stereo_letter == 'R') which_channel = "right";
		else which_channel = "strange_channel";
	}
	else which_channel = "mono";

	printf("Layer %d %s:length of sample %d is %ld; fractions %u\n",
		 l+1, which_channel,
		 i+1, sample.len, buf[7]);

	if (opt_verbose) printf("loop start %ld, loop end %ld, base_freq %d\n",
	  sample.loop_start, sample.loop_end, sample.base_freq);

	if (opt_verbose && (sample.modes & WAVE_16_BITS)) {
		int much = sample.len;
		int sample_count, s_i, sample_peak = 0;
		short sample_max = 0;
		if (much > 2 * MAX_SAMPLE_LEN) much = 2 * MAX_SAMPLE_LEN;
		sample_count = much/2;
		if (read (patfd, &sample_buf, much) != much) {
			printf("short sample\n");
			exit(1);
		}
		sample_count = much/2;
		for (s_i = 0; s_i < sample_count; s_i++)
			if (abs(sample_buf[s_i]) > sample_max) {
				sample_max = abs(sample_buf[s_i]);
				sample_peak = s_i;
			}
		//printf("\tHere are the details:\n");
		printf("\t\t%d samples, %dms duration\n", sample_count,
			       	sample_count * 1000 / sample.base_freq
				);
		printf("\t\t%d sample max at sample %d, time %dms\n", sample_max, sample_peak,
			       	sample_peak * 1000 / sample.base_freq
				);
#if 0
		for (s_i = 0; s_i < sample_count; s_i++) {
			printf("\t\t\t%6d", sample_buf[s_i]);
			if (s_i == sample_peak) printf(" PEAK");
			if (s_i == sample.loop_start/2) printf(" LOOP");
			if (s_i == sample.loop_end/2) printf(" TAIL");
			printf("\n");
		}
#endif

	}

	for (n = 0; n <= 127; n++) if (sample.low_note <= freq_table[n]) break;
	lo_note = n;
	for (n = 0; n <= 127; n++) if (sample.high_note <= freq_table[n]) break;
	hi_note = n;
	for (n = 0; n <= 127; n++) if (sample.base_note <= freq_table[n]) break;
	base_note = n;


	printf("\tlow note %ld (midi %d), high note %ld (midi %d), base note %ld (midi %d)\n",
	  sample.low_note, lo_note, sample.high_note, hi_note, sample.base_note, base_note);

	if (opt_verbose) printf("\t\tdetune %ld, panning %u\n",
	  sample.detune, sample.panning);

	if (opt_verbose) {
		if (sf2flag) printf("\t\tvol env delay %u\n", sample.volume_envelope_delay);
		printf("\t\tvol env points: ");
		for (j = 0; j < 6; j++)
	 	printf("pt %d [%u,%u]", j, sample.envelope_rate[j],
		 sample.envelope_offset[j]);
		printf("\n");
	}

	if (opt_verbose && sf2flag) {
		printf("\t\tmod env delay %d, modEnvToPitch %d, modEnvToFilterFc %d\n",
			       	sample.mod_envelope_delay, sample.modEnvToPitch,
				sample.modEnvToFilterFc);
		printf("\t\tmod env points: ");
		for (j = 0; j < 6; j++)
	 	printf("pt %d [%u,%u]", j, sample.mod_envelope_rate[j],
		 sample.mod_envelope_offset[j]);
		printf("\n");
	}

	if (opt_verbose) printf("\t\ttremolo: sweep %u, rate %u, depth %u\n",
		sample.tremolo_sweep,
		sample.tremolo_rate,
		sample.tremolo_depth);

	if (opt_verbose) {
		if (sf2flag) {
			printf("\t\tvibrato: sweep %u, rate %u, depth %u, delay %u\n",
			sample.vibrato_sweep,
			sample.vibrato_rate,
			sample.vibrato_depth, sample.vibrato_delay);
			printf("\t\tlfo: rate %u, depth %u, modLfoToFilterFc %d\n",
			sample.lfo_rate,
			sample.lfo_depth,
			sample.modLfoToFilterFc);
			printf("\t\taps parameter: %d\n", sample.aps_parameter);
		}
		else printf("\t\tvibrato: sweep %u, rate %u, depth %u\n",
			sample.vibrato_sweep,
			sample.vibrato_rate,
			sample.vibrato_depth);
	}

	printf("\tmodes 0x%02X:", sample.modes);
	if (sample.modes & WAVE_16_BITS) printf(" 16bits");
	if (sample.modes & WAVE_UNSIGNED) printf(" unsigned");
	if (sample.modes & WAVE_LOOPING) printf(" looping");
	if (sample.modes & WAVE_BIDIR_LOOP) printf(" bidir_loop");
	if (sample.modes & WAVE_LOOP_BACK) printf(" loop_back");
	if (sample.modes & WAVE_SUSTAIN_ON) printf(" sustain_on");
	if (sample.modes & WAVE_ENVELOPES) printf(" envelopes");
	if (sample.modes & WAVE_FAST_RELEASE) printf(" fast_release");
	printf("\n");

	if (opt_verbose) printf("\t\tscale freq %d, scale factor %d\n",
	  sample.scale_frequency, sample.scale_factor);

	if (opt_verbose) printf("\t\tvolume %d\n", sample.volume);
	if (opt_verbose && sf2flag) printf("\t\tchorus %d, reverb %d\n",
		       	sample.chorus_effect, sample.reverb_effect);

	resonance = pow (10.0, (double) sample.resonance / 2.0 / 200.0) - 1;

	if (opt_verbose && sf2flag) printf("\t\tcutoff %u, resonance %d (-> %f)\n",
		       	sample.cutoff_frequency, sample.resonance, resonance);

	if (opt_verbose && sf2flag) printf("\t\tkeynumToVolEnvHold %d, keynumToVolEnvDecay %d\n",
			sample.keynumToVolEnvHold, sample.keynumToVolEnvDecay);
	offset += 96;
        offset += sample.len;

      } /* keyrange */
   } /* stereo */
 } /* vlayer */

	close(patfd);
	return 0;
}
