/*
**	VPR -- Print voice and performance data
** For each Yamaha system exclusive voice bulk data file, pretty print
** something like the TX816 manual table.
** For each Yamaha system exclusive performance bulk data file, pretty print
** something like the TX816 manual table.
** Handles 1-voice, 32-voice, 1-performance, and 64-performance dumps.
**	psl, 11/87; based on vprint.c by Mike Hawley.
** Note, the location of KMOD (from byte 32 of the 64-p dump) in the 1-perf
** dump is undocumented; I made a guess (TX1PKMODLOC) in libdx7.h, but if
** we ever find out the true location, that should be updated...
*/

#include <stdio.h>
#include <midi.h>
#include <dx7voice.h>

Dx7Voice *dx7readVoice();

#define	MAXBUF	(6+DX7VOXLEN+2)	/* actually we only need 6+DX732VOXLEN+2 */

static	char	*Wave[] = { "TRI ", "SAW-", "SAW+", "SQU ", "SINE", "S/H ", };
static	char	*On[] = { "OFF", "ON ", };
static	char	*Curve[] = {"-L", "-E", "+E", "+L", };
static	char	*Mode[] = {"R", "F", };
static	char	*Kmod[]	= { "SINGLE", "DUAL", "SPLIT", };
static	int	Nflg	= 0;		/* name only option */
static	int	Bflg	= 0;		/* both A & B performance memories */

#define p fprintf

main(argc,argv)
char *argv[];
{
	int i, n, fh;

	n = 0;
	for (i = 1; i < argc; i++) {
	    if (argv[i][0] == '-') {
		switch (argv[i][1]) {
		case 'b':	/* both a & b perf. memories */
		    Bflg++;
		    break;
		case 'n':	/* name only */
		    Nflg++;
		    break;
		default:
		    p(stderr, "Usage: %s [-nameonly] [files or stdin]\n",
		     argv[0]);
		    exit(2);
		}
	    } else
		n++;
	}
	if (n == 0)
	    n = pr(0, "stdin", stdout);
	else {
	    n = 0;
	    for (i = 1; i < argc; i++) {
		if (argv[i][0] == '-')
		    continue;
		if ((fh = open(argv[i], 0)) < 0)
		    perror(argv[i]);
		else {
		    n += pr(fh, argv[i], stdout);
		    close(fh);
		}
	    }
	}
	exit(n? 0 : 1);
}

pr(ifh, name, ofp)
char	*name;
FILE	*ofp;
{
	unsigned char buf[MAXBUF], *bp, sbuf[TX1PLEN], *sp;
	int fmt, i, n, op, detune;
	Dx7Voice v;

	while (read(ifh, buf, 1) == 1 && *buf != SX_CMD);
	if (*buf != SX_CMD) {
	    fprintf(stderr, "No system exclusive dump found in %s\n", name);
	    return(0);
	}
	if (read(ifh, &buf[1], 5) != 5) {		/* get rest of header */
	    fprintf(stderr, "No header in %s\n", name);
	    return(0);
	}
	if (buf[1] != ID_YAMAHA) {
bad:
	    fprintf(stderr, "Header problem in: %x %x %x %x %x %x\n",
	     buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
	    return(0);
	}
	if ((buf[2] >> 4) != DX7_SXSS_BD)
	    goto bad;
	fmt = buf[3];
	if ((fmt != DX7_SXF_1V  || buf[4] != 0x01 || buf[5] != 0x1B)
	 && (fmt != DX7_SXF_32V || buf[4] != 0x20 || buf[5] != 0x00)
	 && (fmt != TX_SXF_1P  || buf[4] != 0x00 || buf[5] != 0x5E)
	 && (fmt != TX_SXF_64P || buf[4] != 0x20 || buf[5] != 0x00))
	    goto bad;
	if (fmt == DX7_SXF_1V) {
	    if (read(ifh, &v, sizeof v) != sizeof v) {
		perror("DX7_SXF_1V read");
		return(0);
	    }
	    pvdx7(&v, stdout);
	} else if (fmt == DX7_SXF_32V) {
	    for (n = 0; n < 32; n++) {
		if (read(ifh, bp = buf, DX732VOXLEN) != DX732VOXLEN) {
		    perror("DX7_SXF_32V read");
		    return(0);
		}
		/* copy condensed bulk format into single voice format */
		sp = (u_char *) &v; 
		for (op = 6; --op >= 0; ) {
		    for (i = 11; --i >= 0; *sp++ = *bp++);
		    *sp++ = (*bp & 0x03);		/* LC */
		    *sp++ = (*bp++ >> 2);		/* RC */
		    *sp++ = (*bp & 0x07);		/* RS */
		    detune = (*bp++ >> 3);		/* PD */
		    *sp++ = (*bp & 0x03);		/* AMS */
		    *sp++ = (*bp++ >> 2);		/* KVS */
		    *sp++ = *bp++;
		    *sp++ = (*bp & 0x01);		/* M */
		    *sp++ = (*bp++ >> 1);		/* FC */
		    *sp++ = *bp++;
		    *sp++ = detune;
		}
		for (i = 9; --i >= 0; *sp++ = *bp++);
		*sp++ = (*bp & 0x07);		/* FB */
		*sp++ = (*bp++ >> 3);		/* OKS */
		for (i = 4; --i >= 0; *sp++ = *bp++);
		*sp++ = (*bp & 0x01);		/* LFKS */
		*sp++ = ((*bp >> 1) & 0x07);	/* LFKS */
		*sp++ = (*bp++ >> 4);		/* LPMS */
		for (i = 11; --i >= 0; *sp++ = *bp++);
		pvdx7(&v, stdout);
	    }
	} else if (fmt == TX_SXF_1P) {
	    if (read(ifh, sbuf, TX1PLEN) != TX1PLEN) {
		perror("TX_SXF_1P read");
		return(0);
	    }
	    pptx7(sbuf, stdout);
	} else if (fmt == TX_SXF_64P) {
	    for (n = 0; n < 64; n++) {
		if (read(ifh, buf, TX64PLEN) != TX64PLEN) {
		    perror("TX_SXF_64P read");
		    return(0);
		}
		/* copy condensed bulk format into single performance format */
		for (i = 0; i < 2; i++) {
		    sp = sbuf + i * TX1PVLEN;
		    bp = buf + i * TX64PVLEN;
		    sp[2] = (bp[0])? 1 : 0;		/* P/M */
		    sp[3] = (bp[1] & 0x0F);		/* PBR */
		    sp[4] = (bp[1] >> 4) | (bp[15] >> 3);	/* PBS [!] */
		    sp[5] = bp[2];			/* PTIM */
		    sp[6] = (bp[3] & 0x01);		/* GL */
		    sp[7] = (bp[3] >> 1);		/* M */
		    sp[9] = (bp[4] & 0x0F);		/* MWS */
		    sp[10] = (bp[4] >> 4);		/* MWA */
		    sp[11] = (bp[5] & 0x0F);		/* FCS */
		    sp[12] = (bp[5] >> 4);		/* FCA */
		    sp[13] = (bp[6] & 0x0F);		/* ATS */
		    sp[14] = (bp[6] >> 4);		/* ATA */
		    sp[15] = (bp[7] & 0x0F);		/* BCS */
		    sp[16] = (bp[7] >> 4);		/* BCA */
		    sp[26] = bp[14];			/* ATN */
		}
		sbuf[61] = (!(buf[32] & 03) && (buf[32] & 04))? 1 : 0; /* VMS */
		for (i = 0; i < 30; i++)
		    sbuf[64 + i] = buf[34 + i];
		pptx7(sbuf, stdout);
	    }
	}
	return(1);
}

#define SENS(n) ((Dx7Operator *)&(v->op[5-n][0]))->modSensitivity

pvdx7(v, o)
Dx7Voice *v;
FILE *o;
{
	int j;

	if (Nflg) {
	    p(o, "%-10.10s\n", v->name);
	    return;
	}
	p(o,
"|========================================================================|\n");
	p(o,
"|                         pitch E G rate   pitch E G level   transpose   |\n");
	p(o,
   "|   %10.10s             %3d%3d%3d%3d     %3d%3d%3d%3d       %3s       |\n",
	 v->name,
	 v->pitchRate[0], v->pitchRate[1], v->pitchRate[2], v->pitchRate[3],
	 v->pitchLevel[0], v->pitchLevel[1], v->pitchLevel[2], v->pitchLevel[3],
	 itop(v->transpose+24));
	p(o,
"|------------------------------------------------------------------------|\n");
	p(o,
"| algo- feed osc. |______________ L F O _____________|  mod_sensitivity  |\n");
	p(o,
"| rithm back sync |wave  speed delay  pmd   amd  sync| pitch  amplitude  |\n");
	p(o,
"| %3d   %3d   %3s  %s  %4d  %4d  %4d  %4d   %3s  %3d  [%d %d %d %d %d %d]|\n",
	 v->algorithm+1, v->feedback, On[v->oscSync],
	 Wave[v->lfoWave], v->lfoSpeed, 
	 v->lfoDelay, v->lfoPmd, v->lfoAmd, On[v->lfoSync],
	 v->modSensitivityPitch,
	 SENS(0), SENS(1), SENS(2), SENS(3), SENS(4), SENS(5));
	p(o,
"|------------------------------------------------------------------------|\n");
	p(o,
"| op   freq de-  ____rate___  ___level___ kbd curve depth kbd  out vel   |\n");
	p(o,
"| mode c  f tune  1  2  3  4   1  2  3  4 brk  L  R  L  R rate lvl sens  |\n");
	for (j = 0; j < 6; j++) {
		p(o,"|%d  ", j+1);
		dx7PrintOperator(v->op[5 - j], o);
		p(o, " %d|\n", j+1);
	}
	p(o,
"|========================================================================|\n");
}

dx7PrintOperator(op,o)
Dx7Operator *op;
FILE *o;
{
	p(o,"%s %2d %2d  %2d  ",
	 Mode[op->oscMode],		/* 0 ~ 1 */
	 op->oscFreqCoarse,		/* 0 ~ 31 */
	 op->oscFreqFine,		/* 0 ~ 99 */
	 op->oscDetune-7);		/* 0 ~ 14 */
	p(o,"%2d %2d %2d %2d  %2d %2d %2d %2d ",
	 op->rate[0], op->rate[1], op->rate[2], op->rate[3],
	 op->level[0], op->level[1], op->level[2], op->level[3]);
	p(o, "%3s %2s %2s %2d %2d   %1d  %2d   %1d  ",
	 itop(op->kbdBreakPoint + 9),
	 Curve[op->kbdLCurve],		/* 0 ~ 3 */
	 Curve[op->kbdRCurve],		/* 0 ~ 3 */
	 op->kbdLDepth,			/* 0 ~ 99 */
	 op->kbdRDepth,			/* 0 ~ 99 */
	 op->kbdRateScale,		/* 0 ~ 7 */
	 op->outputLevel,		/* 0 ~ 99 */
	 op->keyVelSensitivity);	/* 0 ~ 7 */
}

pptx7(buf, o)
unsigned char *buf;
FILE	*o;
{
	if (Nflg) {
	    p(o, "%-30.30s\n", &buf[64]);
	    return;
	}
	p(o,"Performance Name: %30.30s  KMOD: %-6s   VMS:%c\n",
	 &buf[64], Kmod[buf[TX1PKMODLOC]], "AB"[buf[61]]);
	pp(buf, o, "A");
	if (Bflg)
	    pp(&buf[TX1PVLEN], o, "B");
}

#define	ONOFF(byte,bit)		On[(buf[byte]>>bit)&1]

pp(buf, o, vm)
unsigned char *buf, *vm;
FILE	*o;
{
	p(o,
"|------------------------------| |---------------------------------------|\n");
	p(o,
"| Poly/ |    < PORTAMENTO >    | |             < MODULATION >            |\n");
	p(o,
"| /Mono |  mode    gliss  time | |         MOD      F.C     B.C    A.TCH |\n");
	p(o,
"|------------------------------| |---------------------------------------|\n");
	p(o,
"| %4s  |  %6s  %3s    %2d   | | range   %2d       %2d      %2d      %2d   |\n",
 buf[2]? "MONO" : "POLY", buf[7]? "FOLLOW" : "RETAIN", On[buf[6]], buf[5],
 (buf[9] * 66 + 5) / 10, (buf[11] * 66 + 5) / 10,
 (buf[15] * 66 + 5) / 10, (buf[13] * 66 + 5) / 10);
	p(o,
"|==============================| | pitch   %3s      %3s     %3s     %3s  |\n",
 ONOFF(10,0), ONOFF(12,0), ONOFF(16,0), ONOFF(14,0));
	p(o,
"| LEVEL |    < P. BENDER >     | | amp     %3s      %3s     %3s     %3s  |\n",
 ONOFF(10,1), ONOFF(12,1), ONOFF(16,1), ONOFF(14,1));
	p(o,
"|  ATT  |   range     step     | | EG-bias %3s      %3s     %3s     %3s  |\n",
 ONOFF(10,2), ONOFF(12,2), ONOFF(16,2), ONOFF(14,2));
	p(o,
"|------------------------------| |=======================================|\n");
	p(o,
"| %3d   |   %3d       %3d      | |       Voice Memory: %-4s              |\n",
 buf[26], buf[3], buf[4], vm);
	p(o,
"|------------------------------| |---------------------------------------|\n");
	
}
