From: durian@advtech.uswest.com (Mike Durian)
Subject: tclm0.1 - tcl with MIDI extensions (6/7)
Organization: U S WEST Advanced Technologies
Date: Tue, 12 Jan 1993 20:30:53 GMT

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	tclm0.1/mlib/Makefile
#	tclm0.1/mlib/mfileutil.c
#	tclm0.1/mplay
#
echo x - tclm0.1/mlib/Makefile
sed 's/^X//' >tclm0.1/mlib/Makefile << 'END-of-tclm0.1/mlib/Makefile'
X# add -DMPU to defines if you wish to compile in the ability to
X# play standard MIDI files.  This will only work on BSD/386
X# (BSDI) machines equipped with a MPU401 compatible MIDI card.
X# DEFS =
XDEFS = -DMPU
X
X# set RANLIB to ranlib if your system has it - otherwise set it to
X# true
X# RANLIB = true
XRANLIB = ranlib
X
XSRCS=mfileutil.c mplayutil.c mpu401.c
XOBJS=mfileutil.o mplayutil.o mpu401.o
XAR=libmutil.a
X
XCFLAGS= $(DEFS) -O -I.
XCC=cc
X
Xall:	$(AR)
X
X$(AR):	$(OBJS)
X	rm -f $(AR)
X	ar cr $(AR) $(OBJS)
X	$(RANLIB) $(AR)
X
Xclean:
X	rm -f $(AR) $(OBJS)
Xlint:
X	lint $(SRCS) > lint.out >&1
END-of-tclm0.1/mlib/Makefile
echo x - tclm0.1/mlib/mfileutil.c
sed 's/^X//' >tclm0.1/mlib/mfileutil.c << 'END-of-tclm0.1/mlib/mfileutil.c'
X/* mfileutil.c,v 1.3 1993/01/12 19:23:38 durian Exp */
X/*
X * Copyright (c) 1993 Michael B. Durian.  All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by Michael B. Durian.
X * 4. The name of the the Author may be used to endorse or promote 
X *    products derived from this software without specific prior written 
X *    permission.
X *
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 
X * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
X * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
X * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X */
X#include <stdio.h>
X#include <stdlib.h>
X#include <unistd.h>
X#include <string.h>
X#include <sys/fcntl.h>
X#include "mutil.h"
X
X/* hack so we know if a read failed because of an eof instead of bad data */
Xint MidiEof = 0;
Xchar MidiError[256];
X
Xint
Xread_header_chunk(mfile, hchunk)
X	int mfile;
X	HCHUNK *hchunk;
X{
X
X	if (mread(mfile, hchunk->str, sizeof(hchunk->str)) !=
X	    sizeof(hchunk->str)) {
X		if (!MidiEof)
X			sprintf(MidiError,
X			    "Couldn't read header chunk identifier");
X		return (0);
X	}
X
X	if (mread(mfile, (char *)&hchunk->length, sizeof(hchunk->length)) !=
X	    sizeof(hchunk->length)) {
X		sprintf(MidiError, "Couldn't read header chunk length");
X		return (0);
X	}
X
X	if (mread(mfile, (char *)&hchunk->format, sizeof(hchunk->format)) !=
X	    sizeof(hchunk->format)) {
X		sprintf(MidiError, "Couldn't read header chunk format");
X		return (0);
X	}
X
X	if (mread(mfile, (char *)&hchunk->num_trks, sizeof(hchunk->num_trks))
X	    != sizeof(hchunk->num_trks)) {
X		sprintf(MidiError, "Couldn't read header chunk num_trks");
X		return (0);
X	}
X
X	if (mread(mfile, (char *)&hchunk->division, sizeof(hchunk->division))
X	    != sizeof(hchunk->division)) {
X		sprintf(MidiError, "Couldn't read header chunk division");
X		return (0);
X	}
X
X	/* fix byte ordering */
X	hchunk->length = mtohl(hchunk->length);
X	hchunk->format = mtohs(hchunk->format);
X	hchunk->num_trks = mtohs(hchunk->num_trks);
X	hchunk->division = mtohs(hchunk->division);
X
X	if (strncmp(hchunk->str, "MThd", 4) != 0) {
X		sprintf(MidiError, "Not a standard MIDI file");
X		return (0);
X	}
X
X	if (hchunk->length != 6) {
X		sprintf(MidiError, "Bad header chunk size");
X		return (0);
X	}
X
X	if (hchunk->format == 0 && hchunk->num_trks != 1) {
X		sprintf(MidiError,
X		    "Midi format 0, but number of tracks (%d) is > 1",
X		    hchunk->num_trks);
X		return (0);
X	}
X
X	return (1);
X}
X
Xint
Xread_track_chunk(mfile, tracks)
X	int mfile;
X	TCHUNK *tracks;
X{
X
X	if (mread(mfile, tracks->str, 4) != 4) {
X		sprintf(MidiError, "Couldn't read track chunk identifier");
X		return (0);
X	}
X
X	if (strncmp(tracks->str, "MTrk", 4) != 0) {
X		sprintf(MidiError, "Bad track chunk identifier");
X		return (0);
X	}
X
X	if (mread(mfile, (char *)&tracks->length, sizeof(tracks->length)) !=
X	    sizeof(tracks->length)) {
X		sprintf(MidiError, "Couldn't read track length");
X		return (0);
X	}
X
X	tracks->msize = tracks->length = mtohl(tracks->length);
X	tracks->pos = 0;
X
X	/* allocate space for tracks events */
X	if ((tracks->events = (unsigned char *) malloc(tracks->length)) ==
X	    NULL) {
X		sprintf(MidiError, "Not enough memory for track data");
X		return (0);
X	}
X	tracks->event_start = tracks->events;
X
X	if (mread(mfile, (char *)tracks->events, tracks->length) !=
X	    tracks->length) {
X			sprintf(MidiError, "Couldn't read track data");
X			return (0);
X	}
X	tracks->events = tracks->event_start;
X
X	return (1);
X}
X
Xint
Xskip_track_chunk(mfile)
X	int mfile;
X{
X	long length;
X	char str[4];
X
X	if (mread(mfile, str, sizeof(str)) != sizeof(str)) {
X		sprintf(MidiError, "Couldn't read track chunk identifier");
X		return (0);
X	}
X
X	if (strncmp(str, "MTrk", 4) != 0) {
X		sprintf(MidiError, "Bad track chunk identifier");
X		return (0);
X	}
X
X	if (mread(mfile, (char *)&length, sizeof(length)) != sizeof(length)) {
X		sprintf(MidiError, "Couldn't read track length");
X		return (0);
X	}
X
X	length = mtohl(length);
X
X	if (lseek(mfile, length, SEEK_CUR) == -1) {
X		sprintf(MidiError, "Couldn't seek past track");
X		return (0);
X	}
X
X	return (1);
X}
X
Xint
Xsplit_type_zero(tracks)
X	TCHUNK *tracks;
X{
X	long tempo_delta;
X	long data_delta;
X	long timing;
X	EVENT_TYPE event_type;
X	int data_size;
X	int event_size;
X	int i;
X	int offset;
X	int malloced_data;
X	int malloced_tempo;
X	int malloc_inc;
X	int tempo_size;
X	int time_len;
X	unsigned char event[256];
X	unsigned char *data_events;
X	unsigned char *data_ptr;
X	unsigned char *last_events;
X	unsigned char *tempo_events;
X	unsigned char *tempo_ptr;
X	unsigned char var_time[4];
X
X	data_size = 0;
X	tempo_size = 0;
X	tempo_delta = 0;
X	data_delta = 0;
X	malloc_inc = 512;
X
X	if ((data_events = (unsigned char *) malloc(malloc_inc))
X		    == NULL) {
X		sprintf(MidiError, "Not enought memory to split track");
X		return (0);
X	}
X	malloced_data = malloc_inc;
X	if ((tempo_events = (unsigned char *) malloc(malloc_inc))
X		    == NULL) {
X		sprintf(MidiError, "Not enought memory to split track");
X		return (0);
X	}
X	malloced_tempo = malloc_inc;
X
X	data_ptr = data_events;
X	tempo_ptr = tempo_events;
X	while (event_size = get_smf_event(&tracks[0], event, &event_type)) {
X		if (event_size == -1) {
X			sprintf(MidiError,
X			    "Problem getting event while splitting");
X			return (0);
X		}
X
X		if (event_type ==  NORMAL || event_type == SYSEX) {
X			timing = var2fix(event, &offset);
X			data_delta += timing;
X			time_len = fix2var(data_delta, var_time);
X			data_size += event_size - offset + time_len;
X			if (data_size > malloced_data) {
X				last_events = data_events;
X				if (!more_memory((char **)&data_events,
X				    malloced_data + malloc_inc)) {
X					sprintf(MidiError,
X					    "Not enough memory");
X					return (0);
X				}
X				if (data_events != last_events)
X					data_ptr = data_events + (data_ptr -
X					    last_events);
X			}
X			/* copy in timing */
X			for (i = 0; i < time_len; i++)
X				*data_ptr++ = var_time[i];
X
X			/* copy in data */
X			for (i = offset; i < event_size; i++)
X				*data_ptr++ = event[i];
X
X			/* add timing to tempo delta */
X			tempo_delta += timing;
X			data_delta = 0;
X		} else {
X			timing = var2fix(event, &offset);
X			tempo_delta += timing;
X			time_len = fix2var(tempo_delta, var_time);
X			tempo_size += event_size - offset + time_len;
X			if (tempo_size > malloced_tempo) {
X				last_events = tempo_events;
X				if (!more_memory((char **)&tempo_events,
X				    malloced_tempo + malloc_inc)) {
X					sprintf(MidiError,
X					    "Not enough memory");
X					return (0);
X				}
X				if (tempo_events != last_events)
X					tempo_ptr = data_events + (tempo_ptr -
X					    last_events);
X			}
X			/* copy in timing */
X			for (i = 0; i < time_len; i++)
X				*tempo_ptr++ = var_time[i];
X
X			/* copy in data */
X			for (i = offset; i < event_size; i++)
X				*tempo_ptr++ = event[i];
X
X			/* add timing to tempo delta */
X			data_delta += timing;
X			tempo_delta = 0;
X		}
X	}
X
X	/* add eot to tempo track */
X	event[0] = 0xff;
X	event[1] = METAEOT;
X	event[2] = 0;
X	event_size = 3;
X
X	time_len = fix2var(tempo_delta, var_time);
X	tempo_size += event_size + time_len;
X	if (tempo_size > malloced_tempo) {
X		last_events = tempo_events;
X		if (!more_memory((char **)&tempo_events, malloced_tempo +
X		    malloc_inc)) {
X			sprintf(MidiError, "Not enough memory");
X			return (0);
X		}
X		if (tempo_events != last_events)
X			tempo_ptr = data_events + (tempo_ptr - last_events);
X	}
X	/* copy in timing */
X	for (i = 0; i < time_len; i++)
X		*tempo_ptr++ = var_time[i];
X
X	/* copy in data */
X	for (i = 0; i < event_size; i++)
X		*tempo_ptr++ = event[i];
X			
X	free(tracks[0].events);
X	tracks[0].events = tracks[0].event_start = tempo_events;
X	tracks[1].events = tracks[1].event_start = data_events;
X	tracks[0].msize = malloced_tempo;
X	tracks[1].msize = malloced_data;
X	tracks[0].length = tempo_size;
X	tracks[1].length = data_size;
X	tracks[0].pos = 0;
X	tracks[1].pos = 0;
X	(void) strncpy(tracks[0].str, "MTrk", 4);
X	(void) strncpy(tracks[1].str, "MTrk", 4);
X	return (1);
X}
X
Xint
Xmore_memory(ptr, size)
X	char **ptr;
X	int size;
X{
X
X	if ((*ptr = realloc(*ptr, size)) == NULL)
X		return (0);
X	else
X		return (1);
X}
X
Xint
Xget_smf_event(track, event, event_type)
X	TCHUNK *track;
X	unsigned char *event;
X	EVENT_TYPE *event_type;
X{
X	long length = 0;
X	int i;
X	int size;
X	static int extra_bytes[] = {2, 2, 2, 2, 1, 1, 2, 0};
X	unsigned char etype;
X	static unsigned char r_state;
X			
X	if (track->pos >= track->length)
X		return (0);
X
X	/*
X	 * get timing bytes
X	 */
X	if (track->pos++ == track->length)
X		return (-1);
X	*event++ = *track->events++;
X	size = 1;
X	while (*(event - 1) & 0x80) {
X		if (track->pos++ == track->length)
X			return (-1);
X		*event++ = *track->events++;
X		size++;
X	}
X
X	if (track->pos++ == track->length)
X		return (-1);
X	*event = *track->events++;
X
X	/* get event type */
X	switch (*event) {
X	case 0xff:
X		/* meta event */
X		if (track->pos++ == track->length)
X			return (-1);
X		size++;
X		event++;
X		*event_type = *event++ = *track->events++;
X		size++;
X		/* get length as a variable length */
X		do {
X			if (track->pos++ == track->length)
X				return (-1);
X			length = (length << 7) + (*track->events & 0x7f);
X			*event = *track->events++;
X			size++;
X		} while (*event++ & 0x80);
X		for (; length > 0; length--) {
X			/* get event */
X			if (track->pos++ == track->length)
X				return (-1);
X			*event++ = *track->events++;
X			size++;
X		}
X		break;
X	case 0xf0:
X	case 0xf7:
X		event++;
X		size++;
X		*event_type = SYSEX;
X		do {
X			if (track->pos++ == track->length)
X				return (-1);
X			*event = *track->events++;
X			size++;
X		} while (*event++ != 0xf7);
X		break;
X	default:
X		*event_type = NORMAL;
X		size++;
X		if (*event & 0x80) {
X			etype = *event;
X			r_state = (*event >> 4) & 0x0f;
X			for (i = 0; i < extra_bytes[(etype >> 4) & 0x07];
X			    i++) {
X				event++;
X				if (track->pos++ == track->length)
X					return(-1);
X				*event = *track->events++;
X				size++;
X			}
X		} else {
X			/* assume it is a continuation of note on */
X			/* get other data byte */
X			switch (r_state) {
X			case 0x08:
X			case 0x09:
X			case 0x0a:
X			case 0x0b:
X			case 0x0e:
X				event++;
X				if (track->pos++ == track->length)
X					return (-1);
X				*event = *track->events++;
X				size++;
X				break;
X			default:
X				break;
X			}
X		}
X	}
X	return (size);
X}
X
Xint
Xput_smf_event(track, event, event_size)
X	TCHUNK *track;
X	unsigned char *event;
X	int event_size;
X{
X
X	if (track->msize == 0) {
X		if ((track->event_start = (unsigned char *)malloc(
X		    BUFSIZ)) == NULL) {
X			sprintf(MidiError, "Not enough memory for new event");
X			return (0);
X		}
X		track->msize = BUFSIZ;
X		track->events = track->event_start;
X	}
X	track->length += event_size;
X	if (track->pos + event_size > track->msize) {
X		track->msize += BUFSIZ;
X		if ((track->event_start =
X		    (unsigned char *)realloc(track->event_start, track->msize))
X		    == NULL) {
X			perror("");
X			sprintf(MidiError, "Not enough memory for new event");
X			track->length -= event_size;
X			track->msize -= BUFSIZ;
X			return (0);
X		}
X		/* starting position might move */
X		track->events = track->event_start + track->pos;
X	}
X
X	memcpy(track->events, event, event_size);
X	track->events += event_size;
X	track->pos += event_size;
X	return (1);
X}
X
Xvoid
Xrewind_track(track)
X	TCHUNK *track;
X{
X	track->pos = 0;
X	track->events = track->event_start;
X}
X
Xvoid
Xreset_track(track)
X	TCHUNK *track;
X{
X	track->pos = 0;
X	track->length = 0;
X	track->events = track->event_start;
X}
X
X
Xint
Xwrite_header_chunk(mfile, hchunk)
X	int mfile;
X	HCHUNK *hchunk;
X{
X
X	(void)strncpy(hchunk->str, "MThd", 4);
X	if (mwrite(mfile, hchunk->str, 4) != 4) {
X		sprintf(MidiError, "Couldn't write header chunk identifier");
X		return (0);
X	}
X
X	hchunk->length = 6;
X	hchunk->length = htoml(hchunk->length);
X	if (mwrite(mfile, (char *)&hchunk->length, sizeof(hchunk->length)) !=
X		sizeof(hchunk->length)) {
X		sprintf(MidiError, "Couldn't write header chunk length");
X		return (0);
X	}
X	hchunk->length = mtohl(hchunk->length);
X
X	hchunk->format = htoms(hchunk->format);
X	if (mwrite(mfile, (char *)&hchunk->format, sizeof(hchunk->format)) !=
X		sizeof(hchunk->format)) {
X		sprintf(MidiError, "Couldn't write header chunk format");
X		return (0);
X	}
X	hchunk->format = mtohs(hchunk->format);
X
X	hchunk->num_trks = htoms(hchunk->num_trks);
X	if (mwrite(mfile, (char *)&hchunk->num_trks, sizeof(hchunk->num_trks))
X	    != sizeof(hchunk->num_trks)) {
X		sprintf(MidiError, "Couldn't write number of tracks");
X		return (0);
X	}
X	hchunk->num_trks = mtohs(hchunk->num_trks);
X
X	hchunk->division = htoms(hchunk->division);
X	if (mwrite(mfile, (char *)&hchunk->division, sizeof(hchunk->division))
X	    != sizeof(hchunk->division)) {
X		sprintf(MidiError, "Couldn't write header chunk division");
X		return (0);
X	}
X	hchunk->division = mtohs(hchunk->division);
X
X	return (1);
X}
X
Xint
Xwrite_track_chunk(mfile, tchunk)
X	int mfile;
X	TCHUNK *tchunk;
X{
X	long midi_length;
X
X	(void)strncpy(tchunk->str, "MTrk", 4);
X	if (mwrite(mfile, tchunk->str, 4) != 4) {
X		sprintf(MidiError, "Couldn't write track chunk identifier");
X		return (0);
X	}
X
X	midi_length = htoml(tchunk->length);
X	if (mwrite(mfile, (char *)&midi_length, sizeof(midi_length)) !=
X	    sizeof(midi_length)) {
X		sprintf(MidiError, "Couldn't write track length");
X		return (0);
X	}
X
X	if (mwrite(mfile, (char *)tchunk->event_start, tchunk->length)
X	    != tchunk->length) {
X		sprintf(MidiError, "Couldn't write track events");
X		return (0);
X	}
X
X	return (1);
X}
X
Xint
Xload_tracks(mfile, hchunk, tracks_ptr, spec_tracks)
X	int mfile;
X	HCHUNK *hchunk;
X	TCHUNK **tracks_ptr;
X	TRKS *spec_tracks;
X{
X	TCHUNK *tracks;
X	int i;
X	int cur_track;
X
X	if (hchunk->format == 0) {
X		if (spec_tracks->num_tracks) {
X			sprintf(MidiError,
X			    "Specified tracks are ignored on file type 0");
X		}
X
X		if ((tracks = (TCHUNK *) malloc(sizeof(TCHUNK) * 2)) == NULL) {
X			sprintf(MidiError, "No memory available for tracks");
X			return (0);
X		}
X
X		if (!read_track_chunk(mfile, &tracks[0])) {
X			sprintf(MidiError, "Couldn't read track 0");
X			return (0);
X		}
X
X		if (!split_type_zero(tracks)) {
X			sprintf(MidiError, "Couldn't split MIDI file type 0");
X			return (0);
X		}
X
X		*tracks_ptr = tracks;
X
X		return (2);
X	}
X		
X	if (spec_tracks->num_tracks == 0) {
X		for (i = 0; i < hchunk->num_trks; i++)
X			spec_tracks->tracks[i] = i;
X		spec_tracks->num_tracks = hchunk->num_trks;
X	}
X
X	if ((tracks = (TCHUNK *) malloc(sizeof(TCHUNK) *
X		    spec_tracks->num_tracks)) == NULL) {
X		sprintf(MidiError, "No memory available for tracks");
X		return (0);
X	}
X
X	cur_track = 0;
X	/* read in tracks */
X	for (i = 0; i <= spec_tracks->tracks[spec_tracks->num_tracks - 1];
X		    i++) {
X		if (i == spec_tracks->tracks[cur_track]) {
X			/* read track chunk */
X			if (!read_track_chunk(mfile, &tracks[cur_track++])) {
X				sprintf(MidiError, "Couldn't read track %d",
X				    i);
X				return (0);
X			}
X		} else {
X			if (!skip_track_chunk(mfile)) {
X				sprintf(MidiError,
X				    "Couldn't skip track chunk %d", i);
X				return (0);
X			}
X		}
X	}
X
X	*tracks_ptr = tracks;
X	return (spec_tracks->num_tracks);
X}
X
Xint
Xfix2var(val, ptr)
X	long val;
X	unsigned char *ptr;
X{
X	int i;
X	unsigned char buf[4];
X	unsigned char *bptr;
X
X	buf[0] = buf[1] = buf[2] = buf[3] = 0;
X	bptr = buf;
X	*bptr++ = val & 0x7f;
X	while ((val >>= 7) > 0) {
X		*bptr |= 0x80;
X		*bptr++ += (val & 0x7f);
X	}
X
X	i = 0;
X	do {
X		*ptr++ = *--bptr;
X		i++;
X	} while (bptr != buf);
X
X	return (i);
X}
X
Xlong
Xvar2fix(var, delta)
X	unsigned char *var;
X	int *delta;
X{
X	long fix;
X
X	fix = 0;
X	*delta = 0;
X	if (*var & 0x80)
X		do {
X			fix = (fix << 7) + (*var & 0x7f);
X			(*delta)++;
X		} while (*var++ & 0x80);
X	else {
X		fix = *var++;
X		(*delta)++;
X	}
X
X	return (fix);
X}
X
Xvoid
Xfree_track(track)
X	TCHUNK *track;
X{
X
X	if (track->event_start != NULL)
X		free(track->event_start);
X	track->event_start = NULL;
X	track->events = NULL;
X	track->length = 0;
X	track->pos = 0;
X	track->msize = 0;
X}
X
Xint
Xmread(dev, buf, size)
X	int dev;
X	char *buf;
X	int size;
X{
X	int num_read;
X	int total_read;
X
X	total_read = 0;
X	do {
X		if ((num_read = read(dev, buf, size - total_read)) == -1) {
X			perror("");
X			return (-1);
X		}
X		if (num_read == 0)
X			break;
X		total_read += num_read;
X		buf += num_read;
X	} while (size > total_read);
X	if (total_read == 0)
X		MidiEof = 1;
X	return (total_read);
X}
X
Xint
Xmwrite(dev, buf, size)
X	int dev;
X	char *buf;
X	int size;
X{
X	int num_written;
X	int total_written;
X
X	total_written = 0;
X	do {
X		if ((num_written = write(dev, buf, size - total_written))
X		    == -1) {
X			perror("");
X			return (-1);
X		}
X		if (num_written == 0)
X			break;
X		total_written += num_written;
X		buf += num_written;
X	} while (size > total_written);
X	return (total_written);
X}
END-of-tclm0.1/mlib/mfileutil.c
echo x - tclm0.1/mplay
sed 's/^X//' >tclm0.1/mplay << 'END-of-tclm0.1/mplay'
X#!/usr/local/bin/tclm -f
X#
X# Copyright (c) 1993 Michael B. Durian.  All rights reserved.
X#
X# Redistribution and use in source and binary forms, with or without
X# modification, are permitted provided that the following conditions
X# are met:
X# 1. Redistributions of source code must retain the above copyright
X#    notice, this list of conditions and the following disclaimer.
X# 2. Redistributions in binary form must reproduce the above copyright
X#    notice, this list of conditions and the following disclaimer in the
X#    documentation and/or other materials provided with the distribution.
X# 3. All advertising materials mentioning features or use of this software
X#    must display the following acknowledgement:
X#	This product includes software developed by Michael B. Durian.
X# 4. The name of the the Author may be used to endorse or promote 
X#    products derived from this software without specific prior written 
X#    permission.
X#
X# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 
X# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
X# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
X# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
X# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X# SUCH DAMAGE.
X#
X
Xif {! [midimpu]} {
X	puts stderr [concat "Cannot play.  Tclm was not compiled with the " \
X	    "MPU functionality turned on."]
X	exit 1
X}
X
Xset repeat 0
Xset tracks {}
Xset speed 1.0
Xset filenames {}
X
Xproc parse_arg {args} {
X	global repeat
X	global tracks
X	global speed
X	global filenames
X
X	# strip away extra {}'s
X	set args [lindex $args 0]
X	set num_args [llength $args]
X	if {$num_args > 1 && [string compare [lindex $args 0] "-f"] == 0} {
X		set i 2
X	} else {
X		set i 0
X	}
X	for {} {$i < $num_args} {incr i} {
X
X		set arg [lindex $args $i]
X		case $arg in \
X		-r {
X			set repeat 1
X		} -t {
X			set tracks [lindex $args [incr i]]
X		} -s {
X			set speed [lindex $args [incr i]]
X		} default {
X			lappend filenames $arg
X		}
X	}
X}
X
Xparse_arg $argv
X
X#set midi_file_name /u/durian/midi/songs/chorale.mid
X
Xif {$repeat} {
X	set repeat repeat
X} else {
X	set repeat ""
X}
X
Xif {[llength $tracks] != 0} {
X	set tracks "tracks \"$tracks\""
X} else {
X	set tracks ""
X}
X
Xif {$speed != 1.0} {
X	set speed "reltempo $speed"
X} else {
X	set speed ""
X}
X
Xif {[llength $filenames] == 0} {
X	set midi_file stdin
X	set done 0
X	while {1} {
X		if {[catch {midiread $midi_file} mfile]} {
X			if {[string compare "$mfile" "EOF"] == 0} {
X				exit 0
X			} else {
X				puts stderr $mfile
X				exit 1
X			}
X		}
X		eval "midiplay $repeat $tracks $speed $mfile"
X		midifree $mfile
X	}
X} else {
X	foreach file $filenames {
X		if {[catch {open $file} midi_file]} {
X			puts stderr $midi_file
X			exit 1
X		}
X		if {[catch {midiread $midi_file} mfile]} {
X			puts stderr $mfile
X			exit 1
X		}
X		eval "midiplay $repeat $tracks $speed $mfile"
X		midifree $mfile
X		close $midi_file
X	}
X}
END-of-tclm0.1/mplay
exit