#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 4 (of 6)."
# Contents:  aintr.asm moxc.c record.c
# Wrapped by lee@uhccux on Wed Mar 29 09:58:10 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'aintr.asm' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'aintr.asm'\"
else
echo shar: Extracting \"'aintr.asm'\" \(14569 characters\)
sed "s/^X//" >'aintr.asm' <<'END_OF_FILE'
X;*****************************************************************************
X;	    Change Log
X;  Date	    | Change
X;-----------+-----------------------------------------------------------------
X; 31-Dec-85 | Created change log
X; 31-Dec-85 | Make sure DS: register is set properly!
X;	    | Note: Why the CLD at the start of the routine?  CLI?
X;  1-Jan-86 | Removed CLD.  Roger suggests this was carryover from 6502 code
X;	    | where CLD is clear-decimal-mode.
X;	    | Change 62H EOI code to 20H EOI code like everything else that
X;	    | talks to interrupt chip.	Note that we are tweaking the primary
X;	    | interrupt controller chip on a /AT, but that is OK because the
X;	    | EOI was sent to the secondary controller via the RE_DIRECT code
X;	    | (PC/AT Tech Ref page 5-71)
X;	    | This is the same mechanism used on both the /XT and /AT, e.g.
X;	    | PC/XT Tech Ref page A-80, lines 5729-5730
X;  5-Feb-86 | Keep interrupts off during interrupt handler
X;  8-Feb-86 | Added code to capture system exclusive messages
X;	    | Removed some debugging stores into d0,d1,d2,d3
X;	    | Removed interrupt nesting counter -- if interrupts nest, you'll
X;	    | crash before you can print the error report
X; 13-Feb-86 | Changed DATA macro to a more sane name: GETMIDI
X; 14-Feb-86 | Moved all variables to DSEG (they were in PSEG -- why did this
X;	    | ever work before?)
X;  5-May-86 | Optimized input for better transcription speed
X;  9-Jul-86 | Added loop to avoid exiting interrupts with more data available
X; 18-Jul-86 | Fixed a running status bug and cleaned up some debugging code
X;*****************************************************************************
X;;
X;; MPU-401 interrupt handler
X;;	modelled after MPU-401 manual, pages 55-56
X;;	except that Ack commands are handled by setting
X;;	a flag and other commands are handled by
X;;	putting data into a buffer.  Once things are
X;;	initialized, this is the only place that should
X;;	read data from the MPU-401.  All writes (commands)
X;;	are issued from C routines.
X;;
X;; Notes:  (Joe Newcomer, 31-Dec-85)
X;;   Because an interrupt can occur from anywhere, including DOS and
X;;   the BIOS, we cannot, repeat CANNOT assume the validity of any
X;;   register except CS:.  In particular, SS:SP is quite possibly a
X;;   BIOS stack segment which are infinitesmally small.	 We CANNOT
X;;   push anything onto the BIOS stack segment without risking severe
X;;   damage to the integrity of the system.  So we have here a large
X;;   private stack segement; we switch attention to it, *very carefully*
X;;   save our state on it, and then call the code which handles our
X;;   MPU-401 interrupt.	 Upon return, we *very carefully* reset the stack
X;;   and return to our caller.	Since we need to address the C data segment,
X;;   we must also load DS:, which we need to set intnest and various buffer
X;;   headers.  See the note associated with the setting of DS:; this
X;;   code works only in the small data model.
X
Xinclude dos.mac
X
X; DEBUG = 1		;; define DEBUG to enable some extra record keeping
X
XDSEG
Xextrn _interror:word	;; report errors up to C handlers
Xextrn _timeerr:word	;; reports timeout errors to C handlers
Xextrn _time_req:word	;; set to true if next Ack will be timing byte
X
XIFDEF DEBUG
Xextrn _loop_cnt:word	;; count loop interations
Xextrn _loop_max:word	;; max value of loop interations
Xextrn _intcnt:word	;; count of interrupts taken
XENDIF
X
Xextrn _buff:byte		;; data from mpu401
Xextrn _buffhead:word	;; data is removed from head offset
Xextrn _bufftail:word	;; data is inserted at the tail offset
X
Xextrn _xbuff:word	;; system exclusive buffer pointer
Xextrn _xbuffhea:word
Xextrn _xbufftai:word
Xextrn _xbuffmas:word
X
X;;
X;; Globals used in communication with mpu.c
X;;
X
Xextrn _Ack:word		;; set if ack received
Xextrn _Unknown:word		;; set for unknown command (for debugging)
Xextrn _Ticks:dword		;; Clock ticks (400 = 1 second)
X
X;;Midi information
Xextrn _MidiTime:byte	;; extra timing byte
Xextrn _MidiStat:byte	;; Running status
Xextrn _Midi1:byte	;; First arg
Xextrn _Midi2:byte	;; Second arg
Xextrn _Midi3:byte	;; Third arg (not used)
X
Xextrn _rd_delay:word	;; counts down wait for mpu data
X
XENDDS
X
X;_TEXT	SEGMENT
XPSEG
Xpublic _a_intr, _init_asm
X
X; These must be in the pseg because on entry only the CS: is addressible
X
XDASEG	DW	0
X
XOldAX	DW	?
XOldSS	DW	?		; old stack segment
XOldSP	DW	?		; old stack pointer
X	DW	512 DUP(?)	; local stack space for intercept routine
XSTACK	label	WORD
X
X
XNESTERR = 1		;;nested interrupt error
XBUFFERR = 2		;;input buffer overflow error
XCMDERR = 3		;;unknown command
XTIMEOUT = 4		;;timeout waiting to read data
X
XBUFFMASK = 3FFH		;; buffer size is 1024 bytes, 3FF=1023
X
X;; Status byte masks
X;;
X	DRR	=	40h	;; Data Receive Ready
X	DSR	=	80h	;; Data Send Ready
X
X	STATPORT = 331H		;; MPU-401 Status port
X	DATAPORT = 330H		;; MPU-401 Data (MPU to PC) port
X
X;*****************************************************************************
X; _init_asm(): called to save the data segment into a place where
X;	      the interupt routine can get at it.
X
X_init_asm proc near
X
X	push	bp		;save bp
X	mov	bp,sp		;move sp into bp
X	mov	cs:DASEG,ds	;save the ds in DASEG
X	pop	bp
X	ret
X
X_init_asm	endp
X
X;*****************************************************************************
X;				    _a_intr
X; Called via:
X;	far call from interrupt handler.  NOTE: proc is declared 'near' so
X;	that funny fixups are not required when linking it into C small model
X;	code.  Since we return via IRET, the near/far distinction does not
X;	matter.	 HOWEVER if one were to play funny games with doing returns
X;	and twiddling flags (unlikely) the near/far distinction would matter
X;*****************************************************************************
X_a_intr	proc	near
X; Establish a stack for us
X	mov	OldSS,SS	; save old stack
X	mov	OldSP,SP	; ...
X	mov	OldAX,AX	; and scratch register
X	cli			; don't play with fire, turn 'em off
X	mov	AX,CS		; our new stack segment is addressible by CS:
X	mov	SS,AX		; ..
X	mov	SP,offset STACK ; always change SS,SP in adjacent instructions
X
X				; In principle, we didn't need to turn 
X				; interrupts off because doing it in that 
X				; order guarantees that no interrupt will 
X				; occur between mov SS and mov SP, but early
X				; 8088s had a bug and it didn't work.
X				; Better safe than sorry.  An /XT could be
X				; repaired with one of these bogus chips
X
X;	sti			; allow interrupts again
X; Save state
X	push	ds		; save state
X	push	es
X	push	ax
X	push	bx
X	push	cx
X	push	dx
X	push	di
X	push	si
X	push	ds
X; begin body
X
X;	Restore DS from value  saved in pgroup:DASEG.
X	mov	bx,offset PGROUP:DASEG
X	mov	ds,cs:[bx]	; now DS has the offset of dgroup segment
X	assume	ds:DGROUP
X
X;	mov	ax,DGROUP
X;	mov	ax,SEG intnest	; make DS be correct
X				; note: All variables have the same DS
X				; so doing it for one will do it for
X				; all
X				; This trick will not work in the large
X				; memory model; there we have to load
X				; DS: for each variable, because they
X				; could be in different segments
X				; No, I don't know how to handle the
X				; case where a long vector falls across
X				; a segment boundary
X
X; at this point we may now validly address data
XIFDEF DEBUG
X	inc	intcnt		; up interrupt count
X	mov	_loop_cnt, 0	; initialize iteration counter
XENDIF
X
Xreadit:	call	mpu_aintr
X; end body
XIFDEF DEBUG
X	inc	_loop_cnt
XENDIF
X	mov	al,20h		;;; EOI code
X	out	20h,al		;;; Announce end of interrupt
X	mov	dx,STATPORT	;; load port number
X	in	al,dx		;; read in char from port	
X	test	al,DSR		;
X	jz	readit		;loop to handle next data byte
X;;				See note about the fact that we are
X;;				twiddling the primary interrupt controller
X;;				chip on an /AT, but this is no different
X;;				than what is required on the /XT
XIFDEF DEBUG
X	mov	ax, _loop_cnt	;; _loop_max = max(_loop_max, _loop_cnt)
X	cmp	ax, _loop_max
X	jb	leave
X	mov	_loop_max, ax
XENDIF
Xleave:	pop	ds
X	pop	si
X	pop	di
X	pop	dx
X	pop	cx
X	pop	bx
X	pop	ax
X	pop	es
X	pop	ds
X; Now restore our old stack
X	cli			; do it safely...
X	mov	SS,OldSS	; restore SS
X	mov	SP,OldSP	; restore SP
X;	sti			; allow them again
X	mov	AX,OldAX	; restore AX
X	iret
X_a_intr	endp
X
X;;
X
X;;
X;; Data from mpu-401
X;;
X
X	MPU_ACK		=	0feh	;; acknowledgment of end of command
X	ABOVE_TIMING_BYTE =	0f0h	;; 1st value greater than legal timing
X					;;  byte values (0 - 0efh)
X	TIMER_OVERFLOW	=	0f8h	;; record timer reached 240
X	TIMER_INCR	=	240d	;; add when TIMER_OVERFLOW comes
X	SYSTEM_MESSAGE	=	0ffh	;; MIDI system message
X	MIDI_EXCLUSIVE	=	0f0h	;; MIDI exclusive message
X	MIDI_EOX	=	0f7h	;; MIDI EOX (end of MIDI exclusive)
X	MPUNOOP		=	0f8h	;; MPU Mark: No Operation
X
X;;
X;; midi codes
X;; high order 4 bits (of 8) give command
X;; low order 4 bits give midi channel number
X;;
X	MCOMMASK	=	0f0h	;; These bits give MIDI command
X
X	MSTATUSMASK	=	080h	;; This bit set if MIDI status byte
X	MCHANMASK	=	00fh	;; These bits give MIDI channel number
X
X	NOTEOFF		=	080h	;; status,pitch,veloc
X	NOTEON		=	090h	;; status,pitch,veloc (=0 means off)
X	NOTEAFTERTOUCH	=	0a0h	;; status,pitch,arg2
X	CONTROLCHANGE	=	0b0h	;; status,arg1,arg2
X	PROGRAMCHANGE	=	0c0h	;; status,program
X	CHAFTERTOUCH	=	0d0h	;; status,arg
X	PITCHWHEEL	=	0e0h	;; status,arg1,arg2
X	MPUCOM		=	0f0h	;; fake midi command, really mpu401
X	
XMAXDELAY = 20000	;; mpu_get times out after this many tries
X
X
X;*****************************************************************************
X;				    mpu_get
X;
X;*****************************************************************************
Xmpu_get proc near		;; read data from mpu 401
X	mov _rd_delay,MAXDELAY
Xtryagain:
X	mov	dx,STATPORT	;; read status port
X	in	al,dx
X	test	al,DSR		;; data ready to send?
X	jz	gotit		;;   yes - read the data
X	dec	_rd_delay	;;   no - test for timeout
X	jnz	tryagain	;; timed out? no - repeat
X	mov	_timeerr,TIMEOUT ;;  yes - report error,
X	mov	al,0f8h		;;  and return innocuous (I hope) data
X	ret
Xgotit:	mov	dx,DATAPORT	;; load port number
X	in	al,dx		;; read in char from port	
X	ret
Xmpu_get endp
X
X
X;*****************************************************************************
X;				    putbuf
X;*****************************************************************************
Xputbuf proc near		;; put data into buffer
X	mov	dx,_bufftail
X	add	dx,4
X	and	dx,BUFFMASK	;; wrap around ( dx = dx mod buffersize )
X	cmp	dx,_buffhead
X	je	bufferfull
X;; save new _bufftail in dx, copy bytes
X	mov	si,_bufftail
X	mov	bl, _MidiStat
X	mov	byte ptr _buff[si],bl
X	inc	si
X	mov	bl, _Midi1
X	mov	byte ptr _buff[si],bl
X	inc	si
X	mov	bl,_Midi2
X	mov	byte ptr _buff[si],bl
X	mov	_bufftail,dx
X	ret
Xbufferfull:
X	mov	_interror,BUFFERR
X	ret
Xputbuf endp
X
XGETMIDI macro ;; read the mpu 401 data port into al
X		call	mpu_get
X	endm
X
X
X;*****************************************************************************
X;				   mpu_aintr
X;*****************************************************************************
Xmpu_aintr proc	near
X
X	GETMIDI 1,gm1		;; get what 401 want us to get
X
X	mov	ah,0			;; several places assume ax = al
X	cmp	ax,ABOVE_TIMING_BYTE	;; Timing byte?
X	jb	l_timing_byte		;; (usually followed by midi data)
X	cmp	al, TIMER_OVERFLOW
X	je	l_timer_overflow
X	cmp	al,MPU_ACK		;; Ack?
X	je	l_mpu_ack
X	cmp	al,SYSTEM_MESSAGE	;; Midi system message?
X	jne	bad
X	jmp	l_system_message
X
X
X;;
X;; This routine does not handle:
X;;	Track data requests
X;;	Conductor requests
X;;	Clock to host
X;; musicinit() initializes the MPU-401 in such a way so that these bytes
X;; are never sent.  If they do appear, they end up here.
X;;
X
Xbad:
X	mov	_Unknown,ax
X	mov	_interror,CMDERR
X	jmp	bye
X;;
X;; Handle each class of 401 message
X;;
X
X;; An ack, set Ack so that mpu_wait() can see it.
Xl_mpu_ack:
X	inc	_Ack
X	cmp	_time_req, 0	;; Does this command return timing data?
X	je	ack_done	;; if not, just return
X	GETMIDI 2,gm2		;; otherwise, read one more byte
X	mov	ah, 0		;; increment Ticks by result
X	add	WORD PTR _Ticks, ax
X	adc	WORD PTR _Ticks+2, 0
X	mov	_time_req, 0
Xack_done:
X	jmp	bye
X
X;; A timer overflow, increment clock by appropriate number of ticks
Xl_timer_overflow:
X	add	WORD PTR _Ticks,TIMER_INCR ;; yes, do 32 bit incr of clock
X	adc	WORD PTR _Ticks+2,0
X	jmp	bye
X
X;; A timing byte - the hard case
X;; There are a number of possibilities, on which we branch
Xl_timing_byte:
X	mov	_MidiTime,al		;; save timing byte
X	add	WORD PTR _Ticks,ax	;; yes, do 32 bit incr of clock
X	adc	WORD PTR _Ticks+2,0
X	GETMIDI 3,gm3			;; get next byte
X	test	al,MSTATUSMASK		;; It's midi, is it a status byte?
X	je	runstat
X
X;; Here we have new midi status byte.  Stash it and read in first data
X
X	mov	_MidiStat,al
X	mov	bl,al			;; copy command to bl
X	
X	and	bl,MCOMMASK		;; "And" off channel bits
X	cmp	bl,MPUCOM		;; Is it an MPU command in disguise?
X	je	l_mpucom		;; Yes, deal with it.
X
X	GETMIDI 4,gm4			;; read in first data byte
X	jmp	decode			;; decide whether 1 or 2 data bytes
X
Xrunstat:
X	mov	bl,_MidiStat		;; no, use previous (running) status
X	and	bl,MCOMMASK
X
X;; Commands 0c0h (program change) and 0d0h (channel after touch) have 2 bytes
X;; at this point, al has 1st data byte, bl has upper four bits of status byte
X
Xdecode:	mov	_Midi1,al		;; save first data byte
X	cmp	bl,CHAFTERTOUCH
X	je	gotmsg
X	cmp	bl,PROGRAMCHANGE
X	je	gotmsg
X
X	GETMIDI 5,gm5			;; read second data byte
X	mov	_Midi2,al		;; save second data bytes
Xgotmsg:
X
X;;
X;; Here the midi command is contained in the (2 or) 3 bytes
X;; MidiStat, Midi1, and Midi2
X;;
X	call	putbuf		;; put the data in the buffer
X				;; optimization note: only one call to putbuf
Xgobye:	jmp	bye
X
X
X;;
X;; MPU-401 marks 
X;;	These shouldn't happen and are ignored.  The NOOP mark IS sent
X;;	when recording, contrary to the MPU401 manual.  Since it seems
X;;	harmless, no error is reported if a NOOP is sent.  Otherwise,
X;;	report a bad command with the timing byte in the high-order byte
X;;	of the error data (Unknown) to distinguish the data as mark data.
X;;
Xl_mpucom:
X	cmp	al,MPUNOOP
X	je	gobye		;; MPU-401 manual is wrong!  The 
X	mov	ah,_MidiTime	;; report two bytes as unknown
X	jmp	bad
X
X;; A MIDI system message, currently only read sys. exclusive messages
Xl_system_message:
X	;; see what the message is
X	GETMIDI 6,gm6
X	cmp	al,MIDI_EXCLUSIVE
X	je	store_x
X	jmp	bad			;; only handle MIDI_EXCLUSIVE
X
Xstore_x:	;; put data in buffer until MIDI_EOX read	
X	mov	bx,_xbuff		;; do not store if _xbuff is NULL
X	cmp	bx,0
X	je	nobuff
X
X	add	bx,_xbufftai		;; add index
X	mov	byte ptr [bx],al	;; and store midi data
X	mov	dx,_xbufftai		;; increment with wrap-around
X	add	dx,1
X	and	dx,_xbuffmas
X	mov	_xbufftai,dx
Xnobuff:	test	al,MSTATUSMASK		;; are we done? 
X	je	ex_continue		;; stop on any status byte ...
X	cmp	al,MIDI_EXCLUSIVE	;; ... except midi exclusive
X	jne	bye
Xex_continue:
X	GETMIDI 7,gm7
X	jmp	store_x
X;; common return point
X	
Xbye:	ret
Xmpu_aintr endp
X	ENDPS
X;_TEXT	ENDS
X	END
END_OF_FILE
if test 14569 -ne `wc -c <'aintr.asm'`; then
    echo shar: \"'aintr.asm'\" unpacked with wrong size!
fi
# end of 'aintr.asm'
fi
if test -f 'moxc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'moxc.c'\"
else
echo shar: Extracting \"'moxc.c'\" \(15273 characters\)
sed "s/^X//" >'moxc.c' <<'END_OF_FILE'
X/* MOXC -- a C version of Collinge's MOXIE language	*/
X
X/*****************************************************************************
X*	Change Log
X*  Date	    | Change
X*-----------+-----------------------------------------------------------------
X* 31-Dec-85 | Modified for use with midi
X*  5-Feb-86 | Added m_rest and m_restuntil allowing rests at top level
X* 28-May-86 | Added command line parsing
X*  4-Jun-86 | changed keyevent to separate calls for each event type
X* 10-Jul-86 | put loop in mainscore with prompt to play and replay
X*****************************************************************************/
X
X/* IMPORTS:
X	asciievent(k)		user-defined action for terminal input
X	bendchange(ch, val)	user-defined pitch bend handler
X	ctrlchange(ch, c, val)	user-defined control change handler
X	keydown(ch, p, v)	user-defined MIDI note on handler
X	keyup(ch, p)		user-defined MIDI note off handler
X	mainscore()		user-defined first action(s)
X	musicfns		lots of time and io functions
X	peddown(ch)		user-defined pedal down handler
X	pedup(ch)		user-defined pedal up handler
X	touchchange(ch, val)	user-defined aftertouch handler
X*/
Xasciievent();
Xbendchange();
Xctrlchange();
Xkeydown();
Xkeyup();
Xmainscore();
Xpeddown();
Xpedup();
Xprgmchange();
Xtouchchange();
X
X/* EXPORTS:
X	cause(delay, routine, p1, p2, ..., p8)
X	moxcdone -- set to true to quit
X	eventtime -- ideallized current time
X*/
X
X#include "cext.h"
X#include "stdio.h"
X#include "malloc.h"
X#include "mpu.h"
X#include "cmdline.h"
X#include "midicode.h"
X#include "moxc.h"
X
X#define SAFEMOXC true
X
X/* lists of switches and options */
X#define nswitches 8
Xprivate char *switches[nswitches] = 
X    { "-debug", "-d", "-miditrace", "-m", "-trace", "-t", "-help", "-block" };
X#define noptions 1
Xprivate char *options[1] = { "-tune" };
X
X#define n_d_sw 2
Xprivate char *d_switches[n_d_sw] = { "-d", "-debug" };
X
X
X/* number of events to prepare to run on each main loop */
X#define NPREPARE 5
X
X#define NPRIORITY 10
X
Xtypedef struct event {
X    struct event *next; /* link to next event record */
X    long time;		/* time of this event */
X    int priority;	/* priority of this event (0 is highest) */
X    int (*routine)();	/* who to call */
X    int p1, p2, p3, p4, p5, p6, p7, p8; /* what to pass */
X} *event_type;
X
X/*
X * pending is used to sort events by priority and hold them until
X * there is time to insert them in the sorted evqueue.  Note that
X * in this implementation, only one priority is used.
X * nodes are allocated/returned from/to evfree
X */
Xevent_type pending[NPRIORITY];
Xint npending;	/* tells how many events are in pending array */
Xevent_type evqueue;		/* waiting to run event queue */
Xevent_type evfree = NULL;	/* free list */
Xint moxcdone;	/* flag to halt execution */
Xlong eventtime; /* time of current event -- used to avoid timing errors due */
X		/*  to finite execution speed */
Xint debug = false;
X
X/*****************************************************************************
X*	Routines local to this module
X*****************************************************************************/
Xprivate void		cmdline_help();
Xprivate event_type	evallocate();
Xprivate event_type	evcdr();
Xprivate event_type	evcons();
Xprivate	void		evdeallocate();
Xprivate boolean		evgtr();
Xprivate event_type	evinsert();
Xprivate	void		evrun();
Xprivate	void		evshow();
Xprivate	void		moxcinit();
Xprivate	void		moxcpoll();
Xprivate	void		schedule();
X
X/****************************************************************************
X*				cause
X* Inputs:
X*	int delay: time before this event should occur
X*	int (*routine)(): routine that implements the event
X*	int p1 through p8: parameters to pass to routine
X* Effect: 
X*	builds an event and puts it in pending queue for later scheduling
X****************************************************************************/
X
Xvoid cause(delay, routine, p1, p2, p3, p4, p5, p6, p7, p8)
X    int delay, (*routine)(), p1, p2, p3, p4, p5, p6, p7, p8;
X{
X    event_type ev;
X    int priority;
X/*    priorities are not currently implemented:
X    if (priority >= NPRIORITY) priority = NPRIORITY-1;
X    if (priority <= 0) priority = 0;
X*/
X    priority = 0;
X#ifdef SAFEMOXC
X    if (routine == 0) {
X        printf("Error: cause called with NULL routine\n");
X	musicterm();
X	exit(1);
X    }
X#endif
X    ev = evallocate();
X    ev->time = eventtime + delay;
X    ev->priority = priority;
X    ev->routine = routine;
X    ev->p1 = p1;
X    ev->p2 = p2;
X    ev->p3 = p3;
X    ev->p4 = p4;
X    ev->p5 = p5;
X    ev->p6 = p6;
X    ev->p7 = p7;
X    ev->p8 = p8;
X
X    npending++;
X    pending[priority] = evcons(ev, pending[priority]);
X    if (debug) {
X	printf("(cause) event is pending:");
X	evshow(ev);
X    }
X}
X
X/****************************************************************************
X*				 cmdline_help
X* Effect: 
X*	Prints out command line help
X****************************************************************************/
X
Xprivate void cmdline_help()
X{
X    fprintf(stderr,"program_name [options]\n");
X    fprintf(stderr,"	 Options are below.  Those with * are for wizards:\n");
X    fprintf(stderr,"	   -block	     disable MIDI thru\n");
X    fprintf(stderr,"	   -debug (-d)	     enable verbose debug mode\n");
X    fprintf(stderr,"	   -help	     this message\n");
X    fprintf(stderr,"	   -miditrace (-m)   turn on MIDI command trace\n");
X    fprintf(stderr,"	   -tune file	     use tuning from file\n");
X    fprintf(stderr,"	   -trace (-t)	     trace music\n");
X}
X
X/****************************************************************************
X*			    evallocate
X* Outputs:
X*	returns event_type allocated from freelist or with malloc
X****************************************************************************/
X
Xprivate event_type evallocate()
X{
X    if (evfree) return evcdr(&evfree);
X    /* else */ return ((event_type) malloc(sizeof(struct event)));
X}
X
X/****************************************************************************
X*				evcdr
X* Inputs:
X*	event_type *evlist: address of a list pointer
X* Outputs:
X*	returns the head of the list
X* Effect:
X*	removes the head of the list from *evlist
X****************************************************************************/
X
Xprivate event_type evcdr(evlist)
X    event_type *evlist;
X{
X    event_type ptr;
X    ptr = *evlist;
X    *evlist = (*evlist)->next;
X    ptr->next = NULL;
X    return ptr;
X}
X
X/****************************************************************************
X*				evcons
X* Inputs:
X*	event_type ev: event to put at the head of the list
X*	event_type evlist: the list
X* Outputs:
X*	returns list with ev at the head
X* Effect: modifies ev's next pointer
X****************************************************************************/
X
Xprivate event_type evcons(ev, evlist)
X    event_type evlist, ev;
X{
X    ev->next = evlist;
X    return ev;
X}
X
X/****************************************************************************
X*			    evdeallocate
X* Inputs:
X*	event_type ev: a node to deallocate
X* Effect: 
X*	returns ev to free list
X****************************************************************************/
X
Xprivate void evdeallocate(ev)
X    event_type ev;
X{
X    evfree = evcons(ev, evfree);
X}
X
X/****************************************************************************
X*			    evgtr
X* Inputs:
X*	event_type ev1, ev2: two events to be compared
X* Outputs:
X**	returns true if ev1 is earlier or has lower priority number than ev2
X****************************************************************************/
X
Xprivate boolean evgtr(ev1, ev2)
X    event_type ev1, ev2;
X{
X    return (ev1->time < ev2->time) ||
X	   ((ev1->time == ev2->time) && (ev1->priority < ev2->priority));
X}
X
X/****************************************************************************
X*			    evinsert
X* Inputs:
X*	event_type evlist: the list (a priority queue)
X*	event_type ev: the event to insert in evlist
X* Outputs:
X*	returns list resulting from inserting ev into evlist
X* Implementation:
X*	if ev goes at the head, ev is cons onto evlist and returned
X*	otherwise evlist is modified by inserting ev at the right spot
X****************************************************************************/
X
Xprivate event_type evinsert(evlist, ev)
X    event_type evlist, ev;
X{
X    event_type ptr;
X    if (!evlist) {	/* no list, return event */
X	ev->next = NULL;
X	return ev;
X    }
X    if (evgtr(ev, evlist)) { /* make event first on list */
X	return evcons(ev, evlist);
X    }
X    ptr = evlist;
X    while ((ptr->next) && evgtr(ptr->next, ev)) {
X	ptr = ptr->next;
X    }
X    ev->next = ptr->next;
X    ptr->next = ev;
X    return evlist;
X}
X
X/****************************************************************************
X*				evrun
X* Inputs:
X*	event_type ev: the event to execute
X* Effect: 
X*	executes the previously scheduled event ev and deallocates it
X****************************************************************************/
X
Xprivate void evrun()
X{
X    event_type ev;
X    if (debug) {
X	printf("(evrun) running an event: \n");
X    }
X    ev = evcdr(&evqueue);
X    eventtime = ev->time;
X    if (debug) evshow(ev);
X    (*(ev->routine))
X	(ev->p1, ev->p2, ev->p3, ev->p4, ev->p5, ev->p6, ev->p7, ev->p8);
X    evdeallocate(ev);
X}
X
X/****************************************************************************
X*				evshow
X* Inputs:
X*	eventtype ev: the event to show
X* Effect: 
X*	prints a description of ev
X* Assumes:
X*	ev is not null
X****************************************************************************/
Xprivate void evshow(ev)
X    event_type ev;
X{
X    printf("address:  %d\n", ev);
X    printf("time:     %ld\n", ev->time);
X    printf("priority: %d\n", ev->priority);
X    printf("routine:  %d\n", ev->routine);
X    printf("parameters: %d, %d, %d, %d, %d, %d, %d, %d\n",
X	 ev->p1, ev->p2, ev->p3, ev->p4, ev->p5, ev->p6, ev->p7, ev->p8);
X}
X
X/****************************************************************************
X*				    m_rest
X* Inputs:
X*	int time: Amount of time to rest
X* Effect: 
X*	Waits until the amount of time specified has lapsed
X* Assumes:
X*	Must not be called from a "caused" routine.  Must only be called
X*	from "mainscore" or a routine called directly or indirectly from
X*	"mainscore" without using "cause".
X****************************************************************************/
X
Xvoid m_rest(time)
X    int time;
X{
X    m_restuntil(time + gettime());	
X}
X
X/****************************************************************************
X*				  m_restuntil
X* Inputs:
X*	int time: Event time to rest until
X* Effect: 
X*	Waits until the specified time has been reached (absolute time).
X*	Other "caused" events will take place during the rest provided
X*	this routine is called from "mainscore" (see m_rest description).
X****************************************************************************/
Xvoid m_restuntil(time)
X    int time;
X{
X    while(time > gettime()) {
X	moxcpoll();
X    }
X}
X
X/****************************************************************************
X*				main
X* Inputs:
X*	int argc: number of command line arguments
X*	char * argv: command line argument array
X* Effect: 
X*	initializes and runs moxc program
X****************************************************************************/
X
Xvoid main(argc,argv)
X    int argc;
X    char * argv[];
X{
X    while (askbool("Type RETURN to play, or N RETURN to quit", true)) {
X	moxcinit(argc, argv);	/* initialize structures */
X	mainscore();		/* call user's start program */
X	while (!moxcdone) {		/* test for finish */
X	    if (!evqueue) moxcdone |= (npending == 0);
X	    moxcpoll();		/* do work */
X	}
X	musicterm();
X	printf("End of Moxc execution.\n");
X    }
X}
X
X/****************************************************************************
X*				moxcinit
X* Inputs:
X*	int argc: number of command line arguments
X*	char * argv: command line argument array
X* Effect: initializes moxc system
X****************************************************************************/
X
Xprivate void moxcinit(argc, argv)
X    int argc;
X    char * argv[];
X{
X    int i;	/* loop variable */
X
X    cl_init(switches, nswitches, options, noptions, argv, argc);
X
X    if (cl_switch("-help")) {
X	cmdline_help(); 
X	exit(0);
X    }
X
X    debug = (cl_nswitch(d_switches, n_d_sw) != NULL);
X
X    for (i=0; i < NPRIORITY; i++) pending[i] = NULL;
X    evqueue = NULL;
X    eventtime = 0;
X
X    musicinit();    
X
X    moxcdone = 0;
X}
X
X/****************************************************************************
X*				moxcpoll
X* Effect: dispatch on user inputs, cause events
X****************************************************************************/
X
Xprivate void moxcpoll()
X{
X    long now;		/* current time */
X    int k;		/* terminal input (this is int so users do not
X			 * have to declare type of parameter */
X    byte midi_data[4];	/* midi input */
X
X    /* get the time */
X	now = gettime();
X    /* see if any user-caused events have happened */
X	eventtime = now;
X	/* poll for and decode midi keyboard input */
X	    if (getbuf(false, midi_data)) {
X		byte code = midi_data[0] & MIDI_CODE_MASK;
X		if (code == MIDI_ON_NOTE) {
X		    if (midi_data[2] == 0) {	/* velocity 0 -> note off */
X			keyup((midi_data[0] & MIDI_CHN_MASK) + 1,
X			      midi_data[1] - 12);
X		    } else {
X			keydown((midi_data[0] & MIDI_CHN_MASK) + 1,
X				midi_data[1] - 12, midi_data[2]);
X		    }
X		} else if (code == MIDI_OFF_NOTE) {
X		    keyup((midi_data[0] & MIDI_CHN_MASK) + 1,
X		    	  midi_data[1] - 12);
X		} else if (code == MIDI_TOUCH) {
X		    touchchange((midi_data[0] & MIDI_CHN_MASK) + 1,
X				midi_data[1]);
X		} else if (code == MIDI_BEND) {
X		    bendchange((midi_data[0] & MIDI_CHN_MASK) + 1,
X			       midi_data[1] + (midi_data[2] << 7));
X		} else if (code == MIDI_CTRL && midi_data[1] == SUSTAIN) {
X		    if (midi_data[2] == 0)
X			pedup((midi_data[0] & MIDI_CHN_MASK) + 1);
X		    else peddown((midi_data[0] & MIDI_CHN_MASK) + 1);
X		} else if (code == MIDI_CTRL) {
X		    ctrlchange((midi_data[0] & MIDI_CHN_MASK) + 1,
X			       midi_data[1], midi_data[2]);
X		} else if (code == MIDI_CH_PROGRAM) {
X		    prgmchange((midi_data[0] & MIDI_CHN_MASK) + 1,
X		    		midi_data[1]);
X		}
X	    }
X	/* poll ASCII keyboard */
X	    if (kbhit()) {
X		k = getch();
X		asciievent(k);
X		if (debug && evqueue)
X		    printf("nextevent is scheduled for %ld\n", evqueue->time);
X	    }
X    /* if it is now time, run the next event */
X	else if ((evqueue != NULL) && (now >= evqueue->time)) evrun();
X    /* if events are waiting to be scheduled ... */
X	if (npending > 0) schedule();
X}
X
X/****************************************************************************
X*				quit
X* Effect: tells moxc to shut down
X****************************************************************************/
X
Xvoid quit()
X{
X    moxcdone = true;
X}
X
X/****************************************************************************
X*				schedule
X* Effect: takes events off pending queues and prepares them to run
X****************************************************************************/
X
Xprivate void schedule()
X{
X    int i, n;
X    event_type ev;
X
X    /* insert up to NPREPARE events from pending queues */
X    i = 0;	/* i is number of events inserted */
X    n = 0;	/* n is the priority */
X    while (i < NPREPARE && n < NPRIORITY) {
X	if (pending[n]) {
X	    npending--;
X	    ev = evcdr(&pending[n]);
X	    evqueue = evinsert(evqueue, ev);
X	    i++;
X	    if (debug) {
X		printf("(main) event inserted: \n");
X		evshow(ev);
X	    }
X	} else {
X	    n++;
X	}
X    }		
X}
END_OF_FILE
if test 15273 -ne `wc -c <'moxc.c'`; then
    echo shar: \"'moxc.c'\" unpacked with wrong size!
fi
# end of 'moxc.c'
fi
if test -f 'record.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'record.c'\"
else
echo shar: Extracting \"'record.c'\" \(17973 characters\)
sed "s/^X//" >'record.c' <<'END_OF_FILE'
X/* record.c -- keyboard to adagio recorder
X *
X * the interface consists of three routines:
X *	    rec_init()		-- initialization
X *	int rec_poll(long time) -- called during recording, returns true if
X *					recording space is exhausted
X *	    rec_final()		-- called to finish up
X */
X
X/*****************************************************************************
X*	    Change Log
X*  Date	    | Change
X*-----------+-----------------------------------------------------------------
X* 27-Feb-86 | Created changelog
X*	    | Use pedal information when computing durations (code taken
X*	    |  from transcribe.c)
X* 23-Mar-86 | Determine size of transcription when rec_init is called.
X* 21-May-86 | Major rewrite to use continuous controls (code taken 
X*	    |  from transcribe.c)
X*****************************************************************************/
X
X#include "cext.h"
X#include "stdio.h"
X#include "malloc.h"
X#include "mpu.h"
X#include "userio.h"
X#include "midicode.h"
X#include "record.h"
X		
Xextern long space;	/* how much space is left? */
X
Xint debug_rec = false;	/* verbose debug flag for this module */
Xint max_notes = -1;	/* -1 is flag that space must be allocated */
X
X/****************************************************************
X* data structure notes: the midi stream is stored as an array 
X* of 4-byte records, each of which is either a time or midi
X* data.	 Midi data always begins with a control byte (high
X* order bit set), and it is assumed times are positive (high
X* order bit clear), so the two are easy to distinguish
X* IF THE COMPILER PUTS THESE BITS IN THE SAME PLACE.  It looks
X* like the high order byte of the time lines up with the last
X* byte of a 4 byte array, so we will always set the high order
X* bit of the last array byte when the first 3 bytes are filled
X* with MIDI data.  This is refered to as the "tag" bit.
X* WARNING: Lattice C longs are UNSIGNED, therefore always
X* positive.  Test the high order bit with a mask.
X****************************************************************/
X
X#define MIDI_CMD_BIT		0x80
X#define HIGH_BIT		0x80000000
X#define istime(note) (!(((note)->when) & HIGH_BIT))
X
X#define ndsw 2
Xprivate char *dsw[ndsw] = { "-d", "-debug" };
X
Xtypedef union note_struct {
X	byte n[4];
X	long when;
X} *note_type, note_node;
X
Xprivate note_type event_buff;	/* pointer to allocated buffer */
Xprivate FILE *fp;
Xprivate char file_name[100];
Xprivate note_type next;	/* pointer to next entry in buffer */
Xprivate note_type last;	/* pointer to last entry in buffer */
Xprivate int pile_ups;	/* inner loop iteration count */
Xprivate int max_pile;	/* maximum of pile_ups */
X
X/****************************************************************************
X*	Routines local to this module
X****************************************************************************/
Xprivate void	bend_filter();
Xprivate void	byteorder();
Xprivate void	ctrl_filter();
Xprivate int	event_bend();
Xprivate void	filter();
Xprivate long	getdur();
Xprivate long	getnext();
Xprivate char	map_ctrl();
Xprivate void	output();
Xprivate void	put_pitch();
X
X/****************************************************************************
X*				bend_filter
X* Inputs:
X*	note_type note: the current note
X*	note_type last: the last recorded event
X*	long now: the current time
X* Effect:
X*	remove pitch bend events in same 0.01 sec time slot
X* Implementation:
X*	If the current event is a pitch bend that bends again
X*	in the same time slot, make it a no-op by replacing it with
X*	the time.
X****************************************************************************/
X
Xprivate void bend_filter(note, last, now)
X    note_type note;	/* current note */
X    note_type last;	/* the last recorded event */
X    long now;		/* the current time */
X{
X    /* first see if there is another bend in this time
X     * slot.
X     */
X    note_type note2 = note + 1;
X    while (note2 < last) {
X	if (istime(note2) && (note2->when > now)) {
X	    break; /* new time slot */
X	} else if (note->n[0] == note2->n[0]) {
X	    note->when = now;
X	    return; /* found another bend */
X	}
X	note2++;
X    }
X}
X
X/****************************************************************************
X*				byteorder
X* Effect: 
X*	check out assumptions about byte order and placement
X****************************************************************************/
X
Xprivate void byteorder()
X{
X    if ((sizeof(event_buff[0]) != 4) ||
X	(sizeof(event_buff[0].when) != 4) ||
X	(sizeof(event_buff[0].n[0]) != 1)) {
X	fprintf(stderr, "implementation error: size problem\n");
X	exit(1);
X    }
X    event_buff[0].n[0] = 0x12;
X    event_buff[0].n[1] = 0x34;
X    event_buff[0].n[2] = 0x56;
X    event_buff[0].n[3] = 0x78;
X    if ((event_buff[0].when != 0x78563412) &&
X	(event_buff[0].when != 0x12345678)) {
X	fprintf(stderr, "implementation error: layout problem\n");
X	exit(1);
X    }
X}
X
X/****************************************************************************
X*				ctrl_filter
X* Inputs:
X*	note_type note: the current note
X*	note_type last: the last recorded event
X*	long now: the current time
X* Effect:
X*	remove ctrl change events in same 0.01 sec time slot
X* Implementation:
X*	If the current event is a control change that changes again
X*	in the same time slot, make it a no-op by replacing it with
X*	the time.
X****************************************************************************/
X
Xprivate void ctrl_filter(note, last, now)
X    note_type note;	/* the current note */
X    note_type last;	/* the last recorded event */
X    long now;		/* the current time */
X{
X    /* see if there is another control change in this time
X     * slot.
X     */
X    note_type note2 = note+1;
X    while (note2 < last) {
X	if (istime(note2) && (note2->when > now)) {
X	    break;	/* new time slot */
X	} else if ((note->n[0] == note2->n[0]) &&
X		   (note->n[1] == note2->n[1])) {
X	    note->when = now;
X	    return; /* found another change */
X	}
X	note2++;
X    }
X}
X
X/****************************************************************************
X*				event_bend
X* Inputs:
X*	note_type note: pointer to a pitch bend event
X* Outputs:
X*	returns int: an 8 bit pitch bend number
X****************************************************************************/
X
Xprivate int event_bend(note)
X    note_type note;
X{
X    return (int) (((note->n[1]) >> 6) + ((note->n[2]) << 1)); 
X}
X
X/****************************************************************************
X*				filter
X* Inputs:
X*	note_type last: the last note recorded
X* Effect: allow only one control change per time slot (0.01 sec)
X* Implementation:
X*	call ctrl_filter and bend_filter to overwrite control changes with
X*	noop data (the current time is used as a noop)
X****************************************************************************/
X
Xprivate void filter(last)
X    note_type last;
X{
X    note_type note;	/* loop control variable */
X    long now;		/* last time seen */
X    int command;	/* command pointed to by note */
X    int chan;		/* channel pointed to by note */
X
X    for (note = event_buff; note <= last; note++) {
X	if (istime(note)) {
X	    now = note->when;
X	} else {
X	    command = note->n[0] & MIDI_CODE_MASK;
X	    chan = note->n[0] & MIDI_CHN_MASK;
X
X	    if (command == MIDI_CTRL &&
X		note->n[1] == SUSTAIN) {
X		/* do nothing */;
X	    } else if (command == MIDI_CTRL) {
X		ctrl_filter(note, last, now);
X	    } else if (command == MIDI_TOUCH) {
X		bend_filter(note, last, now);	/* bend and touch use the */
X	    } else if (command == MIDI_BEND) {	/*  same filter routines  */
X		bend_filter(note, last, now);
X	    }
X	}
X    }
X}
X
X
X/****************************************************************************
X*				getdur
X* Inputs:
X*	int i: index of the note
X*	note_type last: pointer to the last event recorded
X*	int ped: true if pedal is down at event i
X*	long now: the time at event i
X* Outputs:
X*	returns long: the duration of note i
X* Assumes:
X*	assumes i is a note
X* Implementation:
X*	This is tricky because of pedal messages.  The note is kept on by
X*	either the key or the pedal.  Keep 2 flags, key and ped.  Key is
X*	turned off when a key is released, ped goes off and on with pedal.
X*	Note ends when (1) both key and ped are false, (2) key is
X*	pressed (this event will also start another note).
X****************************************************************************/
X
Xprivate long getdur(i, last, ped, now)
X    int i;
X    note_type last;
X    int ped;
X    long now;
X{
X    int key = true;	/* flag that says if note is on */
X    long start = now;
X    int chan = event_buff[i].n[0] & MIDI_CHN_MASK;
X    int pitch = event_buff[i].n[1];
X    note_type note = &(event_buff[i+1]);
X    int noteon; /* true if a noteon message received on chan */
X    int keyon;	/* true if noteon message had non-zero velocity */
X
X    /* search from the next event (i+1) to the end of the buffer:
X     */
X    for (; note < last; note++) {
X	if (istime(note)) {
X	    now = note->when;
X	} else {
X	    noteon = keyon = false;
X	    if ((note->n[0] & MIDI_CHN_MASK) == chan) {
X		noteon = ((note->n[0] & MIDI_CODE_MASK) == MIDI_ON_NOTE) &&
X		     (note->n[1] == pitch);
X		keyon = noteon && (note->n[2] != 0);
X		if ((noteon && (note->n[2] == 0)) ||
X		    (((note->n[0] & MIDI_CODE_MASK) == MIDI_OFF_NOTE) &&
X		     (note->n[1] == pitch))) key = false;
X		if (((note->n[0] & MIDI_CODE_MASK) == MIDI_CTRL) &&
X		    note->n[1] == SUSTAIN && note->n[2] == 127) ped = true;
X		if (((note->n[0] & MIDI_CODE_MASK) == MIDI_CTRL) &&
X		    note->n[1] == SUSTAIN && note->n[2] == 0) ped = false;
X
X		if ((!key && !ped) || keyon)
X		    return now - start;
X	    }
X	}
X    }
X    return last->when - start;
X}
X
X/****************************************************************************
X*				getnext
X* Inputs:
X*	int i: the index of the current note
X*	note_type last: pointer to last valid data
X*	long now: the current time
X* Outputs:
X*	returns long: the time of the next note, program, or control change
X*		(returns time of last event if nothing else is found)
X****************************************************************************/
X
Xprivate long getnext(i, last, now)
X    int i;	/* the index of the current note */
X    note_type last;	/* pointer to last valid data */
X    long now;	/* the current time */
X{
X    i++;	/* advance to next item */
X    for (; event_buff + i < last; i++) {
X	note_type note = &(event_buff[i]);
X	int cmd = note->n[0] & MIDI_CODE_MASK;
X
X	if (istime(note)) {
X	    now = note->when;
X	} else if (((cmd == MIDI_ON_NOTE) &&
X		    (note->n[2] != 0)) /* note on */ ||
X		   (cmd == MIDI_CH_PROGRAM) /* program change */ ||
X		   ((cmd == MIDI_CTRL) &&
X		    (note->n[1] != SUSTAIN) /* control change */ ) ||
X		   (cmd == MIDI_TOUCH) ||
X		   (cmd == MIDI_BEND)) {
X	    return now;
X	}
X    }
X    return last->when;
X}
X
X/****************************************************************************
X*				map_ctrl
X* Inputs:
X*	int control: a midi control number
X* Outputs:
X*	returns char: an adagio control change command letter, NULL if
X*		control change is not one of PORTARATE, PORTASWITCH,
X*		MODWHEEL, FOOT
X****************************************************************************/
X
Xprivate char map_ctrl(control)
X    int control;
X{
X    switch (control) {
X	case PORTARATE:		return 'J';
X	case PORTASWITCH:	return 'K';
X	case MODWHEEL:		return 'M';
X	case FOOT:		return 'X';
X	default:		return  0;
X    }
X    return NULL;	/* make Lattice C type cheker happy */
X}
X
X/****************************************************************************
X*				output
X* Inputs:
X*	FILE *fp: an opened file pointer
X*	note_type last: the last data in the buffer
X*	boolean absflag: set to true if first line of the adagio score should
X*		include the absolute time
X* Effect: 
X*	write adagio file using data in event_buff
X* Implementation:
X*	NOTE: put all program changes in rests
X*	use N(ext) notation for all timing
X*	output no more than one continuous parameter change per
X*	clock tick for each continuous change parameter
X****************************************************************************/
X
Xprivate void output(fp, last, absflag)
X    FILE *fp;
X    note_type last;
X    boolean absflag;
X{
X    int i;			/* loop counter */
X    int command;		/* the current command */
X    int chan;			/* the midi channel of the current event */
X    int lastchan = 0;		/* the default adagio channel (1) */
X    int ped = false;		/* flag maintains state of pedal */
X    int how_many = last - event_buff;
X    long now;		/* the time of the next event */
X    boolean bad_ctrl_flag = false;	/* set to true if unknown ctrl read */
X
X    if (fp == NULL) {
X	fprintf(stderr, "internal error: output called with NULL file.\n");
X	exit(1);
X    }
X
X    if (debug_rec)
X	printf("hint: if file is not being closed, decrease MAXSPACE\n");
X
X    /* set the initial absolute time, all other times are relative */
X    if (absflag)
X	fprintf(fp, "t%ld ", event_buff[0].when);
X
X    for (i = 0; i < how_many; i++) {
X	if (debug_rec) {
X	    printf("ev %d: %x %x %x (%ld)\n", i, event_buff[i].n[0],
X		event_buff[i].n[1], event_buff[i].n[2], event_buff[i].when);
X	}
X
X	if (istime(event_buff+i)) {
X	    now = event_buff[i].when;
X	    if (debug_rec) printf("i = %d, now = %ld\n", i, now);
X	} else {
X	    command = event_buff[i].n[0] & MIDI_CODE_MASK;
X	    chan = event_buff[i].n[0] & MIDI_CHN_MASK;
X
X	    if (command == MIDI_ON_NOTE && event_buff[i].n[2] != 0) {
X		put_pitch(fp, event_buff[i].n[1] - 12);
X		fprintf(fp, " u%ld l%d n%ld", getdur(i, last, ped, now),
X			event_buff[i].n[2], getnext(i, last, now) - now);
X		if (lastchan != chan) {
X		    fprintf(fp, " v%d\n", chan + 1);
X		    lastchan = chan;
X		} else fprintf(fp, "\n");
X	    } else if (command == MIDI_CH_PROGRAM) {
X		fprintf(fp, "r z%d n%ld", event_buff[i].n[1] + 1,
X			getnext(i, last, now) - now);
X		if (lastchan != chan) {
X		    fprintf(fp, " v%d\n", chan + 1);
X		    lastchan = chan;
X		} else fprintf(fp, "\n");
X	    } else if (command == MIDI_CTRL &&
X		       event_buff[i].n[1] == SUSTAIN) {
X		ped = (event_buff[i].n[2] != 0);
X	    } else if (command == MIDI_CTRL) {
X		char c = map_ctrl(event_buff[i].n[1]);
X		if (c != 0) {
X		    fprintf(fp, "%c%d n%d\n", c,
X			event_buff[i].n[2], getnext(i, last, now) - now);
X		} else bad_ctrl_flag = true;
X	    } else if (command == MIDI_TOUCH) {
X		fprintf(fp, "O%d n%d\n", event_buff[i].n[1],
X			getnext(i, last, now) - now);
X	    } else if (command == MIDI_BEND) {
X		fprintf(fp, "Y%d n%d\n", event_bend(&event_buff[i]),
X			getnext(i, last, now) - now);
X	    } else if (command != MIDI_ON_NOTE) {
X		fprintf(stderr, "Command 0x%x ignored\n", command);
X	    }
X	}
X    }
X    if (bad_ctrl_flag)
X	fprintf(stderr,
X	       "Some unrecognized control changes were omitted from file.\n");
X}
X
X/****************************************************************************
X*				put_pitch
X* Inputs:
X*	FILE *fp: an open file
X*	int p: a pitch number
X* Effect: write out the pitch name for a given number
X****************************************************************************/
X
Xprivate void put_pitch(fp, p)
X    FILE *fp;
X    int p;
X{
X    static char *ptos[] = {"c", "cs", "d", "ef", "e", "f", "fs", "g",
X			   "gs", "a", "bf", "b"};
X    fprintf(fp, "%s%d", ptos[p % 12], p / 12);
X}
X
X/**********************************************************************
X*			rec_final
X* Inputs:
X*	boolean absflag: output absolute time of first note if true
X* Effect:
X*	Write recorded data to a file
X**********************************************************************/
X
Xvoid rec_final(absflag)
X    boolean absflag;
X{
X    next->when = gettime();
X    last = next;
X    if (debug_rec) printf("max_pile_up = %d, ", max_pile);
X    printf("%d times and events recorded.\n", last - event_buff);
X    filter(last);
X    output(fp, last, absflag);
X    fclose(fp);
X}
X
X/****************************************************************************
X*				rec_init
X* Inputs:
X*	char *file:  pointer to file name from command line (if any)
X*	boolean bender: true if pitch bend should be enabled
X* Outputs:
X*	returns true if initialization succeeds
X* Effect:
X*	prepares module to record midi input
X****************************************************************************/
X
Xboolean rec_init(file, bender)
X    char *file;
X    boolean bender;
X{
X    char *malloc();	/* memory allocation */
X
X    debug_rec = (cl_nswitch(dsw, ndsw) != NULL);
X
X    pile_ups = 0;
X    max_pile = 0;
X
X    fp = fileopen(file, "gio", "w", "Name of output file");
X    if (max_notes == -1) {	/* allocate space 1st time rec_init called */
X	max_notes = space/sizeof(note_node);
X	event_buff = (note_type) malloc(sizeof(note_node) * max_notes);
X	if (event_buff == NULL) {
X	    fprintf(stderr, "Internal error allocating record space.");
X	    musicterm();
X	    exit(1);
X	}
X	byteorder();
X	printf("Space for %d events has been allocated.\n", max_notes);
X    }
X    next = event_buff;
X    last = event_buff + max_notes - 2;
X
X    while (getkey(false) != -1) ;	/* flush old midi events */
X    midi_cont(bender);
X    return max_notes > 10;
X}
X
X/****************************************************************************
X*				rec_poll
X* Inputs:
X*	long time: the current time
X* Outputs:
X*	returns true if there is no more memory
X* Effect: reads and stores any input
X* Assumes: rec_poll must be called frequently to get accurate results
X* Implementation:
X*	time stamps and midi events share the same buffer of 4-byte events
X*	save time at most once per call to rec_poll
X*	save time only if midi data is present
X****************************************************************************/
X
Xboolean rec_poll(time)
X    long time;
X{
X    next->when = time;	/* this will overwrite an earlier time unless data */
X			/* was recorded */
X    if (getbuf(false, (next+1)->n)) {	/* buffer nonempty? */
X	next++;
X	next->n[3] = MIDI_CMD_BIT;	/* set tag bit */
X	pile_ups = 1;
X	while (getbuf(false, (++next)->n)) {
X	    next->n[3] = MIDI_CMD_BIT;	/* set tag bit */
X	    pile_ups++;
X	    if (next >= last) {
X		break;
X	    }
X	}
X    }
X    if (pile_ups > max_pile) max_pile = pile_ups;
X    if (next >= last) {
X	fprintf(stderr, "No more memory.\n");
X	return true;
X    } /* else */ return false;
X}
END_OF_FILE
if test 17973 -ne `wc -c <'record.c'`; then
    echo shar: \"'record.c'\" unpacked with wrong size!
fi
# end of 'record.c'
fi
echo shar: End of archive 4 \(of 6\).
cp /dev/null ark4isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 6 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
