/***************************************************************************** * 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 | 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 }