/*****************************************************************************
*	    Change Log
*  Date	    | Change
*-----------+-----------------------------------------------------------------
* 31-Dec-85 | Created changelog
* 31-Dec-85 | Add c:\ to include directives
* 31-Dec-85 | Call setctl/clrctl to deal with ctrl-break intercept.  Note that
*	    | clrctl can be called several times, as might be the case during
*	    | a ctrl-break abort
*  1-Jan-86 | <rgd/jmn> Added keyloud
*	    | Added initialized flag
* 21-Jan-86 | Abort l_restuntil if Ctrl-C or Ctrl-Break is typed
* 21-Jan-86 | Took out mpu_unknown, replaced with mpu_error_check
*  3-Feb-86 | Fixed MIDI_PROGRAM and MIDI_CHANNEL to subtract one to account
*	    | for MIDI encodings, e.g. channel 1 is represented by 0
*  8-Feb-86 | Added midi_exclusive to turn on/off flag in mpu-401.
*	    | Added midi_buffer function.
* 18-Feb-86 | Changed midi_exclusive to exclusive.
*	    | Added midi_exclusive to send an exclusive message.
* 19-Feb-86 | Changed MC_SEND_... to MC_SND_... to avoid name clash
* 23-Feb-86 | Added midi_control, midi_bend, midi_touch routines
* 24-Feb-86 | Added call to init_asm from musicinit.
* 26-Mar-86 | Added midi_cont call to enable continuous control messages
* 18-Apr-86 | Cleaned up documentation, renamed midi_control to midi_ctrl
*  2-Jun-86 | Added tuning definition code.
* 30-Jun-86 | Added switch to use timdif.asm instead of MPU-401 for timing
* 18-Jul-86 | Added DEBUG switch in parallel with DEBUG in AINTR.ASM
* 29-Jul-86 | Changed trace flag checking to first initialization only.
*  5-Aug-86 | Minor changes for Lattice C Version 3.00
*****************************************************************************/

/*
 * Driver for MPU-401
 *
 * This module initializes things and then issues commands to the
 * MPU-401 in response to higher-level operations like note( , , ).
 * Unlike examples in the MPU-401 manual, these routines do not wait
 * for Acks by polling the MPU-401.  Instead, Acks and all other
 * input are handled by the interrupt routine (see aintr.asm), which
 * sets a flag whenever it sees an Ack.	 Therefore, these routines
 * just poll the Ack flag rather than directly reading the MPU-401.
 * This avoids the problem of the MPU-401 having data queued in front
 * of the Ack message.
 */

/*
 * Interface Specifications for aintr.asm to mpu.c:
 *
 * aintr.asm is an interrupt handler that also parses midi data and
 * queues it for receipt by mpu.c.  In this way, interrupts are hidden
 * from C programmers and yet the mpu-401 never hangs trying to deliver
 * data because the interrupt routine is always ready to read data.
 * 
 * Data is parsed by aintr.asm in order to know how much data to read.
 * To avoid reparsing at the C level, the data is buffered in a
 * queue of messages rather than a simple byte stream.	Each message
 * is a 4-byte block.  The first byte is a status byte and the following
 * bytes (one or two) are data bytes.
 * 
 * Note that timing is not specified.  Later, we may add a parallel
 * buffer to store 4-byte timing blocks if it seems necessary.
 * 
 * System exclusive messages are handled by copying the midi exclusive
 * data into a separate buffer provided by the application program
 * through the midi_buffer call.
 * 
 */

/* define DEBUG to enable some extra debugging */
/* #define DEBUG 1 */


/* define TIMDIF to use the timdif.asm module */
/* #define TIMDIF 1 */

#include "cext.h"
#include "stdio.h"
#include "atxt.h"
#include "mpu.h"
#include "midicode.h"
#include "cmdline.h"
#include "userio.h"
#include "pitch.h"
#include "cintr.h"

#ifdef TIMDIF
#include "timdif.h"
#endif

#define BREAKTEST	if (CBREAK) {	/* Ctrl-C or Ctrl-Break handler */ \
		printf("Exiting.\n");\
		musicterm();\
		exit(1);\
	}
#define num_voices 16

/****************************************************************************
*
* MPU 401 commands
*
****************************************************************************/

#define MC_RESET		0xff
#define MC_SET_TEMPO		0xe0
#define MC_TIMEBASE_192		0xc8
#define MC_START_RECORDING	0x22
#define MC_RECORD_COUNTER	0xab
#define MC_ON_METRONOME		0x85
#define MC_VERSION		0xac
#define MC_REVISION		0xad
#define MC_NO_MEASURE_END	0x8c
#define MC_SND_MIDI		0xd0
#define MC_SND_EXCLUSIVE	0xdf
#define MC_EXCLUSIVE		0x96
#define MC_OFF_BEND		0x86
#define MC_ON_BEND		0x87
#define MC_OFF_THRU		0x88
#define MC_ON_THRU		0x89

/****************************************************************************
*
* useful defines and macros
*
****************************************************************************/

#define MD_BEATS_PER_MINUTE	125	/* to get 400 ticks per sec */
#define TICKS_TO_HUNDREDTHS(t)	((t) >> 2)
#define HUNDREDTHS_TO_TICKS(h)	((h) << 2)
#define NONE			(-1)

/****************************************************************************
*
* MPU-401 interface to IBM-XT
*
****************************************************************************/

#define DATAPORT	0x330		/* Input data port of MPU 401 */
#define STATPORT	0x331		/* status and request port */
#define COMPORT		0x331		/* Send MPU 401 commands to here */
#define DSR		(1<<7)		/* This bit of STATPORT, when low,
					   means the 401 has Data to send */
#define DRR		(1<<6)		/* This bit of STATPORT, when low,
					   means the 401 will take commands */
#define ACK		0xfe		/* Acknowledgement data code */
#define IRQ		2		/* Interrupt request line of 401 */
#define MAX_ACK_WAIT 1000

/****************************************************************************
*
* exported flags
*
****************************************************************************/

boolean musictrace = false;	/* enables printed trace of commands */
boolean miditrace = false;	/* enables printed trace of MIDI output */

#define n_t_sw 2
private char *t_switches[n_t_sw] = { "-t", "-trace" };

#define n_m_sw 2
private char *m_switches[n_m_sw] = { "-m", "-miditrace" };

/****************************************************************************
*
* exported variables
*
****************************************************************************/

int keyloud;	/* set to velocity of last getkey event */


/****************************************************************************
*
* variables shared with aintr.asm
*
****************************************************************************/

int	intnest = 0;	/* do not touch...read-only except by aintr.asm */
int	rd_delay = 0;	/* do not touch...read-only except by aintr.asm */

int	Unknown = ACK;
int	interror = 0;	/* reports errors from interrupt handler */
int	timeerr = 0;	/* reports timeout errors */

#ifdef DEBUG
int	intcnt = 0;	/* Count of interrupts taken */
int	loop_cnt, loop_max;	/* iteration counts */
#endif

long	Ticks;		/* Number of clock ticks since init */
int	Ack;		/* True if command has been acknowledged */
char	MidiTime, MidiStat = 0x90, Midi1, Midi2, Midi3; /* midi and time */
char	exclflag = 0;	/* used by aintr,  DO NOT TOUCH */
int	time_req = 0;	/* set to 1 when Ack will be followed by data */
int	mpu_result;	/* the data following the Ack */


/****************************************************************************
*
* Variable set by BREAK module
*
****************************************************************************/

extern int CBREAK;


/****************************************************************************
*
* variable imported from cintr.c
*
****************************************************************************/

extern int enabled;


/****************************************************************************
*
* local module variables
*
****************************************************************************/

private int initialized = false;  /* set by musicinit, cleared by musicterm */
private boolean tune_flag = false; /* set by musicinit, never cleared */
private boolean metroflag = false; /* flag to turn on metronome */
private boolean mpuflag = true;    /* true iff mpu401 present */
private int len;		/* length of trace string */
private int last_cmd = 0;	/* last mpu_command, used by trace */
private int user_scale = false; /* true if user-defined scale */
private int bend[num_voices];	/* current pitch bend on channel */
private pitch_table pit_tab[128];	/* scale definition */

/* "temporary" instrumentation: how long should we wait? */
private int max_ack_wait = 0;		/* maintained by mpu_wait */

/****************************************************************************
*
* functions declared in this module
*
****************************************************************************/

private void	fixup();
private void	mpu_command();
private void	mpu_drr_wait();
private int	mpu_read();
private void	mpu_wait();
private void	mpu_write();
private void	trace_mpu_command();
private void	wfa();


/****************************************************************************
*
* Buffer 
*	shares data with aintr.asm (the producer)
*
****************************************************************************/

#define BUFFERSIZE 1024
byte	buff[BUFFERSIZE];	/* data buffer */
int	buffhead = 0;		/* buffer pointers */
int	bufftail = 0;


/****************************************************************************
*
* System exclusive buffer variables (shared with aintr.asm)
*
****************************************************************************/

byte *xbuff = 0;	/* address of the user-supplied buffer */
int xbuffmask;		/* mask for circular buffer address calculation */
int xbuffhead = 0;	/* buffer pointers */
int xbufftail = 0;

/****************************************************************************
*				 exclusive
* Inputs:
*	boolean onflag -- set to true to receive midi exclusive data
* Effect: 
*	Tells MPU401 to read exclusive messages into buffer
****************************************************************************/

void exclusive(onflag)
    boolean onflag; /* on or off? */
{
	if  (!initialized) fixup();
	if (musictrace)
	    printf("exclusive: %d\n", onflag);
	mpu_command(MC_EXCLUSIVE | (onflag ? 1 : 0));
}

/****************************************************************************
*				     fixup
* Effect: 
*	Print error message and call musicinit
****************************************************************************/

private void fixup()
{
    printf("You forgot to call musicinit.  I'll do it for you.\n");
    musicinit();
}

/****************************************************************************
*				    getbuf
* Inputs:
*	boolean waitflag: true if routine should wait for data
*	byte * p: Pointer to data destination
* Result: boolean
*	true if data was written to *p
*	false if data not written to *p
* Effect: 
*	copies data from buffer to *p
*	will wait for buffer to become nonempty if waitflag is true
****************************************************************************/

boolean getbuf(waitflag, p)
   boolean waitflag;	/* true if routine should wait for data */
   byte *p;	/* pointer to data destination */
{
/*    register int head;*/

    if (!initialized) fixup();
    if (waitflag) while (buffhead == bufftail) /* wait */ ;
    else if (buffhead == bufftail) return false;

    *(long *)p = *(long *)(buff+buffhead);
    buffhead += 4;
    if (buffhead >= BUFFERSIZE) buffhead = 0;

/* the previous three lines are an optimization of:
 *    head = buffhead;
 *    *p++ = buff[head++];
 *    *p++ = buff[head++];
 *    *p++ = buff[head++];
 *    head++;
 *
 *    if (head >= BUFFERSIZE) head = 0;
 *    buffhead = head;
 */
    return true;
}

/****************************************************************************
*				    getkey
* Inputs:
*	boolean waitflag: true if wait until key depression, false if
*			  return immediately
* Result: int
*	key number of key which has been depressed
*	It returns -1 if waitflag is false and no key has been pressed
*	If waitflag is true this routine will block until a key is pressed
* Effect: 
*	reads a key
****************************************************************************/

int getkey(waitflag)
{
    byte msg[4];
    int k;

    if (!initialized) fixup();
    while (true) {	/* process data until you find a note */
	/* look for data and exit if none found */
	/* NOTE: waitflag will force waiting until data arrives */
	if (!getbuf(waitflag, msg)) { /* nothing there */
	    k = -1;
	    break;
	} else if ((msg[0] & MIDI_CODE_MASK) == MIDI_ON_NOTE) {
	    if (msg[2] == 0) { /* velocity 0 -> note off */
		keyloud = 0;
		k = (msg[1]-12) + 128;
	    } else {
		keyloud = msg[2];
		k = (msg[1]-12);
	    }
	    break;
	} else if ((msg[0] & MIDI_CODE_MASK) == MIDI_OFF_NOTE) {
	    keyloud = 0;
	    k = (msg[1]-12) + 128;
	    break;
	}
    }
    if (musictrace) {
	if (k != -1) printf("getkey got %d\n", k);
    }
    return k;
}

/****************************************************************************
*				    gettime
* Result: long
*	current timestamp from MPU-401
*	 Return the time in 100ths of seconds since the last call to
*	 musicinit or timereset
* Effect: 
*	Reads the MPU-401 time
****************************************************************************/

/* just to make sure we're using the timdif module: */
long gettime()
{
    BREAKTEST	/* abort if user typed Ctrl Break */

    if	(!initialized) fixup();
#ifndef TIMDIF
    time_req = 1;	/* tell aintr.asm to read extra byte */
    mpu_command(MC_RECORD_COUNTER);		/* Read counter */
    return TICKS_TO_HUNDREDTHS(Ticks);
#else
    return ibm_time();
#endif
}

/****************************************************************************
*				    l_rest
* Inputs:
*	long time: Amount of time to rest
* Effect: 
*	Waits until the amount of time specified has lapsed
****************************************************************************/

void l_rest(time)
    long time;
{
    if (!initialized) fixup();
    l_restuntil(time + gettime());	
}

/****************************************************************************
*				  l_restuntil
* Inputs:
*	long time: Event time to rest until
* Effect: 
*	Waits until the specified time has been reached (absolute time)
****************************************************************************/

void l_restuntil(time)
    long time;
{
    while(time > gettime()) ;
}

/****************************************************************************
*				metronome
* Inputs:
*	int onflag: true or false
* Effect:
*	enables (true) or disables (false) MPU-401 metronome function.
*	must be called before musicinit
****************************************************************************/

void metronome(onflag)
    int onflag;
{
    metroflag = onflag;
}

/****************************************************************************
*				   midi_bend
* Inputs:
*	int channel: midi channel on which to send data
*	int value: pitch bend value
* Effect: 
*	Sends a midi pitch bend message
****************************************************************************/

void midi_bend(channel, value)
    int channel, value;
{
    if	(!initialized) fixup();
    if (musictrace)
	printf("midi_bend: ch %d, val %d\n", channel, value);
    bend[MIDI_CHANNEL(channel)] = value;
    mpu_command(MC_SND_MIDI);
    mpu_write(MIDI_BEND | MIDI_CHANNEL(channel));
    mpu_write(MIDI_DATA(value));
    mpu_write(MIDI_DATA(value>>7));
}

/****************************************************************************
*				midi_buffer
* Inputs:
*	byte * buffer: the buffer address
*	int size: number of bytes in buffer
* Returns:
*	false if size is less than 16 or buffer is NULL, otherwise true
* Effect:
*	tells interrupt routine to store system exclusive messages in
*	buffer.	 The largest power of 2 bytes less than size will be
*	used.  xbuffhead and xbufftail will be initialized to zero,
*	and xbufftail will be one greater than the index of the last
*	system exclusive byte read from mpu401.
****************************************************************************/

int midi_buffer(buffer, size)
    byte *buffer;
    int size;
{
    int mask;
    mask = 16 - 1;
    if (size < 16 || buffer == NULL) return false;
    while (mask < size && mask > 0) mask = (mask << 1) + 1;
    xbuff = NULL;	/* turn off buffering */
    xbuffmask = mask >> 1;
    xbuffhead = xbufftail = 0;
    xbuff = buffer;	/* set buffer, turn on buffering */
    return true;
}

/****************************************************************************
*				midi_cont
* Inputs:
*	boolean onflag: true or false
* Effect:
*	enables (true) or disables (false) continuous control info from
*	MPU-401 to host.
****************************************************************************/

void midi_cont(onflag)
    boolean onflag;
{
    if (onflag) mpu_command(MC_ON_BEND);
    else mpu_command(MC_OFF_BEND);
}

/****************************************************************************
*				   midi_ctrl
* Inputs:
*	int channel: midi channel on which to send data
*	int control: control number
*	int value: control value
* Effect: 
*	Sends a midi control change message
****************************************************************************/

void midi_ctrl(channel, control, value)
    int channel, control, value;
{
    if (!initialized) fixup();
    if (musictrace)
	printf("midi_ctrl: ch %d, ctrl %d, val %d\n", 
		channel, control, value);
    mpu_command(MC_SND_MIDI);
    mpu_write(MIDI_CTRL | MIDI_CHANNEL(channel));
    mpu_write(MIDI_DATA(control));
    mpu_write(MIDI_DATA(value));
}

/****************************************************************************
*				 midi_exclusive
* Inputs:
*	byte * msg: pointer to a midi exclusive message, terminated by 0xF7
* Effect: 
*	Sends a midi exclusive message
****************************************************************************/

void midi_exclusive(msg)
    byte *msg;	/* the data to be sent */
{
    int i;	/* can DX7 keep up? */

    /* if user mistakenly called midi_exclusive instead of exclusive,
     * the argument will be true or false, both of which are highly	
     * unlikely valid arguments for midi_exclusive:
     */
    if (msg == (byte *) false || msg == (byte *) true) {
	printf("midi_exclusive: invalid argument %d.\n", (int) msg);
	if (initialized) musicterm();
	exit(1);
    }

    if	(!initialized) fixup();
    if (musictrace) printf("midi_exclusive\n");
    mpu_command(MC_SND_EXCLUSIVE);
    while (*msg != MIDI_EOX) {
	mpu_write(*msg);
	msg++;
	/* This is a delay loop.  Without it, your DX7 will crash. */
	for (i = (atxt() == ISAT ? 4 : 2); i > 0; i--) {
	    BREAKTEST
	}
    }
    mpu_write(MIDI_EOX);
}

/****************************************************************************
*				   midi_note
* Inputs:
*	int channel: midi channel on which to send data
*	int pitch: midi pitch code
*	int velocity: velocity with which to sound it (0=> release)
* Effect: 
*	Sends a midi note-play request out
****************************************************************************/

void midi_note(channel, pitch, velocity)
    int channel, pitch, velocity;
{
    if	(!initialized) fixup();
    if (musictrace)
	printf("midi_note: ch %d, key %d, vel %d\n",
	    channel, pitch, velocity);
    if (user_scale) {
	/* check for correct pitch bend */
	if ((pit_tab[pitch+12].pbend != bend[MIDI_CHANNEL(channel)]) &&
	    (velocity != 0)) {
	    midi_bend(channel, pit_tab[pitch+12].pbend);
	    bend[channel] = pit_tab[pitch+12].pbend;
	}
	pitch = pit_tab[pitch+12].ppitch;
    }
    mpu_command(MC_SND_MIDI);
    mpu_write(MIDI_ON_NOTE | MIDI_CHANNEL(channel));
    mpu_write(MIDI_DATA(12 + pitch)); /* cmu standard to midi standard */
    mpu_write(MIDI_DATA(velocity));
}

/****************************************************************************
*				 midi_program
* Inputs:
*	int channel: Channel on which to send midi program change request
*	int program: Program number to send (decremented by 1 before
*			being sent as midi data)
* Effect: 
*	Sends a program change request out the channel
****************************************************************************/

void midi_program(channel, program)
    int channel;	/* midi channel */
    int program;	/* the program number */
{
    if	(!initialized) fixup();
    if (musictrace)
	printf("midi_program: ch %d, prog %d\n",
		channel, program);
    mpu_command(MC_SND_MIDI);
    mpu_write(MIDI_CH_PROGRAM | MIDI_CHANNEL(channel));
    mpu_write(MIDI_PROGRAM(program));
}

/****************************************************************************
*				midi_thru
* Inputs:
*	boolean onflag: true or false
* Effect:
*	enables (true) or disables (false) midi thru info from
*	MPU-401 to host.  (Default is set; reset with cmdline -block.)
****************************************************************************/

void midi_thru(onflag)
    boolean onflag;
{
    if (onflag) mpu_command(MC_ON_THRU);
    else mpu_command(MC_OFF_THRU);
}

/****************************************************************************
*				   midi_touch
* Inputs:
*	int channel: midi channel on which to send data
*	int value: control value
* Effect: 
*	Sends a midi after touch message
****************************************************************************/

void midi_touch(channel, value)
    int channel, value;
{
    if	(!initialized) fixup();
    if (musictrace)
	printf("midi_touch: ch %d, val %d\n", channel, value);
    mpu_command(MC_SND_MIDI);
    mpu_write(MIDI_TOUCH | MIDI_CHANNEL(channel));
    mpu_write(MIDI_DATA(value));
}

/****************************************************************************
*				   mpu_command
* Inputs:
*	int c: Character to write to MPU-401 command port
* Effect: 
*	Writes the data to the MPU-401 command port
****************************************************************************/

private void mpu_command(c)
    int c;
{
    if (!mpuflag) { /* simulated */
	trace_mpu_command(c);
    } else { /* real */
	if (miditrace) trace_mpu_command(c);
	mpu_drr_wait();
	Ack = 0;
	outp(COMPORT, c);
	if (enabled) mpu_wait();
	else wfa();
    }
}

/****************************************************************************
*				 mpu_drr_wait
* Effect: 
*	Waits until the MPU-401 is ready to receive data
****************************************************************************/

#define MAX_TRIES	2000

private void mpu_drr_wait()
{
    int i;

    if (!mpuflag) return;	/* always ready if not there! */

    for (i = 0; i < MAX_TRIES; i++)
	if ((inp(STATPORT) & DRR) == 0) break;
#ifdef DEBUG
    if (i == MAX_TRIES)
	printf("mpu-401 not ready to receive; intcnt=%d\n",intcnt);
#endif
}

/****************************************************************************
*				  mpu_error_check
* Effect: 
*	Reports any errors originating in the interrupt handler
****************************************************************************/

void mpu_error_check()
{
    if (Unknown != ACK) {
	printf("Unknown command: %x\n", Unknown);
	Unknown = ACK;
    }
    if (interror != 0) {
	char *cause;
	switch (interror) {
	    case NESTERR: cause = "nested interrupts";	break;
	    case BUFFERR: cause = "buffer overflow";	break;
	    case CMDERR:  cause = "unknown command";	break;
	    default: cause = "";		   break;
	}
	printf("interror: %s\n", cause);
	if (*cause == NULL) printf("%d\n", interror);

	interror = 0;
    }
    if (timeerr != 0) {
	if (timeerr == TIMEOUT) printf("timeerr: timeout error\n");
	else printf("timeerr = %d\n", timeerr);
	timeerr = 0;
    }
}

/****************************************************************************
*				   mpu_read
* Result: int
*	character read from MPU-401
* Effect: 
*	Reads the MPU-401
****************************************************************************/

private int mpu_read()
{
    int delay;
    for (delay = 0; delay < 2000; delay++) {
	if ((inp(STATPORT) & DSR) == 0)
	    return inp(DATAPORT);
	}
#ifdef DEBUG
    printf("mpu_read: DSR never went low, returning 0, intcnt=%d\n",intcnt);
#endif
    return 0;
}

/****************************************************************************
*				   mpu_wait
* Effect: 
*	Called when interrupts are enabled.  Polls the 'Ack' flag, which is
*	set by the interrupt handler.  If more than MAX_ACK_WAIT iterations
*	occur without 'Ack' being set, issues an error message.
*	Ack is cleared when it is detected.
****************************************************************************/

private void mpu_wait()
{
    int ackcnt; /* delay counter */

    if (!mpuflag) return;

    for (ackcnt = 0; ackcnt < MAX_ACK_WAIT; ackcnt++) {
	if (Ack) {
	    if (max_ack_wait < ackcnt) max_ack_wait = ackcnt;
	    Ack = 0;
	    return;
	}
    }
#ifdef DEBUG
    printf("mpu_wait: No ack; incnt = %d\n",intcnt);
#endif
}

/****************************************************************************
*				   mpu_write
* Inputs:
*	int c: Character to write to MPU-401 data port
* Effect: 
*	Writes the data to the MPU-401 data port
****************************************************************************/

private void mpu_write(c)
    int c;
{
    if (!mpuflag) { /* simulate */
	printf("%02x",c);
	len += 2;
    } else { /* real */
	if (miditrace) { /* trace */
	    printf("%02x",c);
	    len += 2;
	} /* trace */
	mpu_drr_wait();
	outp(DATAPORT, c);
    }
}

/****************************************************************************
*				mpuexists
* Inputs:
*	boolean flag: true or false
* Effect:
*	if argument is false, indicates no mpu is on the machine, so
*	simulate mpu-401 (for debugging only)
****************************************************************************/

void mpuexists(flag)
    boolean flag;
{
    mpuflag = flag;
}

/*****************************************************************
*			set_pitch_default
*****************************************************************/
private void set_pitch_default()
{
    int i;

    for (i = 0; i < 128; i++) {
	pit_tab[i].pbend = 8192;
	pit_tab[i].ppitch = i;
    }
}

/*****************************************************************
*			read_tuning
*****************************************************************/
void read_tuning(filename)
    char *filename;
{
    int index, pit, lineno = 0;
    float bend;
    FILE *fpp;

    user_scale = true;
    set_pitch_default();
    fpp = fileopen(filename, "tun", "r", "Tuning definition file");
    while ((fscanf(fpp, "%d %d %f\n", &index, &pit, &bend) > 2) &&
	   (lineno < 128)) {
	lineno++;
	if (index >= -12 && index <= 115) {
	    pit_tab[index+12].pbend = (int)(8192 * bend/100 + 8192);
	    pit_tab[index+12].ppitch = pit;
	}
    }
}

/****************************************************************************
*				   musicinit
* Effect: 
* Initialize the mpu 401 device driver
*	Initialize mpu 401
*		Reset 401, change defaults
*	Set up interrupts
*	Start up mpu record clock
****************************************************************************/

void musicinit()
{
    int version, revision;
    int i;
    char *filename;

    if (!tune_flag) {	/* do this code only once */
	miditrace = (cl_nswitch(m_switches, n_m_sw) != NULL);
	musictrace = (cl_nswitch(t_switches, n_t_sw) != NULL);

    	tune_flag = true;
	filename = cl_option("-tune");
	if (filename != NULL) {
	    read_tuning(filename);
	}

	intr_init();
    }

    last_cmd = 0;

#ifdef TIMDIF
	if (!initialized) settime();
#endif

    initialized = true;

    intr_disable(IRQ);		/* Turn off 401 interrupts */

    for (i = 0; i < 100; i++) { /* flush out buffer, ignore DSR */
	inp(DATAPORT);
    }

    mpu_command(MC_RESET);	/* Reset the device */

    mpu_command(MC_VERSION);
    version = mpu_read();

    mpu_command(MC_REVISION);
    revision = mpu_read();

    mpu_command(MC_SET_TEMPO);	/* Set tempo and timebase to get */
    mpu_write(MD_BEATS_PER_MINUTE);	/* 400 ticks per second */

    mpu_command(MC_TIMEBASE_192);

    if (metroflag)
	mpu_command(MC_ON_METRONOME);	/* Just for debugging */

    mpu_command(MC_NO_MEASURE_END);	/* Don't want measure end bytes */

    init_asm();			/* Do any asm init needed(aintr.asm)*/
    intr_routine(IRQ);		/* Set up vector */ 

    CBREAK = false;
    setctl();			/* Set up ctrl-break intercept */

#ifdef DEBUG
    loop_max = 0;
#endif
    intr_enable(IRQ);		/* allow 401 interrupts */

    if (user_scale) {
	for (i = 0; i < num_voices; i++) {
	    midi_bend(i, 8192);
	    bend[i] = 8192;
	}
    }
    midi_thru(!(cl_switch("-block")));	/* set MIDI thru on MPU-401 */

    timereset();		/* Reset clock */
}

/****************************************************************************
*				   musicterm
* Effect: 
*	Cleans up; disables MPU-401 interrupts; resets MIDI devices
****************************************************************************/

void musicterm()
{
    if (initialized) {
#ifdef TIMDIF
	cletime();
#endif
	intr_disable(IRQ);	/* No more 401 interrupts */
#ifdef DEBUG
	printf("loop_max is %d\n", loop_max);
#endif
	clrctl();		/* reset ctrl-break handler */
	intr_cleanup(IRQ);	/* Restore default vector */
	mpu_command(MC_RESET);	/* Reset the device */
	initialized = false;
    }
/*  printf("maximum successful wait for ack: %d\n", max_ack_wait);*/
}

/****************************************************************************
*				    random
* Inputs:
*	int lo: Lower limit of value
*	int hi: Upper limit of value
* Result: int
*	random number (lo <= result <= hi)
****************************************************************************/

private long seed = 1534781;

int random(lo, hi)
    int lo, hi;
{
    seed *= 13;
    seed += 1874351;
    return (int) (lo + 
	    (((hi + 1 - lo) * ((0x00ffff00 & seed) >> 8)) >> 16));
}

/****************************************************************************
*				   timereset
* Effect: 
*	Resets the time on the MPU-401.	 Ticks is reset to 0
****************************************************************************/

void timereset()
{
    if (!initialized) fixup();
    if (musictrace) printf("timereset()\n");
    Ticks = 0;				/* Reset clock */
    mpu_command(MC_START_RECORDING);	/* Starts up clock */
#ifdef TIMDIF
    cletime();
    settime();
#endif    
}

/****************************************************************************
*			       trace
* Inputs:
*	boolean flag: true for trace on
* Effect: 
*	turns tracing on (flag == true) or off (flag == false)
****************************************************************************/
void trace(flag)
    boolean flag;
{
    musictrace = flag;
}

/****************************************************************************
*			       tracemidi
* Inputs:
*	boolean flag: true for trace on
* Effect: 
*	turns midi tracing on (flag == true) or off (flag == false)
****************************************************************************/
void tracemidi(flag)
    boolean flag;
{
    miditrace = flag;
}

/****************************************************************************
*			       trace_mpu_command
* Inputs:
*	int c: Command
* Effect: 
*	Writes command to stdout
****************************************************************************/

private void trace_mpu_command(c)
    int c;
{
    char * p;
    char buf[10];

    switch(c) { /* decode */
	case MC_RESET:
		p = " RESET:";
		break;
	case MC_ON_METRONOME:
		p =" MET-ON:";
		break;
	case MC_SET_TEMPO:
		p =" TEMPO:";
		break;
	case MC_TIMEBASE_192:
		p =" TIME-192:";
		break;
	case MC_START_RECORDING:
		p =" REC-ON:";
		break;
	case MC_VERSION:
		p =" VERSION:";
		break;
	case MC_REVISION:
		p =" REVISION:";
		break;
	case MC_NO_MEASURE_END:
		p =" NO-MEAS-END:";
		  break;
	case MC_SND_MIDI:
		p =" MIDI:";
		break;
	case MC_RECORD_COUNTER:
		if (last_cmd == c) p = "#";
		else p = " COUNTER:";
		break;
	case MC_ON_BEND:
		p = "Bender:On";
		break;
	case MC_OFF_BEND:
		p = "Bender:Off";
		break;
	case MC_ON_THRU:
		p = "Thru:On";
		break;
	case MC_OFF_THRU:
		p = "Thru:Off";
		break;
	default:sprintf(buf," %02x",c);
		p = buf;
		break;
    } /* decode */

    last_cmd = c;

    if (len + strlen(p) > 70) { /* overflow */
	printf("\n");
	len = 0;
    } /* overflow */
    len += strlen(p);
    printf("%s",p);
}

/****************************************************************************
*				      wfa
* Effect: 
*	Waits for an acknowledgement from the MPU-401.	Will not wait more
*	than MAX_ACK_WAIT iterations thru its loop.
* Conditions:
*	Called only if interrupts are not enabled, and the MPU-401 is
*	being polled
****************************************************************************/


private void wfa()
{
    int ackcnt;
    int x;
   
    if (!mpuflag) return;

    for (ackcnt = 0; ackcnt < MAX_ACK_WAIT; ackcnt++)
	if ((x = mpu_read()) == ACK) break;
#ifdef DEBUG
	else printf("wfa: got %x; intcnt = %d\n", x, intcnt);
#endif
}

