[alt.binaries.multimedia] SMUS2TeX

glewis@fws204.intel.com (Glenn M. Lewis ~) (12/24/90)

[]

        I have written a program that reads SMUS (IFF Simple Musical Score)
files, and writes TeX files that are processed by the MusicTeX package written
by Daniel Taupin at the "Laboratoire de Physique des Solides."

        I worked over 100 hours on this program, and am basically burnt out.
I have got it to a point where it produces syntactically correct TeX files
on the SMUS files I have tested it on, and produces good-but-not-perfect
output from TeX.

        What I wish to do now is to place this code in the public domain with
the only restrictions that you keep my name in the source and the generated
TeX files as the original author of the package, and do not use it in a
commercial product without first contacting me.  If you take this code and
make something completely awesome out of it, please let me know.  I take no
responsibility for the use or abuse of this code, and provide NO WARRANTY of
its fitness for a particular purpose.  This is given to you as-is and you
take full responsibility for use or abuse of it.

        OK, for those that don't know what the heck I am talking about, I
will give a little more information.  I saw the "MusicTeX" package mentioned
in comp.text.tex, and decided to get it (from sol.cs.ruu.nl or 131.211.80.5).
I ran the demos on it, and the output looked beautiful -- the scores (in
Standard Musical Notation) looked like they were professionally typeset.
Then I saw Peter da Silva post "Entertainer.smus" to alt.binaries.multimedia.
And lastly, Peter posted the SMUS format documentation.  Something snapped in
my brain.  :-)  I decided I would write a SMUS2TeX converter.

        Well, parsing the SMUS file was a breeze.  My previous big Amiga project
was TTDDD (Textual Three Dimension Data Description) for Turbo Silver and
Imagine.  Parsing that IFF was much more complex.  Parsing SMUS was a walk in
the park.  Then, I started creating the MusicTeX files.  This is not easy, for
three main reasons:  First, the English documentation for MusicTeX has errors
in it, and was poorly written.  Second, I don't know German, so I couldn't
read the German documentation for MusicTeX.  Third, formatting an arbitrary
sequence of notes into Standard Music Notation requires a great deal of
heuristics because so much of the choices made are aesthetic ones.

        So what I have created is the basis for a high-quality musical score
typesetting program.  But in actuality, the output needs to be tweaked either
by hand or at the program level for superior results.  All beams have a slope
of zero at present, and need to be smarter.  The choice of which clef to put
notes in works pretty well, but it can occasionally be fooled, with weird-
looking results.

        The main use I can see for this program is for somebody who has a
music program that does not have Standard Musical Notation (such as Music-X,
right?) but who has TeX and wants a nice printout of their score.  Note that
the score could have been created in any number of ways... entered via a
keyboard and MIDI, generated algorithmically (for those experimenting in
algorithmic music), downloaded from some BBS, etc.

        So here it is, SMUS2TeX.c.  I developed it on a Un*x box, but it
should compile very nicely on an Amiga.  I was careful about byte-ordering.
Feel free to do whatever you want with the code, with the above restrictions.
I can be reached at "glewis@pcocd2.intel.com" if you have any comments.

        Ooops... I am not going to make the same mistake I made with the TTDDD
distribution... I am going to give you usage information!  :-)  Actually, the
usage is the same as for ReadTDDD and WriteTDDD...  SMUS2TeX is simply a
filter.  You can use any of the following:

smus2tex < entertainer.smus > entertainer.tex
smus2tex   entertainer.smus > entertainer.tex
smus2tex   entertainer.smus   entertainer.tex
music-X | smus2tex | tex | dvips     # (just kidding!  :-)

        If you give the "-d" option (i.e. "smus2tex -d file.smus file.tex"),
you will get debugging information enbedded in the TeX file, which will make
it *huge*.  You can see what the program reads from the SMUS file, and what
kind of decisions it makes to format the notes.

        Of course, once the TeX file is generated, you must have MusicTeX
input files hanging around, and then you type:

tex entertainer.tex
dvips -o entertainer.ps entertainer.dvi

        and you will have a PostScript file.  I highly recommend using dvips
by Tomas Rokicki of Radical Eye Software fame.  In fact, AmigaTeX by R.E.S.
is the best implementation of TeX that I have seen on *any* system.
Disclaimer:  I used to be a happy customer of Radical Eye Software.  But now
I am also good friends with the owner of R.E.S. because of the excellent
software and even better support.  :-)  But rest assured, I get no kick-backs
for promoting his products!

        Good luck.  If you know German, you are lucky!  :-)

                                                        -- Glenn Lewis

glewis@pcocd2.intel.com

----------------------- cut here -------------------------
#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 12/19/1990 23:43 UTC by glewis@fws204
# Source directory smus
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   1153 -rw-r--r-- Makefile
#   7214 -rw-r--r-- smus.h
#  34169 -rw-r--r-- smus2tex.c
#
# ============= Makefile ==============
if test -f 'Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping Makefile (File already exists)'
else
echo 'x - extracting Makefile (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
.SUFFIXES: .smus .tex .dvi .ps
X
CFLAGS= -g
X
# "Make" seems to be pretty stupid about its implicit rules
# So we need to declare all combinations of getting from "here" to "there"...
# otherwise "make" would sometimes remove intermediate (eg, .dvi) files.
X
.smus.tex:
X	smus2tex $*.smus $*.tex
X
.tex.dvi:
X	tex $*.tex
X
.dvi.ps:
X	dvips -o $*.ps $*.dvi
X
.smus.dvi:
X	smus2tex $*.smus $*.tex
X	tex $*.tex
X
.smus.ps:
X	smus2tex $*.smus $*.tex
X	tex $*.tex
X	dvips -o $*.ps $*.dvi
X
.tex.ps:
X	tex $*.tex
X	dvips -o $*.ps $*.dvi
X
default: smus2tex entertainer.tex
X
all: entertainer.ps nutcracker.ps bfugue.ps learntune.ps turkish.ps
X
smus2tex: smus2tex.c
X	cc -g -o smus2tex smus2tex.c
X
entertainer.tex:	smus2tex entertainer.smus
nutcracker.tex:		smus2tex nutcracker.smus
bfugue.tex:			smus2tex bfugue.smus
learntune.tex:		smus2tex learntune.smus
turkish.tex:		smus2tex turkish.smus
X
entertainer.dvi:	entertainer.tex
nutcracker.dvi:		nutcracker.tex
bfugue.dvi:			bfugue.tex
learntune.dvi:		learntune.tex
turkish.dvi:		turkish.tex
X
entertainer.ps:	entertainer.dvi
nutcracker.ps:	nutcracker.dvi
bfugue.ps:		bfugue.dvi
learntune.ps:	learntune.dvi
turkish.ps:		turkish.dvi
X
SHAR_EOF
chmod 0644 Makefile ||
echo 'restore of Makefile failed'
Wc_c="`wc -c < 'Makefile'`"
test 1153 -eq "$Wc_c" ||
	echo 'Makefile: original size 1153, current size' "$Wc_c"
fi
# ============= smus.h ==============
if test -f 'smus.h' -a X"$1" != X"-c"; then
	echo 'x - skipping smus.h (File already exists)'
else
echo 'x - extracting smus.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'smus.h' &&
/*
Type Definitions
X
Here's a collection of the C type definitions in this memo. In the 
"struct" type definitions, fields are filed in the order shown. A 
UBYTE field is packed into an 8-bit byte. Programs should set all 
"pad" fields to 0.
*/
X
#define MakeID(a,b,c,d) ( (a)<<<<24 | (b)<<<<16 | (c)<<<<8 | (d) )
X
#define ID_SMUS MakeID('S', 'M', 'U', 'S')
#define ID_SHDR MakeID('S', 'H', 'D', 'R')
X
typedef struct {
X	UWORD tempo;	/* tempo, 128ths quarter note/minute	*/
X	UBYTE volume;	/* overall playback volume 0 through 127	*/
X	UBYTE ctTrack;	/* count of tracks in the score	*/
X	} SScoreHeader;
X
#define ID_NAME MakeID('N', 'A', 'M', 'E')
/* NAME chunk contains a CHAR[], the musical score's name.	*/
X
#define ID_Copyright MakeID('(', 'c', ')', ' ')
/* "(c) " chunk contains a CHAR[], the FORM's copyright notice.	*/
X
#define ID_AUTH MakeID('A', 'U', 'T', 'H')
/* AUTH chunk contains a CHAR[], the name of the score's author.	*/
X
#define ID_ANNO MakeID('A', 'N', 'N', 'O')
/* ANNO chunk contains a CHAR[], author's text annotations.	*/
X
#define ID_INS1 MakeID('I', 'N', 'S', '1')
X
/* Values for the RefInstrument field "type".	*/
#define INS1_Name 0	/* just use the name; ignore data1, data2	*/
#define INS1_MIDI 1	/* <<data1, data2> = MIDI <<channel, preset>	*/
X
typedef struct {
X	UBYTE reg;	/* set this instrument register number	*/
X	UBYTE type;	/* instrument reference type	*/
X	UBYTE data1, data2;	/* depends on the "type" field	*/
X	char  *name;	/* instrument name	*/
} RefInstrument;
X
#define ID_TRAK MakeID('T', 'R', 'A', 'K')
/* TRAK chunk contains an SEvent[].	*/
X
/* SEvent: Simple musical event.	*/
typedef struct {
X	UBYTE sID;	/* SEvent type code	*/
X	UBYTE data;	/* sID-dependent data	*/
X	} SEvent;
X
/* NEvent: Note Event.  Made up by Glenn Lewis to store more information */
struct NEvent {
X	UBYTE sID;	/* SEvent type code */
X	UBYTE data;	/* sID-dependent data	*/
X	UBYTE flag;	/* defined below */
X	UBYTE track;	/* which track did this note belong to? */
};
typedef struct NEvent NEvent;
/* The following defines the bits in the NEvent.flag ubyte... */
#define RLAP_IT		(0x20)		/* This note should be \rlap'd */
#define BEAM_ON		(0x10)		/* This note should be connected with a beam */
#define BEAM_START	(0x08)		/* This note is the start of the beam */
#define BEAM_END	(0x04)		/* This note terminates the beam */
#define STEM_ON		(0x02)		/* This note should have a stem on it */
#define STEM_UP		(0x01)		/* This note's stem should be up */
X
/* SEvent type codes "sID".		*/
#define SID_FirstNote     0
#define SID_LastNote    127	/* sIDs in the range SID_FirstNote through
X			 	 * SID_LastNote (sign bit = 0) are notes. The 
X				 * sID is the MIDI tone number (pitch).	*/
#define SID_Rest        128	/* a rest (same data format as a note).	*/
X
#define SID_Instrument  129	/* set instrument number for this 
track.	*/
#define SID_TimeSig     130	/* set time signature for this track.	*/
#define SID_KeySig      131	/* set key signature for this track.	*/
#define SID_Dynamic     132	/* set volume for this track.	*/
#define SID_MIDI_Chnl   133	/* set MIDI channel number (sequencers)	*/
#define SID_MIDI_Preset 134	/* set MIDI preset number (sequencers)	*/
#define SID_Clef        135 /* inline clef change. 
X                             * 0=Treble, 1=Bass, 2=Alto, 3=Tenor. */
#define SID_Tempo       136 /* Inline tempo change in beats per minute.*/
X
/* SID values 144 through 159: reserved for Instant Music SEvents.	*/
X
/* Remaining sID values up through 254: reserved for future
X * standardization.		*/
X
#define SID_Mark        255	/* sID reserved for an end-mark in RAM.	*/
X
/* SID_FirstNote..SID_LastNote, SID_Rest SEvents	*/
typedef struct {
X	UBYTE    tone;	/* MIDI tone number 0 to 127; 128 = rest	*/
X	unsigned chord    :1,	/* 1 = a chorded note	*/
X        tieOut   :1,	/* 1 = tied to the next note or chord	*/
X        nTuplet  :2,	/* 0 = none, 1 = triplet, 2 = quintuplet,
X			 * 3 = septuplet	*/
X        dot      :1,	/* dotted note; multiply duration by 3/2	*/
X        division :3;	/* basic note duration is 2-division: 0 = whole
X	  	 	 * note, 1 = half note, 2 = quarter note, I 
X			 * 7 = 128th note	*/
X	} SNote;
X
#define noteChord  (1<<<<7)	/* note is chorded to next note	*/
X
#define noteTieOut (1<<<<6)	/* tied to next note/chord	*/
X
#define noteNShift 4	/* shift count for nTuplet field	*/
#define noteN3     (1<<<<noteNShift)	/* note is a triplet	*/
#define noteN5     (2<<<<noteNShift)	/* note is a quintuplet	*/
#define noteN7     (3<<<<noteNShift)	/* note is a septuplet	*/
#define noteNMask  noteN7	/* bit mask for the nTuplet field	*/
X
#define noteDot    (1<<<<3)	/* note is dotted	*/
X
#define noteD1     0	/* whole note division	*/
#define noteD2     1	/* half note division	*/
#define noteD4     2	/* quarter note division	*/
#define noteD8     3 	/* eighth note division	*/
#define noteD16    4 	/* sixteenth note division	*/
#define noteD32    5 	/* thirty-secondth note division	*/
#define noteD64    6 	/* sixty-fourth note division	*/
#define noteD128   7 	/* 1/128 note division	*/
#define noteDMask  noteD128	/* bit mask for the division field	*/
X
#define noteDurMask 0x3F	/* mask for combined duration fields	*/
X
/* SID_Instrument SEvent		*/
/* "data" value is an instrument register number 0 through 255.	*/
X
/* SID_TimeSig SEvent		*/
typedef struct {
X	UBYTE    type;	/* = SID_TimeSig	*/
X	unsigned timeNSig :5,	/* time sig. "numerator" is timeNSig + 1 */
X        timeDSig :3;	/* time sig. "denominator" is 2timeDSig:
X	 	 	 * 0 = whole note, 1 = half note, 2 = quarter 	 
X			 * note, I 7 = 128th note	*/
X	} STimeSig;
X
#define timeNMask  0xF8	/* bit mask for the timeNSig field	*/
#define timeNShift 3	/* shift count for  timeNSig field	*/
X
#define timeDMask  0x07	/* bit mask for the timeDSig field	*/
X
/* SID_KeySig SEvent		*/
/* "data" value 0 = Cmaj; 1 through 7 = G,D,A,E,B,F#,C#;
X * 8 through 14 = F,Bb,Eb,Ab,Db,Gb,Cb.	*/
X
/* SID_Dynamic SEvent		*/
/* "data" value is a MIDI key velocity 0..127.	*/
X
/*
SMUS Regular Expression
X
Here's a regular expression summary of the FORM SMUS syntax. This 
could be an IFF file or part of one.
X
SMUS	::= "FORM" #{	"SMUS" SHDR [NAME] [Copyright] [AUTH] [IRev]
X		ANNO* INS1*  TRAK*  InstrForm* }
X
SHDR	::= "SHDR" #{	SScoreHeader	}
NAME	::= "NAME" #{	CHAR*	} [0]
Copyright	::= "(c) " #{	CHAR*	} [0]
AUTH	::= "AUTH" #{	CHAR*	} [0]
IRev	::= "IRev" #{	...	}
X
ANNO	::= "ANNO" #{	CHAR*	} [0]
INS1	::= "INS1" #{	RefInstrument	} [0]
X
TRAK	::= "TRAK" #{	SEvent*	}
X
InstrForm	::= "FORM" #{	...	}
X
The token "#" represents a ckSize LONG count of the following {braced} 
data bytes. Literal items are shown in "quotes", [square bracket items] 
are optional, and "*" means 0 or more replications. A sometimes-needed 
pad byte is shown as "[0]".
X
Actually, the order of chunks in a FORM SMUS is not as strict as this 
regular expression indicates. The SHDR, NAME, Copyright, AUTH, IRev, 
ANNO, and INS1 chunks may appear in any order, as long as they precede 
the TRAK chunks.
X
The chunk RInstrFormS represents any kind of instrument data FORM 
embedded in the FORM SMUS. For example, see the document "8SVX" IFF 
8-Bit Sampled Voice. Of course, a recipient program will ignore an 
instrument FORM if it doesn't recognize that FORM type.
X
*/
SHAR_EOF
chmod 0644 smus.h ||
echo 'restore of smus.h failed'
Wc_c="`wc -c < 'smus.h'`"
test 7214 -eq "$Wc_c" ||
	echo 'smus.h: original size 7214, current size' "$Wc_c"
fi
# ============= smus2tex.c ==============
if test -f 'smus2tex.c' -a X"$1" != X"-c"; then
	echo 'x - skipping smus2tex.c (File already exists)'
else
echo 'x - extracting smus2tex.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'smus2tex.c' &&
/* smus2tex.c - read an SMUS file and write a MusicTeX file
X *            - written by Glenn M. Lewis - 12/12/90
X */
X
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
X
#define DEBUG
#define BARS_PER_LINE	(4)
#define LINES_PER_PAGE	(5)
#define BARS_PER_PAGE	(BARS_PER_LINE*LINES_PER_PAGE)
#define MAX_NOTES_AT_A_TIME	(8)		/* Just a guess */
X
typedef          char   BYTE;
typedef unsigned char  UBYTE;
typedef          short  WORD;
typedef unsigned short UWORD;
typedef          long   LONG;
typedef unsigned long  ULONG;
X
#include "smus.h"
X
extern char *malloc();
ULONG process_chunk(), Calc_Duration();
X
void process_SHDR();
void process_NAME();
void process_Copyright();
void process_AUTH();
void process_ANNO();
void process_INS1();
void process_TRAK();
X
struct FUNCTION_LIST {
X	char *name;
X	void (*function)();
} function_list[] = {
X	{"SHDR",	process_SHDR},
X	{"NAME",	process_NAME},
X	{"(c) ",	process_Copyright},
X	{"AUTH",	process_AUTH},
X	{"ANNO",	process_ANNO},
X	{"INS1",	process_INS1},
X	{"TRAK",	process_TRAK},
X	{0L,		0L}
};
typedef struct FUNCTION_LIST FUNCTION_LIST;
X
struct {	/* SCORE information */
X	SScoreHeader header;
X	char *name;
X	char *copyright;
X	char *author;
X	char *annotations;
X	BYTE key_signature;
X	UBYTE time_Nsignature, time_Dsignature;
} score = { {0,0,0},0L,0L,0L,0L,100,0,0 };
X
struct track_struct {	/* Track information */
X	SEvent *data, *tptr;		/* An array of SEvents, and a pointer into it */
X	ULONG count, nextevent;	/* For counting */
X	ULONG size;		/* Stores sizes of tracks, measured in SEvents */
X	UBYTE clef, bpm;
X	BYTE key_signature;
X	UBYTE time_Nsignature, time_Dsignature;
X	BYTE beam;	/* Flag indicating if they are in effect */
} *track;
typedef struct track_struct TRACK;
X
#define MAX_TIES	(9)
struct tie_info {		/* Ties in-use */
X	BYTE track;
X	UBYTE sID;
X	ULONG dur;			/* When to shut it off, in case of errors in SMUS */
} tie_info[MAX_TIES];
X
#define MAX_BEAMS	(9)
struct beam_info {		/* Beams in-use */
X	BYTE track;
X	UBYTE dur, dur_count;		/* Duration bitfield of current beamed notes */
X	UBYTE flag_for_deletion;
} beam_info[MAX_BEAMS];
X
FILE *inp, *out, *fopen();
char name[5];
long file_position;
int current_track, debug;
char pitch_information[129];
ULONG measure_count, measure_length, total_measure_count;
int end_of_measure;		/* For assistance in creating beams */
X
/* Here are a few necessary utilities */
X
void get_string(p, size)	/* malloc's the memory too. */
char **p;
ULONG size;
{
X	register char *c;
X	file_position += size;
X	if (!(*p = malloc((unsigned)(size+1)))) mem_error();
X	c = *p;
X	while (size--) {
X		*c = fgetc(inp);
X		if (*c=='\\' || *c=='$' || *c=='_') *c=' ';	/* Bad TeX characters */
X		c++;
X	}
X	*c = '\0';
}
X
void get_name(name, size)
register char  *name;
register int size;
{
X	file_position += size;
X	while (size--) *name++ = fgetc(inp);
X	*name = '\0';
}
X
UBYTE get_UBYTE()
{
X	file_position++;
X	return((UBYTE)fgetc(inp));
}
X
UWORD get_UWORD()
{
X	return(((UWORD)get_UBYTE()<<8)|get_UBYTE());
}
X
ULONG get_ULONG()
{
X	return(((ULONG)get_UWORD()<<16)|get_UWORD());
}
X
/********************/
/* The MAIN section */
/********************/
X
void main(argc, argv)
int argc;
char *argv[];
{
X	int i;
X	register ULONG len;
X
X	inp = stdin;
X	out = stdout;
X	debug = file_position = 0;
X	current_track = -1;
X
X	for (i=1; i<argc; i++) {
X		if (argv[i][0] == '-') {
X			switch(argv[i][1]) {
X				case 'd': debug = 1; break;
X				default:
USAGE:
X					fprintf(stderr, "Usage: %s [infile] [outfile]\n", argv[0]);
X					exit(10);
X					break;
X			}
X		} else {
X			if (inp == stdin) {
X				if ((inp = fopen(argv[i], "r")) == NULL) {
X					fprintf(stderr, "Can't open '%s' for input.\n", argv[i]);
X					exit(20);
X				}
X			} else if (out == stdout) {
X				if ((out = fopen(argv[i], "w")) == NULL) {
X					fprintf(stderr, "Can't open '%s' for output.\n", argv[i]);
X					exit(20);
X				}
X			} else goto USAGE;
X		}
X	}
X
X	/* Parse the IFF TDDD file */
X	get_name(name, 4);
X	if (strcmp(name, "FORM") != 0) {
X		fprintf(stderr, "ERROR: Input is not an IFF file.\n*** ABORT ***\n");
X		exit(20);
X	}
X	if (!(len = get_ULONG())) {
X		fprintf(stderr, "ERROR: Bad FORM length in IFF file.\n*** ABORT ***\n");
X		exit(20);
X	}
X	get_name(name, 4);
X	len -= 4;
X	if (strcmp(name, "SMUS") != 0) {
X		fprintf(stderr, "ERROR: IFF file is not a FORM SMUS file.\n*** ABORT ***\n");
X		exit(20);
X	}
X
/* Give a bit of header information */
X	if (inp != stdin) fprintf(out, "%% SMUS2TeX Version 1.0: %s\n", argv[1]);
X	fprintf(out, "\n");
X
/* Initialize the pitch_information array for the white keys. */
X	initialize_pitch();
X
/* Here is the main read loop */
X	while (len>0)
X		len = process_chunk(len);
X
/* Now that the SMUS file is complete read in, parse the tracks and write the
*  TeX file
*/
X	create_TeX();
X
/* All done.  Close up shop. */
X	fclose(inp);
X	fclose(out);
}
X
mem_error()
{
X	fprintf(stderr, "Out of memory.\n");
X	exit(-1);
}
X
#define A_OFFSET	(33)
#define MIDDLE_C	(60)
#define iTOLO(i)	(i-A_OFFSET+'A')
#define iTOHI(i)	(i-A_OFFSET+'N'-'A'+1+'a')
#define TO_PITCH(i)	((iTOLO(i)<='N')?iTOLO(i):iTOHI(i))
X
initialize_pitch()
{
X	int i;
X	char c, n;
X
X	for (i=0; i<128; i++) pitch_information[i] = ' ';	/* Clear out array */
X	pitch_information[128] = '\0';
X	/* Initialize the white keys only.  Accidentals treated specially. */
X	i = A_OFFSET;
X	n = 'a';
X	c = 'A';
X	while (i<128 && c<='z') {
X		switch(n) {
X			case 'a': case 'c': case 'd': case 'f': case 'g':
X				pitch_information[i] = c; i+=2; break;
X			case 'b': case 'e':
X				pitch_information[i] = c; i++;  break;
X		}
X		if (++c == 'O') c='a';
X		if (++n > 'g') n='a';
X	}
#ifdef DEBUG
X	if (debug)
X	fprintf(out, "%% Pitch table: '%s'\n", pitch_information);
#endif
}
X
ULONG process_chunk(len)
ULONG len;
{
X	ULONG i, size;
X	FUNCTION_LIST *p;
X
X	get_name(name, 4);
X	size = get_ULONG();
X	len -= 8;
X	for (p=function_list; p->name; p++)
X		if (strcmp(name, p->name)==0) {
X			(p->function)(size);
X			break;
X		}
X	if (!p->name) {
X		fprintf(stderr, "WARNING: Unknown sub-chunk at 0x%lx: '%s'\n",
X			file_position-8, name);
X		for (i=size; i--; ) fgetc(inp);				/* Skip this section */
X		file_position += size;
X	}
X	/* Skip odd byte */
X	if ((size % 2) == 1) { fgetc(inp); len--; file_position++; }
X	len -= size;
X	return(len);
}
X
void process_SHDR(size)
ULONG size;
{
X	int i;
X	register TRACK *t;
X	if (size!=4) {
X		fprintf(stderr, "Error in process_SHDR!  Size: %ld (!=4)\n", size);
X		exit(-1);
X	}
X	score.header.tempo   = get_UWORD();
X	score.header.volume  = get_UBYTE();
i = score.header.ctTrack = get_UBYTE();
X	if (!(t = (TRACK *)malloc((unsigned)i*sizeof(TRACK)))) mem_error();
X	track = t;
X	while (i--) {
X		t->size = 0;
X		t->data = (SEvent *)0;
X		t->tptr = (SEvent *)0;
X		t->count = 0;
X		t->nextevent = 0;
X		t->clef = 0;			/* Treble clef */
X		t->bpm = 1;				/* Beats per minute */
X		t->key_signature = 100;	/* Initialize in undefined state */
X		t->time_Nsignature = 0;
X		t->time_Dsignature = 0;
X		t->beam = -1;			/* No bars currently in effect */
X		t++;
X	}
#ifdef DEBUG
X	if (debug)
X	fprintf(out, "%% Tempo=%.1f (1/4 notes per minute)  Volume=%d  Tracks=%d\n",
X		(float)score.header.tempo/128.0,
X		score.header.volume,
X		score.header.ctTrack);
#endif
}
X
void process_NAME(size)
ULONG size;
{
X	get_string(&score.name, size);
#ifdef DEBUG
X	if (debug)
X	fprintf(out, "%% Score name: '%s'\n", score.name);
#endif
}
X
void process_Copyright(size)
ULONG size;
{
X	get_string(&score.copyright, size);
X	fprintf(out, "%% Score copyright: '%s'\n", score.copyright);
}
X
void process_AUTH(size)
ULONG size;
{
X	get_string(&score.author, size);
#ifdef DEBUG
X	if (debug)
X	fprintf(out, "%% Score author: '%s'\n", score.author);
#endif
}
X
void process_ANNO(size)
ULONG size;
{
X	get_string(&score.annotations, size);
X	fprintf(out, "%% Score annotations: '%s'\n", score.annotations);
}
X
X
void process_INS1(size)
ULONG size;
{
X	int i;
X	RefInstrument inst;
X	inst.reg   = get_UBYTE();
X	inst.type  = get_UBYTE();
X	inst.data1 = get_UBYTE();
X	inst.data2 = get_UBYTE();
X	size -= 4;
X	if (!(inst.name = malloc((unsigned)size+1))) mem_error();
X	for (i=0; i<size; i++) inst.name[i] = fgetc(inp);
X	inst.name[i] = '\0';
X	file_position += size;
#ifdef DEBUG
X	if (debug) {
X	fprintf(out, "%% Instrument #%d: '%s'", inst.reg, inst.name);
X	if (inst.type == INS1_MIDI)
X		fprintf(out, " MIDI <%d,%d>", inst.data1, inst.data2);
X	fprintf(out, "\n");
X	}
#endif
}
X
void process_TRAK(size)
ULONG size;
{
X	ULONG i;
X	UBYTE *p;
X	current_track++;
X	if (track[current_track].data) {
X		fprintf(stderr, "Error!  Current Track (%d) already initialized\n",
X			current_track);
X		exit(-1);
X	}
X	if (!(track[current_track].data = (SEvent *)malloc((unsigned)size)))
X		mem_error();
X	track[current_track].tptr = track[current_track].data;
X	track[current_track].size = size/sizeof(SEvent);
X	p = (UBYTE *)track[current_track].data;
X	for (i=0; i<size; i++) *p++ = get_UBYTE();
#ifdef DEBUG
X	if (debug)
X	fprintf(out, "%% Track #%d: Simple musical events: %d.\n",
X		current_track, size/sizeof(SEvent));
#endif
}
X
static int started_flag;
static int treblecount, basscount;
static NEvent *treblelist, *basslist;
static NEvent **measure;
X
static char *TeXheader[] = {
"\\rightline{\\cmbx Created with SMUS2\\TeX\\ V1.0 by Glenn M. Lewis}\\medskip\n",
"\\def\\zqup#1{\\getn@i{#1}\\def\\n@fon{\\qup}\\def\\n@sym{\\q@up}%\n",
"\\ifnum\\n@i<100\\relax\n",
"  \\h@lines{\\qn@width}\\global\\stem@skip=\\qd@skip\n",
"  \\def\\s@tem{\\d@tail}\\plap@symss\\fi}%\n",
"\\def\\zqlp#1{\\getn@i{#1}\\def\\n@fon{\\qlp}\\def\\n@sym{\\q@up}%\n",
"\\ifnum\\n@i<100\\relax\n",
"  \\h@lines{\\qn@width}\\def\\s@tem{\\p@tail}\\plap@symss\\fi}%\n",
"\\def\\zhlp#1{\\getn@i{#1}\\def\\n@fon{\\hlp}\\def\\n@sym{\\h@ap}%\n",
"\\ifnum\\n@i<100\\relax\n",
"  \\h@lines{\\hn@width}\\def\\s@tem{\\p@tail}\\plap@symss\\fi}%\n",
0};
X
create_TeX()
{
X	int i, num, endflag;
X	ULONG min;
X	register TRACK *t;
X
X	/* Print header */
X	fprintf(out, "\\input musicnft\n\\input musictex\n\\input musicadd\n", out);
X	if (score.name)
X		fprintf(out, "\\centerline{\\enorme %s}\\medskip\n", score.name);
X	if (score.author)
X		fprintf(out, "\\rightline{\\cmbx %s}\\medskip\n", score.author);
X	for (i=0; TeXheader[i]; i++)
X		fputs(TeXheader[i], out);
X	num = score.header.ctTrack;
X	fputs("\\def\\nbinstruments{1}\n", out);	/* 1 instrument */
X	fputs("\\nbporteesi=2\\relax\n", out);		/* 2 staffs */
X	fputs("\\cleftoksi{{6}{0}{0}{0}}\n", out);	/* Bass and Treble clefs */
X
X	/* We don't know a-priori how many notes will appear at a time in a clef! */
X	if (!(basslist   = (NEvent*)malloc((unsigned)num*MAX_NOTES_AT_A_TIME
X		*sizeof(NEvent)))) mem_error();
X	if (!(treblelist = (NEvent*)malloc((unsigned)num*MAX_NOTES_AT_A_TIME
X		*sizeof(NEvent)))) mem_error();
X
X	started_flag = 0;	/* No notes processed yet */
X	total_measure_count = 1;	/* Keep track of the measures */
X	measure_count = 0;	/* Start a new measure */
X	for (i=0; i<MAX_TIES; i++)
X		tie_info[i].track = -1;		/* Not in-use */
X	for (i=0; i<MAX_BEAMS; i++)
X		beam_info[i].track = -1;	/* Not in-use */
X	/* Assume that the time signature is 4/4 until further notice */
X	measure_length = 4*Calc_Duration(0,0,2);
X	while (1) {		/* Parse tracks until they are done */
X		endflag = 1;
X
#ifdef DEBUG
X	if (debug) {
X	fprintf(out, "%% Process loop... [@%d:%d] ", measure_count, measure_length);
X	for (i=0,t=track; i<num; i++,t++)
X		if (t->count < t->size)
X			fprintf(out, "[%d=%ld] ", i, t->nextevent);
X	fputs("\n", out);
X	}
#endif
X
X		treblecount = basscount = 0;	/* Start new note lists */
X
X		/* Process each track if possible */
X		for (i=0,t=track; i<num; i++,t++) {
X			if (t->count >= t->size) continue;
X			endflag = 0;
X			while (!t->nextevent)
X				process_SEvent(i);
X		}
X		if (endflag) break;
X
X		/* Set a flag that will assist in creating beams intelligently */
X		end_of_measure = 0;
X		min = ~(0L);
X		for (i=num,t=track; i--; t++)
X			if (t->nextevent < min && t->count < t->size)
X				min = t->nextevent;
X		if (min+measure_count >= measure_length)
X			end_of_measure = 1;
X
X		/* Output notes that have been compiled */
X		output_notes();
X
X		/* reset times to prevent overflow of tracks that are still going */
X		measure_count += min;
X		for (i=num,t=track; i--; t++)
X			if (t->count < t->size)
X				t->nextevent -= min;
X		if (measure_count >= measure_length) {	/* Time for a bar? */
X			/* Reset tie durations for those crossing bar boundaries */
X			for (i=0; i<MAX_TIES; i++)
X				if (tie_info[i].track >= 0)
X					tie_info[i].dur -= measure_length;
X			measure_count = 0;
X			if (measure_count > measure_length)
X				fprintf(out, "%% WARNING: Previous measure had length %d instead of %d!\n", measure_count, measure_length);
X			/* Check if there are no more measures in the score... */
X			/* ...boundary conditions and all that, you know! */
X			endflag=1;
X			for (i=num,t=track; i--; t++)
X				if (t->count < t->size) {
X					endflag=0;
X					break;
X				}
X			if (!endflag) {
X				/* Check if any ties are pending.  If so, add glue */
X				for (i=0; i<MAX_TIES; i++) if (tie_info[i].track>=0) break;
X				if (i<MAX_TIES) fputs("\\temps", out);
/*				if (total_measure_count % BARS_PER_LINE == 0) {		*/
/*					if (total_measure_count % BARS_PER_PAGE == 0)	*/
/*						fputs("\\alapage\n", out);	/* Break the page */
/*					else	*/
X				if (total_measure_count % BARS_PER_PAGE == 0) {
X						fputs("\\alaligne\n", out);	/* Break the line */
X				} else
X					fputs("\\barre\n", out);		/* Normal bar */
X				total_measure_count++;
X				fprintf(out, "%% Measure %d\n", total_measure_count);
X			}
X		}
X	}
X
X	/* All done. */
X	fputs("\\finmorceau\n\\bye\n", out);
}
X
static char *Major_keys[] = {
X	"C", "G", "D", "A", "E", "B", "F#", "C#",	/* Sharps */
X	"F", "Bb", "Eb", "Ab", "Db", "Gb", "Cb" };	/* Flats  */
#ifdef DEBUG
static char *Division_list[] = {
X	"Whole", "Half", "Quarter", "8th", "16th", "32nd", "64th", "128th"
};
#endif
X
ULONG Calc_Duration(nTuplet, dot, division)
UBYTE nTuplet, dot, division;
{
X	ULONG val;
X	/* The common denominator is 210 (!) (2*3*5*7) */
X	val = 210L*(1L<<(7-division));
/*	switch(division) {	/* This is what the above statement means: */
/*		case 0: val = 26880;	/* Whole note */
/*		case 1: val = 13440;	/* Half note */
/*		case 2: val =  6720;	/* Quarter note */
/*		case 3: val =  3360;	/* Eighth note */
/*		case 4: val =  1680;	/* Sixteenth note */
/*		case 5: val =   840;	/* 32nd note */
/*		case 6: val =   420;	/* 64th note */
/*		case 7: val =   210;	/* 127th note */
/*	} */
X	if (dot) val = (val*3L)>>1;	/* *3L/2L */
X	switch(nTuplet) {
X		case 0: break;
X		case 1: val = (val*2L)/3L;
X		case 2: val = (val*4L)/5L;
X		case 3: val = (val*6L)/7L;
X	}
X	return(val);
}
X
process_SEvent(num)
int num;
{
X	register TRACK *t = &track[num];
X	register SEvent *p = track[num].tptr;
X	UBYTE Nsig, Dsig;
X	BYTE keysig;
X	UBYTE chord, tieOut, nTuplet, dot, division;
X	NEvent *nptr;
X
#ifdef DEBUG
X	if (debug)
X	fprintf(out, "%% Track #%d: SEvent: ", num);
#endif
X	if (!(p->sID & 0x80) || p->sID == SID_Rest) {	/* Note or Rest */
X		chord    = p->data & 0x80;
X		tieOut   = p->data & 0x40;
X		nTuplet  =(p->data & 0x30) >> 4;
X		dot      = p->data & 0x08;
X		division = p->data & 0x07;
X		if (!chord) t->nextevent = Calc_Duration(nTuplet, dot, division);
X		/* Send notes to appropriate clef.  Split at Middle C. */
X		if ((p->sID >= MIDDLE_C && !(p->sID&0x80)) ||	/* Note >= Middle C */
X		   ((p->sID&0x80) && !(t->clef&0x01)))			/* or a Treble rest */
X			 nptr = &treblelist[treblecount++];
X		else nptr =   &basslist[basscount++  ];
X		nptr->sID  = p->sID;
X		nptr->data = p->data;
X		nptr->flag = 0;	/* For now */
X		nptr->track = num;
X		/* Should this note be beamed? - look ahead, not behind! */
X		/* Search if same duration is ahead, but wholes, halves, and quarters
X		   do not get beams, as well as rests */
X		if (division>2 && (p->sID!=SID_Rest) && search_future_track(num))
X			nptr->flag |= BEAM_ON;
X
#ifdef DEBUG
X	if (debug) {
X		fprintf(out, "Note: #%d 0x%x Next=%ld (",
X			p->sID, p->data, t->nextevent);
X		if (chord)   fputs("Chord ", out);
X		if (tieOut)  fputs("TieOut ", out);
X		switch (nTuplet) {
X			case 0: break;
X			case 1: fputs("Triplet ",   out); break;
X			case 2: fputs("Quintuplet ",out); break;
X			case 3: fputs("Septuplet ", out); break;
X		}
X		if (dot)  fputs("Dotted ", out);
X		if (nptr->flag & BEAM_ON)  fputs("BEAM_ON ", out);
X		fprintf(out, "%s)\n", Division_list[division]);
X	}
#endif
X		if (!started_flag) {	/* Create staves if not already done */
X			started_flag = 1;
X			fputs("\\debutmorceau\n\\normal\n%\n% Measure 1\n", out);
X			/* fputs("\\autolines{16}45\\relax\n\n",out);	*/
fprintf(out, "\\notes|\\Uptext{\\kern -16mm\\bf\\metron{\\qu}{%d}}\\enotes\n",
X				score.header.tempo/128);
X		}
X	} else if (p->sID == SID_Mark) {
#ifdef DEBUG
X	if (debug)
X		fprintf(out, "MARK end\n");
#endif
X	} else if (p->sID < SID_Mark && p->sID >= 144) {
#ifdef DEBUG
X	if (debug)
X		fprintf(out, "Reserved (#%d 0x%x).  Ignored\n", p->sID, p->data);
#endif
X	} else switch(p->sID) {
X		case(SID_Instrument):
#ifdef DEBUG
X	if (debug)
X			fprintf(out, "Instrument: %d\n", p->data);
#endif
X			break;
X		case(SID_TimeSig):
#ifdef DEBUG
X	if (debug)
X	fprintf(out, "Time signature: 0x%x\n", p->data);
#endif
X			Nsig = ((p->data & timeNMask) >> timeNShift) + 1;
X			Dsig = ((p->data & timeDMask) << 1);
X			t->time_Nsignature = Nsig;
X			t->time_Dsignature = Dsig;
X			if (Nsig != score.time_Nsignature ||
X				Dsig != score.time_Dsignature) {
X				fprintf(out, "\\generalmeter{\\meterfrac{%d}{%d}}%%\n",
X					Nsig, Dsig);
X				score.time_Nsignature = Nsig;
X				score.time_Dsignature = Dsig;
X				measure_length = Nsig*Calc_Duration(0,0,Dsig>>1);
X			}
X			break;
X		case(SID_KeySig):
#ifdef DEBUG
X	if (debug)
X	fprintf(out, "Key signature: %d\n", (BYTE)p->data);
#endif
X			if (p->data > 14)
X				fprintf(stderr, "Key signature error! %d\n", p->data);
X			keysig = ((p->data < 8) ? p->data : (BYTE)7 - p->data);
X			t->key_signature = keysig;
X			if (keysig != score.key_signature) {
X				fprintf(out, "\\signaturegenerale{%d}\\relax\t", keysig);
X				fprintf(out, "%% %d %s (%s Major)\n",
X					((keysig >= 0) ? keysig : -keysig),
X					((keysig >= 0) ? "Sharps" : "Flats"),
X					Major_keys[p->data]);
X				score.key_signature = keysig;
X			}
X			break;
X		case(SID_Dynamic):
#ifdef DEBUG
X	if (debug)
X			fprintf(out, "Volume: %d\n", p->data);
#endif
X			break;
X		case(SID_MIDI_Chnl):
#ifdef DEBUG
X	if (debug)
X			fprintf(out, "MIDI Channel: %d\n", p->data);
#endif
X			break;
X		case(SID_MIDI_Preset):
#ifdef DEBUG
X	if (debug)
X			fprintf(out, "MIDI Preset: %d\n", p->data);
#endif
X			break;
X		case(SID_Clef):
X			t->clef = p->data;
#ifdef DEBUG
X	if (debug)
X			fprintf(out, "Clef: %d (%s)\n",
X				p->data, (p->data ? "Bass" : "Treble"));
#endif
X			break;
X		case(SID_Tempo):
X			t->bpm = p->data;
#ifdef DEBUG
X	if (debug)
X			fprintf(out, "Beats/Minute: %d\n", p->data);
#endif
X			break;
X		default:
#ifdef DEBUG
X	if (debug)
X			fprintf(out, "UNKNOWN: #%d 0x%x\n", p->sID, p->data);
#endif
X			break;
X	}
X	t->tptr++;
X	t->count++;
}
X
static char strin[133], *sptr;
X
output_notes()
{
X	int i;
X	UBYTE min_duration;
X	register NEvent *p;
X
X	sort_notelists();
X
X	if (!basscount && !treblecount) return;
X
X	/* Notes are now sorted by pitch; output notes */
X	sptr = &strin[0];	/* Start building string */
X
X	min_duration = 0;	/* 0 is largest, 7 is smallest */
X
X	/* Stop/start beams in bass clef before processing notes */
X	for (i=basscount,p=basslist; i--; p++)			/* Do bass clef */
X		stop_start_beams(p);
X
X	/* Now, add the notes to the string */
X	for (i=basscount,p=basslist; i--; p++) {		/* Do bass clef */
X		if ((p->data & 0x07) > min_duration)
X			min_duration = p->data & 0x07;
X		if (p->sID == SID_Rest) print_Rest(p);
X		else print_Note(p);
X	}
X	*sptr++ = '|';
X
X	/* Stop/start beams in treble clef before processing notes */
X	for (i=treblecount,p=treblelist; i--; p++)		/* Do treble clef */
X		stop_start_beams(p);
X
X	for (i=treblecount,p=treblelist; i--; p++) {	/* Do treble clef */
X		if ((p->data & 0x07) > min_duration)
X			min_duration = p->data & 0x07;
X		if (p->sID == SID_Rest) print_Rest(p);
X		else print_Note(p);
X	}
X
X	/* Delete beams that have been flagged */
X	for (i=0; i<MAX_BEAMS; i++)
X		if (beam_info[i].flag_for_deletion) {
X			beam_info[i].track = -1;	/* Not in-use */
X			beam_info[i].flag_for_deletion = 0;
X		}
X
X	/* Now, print out the assembled string */
X	*sptr = '\0';	/* Terminate the string for printing */
X	fputs("\\", out);
X	switch(min_duration) {	/* Determine spacing */
X		case 0: fputs("NOTES",out); break;	/* Whole note */
X		case 1: fputs("NOTEs",out); break;	/* Half note */
X		case 2: fputs("NOTes",out); break;	/* Quarter note */
X		case 3: fputs("NOtes",out); break;	/* Eighth note */
X		case 4: fputs("Notes",out); break;	/* Sixteenth note */
X		case 5:								/* 32nd note */
X		case 6:								/* 64th note */
X		case 7: fputs("notes",out); break;	/* 127th note */
X	}
X	fprintf(out, "%s\\enotes\n", strin);
}
X
print_Rest(p)	/* Doesn't know about dotted Rests right now... */
register NEvent *p;
{
X	UBYTE tieOut, nTuplet, dot, division;
X
X	tieOut   = p->data & 0x40;
X	nTuplet  =(p->data & 0x30) >> 4;
X	dot      = p->data & 0x08;
X	division = p->data & 0x07;
X	switch(p->data & 0x07) {	/* Duration */
X		case 0: strcpy(sptr, "\\pause");	sptr+=6; break;
X		case 1: strcpy(sptr, "\\hpause");	sptr+=7; break;
X		case 2: strcpy(sptr, "\\soupir");	sptr+=7; break;
X		case 3: strcpy(sptr, "\\dsoupir");	sptr+=8; break;
X		case 4: strcpy(sptr, "\\qsoupir");	sptr+=8; break;
X		default: fprintf(out, "%% WARNING! Rest has division=%d\n", division);
X	}
X	if (nTuplet || dot)
X		fprintf(out, "%% WARNING!  Rest has nTuplet=%d, dot=%d!\n",
X			nTuplet, dot);
}
X
print_Note(p)
register NEvent *p;
{
X	char c, accidental;
X	c = accidental = ' ';
X	if ((c = pitch_information[p->sID]) == ' ') {
X		if (score.key_signature > 0) {	/* Make it a sharp */
X			c = pitch_information[p->sID-1];
X			accidental='^';
X		} else {						/* Make it a flat  */
X			c = pitch_information[p->sID+1];
X			accidental='_';
X		}
X		if (c == ' ') {		/* Note out of range for MusicTeX */
X			fprintf(out,"%% WARNING! Note out of MusicTeX range: %d\n", p->sID);
X			return;
X		}
X	}
X	if (p->flag & RLAP_IT) { strcpy(sptr, "\\rlap{"); sptr+=6; }
X	print_duration(p, accidental, c);
X	if (accidental==' ') {
X		*sptr++ = ' ';
X		*sptr++ = c;
X	} else {
X		*sptr++ = '{';
X		*sptr++ = accidental;
X		*sptr++ = c;
X		*sptr++ = '}';
X	}
X	if (p->flag & RLAP_IT) { strcpy(sptr, "}\\relax"); sptr+=7; }
}
X
free_beam(i, p)
int i;
NEvent *p;
{
X	/* If it has already been flagged for deletion, don't delete it again */
X	if (beam_info[i].flag_for_deletion) return;
X	strcpy(sptr, "\\tb"); sptr+=3;
X	if (p->flag & STEM_UP) *sptr++ = 'u'; else *sptr++ = 'l';
X	*sptr++ = '0' + i;
X	beam_info[i].flag_for_deletion = 1;
}
X
set_beam(p)
NEvent *p;
{
X	char c;
X	int i, j;
X
X	for (i=0; i<MAX_BEAMS; i++)
X		if (beam_info[i].track < 0) break;	/* This one is unused */
X	if (i >= MAX_BEAMS) return;	/* Too many beams.  Ignore this one */
X
X	/* Calculate the pitch letter.  This should probably be done once only
X	   instead of here *and* in print_Note() */
X	c = ' ';
X	if ((c = pitch_information[p->sID]) == ' ') {
X		if (score.key_signature > 0) {	/* Make it a sharp */
X			c = pitch_information[p->sID-1];
X		} else {						/* Make it a flat  */
X			c = pitch_information[p->sID+1];
X		}
X		if (c == ' ') {		/* Note out of range for MusicTeX */
X			fprintf(out,"%% WARNING! Note out of MusicTeX range: %d\n", p->sID);
X			return;
X		}
X	}
X
X	beam_info[i].track = p->track;
X	beam_info[i].dur   = p->data & 0x07;
X	beam_info[i].dur_count = 1;
X	beam_info[i].flag_for_deletion = 0;
X	strcpy(sptr, "\\i"); sptr+=2;
X	for (j=(p->data&0x07)-2; j-- >0; )
X		*sptr++ = 'b';		/* How many beams? */
X	*sptr++ = (p->flag&STEM_UP ? 'u' : 'l');
X	*sptr++ = '0' + i;		/* Reference number */
X
X	*sptr++ = c;			/* Pitch */
X	*sptr++ = '0';	/* Slope of the beam.  Change later */
}
X
stop_start_beams(p)
NEvent *p;
{
X	int i;
X	UBYTE nTuplet;
X
X	/* If this note is flagged as "tieOut", then force a beam to end */
X	/* (and don't start a new one */
X	if (p->data & 0x40) {
X		p->flag &= ~(BEAM_ON);
X		p->flag |=  (BEAM_END);
X	}
X
X	/* Do we need to end a beam? (!BEAM_ON or BEAM_END) */
X	if (!(p->flag&BEAM_ON) || (p->flag&BEAM_END))
X		for (i=0; i<MAX_BEAMS; i++) {
X			if (beam_info[i].track != p->track) continue;
X			if (beam_info[i].dur == (p->data&noteDurMask)) {
X				free_beam(i, p);
X				break;
X			}
X		}
X
X	/* Do we need to start a beam? (BEAM_START or BEAM_ON (for first time) */
X	/* If we have exceeded the grouping limit, then end the beam */
X	if (!(p->data&0x80)) {
X		if (p->flag & BEAM_START) set_beam(p);
X		else if (p->flag & BEAM_ON) {
X			for (i=0; i<MAX_BEAMS; i++) {
X				if (beam_info[i].track != p->track) continue;
X				if (beam_info[i].dur == (p->data&noteDurMask)) {
X					nTuplet = (p->data & 0x30) >> 4;
X					beam_info[i].dur_count++;
X					if (beam_info[i].dur_count >= (nTuplet ? nTuplet*2+1 : 4))
X						free_beam(i, p);
X					break;
X				}
X			}
X			if (i==MAX_BEAMS) {
X				/* If the note is the end of a tie, don't BEAM it */
X				for (i=0; i<MAX_TIES; i++)	/* Is this note already tied? */
X					if ((tie_info[i].track == p->track &&
X						tie_info[i].sID == p->sID)) {
X						p->flag &= ~(BEAM_ON);
X						return;				/* Don't BEAM it */
X					}
X				set_beam(p);	/* Didn't find it, so BEAM it */
X			}
X		}
X	}
}
X
free_tie(i)
int i;
{
X	strcpy(sptr, "\\tten"); sptr+=5;
X	*sptr++ = '0' + i;
X	tie_info[i].track = -1;	/* Free up tie */
}
X
set_tie(p, duration)
NEvent *p;
ULONG duration;
{
X	int i;
X
X	for (i=0; i<MAX_TIES; i++)
X		if (tie_info[i].track < 0) break;	/* This one is unused */
X	if (i >= MAX_TIES) return;	/* Too many ties.  Ignore this one */
X	tie_info[i].track = p->track;
X	tie_info[i].sID   = p->sID;
X	tie_info[i].dur   = measure_count + duration;
X	strcpy(sptr, "\\iten"); sptr+=5;
X	*sptr++ = (p->flag&STEM_UP ? 'l' : 'u');
X	*sptr++ = '0' + i;
}
X
print_duration(p, accidental, note)	/* ...and other formatting information! */
register NEvent *p;
char accidental, note;
{
X	int i;
X	UBYTE chord, tieOut, nTuplet, dot, division;
X
X	chord    = p->data & 0x80;
X	tieOut   = p->data & 0x40;
X	nTuplet  =(p->data & 0x30) >> 4;
X	dot      = p->data & 0x08;
X	division = p->data & 0x07;
X
/* Do we need to end a tie? */
X	for (i=0; i<MAX_TIES; i++)	/* Is this note already tied? */
X		if ((tie_info[i].track == p->track && measure_count>=tie_info[i].dur) ||
X			(tie_info[i].track == p->track && tie_info[i].sID == p->sID))
X			free_tie(i);						/* Yes, "untie" it */
/* Check if this note needs to be tied */
X	if (tieOut) {
X		set_tie(p, Calc_Duration(nTuplet, dot, division));
X		if (accidental==' ') {
X			*sptr++ = ' ';
X			*sptr++ = note;
X		} else {
X			*sptr++ = '{';
X			*sptr++ = accidental;
X			*sptr++ = note;
X			*sptr++ = '}';
X		}
X	}
X
X	if (chord) {	/* Part of a chord.  Don't print stem yet. */
X		switch(division) {
X			case 0: strcpy(sptr, "\\zw");		sptr+=3; break;
X			case 1: strcpy(sptr, "\\zh");		sptr+=3; break;
X			default:strcpy(sptr, "\\zq");		sptr+=3; break;
X		}
X	} else {
X		/* Check if this note is actually the termination of a beam */
X		for (i=0; i<MAX_BEAMS; i++)
X			if (beam_info[i].flag_for_deletion &&
X				beam_info[i].track == p->track &&
X				beam_info[i].dur == division) {
X				p->flag |= BEAM_ON;
X				break;
X			}
X		if (p->flag&BEAM_ON) {
X			strcpy(sptr, "\\q");	sptr+=2;
X			*sptr++ = (p->flag&STEM_UP ? 'h' : 'b');	/* BEAM UP or DOWN */
X			if (dot) *sptr++ = 'p';						/* Dotted note? */
X			/* Which stem do we tack this note to? */
X			for (i=0; i<MAX_BEAMS; i++) {
X				if (beam_info[i].track != p->track) continue;
X				if (beam_info[i].dur == division) break;	/* This is it */
X			}
X			*sptr++ = '0' + i;
X			return;
X		} else {
X			if (dot && division>=3) {	/* Create the "\pt" for "\c.." notes */
X				strcpy(sptr, "\\pt "); sptr+=4;
X				*sptr++ = note;
X			}
X			switch(division) {
X				case 0: strcpy(sptr, "\\wh");		sptr+=3; break;
X				case 1: strcpy(sptr, "\\h");		sptr+=2; break;
X				case 2: strcpy(sptr, "\\q");		sptr+=2; break;
X				case 3: strcpy(sptr, "\\c");		sptr+=2; break;
X				case 4: strcpy(sptr, "\\cc");		sptr+=3; break;
X				case 5: strcpy(sptr, "\\ccc");		sptr+=4; break;
X				case 6: strcpy(sptr, "\\cccc");		sptr+=5; break;
X				case 7: strcpy(sptr, "\\ccccc");	sptr+=6; break;
X			}
X		}
X	}
X	if (division && (p->flag&STEM_ON))	/* Can't stem a whole note */
X		*sptr++ = (p->flag&STEM_UP ? 'u' : 'l');	/* STEM UP or DOWN */
X	if (dot && division<=2) *sptr++ = 'p';
}
X
sort_notelists()
{
X	/* Check and see if a note in the treble clef belongs in the bass clef */
X	/* and vice-versa - NB: This function must be first */
X	move_notes_to_other_clef();
X
X	/* Sort notes by pitch, rests first */
X	if (basscount)		basscount	= shell_sort(basslist,   basscount);
X	if (treblecount)	treblecount	= shell_sort(treblelist, treblecount);
X
X	/* Attach stems to notes and group into chords */
X	basscount   = attach_stems_and_beams(basslist,   basscount,   1);
X	treblecount = attach_stems_and_beams(treblelist, treblecount, 0);
}
X
shell_sort(notes, total)	/* Sort the notes w/ Shell-Metzner algorithm */
register NEvent *notes;			/* Remove redundant rests */
int total;
{
X	register int i, j, gap;
X	NEvent temp;
X
/* Sort the puppies! */
X
X	for (gap = total/2; gap>0; gap/=2)
X		for (i=gap; i<total; i++)
X			for (j=i-gap; j>=0; j-=gap) {
X				if ((notes[j].sID & 0x7F) <= (notes[j+gap].sID & 0x7F)) break;
X				/* Swap the puppies */
X				bcopy(&notes[j],     &temp,         sizeof(NEvent));
X				bcopy(&notes[j+gap], &notes[j],     sizeof(NEvent));
X				bcopy(&temp,         &notes[j+gap], sizeof(NEvent));
X			}
X
/* Now, remove redundant rests */
X	for (i=0; i<total-1; i++) {
X		if (notes[i].sID != SID_Rest) break;		/* No more rests */
X		for (j=i+1; j<total; ) {
X			if (notes[j].sID != SID_Rest) break;	/* No more rests */
X			if ((notes[i].data & noteDurMask)==(notes[j].data & noteDurMask)) {
X				/* Found two identical rests.  Delete one of them */
X				total--;
X				if (j==total) break;	/* Done. */
X				bcopy(&notes[j+1],	&notes[j], (total-j)*sizeof(NEvent));
X			} else j++;
X		}
X	}
X	return(total);
}
X
attach_stems_and_beams(notes, total, clef)
register NEvent *notes;
int total, clef;
{
X	register int i;
X	register NEvent *nptr;
X	int toggle;
X	UBYTE durmask;
X
X	if (total <= 0) return(0);
/* Now, group the chords properly - More work needs to be done here to */
/* account for ties, bars, different durations, etc. */
X
/* Treble has STEM_UP on top.  Bass has STEM_DOWN on bottom */
/* Also force start or end of BEAMs based on begin or end of measure */
X	if (clef==0) {i=total-1;toggle=0;} else {i=0;toggle=1;}
X	durmask = 0;	/* Whole notes don't have stems */
X	for (nptr= &notes[i]; i>=0 && i<total; ) {
X		if (nptr->sID != SID_Rest) {
X			if ((nptr->data & noteDurMask) != durmask) {
X				toggle = 1 - toggle;
X				durmask = (nptr->data & noteDurMask);
X			}
X			nptr->flag |= (STEM_ON|toggle);
X			if (nptr->flag & BEAM_ON) {	/* Shall we start or end a beam? */
X				if (measure_count==0) nptr->flag |= BEAM_START;
X				if (end_of_measure)   nptr->flag |= BEAM_END;
X			}
X		}
X		if (clef) {i++;nptr++;} else {i--;nptr--;}
X	}
X
/* The bottom-most down-stem should be "not-chorded", as the top-most up-stem */
/* Note that the bottom-most down-stem should be "\rlap"'d */
X	for (i=0,nptr=notes; i<total; i++,nptr++)
X		if ((nptr->flag&0x03)==(STEM_ON|(!STEM_UP))) {
X			nptr->data &= 0x7f;			/* !CHORD */
X			nptr->flag |= RLAP_IT;
X			i++; nptr++;
X			while (i<total) {	/* Make the rest of the down-stems "chord" */
X				if ((nptr->flag&0x03)==(STEM_ON|(!STEM_UP)))
X					nptr->data |= 0x80;	/* CHORD */
X				i++; nptr++;
X			}
X			break;
X		}
X	for (i=total-1,nptr= &notes[i]; i>=0; i--,nptr--)
X		if ((nptr->flag&0x03)==(STEM_ON|STEM_UP)) {
X			nptr->data &= 0x7f;			/* !CHORD */
X			i--; nptr--;
X			while (i>=0) {	/* Make the rest of the up-stems "chord" */
X				if ((nptr->flag&0x03)==(STEM_ON|STEM_UP))
X					nptr->data |= 0x80;	/* CHORD */
X				i--; nptr--;
X			}
X			break;
X		}
X
/* EXPERIMENTAL: If notes appear, remove all rests... */
X	if (total && notes[total-1].sID != SID_Rest)	/* There *are* notes... */
X		for (i=total-1; i--; ) {			/* Check the rest of them */
X			if (notes[i].sID == SID_Rest) {	/* Delete the rests */
X				total--;
X				bcopy(&notes[i+1],	&notes[i], (total-i)*sizeof(NEvent));
X			}
X		}
X
X	return(total);
}
X
move_notes_to_other_clef()		/* If so desired */
{
X	int i, j;
X	UBYTE dur;
X	NEvent *p1, *p2;
X
/* If several notes are from the same track, and they have the same duration,
X * combine them into a chord */
X
X	for (i=0,p1=basslist; i<basscount; i++,p1++) {
X		if (p1->sID == SID_Rest) continue;
X		dur = p1->data & noteDurMask;
X		for (j=0,p2=treblelist; j<treblecount; ) {
X			if (p2->sID == SID_Rest) { j++; p2++; continue; }
X			if (p2->track == p1->track &&
X				(p2->data&noteDurMask) == dur) {	/* Move it */
X				if ((((int)p1->sID+p2->sID)>>1) <= MIDDLE_C) {	/* To bass */
#ifdef DEBUG
X	if (debug)
X			fprintf(out, "%% Moving treble note #%d (%d 0x%x) to bass clef\n",
X				j, p2->sID, p2->data);
#endif
X					/* Don't check this note again! */
X					move_note(p2, treblecount-j-1, basslist, basscount);
X					i++; p1++;
X					treblecount--;
X					basscount++;
X				} else {										/* To treble */
#ifdef DEBUG
X	if (debug)
X			fprintf(out, "%% Moving bass note #%d (%d 0x%x) to treble clef\n",
X				i, p1->sID, p1->data);
#endif
X					move_note(p1, basscount-i-1, treblelist, treblecount);
X					basscount--;
X					treblecount++;
X					i--; p1--;	/* About to be incremented again */
X					break;	/* out of inner loop */
X				}
X			} else { j++; p2++; }
X		}
X	}
}
X
move_note(from, sleft, to, sright)
NEvent *from, *to;
int sleft, sright;
{
X	/* Make room for the new note */
X	if (sright) bcopy(to,to+1,sright*sizeof(NEvent));
X	bcopy(from, to, sizeof(NEvent));
X	if (sleft)  bcopy(from+1,from,sleft*sizeof(NEvent));
}
X
search_future_track(num)
int num;
{
X	int i;
X	register TRACK *t = &track[num];
X	register SEvent *p, *orig;
X	UBYTE Nsig, Dsig;
X	BYTE keysig;
X	UBYTE chord, tieOut, nTuplet, dot, division;
X	NEvent *nptr;
X
X	orig = track[num].tptr;
X	/* First, bypass the current chord */
X	for (p=orig,i=t->count; i<t->size; i++, p++)
X		if (!(p->sID & 0x80)) {		/* Note only */
X			if (!(p->data & 0x80)) break;			/* End of current chord */
X		}
X	if (i >= t->size) return(0);	/* End of track */
X	for (p++,i++; i<t->size; i++,p++) {		/* Look into future notes */
X		if (!(p->sID & 0x80)) {		/* Note only */
X			if ((p->data & noteDurMask) == (orig->data & noteDurMask))
X				return(1);
X			if (!(p->data & 0x80)) break;	/* Stop looking.  Chord is off */
X		}
X	}
X	/* No match of the same duration, if we reach this point. */
X	return(0);
}
X
SHAR_EOF
chmod 0644 smus2tex.c ||
echo 'restore of smus2tex.c failed'
Wc_c="`wc -c < 'smus2tex.c'`"
test 34169 -eq "$Wc_c" ||
	echo 'smus2tex.c: original size 34169, current size' "$Wc_c"
fi
exit 0