/*
 *     #define MIDILIB
 */
/* "undef" this unless you have mike's midi library */

#include <stdio.h>

#ifdef MIDILIB
                       /* all we need is 2 header files */
#include <midi.h>
#include <dx7voice.h>

Dx7Voice *dx7readVoice();
char *VoiceFile();

#else                  /* what follows is relevant code from midi library */

#define Alloc(x) (x *)malloc(sizeof(x))
#define VoiceFile(x) x

#define SizeofDx7Voice 155     /* 155 bytes in a Dx7Voice */

typedef struct {
       unsigned char
            rate[4],   /* 0 ~ 99 */
            level[4],  /* 0 ~ 99 */
            kbdBreakPoint,
            kbdLDepth,
            kbdRDepth,
            kbdLCurve, /* 0 ~ 3 */
            kbdRCurve,
            kbdRateScale,      /* 0 ~ 7 */
            modSensitivity,    /* 0 ~ 3 */
            keyVelSensitivity, /* 0 ~ 7 */
            outputLevel,       /* 0 ~ 99 */
            oscMode,           /* 0 ~ 1 */
            oscFreqCoarse,     /* 0 ~ 31 */
            oscFreqFine,       /* 0 ~ 99 */
            oscDetune;         /* 0 ~ 14 */
} Dx7Operator;

typedef struct {
       char op[6][21]; /* data for 6 operators; cast each as a Dx7Operator */
       char pitchRate[4],
            pitchLevel[4],
            algorithm,         /* 0 ~ 31 */
            feedback,          /* 0 ~ 7 */
            oscSync,           /* 0 ~ 1 */
            lfoSpeed,          /* 0 ~ 99 */
            lfoDelay,
            lfoPmd,
            lfoAmd,
            lfoSync,           /* 0 ~ 1 */
            lfoWave,           /* 0 ~ 4 */
            modSensitivityPitch,       /* 0 ~ 7 */
            transpose,         /* 0 ~ 48 */
            name[11];          /* NB- leave room for \0 */
} Dx7Voice;

#define SX_CMD       0xF0            /* system exclusive command */
#define SX_EOB       0xF7            /* system exclusive end of block */
#define ID_DX7       67      /* Yamaha DX7 manufacturer MIDI id # */

/* Need a header & trailer in dx7 file for da(1) */
static char header[6] = {SX_CMD,ID_DX7,0,0,1,0x1b};
static char trailer[2] = {0,SX_EOB}; /* checksum & end of block */

char *
itop(pitch)
       int pitch;
/*
** Return string name of 'pitch' and octave number
** (e.g., 'itop(61)' is 'C#4', middle C sharp).
*/
{
    static char *p[]={"C","C#","D","D#","E","F","F#","G","G#","A","A#","B"}, r[8
];
    sprintf(r, "%s%d", p[pitch%12], pitch/12-((pitch%12)?1:0));
    return r;
}

char
dx7CheckSum(v)
       char *v;
/*
** Compute and return the check sum for voice 'v'.
** It's just the sum of the bytes with some bits masked out.
** Used by low level read/write routines as a handshake between host and dx7.
*/
{
       int i, c=0;
       for (i=0;i<SizeofDx7Voice;i++) c += v[i];
       return (char) (0x80 - (0x7f & c));
}

MidiError(fmt,arg) { fprintf(stderr, fmt, arg); }

Dx7Voice *
dx7readVoice(f, name)
       FILE *f;
       char *name; /* filename (used only in diagnostics) */
/*
** Read a 'Dx7Voice' from 'f' and return it,
** or NULL if there was a problem.
** The 'Dx7Voice' is expected to be 155 hex bytes.
** Should probably understand alternate formats,
** like (1) output, C-style format, etc.
*/
{
       Dx7Voice *v = Alloc(Dx7Voice);
#define        n SizeofDx7Voice
       if (!v) {
		MidiError("dx7readDx7Voice(%s): no memory.\n",name);
		return (Dx7Voice *)0;
	}
       if (read(fileno(f),v,sizeof header) != sizeof(header) ||
           read(fileno(f),v,n) != n)
          MidiError("dx7readVoice(%s): byte count error\n",name),
          v = (Dx7Voice *)0;
       else
          v->name[10] = '\0';
       return v;
#undef n
}

Dx7Voice *
dx7ReadVoice(name)
       char *name;
/*
** Read a 'Dx7Voice' from the file 'name' and return it,
** or NULL if there was a problem.  See 'readDx7Voice()'.
*/
{
       FILE *f = fopen(name,"r");
       Dx7Voice *v;

       if (!f) {
		MidiError("dx7ReadVoice: can't open '%s'\n",name);
                return (Dx7Voice *)0;
	}
       v = dx7readVoice(f,name);
       fclose(f);
       return v;
}

dx7writeVoice(v,f)
       Dx7Voice *v;
       FILE *f;
/*
** Write 'v' to 'f',
** Return 1 if successful, 0 if not.
** Should perhaps accommodate different formats (C, da).
** As it is, just writes out 155 hex bytes.
*/
{
       int r;
       write(fileno(f),header,sizeof header),
       r = write(fileno(f),v,SizeofDx7Voice)==SizeofDx7Voice;
       trailer[0] = dx7CheckSum(v);
       write(fileno(f),trailer,sizeof trailer);
       return r;
}

dx7WriteVoice(v,name)
       Dx7Voice *v;
       char *name;
/*
** Write 'v' to the file called 'name'.
** Return 1 if successful, 0 if not.
*/
{
       FILE *f = fopen(name,"w");
       int r=0;
       if (f) r=dx7writeVoice(v,f),fclose(f);
       else MidiError("dx7WriteVoice: couldn't write on '%s'\n",name);
       return r;
}

#endif

static char *lfoWave[] = { "tri ", "saw-", "saw+", "squa", "sine", "samp", };
static char *on[] = { "on ","off" };
static char *curve[] = {"-lin", "-exp", "+exp", "+lin"};
static char *mode[] = {"r", "f"};

#define p fprintf

dx7PrintOperator(op,o)
       Dx7Operator *op;
       FILE *o;
{
#define el(x) op->x[0],op->x[1],op->x[2],op->x[3]
p(o,"%s %2d %2d  %2d ",
            mode[op->oscMode],         /* 0 ~ 1 */
            op->oscFreqCoarse, /* 0 ~ 31 */
            op->oscFreqFine,   /* 0 ~ 99 */
            op->oscDetune-7);
p(o,"%2d %2d %2d %2d  %2d %2d %2d %2d ",
     el(rate), el(level));
#undef el
p(o, "%3s %4s %4s %2d %2d   %1d  %2d  %1d",
            itop(op->kbdBreakPoint),
            curve[op->kbdLCurve],      /* 0 ~ 3 */
            curve[op->kbdRCurve],
            op->kbdLDepth,
            op->kbdRDepth,
            op->kbdRateScale,          /* 0 ~ 7 */
            op->outputLevel,           /* 0 ~ 99 */
            op->keyVelSensitivity,     /* 0 ~ 7 */
            0);                        /* 0 ~ 14 */
}

dx7PrintVoice(i,name,o) FILE *i,*o; {
       Dx7Voice *v = dx7readVoice(i,name);
       int j;

       if (!v) return;
p(o,"Voice Name: %s\n",v->name);
/*
p(o,"\n");
p(o,"|  [7] | [8] | [9] | [10]| [11]| [12]| [13]| [14]| [15]| [16]\n");
p(o,"|======|=====|=====|=====|=====|=====|=====|=====|=====|=================|\
n");*/
p(o,"|========================================================================|\
n");
p(o,"| algo  feed |---------------L F O---------------|  mod sensitivity      |\
n");
p(o,"| rithm back |wave  speed delay  pmd   amd  sync | pitch amplitude       |\
n");
p(o,"|  %3d   %3d  %s   %3d   %3d   %3d   %3d   %3d   %3d  ",
    v->algorithm+1,v->feedback,lfoWave[v->lfoWave],v->lfoSpeed,v->lfoDelay,
    v->lfoPmd,v->lfoAmd,v->lfoSync,v->modSensitivityPitch);
#define sens(n) ((Dx7Operator *)&(v->op[n][0]))->modSensitivity
p(o,"[%d %d %d %d %d %d]    |\n",sens(0),sens(1),sens(2),sens(3),sens(4),sens(5)
);
p(o,"|========================================================================|\
n");
p(o,"| mod freq de  ----rate---  ---level--- kbd --curve-- depth kbd  out vel |\
n");
p(o,"|op   c  f tune 1  2  3  4   1  2  3  4 brk  L    R    L  R rate lvl sens|\
n");

/*
p(o,"\n\n");
p(o,"|  [2] | [3] | [4] | [5] | [6] | [7] |\n");
p(o,"|======|=====|=====|=====|=====|=====|\n");
p(o,"| poly | pitchbend |   portamento    |\n");
p(o,"| mono |range|step |mode  gliss time |\n");
*/

       for (j=0;j<6;j++)
               p(o,"|%d ",j+1), dx7PrintOperator(v->op[j],o), p(o, "   |\n");
p(o,"|========================================================================|\
n");
p(o,"| -----pitch EG----  transpose                                           |\
n");
p(o,"| rate  %2d %2d %2d %2d %3s                                                
  |\n",
  v->pitchRate[0],v->pitchRate[1],v->pitchRate[2],v->pitchRate[3],itop(v->transpose+24));
p(o,"| level %2d %2d %2d %2d                                                      |\n",
  v->pitchLevel[0],v->pitchLevel[1],v->pitchLevel[2],v->pitchLevel[3]);
p(o,"|========================================================================|\n");
}

/*
** For each dx7 voice file, pretty print something like the
** Yamaha table
*/
main(ac,av) char *av[]; {
       int i=1;
       if (i==ac) dx7PrintVoice(stdin,"stdin",stdout);
       else for (;i<ac;i++){
               char *s = VoiceFile(av[i]);
               FILE *f = (FILE *)0;
               if (s) f = fopen(s,"r");
               if (f) {
                       if (i>1) printf("\n\n");
                       dx7PrintVoice(f,av[i],stdout);
                       fclose(f);
               }
               else MidiError("Cannot open %s: ",av[0]);
       }
}
