summaryrefslogtreecommitdiff
path: root/cmt
diff options
context:
space:
mode:
Diffstat (limited to 'cmt')
-rw-r--r--cmt/cext.c107
-rw-r--r--cmt/cext.h194
-rw-r--r--cmt/cleanup.c62
-rw-r--r--cmt/cleanup.h9
-rw-r--r--cmt/cmdline.c479
-rw-r--r--cmt/cmdline.h8
-rw-r--r--cmt/cmtcmd.c53
-rw-r--r--cmt/cmtcmd.h35
-rw-r--r--cmt/cmtio.c161
-rw-r--r--cmt/cmtio.h9
-rw-r--r--cmt/hash.h23
-rw-r--r--cmt/hashrout.h220
-rw-r--r--cmt/mem.c91
-rw-r--r--cmt/mem.h12
-rw-r--r--cmt/mfmidi.h19
-rw-r--r--cmt/midibuff.h23
-rw-r--r--cmt/midicode.h62
-rw-r--r--cmt/midierr.h17
-rw-r--r--cmt/midifile.c618
-rw-r--r--cmt/midifile.h27
-rw-r--r--cmt/midifns.c1886
-rw-r--r--cmt/midifns.h137
-rw-r--r--cmt/midimgr.c784
-rw-r--r--cmt/midimgr.h14
-rw-r--r--cmt/moxc.c669
-rw-r--r--cmt/moxc.h44
-rw-r--r--cmt/musiprog.h31
-rw-r--r--cmt/pitch.h9
-rw-r--r--cmt/record.c638
-rw-r--r--cmt/record.h6
-rw-r--r--cmt/seq.c1199
-rw-r--r--cmt/seq.h295
-rw-r--r--cmt/seqdecls.h11
-rw-r--r--cmt/seqmread.c448
-rw-r--r--cmt/seqmread.h4
-rw-r--r--cmt/seqmwrite.c658
-rw-r--r--cmt/seqmwrite.h4
-rw-r--r--cmt/seqread.c1890
-rw-r--r--cmt/seqread.h7
-rw-r--r--cmt/seqwrite.c282
-rw-r--r--cmt/seqwrite.h6
-rw-r--r--cmt/swlogic.h197
-rw-r--r--cmt/tempomap.c124
-rw-r--r--cmt/tempomap.h25
-rw-r--r--cmt/timebase.c322
-rw-r--r--cmt/timebase.h85
-rw-r--r--cmt/userio.c1285
-rw-r--r--cmt/userio.h96
48 files changed, 13385 insertions, 0 deletions
diff --git a/cmt/cext.c b/cmt/cext.c
new file mode 100644
index 0000000..452e18f
--- /dev/null
+++ b/cmt/cext.c
@@ -0,0 +1,107 @@
+/****************************************************************************
+ cext.c
+ Copyright 1989 Carnegie Mellon University
+
+ August 3, 1987
+ Author: Frits Habermann
+----------------------------------------------------------------------------
+ 02-May-1988 | JCD : portable & AMIGA version.
+ 17-Oct-1988 | JCD : more portability (FREE).
+ 28-Apr-2003 | DM : changed includes for portability
+****************************************************************************/
+
+#include "switches.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cext.h"
+#include "userio.h"
+
+#define calc_middle(top, bot) (((top - bot) / 2 ) + bottom )
+
+#define kbyte 1000
+#define outof_mem(blocksize) (blocksize == 0 )
+#define done_search(top, bot, middle) ( (( (top - bot) < kbyte ) && \
+( !toomuch_mem(middle)) ) || \
+ ( outof_mem( middle )) )
+
+private boolean toomuch_mem(ushort maximum)
+{
+ char *test;
+ boolean istoo_much;
+ istoo_much = ( (test = (char *) MALLOC(maximum)) == NULL );
+ if (test) FREE(test);
+ return( istoo_much );
+}
+
+private boolean toolittle_mem(maximum)
+ushort maximum;
+{
+ char *test;
+ boolean istoo_little;
+ istoo_little = !( (test = (char *) MALLOC(maximum)) == NULL );
+ if (test) FREE( test );
+ return(istoo_little);
+}
+
+private ushort get_biggest_block( maximum )
+ushort maximum;
+{
+ ushort maxblock;
+ ushort top = maximum;
+ ushort bottom = 0;
+ if (!toomuch_mem(maximum)) return(maximum); /* If there's enough memory */
+ else {
+ gprintf(ERROR, "Running out of memory...\n");
+ maxblock = calc_middle( top, bottom );
+ while( !done_search(top, bottom, maxblock) ) {
+ if( toomuch_mem(maxblock) ) {
+ top = maxblock;
+ maxblock = calc_middle(top,bottom);
+ }
+ else if (toolittle_mem(maxblock)) {
+ bottom = maxblock;
+ maxblock = calc_middle(top,bottom);
+ }
+ }
+ }
+ return( maxblock );
+}
+
+public ulong MyMaxMem(ushort *growbytes)
+{
+ ulong x;
+ if( growbytes != NULL ) *growbytes = 0;
+ x=( (ulong)get_biggest_block((ushort)BIGGEST_BLOCK));
+/* gprintf(TRANS,"cext: MyMaxMem %ld\n",x); */
+ return x;
+}
+
+/* note: EXIT is defined to be cmt_exit */
+
+void cmt_exit(n)
+ int n;
+{
+ cu_cleanup();
+/* For protection, exit is #defined to hide it. Expose it and call it. */
+#undef exit
+ exit(n);
+}
+
+
+#ifdef AMIGA
+#ifdef LATTICE
+/* for some reason, these don't seem to be defined
+ anywhere in the standard libraries
+ */
+#include "signal.h"
+
+int _FPERR;
+int (*_SIGFPE)(int) = SIG_DFL;
+
+int _oserr;
+
+#endif
+#endif
+
diff --git a/cmt/cext.h b/cmt/cext.h
new file mode 100644
index 0000000..665c13d
--- /dev/null
+++ b/cmt/cext.h
@@ -0,0 +1,194 @@
+/******************************************************************
+* modified JCD 27 Apr-88 for AMIGA
+* cext.h -- extensions to c to make it more portable
+* Copyright 1989 Carnegie Mellon University
+*
+*******************************************************************
+
+cext must provide the following definitions:
+
+true -- a constant
+false -- a boolean constant
+private -- defined as static, used to declare local functions
+public -- defined as empty string, used to declare exported functions
+boolean -- a new type
+byte -- unsigned 8-bit quantity
+ushort -- unsigned 16-bit quantity
+ulong -- unsigned 32-bit quantity
+Pointer -- pointer to char, a generic pointer
+ABS() -- absolute value of any type of number
+MAX() -- maximum of two numbers
+MIN() -- minimum of two numbers
+ROUND() -- round a double to long
+
+NULL -- pointer to nothing, a constant
+EOS -- end of string, a constant '\0'
+MALLOC(x) -- allocates x bytes
+FREE(x) -- frees something from MALLOC
+AVAILMEM -- tells how much memory is available.
+ (N.B.: no parens, no args.)
+EXIT(n) -- calls exit(n) after shutting down/deallocating resources
+
+*****************************************************************************/
+
+/* CHANGE LOG
+ * --------------------------------------------------------------------
+ * 28Apr03 dm many changes for new conditional compilation switches
+ * 28Apr03 rbd removed macro redefinitions: min, max
+ */
+
+#ifndef CEXT_H
+#ifndef SWITCHES
+#include "switches.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#if HAS_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if HAS_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAS_MALLOC_H
+#include <malloc.h>
+#endif
+
+#if NEED_ULONG
+typedef unsigned long ulong;
+#endif
+
+#if NEED_USHORT
+typedef unsigned long ushort;
+#endif
+
+#if NEED_BYTE
+typedef unsigned char byte;
+#endif
+
+/* There's a name conflict between true/false as an enum type in
+ * Apple #includes:Types.h on the Mac, and true/false as #defined below
+ */
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define private static
+#define public
+
+#if NEED_DEFINE_MALLOC
+public void *malloc();
+#endif
+
+typedef char *Pointer;
+
+#ifdef UNIX_MACH
+typedef int boolean;
+#else
+/* hopefully, unsigned short will save sign extension instructions */
+typedef unsigned char boolean;
+#endif
+
+#ifndef ABS
+#define ABS(a) (((a) > 0) ? (a) : -(a))
+#endif
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define MAXULONG 0xffffffff
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+#ifndef EOS
+#define EOS '\0'
+#endif
+
+#define SAFETYBUF 10 /* Safety buffer when allocating memory */
+#define BIGGEST_BLOCK 32765 /* Should find a happy medium for this */
+
+#ifdef MACINTOSH /*DMH: gets AVAILMEM in record.c*/
+#include <stddef.h>
+#define MALLOC(x) malloc((size_t)(x)) /*DMH: size_t is ulong, for MAC*/
+#define FREE(x) free((char *)(x))
+#define AVAILMEM MyMaxMem(NULL)/*???*/
+#endif
+
+#ifdef LATTICE322
+#define MALLOC malloc
+#define FREE free
+#define AVAILMEM MyMaxMem(NULL)
+
+#else
+
+#ifdef DOS /* was MICROSOFT */
+#define MALLOC malloc
+#define FREE free
+#define AVAILMEM MyMaxMem(NULL)
+#endif
+#endif
+
+#ifdef UNIX
+#define MALLOC malloc
+#define FREE free
+#define AVAILMEM 10000000 /* since we have virtual memory, assume 10Mb */
+#endif
+
+#ifdef AMIGA
+#define MALLOC malloc
+#define FREE free
+#define AVAILMEM 128000
+#endif
+
+public ulong MyMaxMem(ushort *);
+
+#ifndef MEM
+#include "mem.h"
+#endif
+
+#ifndef CLEANUP
+#include "cleanup.h"
+#endif
+
+#ifdef CMTSTUFF
+#define EXIT cmt_exit
+public void EXIT(int);
+/* don't allow anyone to call exit directly */
+#define exit(n) PLEASE_CALL_EXIT_NOT_exit
+#else
+#define EXIT(n) exit(n)
+#endif
+
+#define _cext
+
+#ifndef MALLOC
+MALLOC is not defined!
+#endif
+
+#define ROUND(x) ((long) ((x) + 0.5))
+
+/* for compatibility */
+#ifdef NEED_ROUND
+#define round ROUND
+#endif
+
+#ifndef min
+#define min MIN
+#define max MAX
+#endif
+
+#define CEXT_H
+#endif
diff --git a/cmt/cleanup.c b/cmt/cleanup.c
new file mode 100644
index 0000000..a0797d9
--- /dev/null
+++ b/cmt/cleanup.c
@@ -0,0 +1,62 @@
+/* cleanup.c -- registers work to do upon exit */
+
+#include "stdio.h"
+#include "cext.h"
+
+typedef struct cleanup_struct {
+ struct cleanup_struct *next;
+ cu_fn_type fn;
+ cu_parm_type obj;
+} cleanup_node, *cleanup_type;
+
+cleanup_type cleanup_list = NULL;
+
+/* cu_register -- remember function and arg to call in order to clean up */
+/**/
+void cu_register(cu_fn_type fn, cu_parm_type obj)
+{
+ cleanup_type cu = (cleanup_type) memget(sizeof(cleanup_node));
+ cu->fn = fn;
+ cu->obj = obj;
+ cu->next = cleanup_list;
+ cleanup_list = cu;
+}
+
+
+/* cu_unregister -- erase memory of obj (should be unique in cleanup list) */
+/**/
+void cu_unregister(obj)
+ void *obj;
+{
+ cleanup_type *cu = &cleanup_list;
+ while (*cu) {
+ if ((*cu)->obj == obj) {
+ cleanup_type found = *cu;
+ *cu = (*cu)->next; /* splice out found */
+ memfree((char *) found, sizeof(cleanup_node));
+ return;
+ }
+ cu = &((*cu)->next);
+ }
+}
+
+
+/* cu_cleanup -- call the registered functions */
+/**/
+void cu_cleanup()
+{
+ while (cleanup_list) {
+ cleanup_type cu = cleanup_list;
+#ifdef CU_TRACE
+ gprintf(GTRANS, "cu_cleanup: node %lx fn %lx obj %lx\n",
+ cu, cu->fn, cu->obj);
+#endif
+ cu->fn(cu->obj);
+ cleanup_list = cu->next;
+ memfree((char *) cu, sizeof(cleanup_node));
+ }
+#ifdef CU_TRACE
+ gprintf(GTRANS, "cu_cleanup done.\n");
+ fflush(stdout);
+#endif
+}
diff --git a/cmt/cleanup.h b/cmt/cleanup.h
new file mode 100644
index 0000000..62aaf6f
--- /dev/null
+++ b/cmt/cleanup.h
@@ -0,0 +1,9 @@
+/* cleanup.c -- registers work to do upon exit */
+
+typedef void *cu_parm_type;
+typedef void (*cu_fn_type)(cu_parm_type);
+void cu_register(cu_fn_type fn, cu_parm_type obj);
+void cu_unregister(void *obj);
+void cu_cleanup(void);
+
+#define CLEANUP
diff --git a/cmt/cmdline.c b/cmt/cmdline.c
new file mode 100644
index 0000000..a215b08
--- /dev/null
+++ b/cmt/cmdline.c
@@ -0,0 +1,479 @@
+/* cmdline.c -- command line parsing routines */
+/* Copyright 1989 Carnegie Mellon University */
+/*
+ * This module is designed to allow various modules to scan (and rescan)
+ * the command line for applicable arguments. The goal is to hide as
+ * much information about switches and their names as possible so that
+ * switches become more consistent across applications and so that the
+ * author of an application need not do a lot of work to provide numerous
+ * options. Instead, each module scans the command line for its own
+ * arguments.
+ *
+ * Command lines are of the following form:
+ * command -s1 -s2 opt2 -s3 arg1 arg2 -s4 opt4 arg3
+ * Note that there are three kinds of command line parameters:
+ * (1) A Switch is a "-" followed by a name, e.g. "-s1"
+ * (2) An Option is a Switch followed by a space and name, e.g. "-s2 opt2"
+ * (3) An Argument is a name by itself, e.g. "arg1"
+ * Note also that a switch followed by an argument looks just like an
+ * option, so a list of valid option names is necessary to disambiguate.
+ *
+ * Long names are good for readability, but single character abbreviations
+ * are nice for experienced users. cmdline.c allows single character
+ * abbreviations provided that they are unambiguous. These are
+ * recognized with no extra work by the programmer. If an
+ * isolated '?' is encountered in the command line, then all of
+ * the options and switches are printed as help and for debugging.
+ *
+ * Given that we must tell this module about option names and switch
+ * names, how should we do it? We can't wait until modules are
+ * initialized, since often modules want to read the command line
+ * at initialization time. In the original implementation, the
+ * main program was supposed to provide names for the whole program,
+ * but this violates modularity: when an option is added to a module,
+ * the main program has to be modified too. This is a real pain when
+ * different machines support different options and you want to have
+ * a single machine-independent main program. The solution is to
+ * have the main program import strings describing the options and
+ * switches used by each module. These are passed into cmdline.c
+ * before initialization of other modules is begun.
+ *
+ * A main program that uses cmdline.c should do the following:
+ * call cl_syntax(s) for each module's option/switch string.
+ * The string s should have the following format:
+ * "opt1<o>description;opt2<o>description;...;switch1<s>description;..."
+ * where opt1 and opt2 are option names (without the preceding "-"), and
+ * switch1 is a switch name. The <o> and <s> indicate whether the
+ * name is an option or a switch. The descriptions are arbitrary strings
+ * (without semicolons) that are printed out for the user when "?"
+ * is typed on the command line.
+ *
+ * After calling cl_syntax, main() should call
+ * cl_init(argv, argc)
+ * cl_init will report an error (to STDERR) if it finds any illegal
+ * switch or option names in argv, and help will be printed if "?"
+ * is found in argv. If cl_init returns false, then the user has been
+ * given an error message or help, and main should probably exit.
+ *
+ * Afterward, switches, options, and arguments can be accessed by
+ * calling cl_switch, cl_option, and cl_arg. If cl_switch or cl_option
+ * is called with a switch name that was not mentioned in the call to
+ * cl_init, an error will result. This indicates that the application
+ * author omitted a valid switch or option name when calling cl_init.
+ * This is an error because the full set of names is needed for error
+ * checking and to distinguish arguments from options.
+ *
+ */
+
+/*****************************************************************************
+* Change Log
+* Date | Change
+*-----------+-----------------------------------------------------------------
+* 13-Jun-86 | Created Change Log
+* 6-Aug-86 | Modified for Lattice 3.0 -- use "void" to type some routines
+* 20-Sep-89 | Redesigned the interface, adding cl_syntax call.
+* 2-Apr-91 | JDW : further changes
+* 27-Dec-93 | "@file" as first arg reads command line args from file
+* 11-Mar-94 | PLu: Add private to cl_search() definition.
+* 28-Apr-03 | DM: true->TRUE, false->FALSE
+*****************************************************************************/
+
+/* stdlib.h not on PMAX */
+#ifndef mips
+#include "stdlib.h"
+#endif
+#include "stdio.h"
+#include "cext.h"
+#include "userio.h"
+#include "cmdline.h"
+#include "ctype.h"
+#include "string.h"
+
+#define syntax_max 10 /* allow for 10 syntax strings */
+private char *syntax[syntax_max];
+private int n_syntax = 0; /* number of strings so far */
+private char **argv; /* command line argument vector */
+private int argc; /* length of argv */
+
+private boolean cl_rdy = FALSE; /* set to TRUE when initialized */
+
+#define cl_OPT 1
+#define cl_SW 2
+#define cl_INIT 3
+#define cl_ARG 4
+
+/*****************************************************************************
+* Routines local to this module
+*****************************************************************************/
+private char *cl_search();
+private int find_string();
+private void indirect_command(char *filename, char *oldarg0);
+private void ready_check();
+
+/****************************************************************
+* cl_arg
+* Inputs:
+* n: the index of the arg needed
+* Results:
+* pointer to the nth arg, or NULL if none exists
+* arg 0 is the command name
+*****************************************************************/
+
+char *cl_arg(n)
+ int n;
+{
+ return (n <= 0 ? argv[0] :
+ cl_search((char *)NULL, cl_ARG, n));
+}
+
+/* cl_help -- print help from syntax strings */
+/**/
+void cl_help()
+{
+ register int i, j;
+ int count = 0; /* see if there are any switches or flags */
+
+ for (i = 0; i < n_syntax; i++) {
+ register char *ptr = syntax[i];
+ register char c = *ptr++;
+ while (c != EOS) {
+ while (c != EOS && !(isalnum(c))) c = *ptr++;
+ if (c != EOS) {
+ count++;
+ gprintf(TRANS, "-");
+ j = 1;
+ while (c != EOS && c != '<') {
+ gprintf(TRANS, "%c", c);
+ c = *ptr++;
+ j++;
+ }
+ if (c != EOS) {
+ c = *ptr++;
+ if (c == 'o') {
+ gprintf(TRANS, " xxx");
+ j += 4;
+ }
+ }
+ /* attempt to tab */
+ do {
+ gprintf(TRANS, " ");
+ } while (j++ < 16);
+ while (c != EOS && c != '>') c = *ptr++;
+ if (c != EOS) c = *ptr++;
+ while (c != EOS && c != ';') {
+ gprintf(TRANS, "%c", c);
+ c = *ptr++;
+ }
+ gprintf(TRANS, "\n");
+ }
+ }
+ }
+ if (!count) gprintf(TRANS, "No switches or options exist.\n");
+}
+
+/*****************************************************************************
+* cl_init
+* Inputs:
+* char *switches[]: array of switch names
+* int nsw: number of switch names
+* char *options[]: array of option names
+* int nopt: number of option names
+* char *av: array of command line fields (argv)
+* int ac: number of command line fields (argc)
+* Effect:
+* Checks that all command line entries are valid.
+* Saves info for use by other routines.
+* Returns:
+* TRUE if syntax checks OK, otherwise false
+*****************************************************************************/
+
+boolean cl_init(av, ac)
+ char *av[];
+ int ac;
+{
+ argv = av;
+ argc = ac;
+
+ /* check for help request */
+ if (argc == 2 && strcmp(argv[1], "?") == 0) {
+ cl_help();
+ return FALSE; /* avoid cl_search which would complain about "?" */
+ }
+ /* check for indirection */
+ if (argc == 2 && *(argv[1]) == '@') {
+ /* read new args from file */
+ indirect_command(av[1] + 1, av[0]);
+ }
+ /* check command line syntax: */
+ cl_rdy = TRUE;
+ return (cl_rdy = (cl_search("true", cl_INIT, 0) != NULL));
+}
+
+
+/****************************************************************
+* cl_int_option
+* Inputs:
+* char *name: name of option
+* long default: default value for option
+* Result:
+* returns long encoding of the option, deflt if none
+* Implementation:
+* call cl_option and sscanf result
+*****************************************************************/
+
+long cl_int_option(name, deflt)
+ char *name;
+ long deflt;
+{
+ char *opt = cl_option(name);
+ if (opt) {
+ if (sscanf(opt, "%ld", &deflt) != 1) {
+ gprintf(TRANS, "Warning: option %s %s not an integer, ignored\n",
+ name, opt);
+ }
+ }
+ return deflt;
+}
+
+
+/****************************************************************
+* cl_search
+* Inputs:
+* char *name: name of field, must be non-null if opt_sw == cl_INIT
+* int opt_sw: option, switch, init, or arg
+* int n: argument number (if opt_sw is cl_ARG)
+* Result:
+* returns pointer to option value/switch if one exists, otherwise null
+* Implementation:
+* parse the command line until name or arg is found
+* see if the option is followed by a string that does
+* not start with "-"
+*****************************************************************/
+
+private char *cl_search(name, opt_sw, n)
+ char *name;
+ int opt_sw;
+ int n; /* if opt_sw is cl_ARG, n > 0 tells which one */
+{
+ register int i = 1; /* index into command line */
+ boolean abbr;
+ boolean result = TRUE;
+
+ ready_check();
+
+ /* parse command line: */
+ while (i < argc) {
+ register char *arg = argv[i];
+ /* arguments that start with '-' should be quoted and quotes must
+ be removed by the application
+ */
+ if (*arg == '-') {
+ int arg_type = find_string(arg + 1, &abbr);
+ if (arg_type == cl_OPT) {
+ i += 1; /* skip name and option */
+ /* don't look for '-' because the option might be a
+ * negative number
+ */
+ if (i >= argc /* || *arg == '-' */) {
+ if (opt_sw == cl_INIT) {
+ gprintf(ERROR, "missing argument after %s\n", arg);
+ result = FALSE;
+ }
+ } else if (opt_sw == cl_OPT &&
+ (strcmp(arg + 1, name) == 0 ||
+ (abbr && *(arg + 1) == name[0]))) {
+ return argv[i];
+ }
+ } else if (arg_type == cl_SW) {
+ if (opt_sw == cl_SW &&
+ (strcmp(arg + 1, name) == 0 ||
+ (abbr && *(arg + 1) == name[0])))
+ return arg;
+ } else if (opt_sw == cl_INIT) {
+ gprintf(ERROR, "invalid switch: %s\n", arg);
+ result = FALSE;
+ }
+ } else if (opt_sw == cl_ARG) {
+ if (n == 1) return arg;
+ n--;
+ }
+ i++; /* skip to next field */
+ }
+ if (opt_sw == cl_INIT) {
+ /* return name or NULL to represent TRUE or FALSE */
+ return (result ? name : NULL);
+ }
+ return NULL;
+}
+
+/****************************************************************
+* cl_option
+* Inputs:
+* char *name: option name
+* Outputs:
+* returns char *: the option string if found, otherwise null
+****************************************************************/
+
+char *cl_option(name)
+char *name;
+{
+ return cl_search(name, cl_OPT, 0);
+}
+
+/****************************************************************
+* cl_switch
+* Inputs:
+* char *name: switch name
+* Outputs:
+* boolean: TRUE if switch found
+****************************************************************/
+
+boolean cl_switch(name)
+char *name;
+{
+ return (boolean)(cl_search(name, cl_SW, 0) != NULL);
+}
+
+/* cl_syntax -- install a string specifying options and switches */
+/**/
+boolean cl_syntax(char *s)
+{
+ if (n_syntax < syntax_max) {
+ syntax[n_syntax++] = s;
+ return TRUE;
+ } else {
+ gprintf(ERROR, "cl_syntax: out of room\n");
+ return FALSE;
+ }
+}
+
+/****************************************************************
+* find_string
+* Inputs:
+* char *s: string to find, terminated by any non-alphanumeric
+* boolean *abbr: set TRUE if s is an abbreviation, otherwise false
+* Effect:
+* Looks for s in syntax strings
+* Returns:
+* 0 = FALSE = not found, 1 = cl_OPT = option, 2 = cl_SW = switch
+*****************************************************************/
+
+private int find_string(s, abbr)
+ char *s;
+ boolean *abbr;
+{
+ int found_it = FALSE;
+ int i;
+ *abbr = FALSE;
+ for (i = 0; i < n_syntax; i++) { /* loop through strings */
+ register char *syntax_ptr = syntax[i];
+ while (*syntax_ptr != EOS) {
+ register char *s_ptr = s;
+ while (*syntax_ptr != EOS &&
+ !(isalnum(*syntax_ptr))) syntax_ptr++;
+ while (*s_ptr != EOS && (*s_ptr++ == *syntax_ptr))
+ syntax_ptr++; /* only increment if there's a match */
+ if (!(isalnum(*s_ptr)) && *syntax_ptr == '<') {
+ syntax_ptr++; /* advance to the type field */
+ if (*syntax_ptr == 's') return cl_SW;
+ if (*syntax_ptr != 'o')
+ gprintf(ERROR,
+ "(internal error) bad cl_syntax string: %s\n",
+ syntax[i]);
+ return cl_OPT;
+ }
+ /* no match, so go to next */
+ while (*syntax_ptr != ';' && *syntax_ptr != EOS) syntax_ptr++;
+ if (*syntax_ptr == ';') syntax_ptr++;
+ }
+ }
+
+ /* no match, maybe there is a single character match */
+ if (s[0] == EOS || s[1] != EOS) return FALSE;
+
+ for (i = 0; i < n_syntax; i++) { /* loop through strings */
+ char *syntax_ptr = syntax[i];
+ while (*syntax_ptr != EOS) {
+ while (*syntax_ptr != EOS &&
+ !(isalnum(*syntax_ptr))) syntax_ptr++;
+ if (s[0] == *syntax_ptr) {
+ if (found_it) return FALSE; /* ambiguous */
+ /* else, find the type */
+ while (*syntax_ptr != '<' && *syntax_ptr != EOS)
+ syntax_ptr++;
+ syntax_ptr++;
+ if (*syntax_ptr == 's') found_it = cl_SW;
+ else if (*syntax_ptr == 'o') found_it = cl_OPT;
+ else return FALSE; /* error in string syntax */
+ }
+ /* no match, so go to next */
+ while (*syntax_ptr != ';' && *syntax_ptr != EOS) syntax_ptr++;
+ if (*syntax_ptr == ';') syntax_ptr++;
+ }
+ }
+ if (found_it) *abbr = TRUE;
+ return found_it;
+}
+
+
+/* get_arg -- get an argument from a file */
+/**/
+boolean get_arg(file, arg)
+ FILE *file;
+ char *arg;
+{
+ int c;
+ while ((c = getc(file)) != EOF && isspace(c)) ;
+ if (c == EOF) return FALSE;
+ ungetc(c, file);
+ while ((c = getc(file)) != EOF && !isspace(c)) {
+ *arg++ = c;
+ }
+ *arg = 0;
+ return TRUE;
+}
+
+
+/* indirect_command -- get argv, argc from a file */
+/**/
+private void indirect_command(filename, oldarg0)
+ char *filename;
+ char *oldarg0;
+{
+ FILE *argfile = fopen(filename, "r");
+ if (!argfile) {
+ argv = (char **) malloc(sizeof(char *));
+ argv[0] = oldarg0;
+ argc = 1;
+ } else {
+ int i = 1;
+ char arg[100];
+ while (get_arg(argfile, arg)) i++;
+ fclose(argfile);
+ argfile = fopen(filename, "r");
+ argv = (char **) malloc(sizeof(char *) * i);
+ argv[0] = oldarg0;
+ argc = i;
+ i = 1;
+ while (get_arg(argfile, arg)) {
+ argv[i] = (char *) malloc(strlen(arg) + 1);
+ strcpy(argv[i], arg);
+ i++;
+ }
+ fclose(argfile);
+ }
+}
+
+/****************************************************************
+* ready_check
+* Effect:
+* Halt program if cl_rdy is not true.
+*****************************************************************/
+private void ready_check()
+{
+ if (!cl_rdy) {
+ gprintf(ERROR,
+ "Internal error: cl_init was not called, see cmdline.c\n");
+ EXIT(1);
+ }
+}
diff --git a/cmt/cmdline.h b/cmt/cmdline.h
new file mode 100644
index 0000000..06ff4c3
--- /dev/null
+++ b/cmt/cmdline.h
@@ -0,0 +1,8 @@
+/* Copyright 1989 Carnegie Mellon University */
+
+char *cl_arg(int n);
+boolean cl_init(char *av[], int ac);
+long cl_int_option(char *name, long deflt);
+char *cl_option(char *name);
+boolean cl_switch(char *name);
+boolean cl_syntax(char *name);
diff --git a/cmt/cmtcmd.c b/cmt/cmtcmd.c
new file mode 100644
index 0000000..2881467
--- /dev/null
+++ b/cmt/cmtcmd.c
@@ -0,0 +1,53 @@
+/* cmtcmd.c -- routines for the moxc side of the command interface */
+
+#include "switches.h"
+#include "stdio.h"
+#ifdef AMIGA
+#include "exec/types.h"
+#include "exec/exec.h"
+#endif
+#include "cmtcmd.h"
+#include "cext.h"
+#include "userio.h"
+#include "string.h"
+
+#define HASHELEM(p) ((p).symbol_name)
+#define HASHVAL 50
+#define HASHENTRIES 50
+#define HASHENTER lookup
+#define HASHNOCOPY
+
+#include "hashrout.h"
+
+void defvar(name, addr)
+ char *name;
+ int *addr;
+{
+ int i = lookup(name);
+ HASHENTRY(i).symb_type = var_symb_type;
+ HASHENTRY(i).ptr.intptr = addr;
+}
+
+
+void defun(name, addr)
+ char *name;
+ int (*addr)();
+{
+ int i = lookup(name);
+ HASHENTRY(i).symb_type = fn_symb_type;
+ HASHENTRY(i).ptr.routine = addr;
+}
+
+
+void defvec(name, addr, size)
+ char *name;
+ int *addr;
+ int size;
+{
+ int i = lookup(name);
+ HASHENTRY(i).symb_type = vec_symb_type;
+ HASHENTRY(i).size = size;
+ HASHENTRY(i).ptr.intptr = addr;
+}
+
+
diff --git a/cmt/cmtcmd.h b/cmt/cmtcmd.h
new file mode 100644
index 0000000..9ad1cac
--- /dev/null
+++ b/cmt/cmtcmd.h
@@ -0,0 +1,35 @@
+/* cmtcmd.h -- header for remote action and variable setting interface */
+
+#define var_symb_type 333
+#define fn_symb_type 555
+#define vec_symb_type 777
+
+#ifdef AMIGA
+struct cmd_msg {
+ struct Message msg;
+ long symb_type; /* one of var_, fn_, or vec_symb_type */
+ char *symbol_name;
+ long the_args[8]; /* args for function call. If var_symb_type,
+ the_args[0] is the new value. If vec_symb_type,
+ the_args[0] is index, the_args[1] is new value. */
+};
+#endif
+
+typedef struct symb_descr {
+ char *symbol_name;
+ int symb_type;
+ int size; /* for array bounds checking */
+ union {
+ int *intptr;
+ int (*routine)();
+ } ptr;
+} symb_descr_node;
+
+int lookup(char *s);
+void defvar(char *name, int *addr);
+void defvec(char *name, int *addr, int size);
+typedef int (*defun_type)();
+void defun(char *name, defun_type addr);
+
+#define HASHTYPE symb_descr_node
+#include "hash.h"
diff --git a/cmt/cmtio.c b/cmt/cmtio.c
new file mode 100644
index 0000000..e6f1ebc
--- /dev/null
+++ b/cmt/cmtio.c
@@ -0,0 +1,161 @@
+/*
+ **********************************************************************
+ * File io.c
+ **********************************************************************
+ *
+ * Non blocking input routine
+ * Works by puttng the terminal in CBREAK mode and using the FIONREAD
+ * ioctl call to determine the number of characters in the input queue
+ */
+
+#include "stdio.h"
+#include "io.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <sgtty.h>
+#include "cext.h"
+
+int IOinputfd; /* input file descriptor (usually 0) */
+
+int IOnochar; /* Value to be returned by IOgetchar()
+ where there is no input to be had */
+
+static struct sgttyb IOoldmodes, IOcurrentmodes;
+ /* Initial and current tty modes */
+
+/*
+ * IOsetup(inputfd)
+ * Args:
+ * inputfd - input file descriptor (should be zero for standard input)
+ * Returns:
+ * 0 - if all goes well
+ * -1 - if an ioctl fails (also calls perror)
+ * Side Effects:
+ * Puts the terminal in CBREAK mode - before process termination
+ * IOcleanup() should be called to restore old terminal modes
+ * Catch's interrupts (if they are not already being caught) and
+ * calls IOcleanup before exiting
+ *
+ */
+
+#define ERROR(s) return (perror(s), -1)
+
+IOsetup(inputfd)
+{
+ static IOdiegracefully();
+ int (*interrupt_handler)();
+
+ IOinputfd = inputfd;
+ IOnochar = NOCHAR;
+ if(ioctl(IOinputfd, TIOCGETP, &IOoldmodes) < 0)
+ ERROR("IOsetup");
+
+ IOcurrentmodes = IOoldmodes;
+ IOcurrentmodes.sg_flags |= CBREAK;
+ IOcurrentmodes.sg_flags &= ~ECHO;
+ if(ioctl(IOinputfd, TIOCSETP, &IOcurrentmodes))
+ ERROR("IOsetup-2");
+
+ if( (interrupt_handler = signal(SIGINT, IOdiegracefully)) != 0)
+ signal(SIGINT, interrupt_handler);
+ return 0;
+}
+
+static
+IOdiegracefully()
+{
+ write(2, "\nBye\n", 5);
+ IOcleanup();
+ EXIT(2);
+}
+
+/*
+ * IOcleanup()
+ * Returns:
+ * 0 - if all goes well
+ * -1 - if an ioctl fails (also calls perror)
+ * Side Effects:
+ * Restores initial terminal modes
+ */
+
+IOcleanup()
+{
+ if(ioctl(IOinputfd, TIOCSETP, &IOoldmodes) < 0)
+ ERROR("IOclean");
+ return 0;
+}
+
+
+/*
+ * IOgetchar()
+ * Returns:
+ * A character off the input queue if there is one,
+ * IOnochar if there is no character waiting to be read,
+ * -1 if an ioctl fails (shouldn't happen if IOsetup went OK)
+ */
+
+#ifndef UNIX_MACH
+
+IOgetchar()
+{
+ int n;
+ char c;
+
+ if(ioctl(IOinputfd, FIONREAD, &n) < 0)
+ ERROR("IOgetchar");
+ if(n <= 0)
+ return IOnochar;
+ switch(read(IOinputfd, &c, 1)) {
+ case 1:
+ return c;
+ case 0:
+ return EOF;
+ default:
+ ERROR("IOgetchar-read");
+ }
+}
+
+#ifdef IOGETCHAR2
+IOgetchar2()
+{
+ int nfds, readfds = 1 << IOinputfd;
+ char c;
+ static struct timeval zero;
+
+ if(IOinputfd < 0 || IOinputfd >= 32) {
+ printf("IOgetchar2: bad IOinputfd (%d)%s\n", IOinputfd,
+ IOinputfd == -1 ? "Did you call IOsetup(fd)?" : "");
+ }
+ nfds = select(32, &readfds, 0, 0, &zero);
+ if(nfds > 0) {
+ switch(read(IOinputfd, &c, 1)) {
+ case 0:
+ return EOF;
+ case 1:
+ return c;
+ default:
+ printf("IOgetchar2: read failed!\n");
+ return NOCHAR;
+ }
+ }
+ else if(nfds < 0)
+ printf("IOgetchar2: select failed!\n");
+ return NOCHAR;
+}
+#endif
+
+/*
+ * IOwaitchar()
+ * Returns:
+ * A character off the input queue. Waits if necessary.
+ */
+
+int IOwaitchar()
+{
+ char c;
+ if (read(IOinputfd, &c, 1) == 1) return c;
+ else return EOF;
+}
+
+#endif /* not UNIX_MACH */
diff --git a/cmt/cmtio.h b/cmt/cmtio.h
new file mode 100644
index 0000000..3e50ad9
--- /dev/null
+++ b/cmt/cmtio.h
@@ -0,0 +1,9 @@
+#define NOCHAR -2
+
+int IOinputfd;
+int IOnochar;
+
+int IOsetup(int inputfd);
+int IOcleanup(void);
+int IOgetchar(void);
+int IOwaitchar(void);
diff --git a/cmt/hash.h b/cmt/hash.h
new file mode 100644
index 0000000..0257cfe
--- /dev/null
+++ b/cmt/hash.h
@@ -0,0 +1,23 @@
+/*
+ * This file should be included in all files that use
+ * the HASHENTRY macro; see hashrout.h for details
+ */
+/* Copyright 1989 Carnegie Mellon University */
+
+#ifndef HASHTYPE
+# include "-- HASHTYPE undefined"
+#endif
+
+/*
+ * An element really is a HASHTYPE along with a h_next entry,
+ * which chains together entries of the same hash value.
+ */
+
+typedef struct hashelem {
+ HASHTYPE h_elem;
+ struct hashelem *h_next;
+} hashelem;
+
+extern hashelem hashfirstchunk[];
+
+#define HASHENTRY(i) (hashfirstchunk[i].h_elem)
diff --git a/cmt/hashrout.h b/cmt/hashrout.h
new file mode 100644
index 0000000..a566b9f
--- /dev/null
+++ b/cmt/hashrout.h
@@ -0,0 +1,220 @@
+/* hashrout.h -- Rubine's hash table package */
+/* Copyright 1989 Carnegie Mellon University */
+
+/* ChangeLog:
+ * 2-jan-85 rbd Added option to count entries: define COUNTER and
+ * HASHENTER routine will increment it on new entries
+ * 28-Apr-03 DM Explicit declaration of int type for HASHENTER()
+ */
+
+/*
+ * Generic symbol table functions
+ *
+ * After writing this code over for a bunch of interpreters
+ * I think I know how to do it once and for all, generally
+ * enough for most application.
+ * There are enough settable parameters to suit anyone, and
+ * the defaults usually do something sane.
+ *
+ * The basic idea is that you have a bunch of symbol table entries
+ * that need to be entered or looked up given a character string.
+ * Symbol table entries are usually structures, and one element
+ * of the structure must be the character string of the key.
+ * The structure should be given a type name, and these routines
+ * are informed of the type name via
+ * #define HASHTYPE type-name-of-symbol-table-entry
+ * You must inform the routines how to access the character string
+ * given the symbol table entry; for example
+ * #define HASHELEM(p) ((p).name)
+ * There are two size parameters - the number of different hash
+ * values and the number of symbol table entries to allocate at one
+ * time. Both default to 256.
+ * #define HASHVAL 128
+ * #define HASHENTRIES 512
+ * The name of the function that performs both entry and lookup is
+ * enter(name) - it takes one argument, a character string. The name
+ * of the function may be changed via
+ * #define HASHENTER new-name-for-enter-routine
+ * Note that if there is no entry in the table for name, name is entered
+ * automatically. The returned structure will have only the name
+ * field filled in; the rest of the fields are guarenteed to be zero
+ * so an application can check if this is the first time name was entered.
+ * If HASHPOINT is defined, the enter routine returns a pointer to a hash
+ * table entry. In the default case, i.e. HASHPOINT undefined, the
+ * enter routine returns an integer between zero and HASHENTRIES.
+ * The macro HASHENTRY(i) will, given that integer, return the
+ * table element (not a pointer to it), so for example,
+ * HASHENTRY(enter(x)).name == x.
+ * Any file that wishes to use HASHENTRY should have at the top
+ * #define HASHTYPE type-name-of-symbol-table-entry
+ * #include "hash.h"
+ * Note in this case at most HASHENTRIES entries will be allocated
+ * before hash table overflow. If HASHPOINT is defined, allocations
+ * will take place until memory runs out.
+ * By default, hash strings are copied into space obtained from malloc
+ * before being placed in new entry. This copying can be supressed
+ * by defining HASHNOCOPY.
+ * The following is an example of using the hash table stuff.
+
+typedef struct {
+ char *n_name;
+ int n_value;
+} symbolentry;
+
+#define HASHTYPE symbolentry
+#define HASHELEM(p) ((p).n_name)
+#define HASHENTRIES 1024
+
+#include "hashrout.h"
+
+*/
+
+
+/*
+ * OK, now the meat.
+ */
+
+#ifndef HASHTYPE
+# include "-- HASHTYPE undefined"
+#endif
+
+#ifndef HASHELEM
+# include "-- HASHELEM undefined"
+#endif
+
+#ifndef HASHVAL
+# define HASHVAL 256
+#endif
+
+#ifndef HASHENTRIES
+# define HASHENTRIES 256
+#endif
+
+#ifndef HASHENTER
+# define HASHENTER enter
+#endif
+
+/*
+ * HASHNOCOPY, HASHPOINT are undefined by default
+ */
+
+/*
+ * get definition of hash elem structure
+ */
+
+#ifndef HASHENTRY
+# include "hash.h"
+#endif
+
+/*
+ * Table of pointers, indexed by hash values
+ */
+
+hashelem *hashtab[HASHVAL];
+
+/*
+ * First chunk of elements, pointer to the start,
+ * and index for allocation
+ */
+
+hashelem hashfirstchunk[HASHENTRIES];
+hashelem *hashchunk = hashfirstchunk;
+int hashindex = 0;
+
+/*
+ * stdio.h if we don't have it yet
+ */
+
+#ifndef _NFILE
+# include <stdio.h>
+#endif
+
+/*
+ * Declare counter if necessary
+ */
+
+#ifdef COUNTER
+extern COUNTER;
+#endif
+
+/*
+ * The enter routine
+ */
+
+#ifdef HASHPOINT
+HASHTYPE *
+#else
+int
+#endif
+HASHENTER (s)
+char *s;
+{
+ register int i, hash;
+ register hashelem *elem;
+
+ /*
+ * Compute s's hash value
+ * I really should look up some good hash functions, but
+ * I haven't bothered.
+ */
+
+for(i = 0, hash = 0; s[i] != '\0' && i < 15; i++)
+ hash += (i + 1) * s[i];
+hash %= HASHVAL;
+
+/*
+ * search for s in the table
+ */
+
+for(elem = hashtab[hash]; elem != NULL; elem = elem->h_next)
+ if(strcmp(s, HASHELEM((elem->h_elem))) == 0) { /* found it */
+#ifdef HASHPOINT
+ return(&elem->h_elem);
+#else
+ return(elem - hashfirstchunk);
+#endif
+ }
+
+if(hashindex >= HASHENTRIES) {
+#ifdef HASHPOINT
+ char *calloc();
+
+ hashindex = 0;
+ hashchunk = (hashelem *) calloc(HASHENTRIES, sizeof(hashelem));
+ if(hashchunk == NULL) {
+ gprintf(FATAL, "No mem for hash symbol table\n");
+ EXIT(1);
+#ifdef COUNTER
+ COUNTER++; /* optional symbol counter */
+#endif
+ }
+#else
+ gprintf(FATAL, "No hash table space, increase HASHENTRIES\n");
+ EXIT(1);
+#endif
+}
+
+/*
+ * Splice a new entry into the list and fill in the string field
+ */
+
+elem = &hashchunk[hashindex++];
+elem->h_next = hashtab[hash];
+hashtab[hash] = elem;
+
+#ifdef HASHNOCOPY
+HASHELEM((elem->h_elem)) = s;
+#else
+{
+ char *strcpy();
+ HASHELEM((elem->h_elem)) = memget((strlen(s) + 1));
+ strcpy(HASHELEM((elem->h_elem)), s);
+}
+#endif
+
+#ifdef HASHPOINT
+return(&elem->h_elem);
+#else
+return(elem - hashfirstchunk);
+#endif
+}
diff --git a/cmt/mem.c b/cmt/mem.c
new file mode 100644
index 0000000..7256188
--- /dev/null
+++ b/cmt/mem.c
@@ -0,0 +1,91 @@
+/* mem.c -- fast memory allocation/deallocation module */
+
+/* Allocate large chunks of memory using malloc. From the chunks,
+ allocate memory as needed on long-word boundaries. Memory is
+ freed by linking memory onto a freelist. An array of freelists,
+ one for each size, is maintained and checked before going to the
+ chunck for more memory. The freelist array only holds lists of
+ nodes up to a certain size. After that, malloc is used directly.
+ */
+/* CHANGE LOG
+ ----------------------------------------------------------------------
+ 28-Apr-03 | DM : fix #includes for portability
+ ----------------------------------------------------------------------
+ */
+
+#include "switches.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cext.h"
+#include "userio.h"
+
+/* how many bytes in the largest node managed in mem_free_list array */
+#define MAX_SIZE_FOR_FREELIST 256
+
+long *mem_free_list[MAX_SIZE_FOR_FREELIST/4];
+
+#define MEM_CHUNK_SIZE 4096
+char *mem_chunk;
+long mem_chunk_remaining = 0;
+
+void meminit()
+{
+ int i;
+ for (i = 0; i < MAX_SIZE_FOR_FREELIST/4; i++) {
+ mem_free_list[i] = NULL;
+ }
+}
+
+
+void *memget(register size_t size)
+{
+ if (size > MAX_SIZE_FOR_FREELIST) {
+/* gprintf(TRANS, "memget calling MALLOC\n"); */
+ return MALLOC(size);
+ } else {
+ long **p = mem_free_list + ((size - 1) >> 2);
+ if (*p) {
+ register long *result = *p;
+ *p = (long *) *result;
+/* gprintf(TRANS, "memget->%lx\n", result); */
+ return (char *) result;
+ } else if ((size_t) mem_chunk_remaining >= size) {
+ register char *result = mem_chunk;
+ size = (size + 3) & ~3; /* round up to multiple of 4 */
+ mem_chunk += size;
+ mem_chunk_remaining -= size;
+/* gprintf(TRANS, "memget->%lx\n", result); */
+ return result;
+ /* note that we throw away remaining chunk when there isn't enough */
+ } else if ((mem_chunk = (char *) MALLOC(MEM_CHUNK_SIZE))) {
+ register char *result = mem_chunk;
+/* gprintf(TRANS, "mem_chunk at %lx\n", mem_chunk); */
+ size = (size + 3) & ~3; /* round up to multiple of 4 */
+ mem_chunk += size;
+ mem_chunk_remaining = MEM_CHUNK_SIZE - size;
+/* gprintf(TRANS, "memget->%lx\n", result); */
+ return result;
+ } else {
+ return NULL;
+ }
+ }
+}
+
+
+void memfree(register void *ptr, register size_t size)
+{
+ register long **p = (long **) ptr;
+ if (size > MAX_SIZE_FOR_FREELIST) {
+ FREE(ptr);
+ } else {
+ register long **head_ptr = mem_free_list + ((size - 1) >> 2);
+ *p = *head_ptr;
+ *head_ptr = (long *) p;
+ }
+}
+
+
+
+
diff --git a/cmt/mem.h b/cmt/mem.h
new file mode 100644
index 0000000..ea33ba9
--- /dev/null
+++ b/cmt/mem.h
@@ -0,0 +1,12 @@
+#ifdef AMIGA
+#include "stddef.h"
+#endif
+
+extern long *mem_free_list[];
+
+void *memget(register size_t size);
+void memfree(register void *ptr, register size_t size);
+
+void meminit(void);
+
+#define MEM
diff --git a/cmt/mfmidi.h b/cmt/mfmidi.h
new file mode 100644
index 0000000..9621c67
--- /dev/null
+++ b/cmt/mfmidi.h
@@ -0,0 +1,19 @@
+#define NOTEOFF 0x80
+#define NOTEON 0x90
+#define PRESSURE 0xa0
+#define CONTROLLER 0xb0
+#define PITCHBEND 0xe0
+#define PROGRAM 0xc0
+#define CHANPRESSURE 0xd0
+
+/* These are the strings used in keynote to identify Standard MIDI File */
+/* meta text messages. */
+
+#define METATEXT "Text Event"
+#define METACOPYRIGHT "Copyright Notice"
+#define METASEQUENCE "Sequence/Track Name"
+#define METAINSTRUMENT "Instrument Name"
+#define METALYRIC "Lyric"
+#define METAMARKER "Marker"
+#define METACUE "Cue Point"
+#define METAUNRECOGNIZED "Unrecognized"
diff --git a/cmt/midibuff.h b/cmt/midibuff.h
new file mode 100644
index 0000000..f8dc1c1
--- /dev/null
+++ b/cmt/midibuff.h
@@ -0,0 +1,23 @@
+/* midibuff.h -- defines the size of the midi input buffer */
+
+/* midi input buffer */
+/* WARNING: BUFF_SIZE must be a power of 2 so we can use masking to wrap */
+#define EVENT_COUNT 128
+#define EVENT_SIZE 4
+#define BUFF_SIZE (EVENT_COUNT * EVENT_SIZE)
+#define BUFF_MASK (BUFF_SIZE - 1)
+
+#ifdef WINDOWS
+#define huge
+#endif
+
+extern byte huge *xbuff; /* application-supplied sysex buffer */
+extern long xbufmask; /* mask for circular buffer */
+extern long xbufhead; /* buffer head and tail offsets */
+extern long xbuftail;
+extern int midi_error;
+/* midi input buffer */
+/* data buffer, declared long to get 32-bit alignment: */
+extern long buff[BUFF_SIZE/4];
+extern int buffhead; /* buffer head and tail pointers */
+extern int bufftail;
diff --git a/cmt/midicode.h b/cmt/midicode.h
new file mode 100644
index 0000000..26361d3
--- /dev/null
+++ b/cmt/midicode.h
@@ -0,0 +1,62 @@
+/************************************************************************
+*
+* Midi codes
+* Copyright 1989 Carnegie Mellon University
+*
+*************************************************************************
+* Change Log
+* Date | Change
+*-----------+------------------------------------------------------------
+* 11-Mar-94 | PLu : Port to IRI
+************************************************************************/
+
+#define MIDI_DATA(d) (0x7f & (d))
+#define MIDI_CHANNEL(c) (0x0f & ((c) - 1))
+#define MIDI_PORT(c) (((c) - 1) >> 4)
+#define MIDI_PROGRAM(p) MIDI_DATA((p) - 1)
+
+#define MIDI_STATUS_BIT 0x80
+#define MIDI_COMMON 0x70
+#define MIDI_CODE_MASK 0xf0
+#define MIDI_CHN_MASK 0x0f
+#define MIDI_REALTIME 0xf8
+#define MIDI_CHAN_MODE 0xfa
+
+#define MIDI_OFF_NOTE 0x80
+#define MIDI_ON_NOTE 0x90
+#define MIDI_POLY_TOUCH 0xa0
+#define MIDI_CTRL 0xb0
+#define MIDI_CH_PROGRAM 0xc0
+#define MIDI_TOUCH 0xd0
+#define MIDI_BEND 0xe0
+
+#ifdef UNIX_IRIX_MIDIFNS
+#define CMT_MIDI_SYSEX 0xf0
+#define CMT_MIDI_EOX 0xf7
+#else
+#define MIDI_SYSEX 0xf0
+#define MIDI_EOX 0xf7
+#endif
+#define MIDI_Q_FRAME 0xf1
+#define MIDI_SONG_POINTER 0xf2
+#define MIDI_SONG_SELECT 0xf3
+#define MIDI_F4 0xf4
+#define MIDI_F5 0xf5
+#define MIDI_TUNE_REQ 0xf6
+#define MIDI_TIME_CLOCK 0xf8
+#define MIDI_F9 0xf9
+#define MIDI_START 0xfa
+#define MIDI_CONTINUE 0xfb
+#define MIDI_STOP 0xfc
+#define MIDI_FD 0xfd
+#define MIDI_ACTIVE_SENSING 0xfe
+#define MIDI_SYS_RESET 0xff
+
+#define MIDI_LOCAL 0x7a
+#define MIDI_LOCAL_OFF 0x00
+#define MIDI_LOCAL_ON 0x7f
+#define MIDI_ALL_OFF 0x7b
+#define MIDI_OMNI_OFF 0x7c
+#define MIDI_OMNI_ON 0x7d
+#define MIDI_MONO_ON 0x7e
+#define MIDI_POLY_ON 0x7f
diff --git a/cmt/midierr.h b/cmt/midierr.h
new file mode 100644
index 0000000..d00ce4c
--- /dev/null
+++ b/cmt/midierr.h
@@ -0,0 +1,17 @@
+/* midierr.h -- error codes */
+
+#define NESTERR 1 /* nested interrupts */
+#define BUFFOVFL 2 /* input buffer overflow */
+#define CMDERR 3 /* unknown command from mpu-401 */
+#define TIMEOUTERR 4 /* interface timeout */
+#define MSGERR 5 /* invalid message */
+#define SYSEXOVFL 6 /* sysex buffer overflow */
+#define MEMERR 7 /* internal out of memory */
+#define RECVERR 8 /* receive error or device buffer overflow */
+#define MIDIMGRERR 9 /* error reported by midi manager (Macintosh) */
+
+#define SYSEX_ERR ((1<<RECVERR) | (1<<MEMERR) | (1<<SYSEXOVFL))
+extern short midi_error_flags;
+
+void midi_show_errors(void);
+
diff --git a/cmt/midifile.c b/cmt/midifile.c
new file mode 100644
index 0000000..0ec1137
--- /dev/null
+++ b/cmt/midifile.c
@@ -0,0 +1,618 @@
+/*
+ * Read a Standard MIDI File. Externally-assigned function pointers are
+ * called upon recognizing things in the file. See midifile(3).
+ */
+
+/*****************************************************************************
+* Change Log
+* Date | who : Change
+*-----------+-----------------------------------------------------------------
+* 2-Mar-92 | GWL : created changelog; MIDIFILE_ERROR to satisfy compiler
+* 28-Apr-03 | DM : changed #includes and give return types for portability
+*****************************************************************************/
+
+#include "switches.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "mfmidi.h"
+#include "midifile.h"
+#include "cext.h"
+#include "userio.h"
+#include "string.h"
+
+#define MIDIFILE_ERROR -1
+
+#ifdef PROTOTYPES
+#define NOARGS void
+#else
+#define NOARGS
+#endif
+
+/* public stuff */
+extern int abort_flag;
+
+/* Functions to be called while processing the MIDI file. */
+void (*Mf_starttrack)(NOARGS) = 0;
+void (*Mf_endtrack)(NOARGS) = 0;
+int (*Mf_getc)(NOARGS) = 0;
+void (*Mf_eot)(NOARGS) = 0;
+#ifdef PROTOTYPES
+void (*Mf_error)(char *) = 0;
+void (*Mf_header)(int,int,int) = 0;
+void (*Mf_on)(int,int,int) = 0;
+void (*Mf_off)(int,int,int) = 0;
+void (*Mf_pressure)(int,int,int) = 0;
+void (*Mf_controller)(int,int,int) = 0;
+void (*Mf_pitchbend)(int,int,int) = 0;
+void (*Mf_program)(int,int) = 0;
+void (*Mf_chanpressure)(int,int) = 0;
+void (*Mf_sysex)(int,char*) = 0;
+void (*Mf_arbitrary)(int,char*) = 0;
+void (*Mf_metamisc)(int,int,char*) = 0;
+void (*Mf_seqnum)(int) = 0;
+void (*Mf_smpte)(int,int,int,int,int) = 0;
+void (*Mf_timesig)(int,int,int,int) = 0;
+void (*Mf_tempo)(int) = 0;
+void (*Mf_keysig)(int,int) = 0;
+void (*Mf_sqspecific)(int,char*) = 0;
+void (*Mf_text)(int,int,char*) = 0;
+#else
+void (*Mf_error)() = 0;
+void (*Mf_header)() = 0;
+void (*Mf_on)() = 0;
+void (*Mf_off)() = 0;
+void (*Mf_pressure)() = 0;
+void (*Mf_controller)() = 0;
+void (*Mf_pitchbend)() = 0;
+void (*Mf_program)() = 0;
+void (*Mf_chanpressure)() = 0;
+void (*Mf_sysex)() = 0;
+void (*Mf_arbitrary)() = 0;
+void (*Mf_metamisc)() = 0;
+void (*Mf_seqnum)() = 0;
+void (*Mf_smpte)() = 0;
+void (*Mf_tempo)() = 0;
+void (*Mf_timesig)() = 0;
+void (*Mf_keysig)() = 0;
+void (*Mf_sqspecific)() = 0;
+void (*Mf_text)() = 0;
+#endif
+
+int Mf_nomerge = 0; /* 1 => continue'ed system exclusives are */
+ /* not collapsed. */
+long Mf_currtime = 0L; /* current time in delta-time units */
+int Mf_skipinit = 0; /* 1 if initial garbage should be skipped */
+
+/* private stuff */
+
+static long Mf_toberead = 0L;
+
+static long readvarinum(NOARGS);
+static long read32bit(NOARGS);
+static int read16bit(NOARGS);
+static void msgenlarge(NOARGS);
+static char *msg(NOARGS);
+static int readheader(NOARGS);
+static void readtrack(NOARGS);
+static void sysex(NOARGS), msginit(NOARGS);
+static int egetc(NOARGS);
+static int msgleng(NOARGS);
+
+#ifdef PROTOTYPES
+static int readmt(char*,int);
+static long to32bit(int,int,int,int);
+static int to16bit(int,int);
+static void mferror(char *);
+static void badbyte(int);
+static void metaevent(int);
+static void msgadd(int);
+static void chanmessage(int,int,int);
+#else
+static long to32bit();
+static int to16bit();
+static void mferror();
+static void badbyte();
+static void metaevent();
+static void msgadd();
+static void chanmessage();
+#endif
+
+static int midifile_error;
+
+void
+midifile() /* The only non-static function in this file. */
+{
+ int ntrks;
+ midifile_error = 0;
+
+ if ( Mf_getc == 0 ) {
+ mferror("mf.h() called without setting Mf_getc");
+ return;
+ }
+ ntrks = readheader();
+ if (midifile_error) return;
+ if ( ntrks <= 0 ) {
+ mferror("No tracks!");
+ /* no need to return since midifile_error is set */
+ }
+ while ( ntrks-- > 0 && !midifile_error && !check_aborted())
+ readtrack();
+ if (check_aborted()) {
+ mferror("Midifile read aborted\n\
+\tthe rest of your file will be ignored.\n");
+ if (abort_flag == BREAK_LEVEL) abort_flag = 0;
+ }
+}
+
+static int
+readmt(s,skip) /* read through the "MThd" or "MTrk" header string */
+char *s;
+int skip; /* if 1, we attempt to skip initial garbage. */
+{
+ int nread = 0;
+ char b[4];
+ char buff[32];
+ int c;
+ char *errmsg = "expecting ";
+
+ retry:
+ while ( nread<4 ) {
+ c = (*Mf_getc)();
+ if ( c == EOF ) {
+ errmsg = "EOF while expecting ";
+ goto err;
+ }
+ b[nread++] = c;
+ }
+ /* See if we found the 4 characters we're looking for */
+ if ( s[0]==b[0] && s[1]==b[1] && s[2]==b[2] && s[3]==b[3] )
+ return(0);
+ if ( skip ) {
+ /* If we are supposed to skip initial garbage, */
+ /* try again with the next character. */
+ b[0]=b[1];
+ b[1]=b[2];
+ b[2]=b[3];
+ nread = 3;
+ goto retry;
+ }
+ err:
+ (void) strcpy(buff,errmsg);
+ (void) strcat(buff,s);
+ mferror(buff);
+ return(0);
+}
+
+static int
+egetc() /* read a single character and abort on EOF */
+{
+ int c = (*Mf_getc)();
+
+ if ( c == EOF ) {
+ mferror("premature EOF");
+ return EOF;
+ }
+ Mf_toberead--;
+ return(c);
+}
+
+static int
+readheader() /* read a header chunk */
+{
+ int format, ntrks, division;
+
+ if ( readmt("MThd",Mf_skipinit) == EOF )
+ return(0);
+
+ Mf_toberead = read32bit();
+ if (midifile_error) return MIDIFILE_ERROR;
+ format = read16bit();
+ if (midifile_error) return MIDIFILE_ERROR;
+ ntrks = read16bit();
+ if (midifile_error) return MIDIFILE_ERROR;
+ division = read16bit();
+ if (midifile_error) return MIDIFILE_ERROR;
+
+ if ( Mf_header )
+ (*Mf_header)(format,ntrks,division);
+
+ /* flush any extra stuff, in case the length of header is not 6 */
+ while ( Mf_toberead > 0 && !midifile_error)
+ (void) egetc();
+ return(ntrks);
+}
+
+static void
+readtrack() /* read a track chunk */
+{
+ /* This array is indexed by the high half of a status byte. It's */
+ /* value is either the number of bytes needed (1 or 2) for a channel */
+ /* message, or 0 (meaning it's not a channel message). */
+ static int chantype[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 through 0x70 */
+ 2, 2, 2, 2, 1, 1, 2, 0 /* 0x80 through 0xf0 */
+ };
+ long lookfor, lng;
+ int c, c1, type;
+ int sysexcontinue = 0; /* 1 if last message was an unfinished sysex */
+ int running = 0; /* 1 when running status used */
+ int status = 0; /* (possibly running) status byte */
+ int needed;
+
+ if ( readmt("MTrk",0) == EOF )
+ return;
+
+ Mf_toberead = read32bit();
+
+ if (midifile_error) return;
+
+ Mf_currtime = 0L;
+
+ if ( Mf_starttrack ){
+
+ (*Mf_starttrack)();
+ }
+ while ( Mf_toberead > 0 ) {
+
+ Mf_currtime += readvarinum(); /* delta time */
+ if (midifile_error) return;
+
+ c = egetc(); if (midifile_error) return;
+
+ if ( sysexcontinue && c != 0xf7 ) {
+ mferror("didn't find expected continuation of a sysex");
+ return;
+ }
+ if ( (c & 0x80) == 0 ) { /* running status? */
+ if ( status == 0 ) {
+ mferror("unexpected running status");
+ return;
+ }
+ running = 1;
+ }
+ else {
+ status = c;
+ running = 0;
+ }
+
+ needed = chantype[ (status>>4) & 0xf ];
+
+ if ( needed ) { /* ie. is it a channel message? */
+
+ if ( running )
+ c1 = c;
+ else {
+ c1 = egetc();
+ if (midifile_error) return;
+ }
+ chanmessage( status, c1, (needed>1) ? egetc() : 0 );
+ if (midifile_error) return;
+ continue;;
+ }
+
+ switch ( c ) {
+
+ case 0xff: /* meta event */
+
+ type = egetc();
+ if (midifile_error) return;
+ /* watch out - Don't combine the next 2 statements */
+ lng = readvarinum();
+ if (midifile_error) return;
+ lookfor = Mf_toberead - lng;
+ msginit();
+
+ while ( Mf_toberead > lookfor ) {
+ char c = egetc();
+ if (midifile_error) return;
+ msgadd(c);
+ }
+ metaevent(type);
+ break;
+
+ case 0xf0: /* start of system exclusive */
+
+ /* watch out - Don't combine the next 2 statements */
+ lng = readvarinum();
+ if (midifile_error) return;
+ lookfor = Mf_toberead - lng;
+ msginit();
+ msgadd(0xf0);
+
+ while ( Mf_toberead > lookfor ) {
+ c = egetc();
+ if (midifile_error) return;
+ msgadd(c);
+ }
+ if ( c==0xf7 || Mf_nomerge==0 )
+ sysex();
+ else
+ sysexcontinue = 1; /* merge into next msg */
+ break;
+
+ case 0xf7: /* sysex continuation or arbitrary stuff */
+
+ /* watch out - Don't combine the next 2 statements */
+ lng = readvarinum();
+ if (midifile_error) return;
+ lookfor = Mf_toberead - lng;
+
+ if ( ! sysexcontinue )
+ msginit();
+
+ while ( Mf_toberead > lookfor ) {
+ c = egetc();
+ if (midifile_error) return;
+ msgadd(c);
+ }
+ if ( ! sysexcontinue ) {
+ if ( Mf_arbitrary )
+ (*Mf_arbitrary)(msgleng(),msg());
+ }
+ else if ( c == 0xf7 ) {
+ sysex();
+ sysexcontinue = 0;
+ }
+ break;
+ default:
+
+ badbyte(c);
+
+ break;
+ }
+ }
+ if ( Mf_endtrack )
+ (*Mf_endtrack)();
+ return;
+}
+
+static void
+badbyte(c)
+int c;
+{
+ char buff[32];
+
+ (void) sprintf(buff,"unexpected byte: 0x%02x",c);
+ mferror(buff);
+}
+
+static void
+metaevent(type)
+{
+ int leng = msgleng();
+ char *m = msg();
+
+ switch ( type ) {
+ case 0x00:
+ if ( Mf_seqnum )
+ (*Mf_seqnum)(to16bit(m[0],m[1]));
+ break;
+ case 0x01: /* Text event */
+ case 0x02: /* Copyright notice */
+ case 0x03: /* Sequence/Track name */
+ case 0x04: /* Instrument name */
+ case 0x05: /* Lyric */
+ case 0x06: /* Marker */
+ case 0x07: /* Cue point */
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ case 0x0e:
+ case 0x0f:
+ /* These are all text events */
+ if ( Mf_text )
+ (*Mf_text)(type,leng,m);
+ break;
+ case 0x2f: /* End of Track */
+ if ( Mf_eot )
+ (*Mf_eot)();
+ break;
+ case 0x51: /* Set tempo */
+ if ( Mf_tempo )
+ (*Mf_tempo)(to32bit(0,m[0],m[1],m[2]));
+ break;
+ case 0x54:
+ if ( Mf_smpte )
+ (*Mf_smpte)(m[0],m[1],m[2],m[3],m[4]);
+ break;
+ case 0x58:
+ if ( Mf_timesig )
+ (*Mf_timesig)(m[0],m[1],m[2],m[3]);
+ break;
+ case 0x59:
+ if ( Mf_keysig )
+ (*Mf_keysig)(m[0],m[1]);
+ break;
+ case 0x7f:
+ if ( Mf_sqspecific )
+ (*Mf_sqspecific)(leng,m);
+ break;
+ default:
+ if ( Mf_metamisc )
+ (*Mf_metamisc)(type,leng,m);
+ }
+}
+
+static void
+sysex()
+{
+ if ( Mf_sysex )
+ (*Mf_sysex)(msgleng(),msg());
+}
+
+static void
+chanmessage(status,c1,c2)
+int status;
+int c1, c2;
+{
+ int chan = status & 0xf;
+
+ switch ( status & 0xf0 ) {
+ case NOTEOFF:
+ if ( Mf_off )
+ (*Mf_off)(chan,c1,c2);
+ break;
+ case NOTEON:
+ if ( Mf_on )
+ (*Mf_on)(chan,c1,c2);
+ break;
+ case PRESSURE:
+ if ( Mf_pressure )
+ (*Mf_pressure)(chan,c1,c2);
+ break;
+ case CONTROLLER:
+ if ( Mf_controller )
+ (*Mf_controller)(chan,c1,c2);
+ break;
+ case PITCHBEND:
+ if ( Mf_pitchbend )
+ (*Mf_pitchbend)(chan,c1,c2);
+ break;
+ case PROGRAM:
+ if ( Mf_program )
+ (*Mf_program)(chan,c1);
+ break;
+ case CHANPRESSURE:
+ if ( Mf_chanpressure )
+ (*Mf_chanpressure)(chan,c1);
+ break;
+ }
+}
+
+/* readvarinum - read a varying-length number, and return the */
+/* number of characters it took. */
+
+static long
+readvarinum()
+{
+ long value;
+ int c;
+
+ c = egetc();
+ if (midifile_error) return 0;
+
+ value = (long) c;
+ if ( c & 0x80 ) {
+ value &= 0x7f;
+ do {
+ c = egetc();
+ if (midifile_error) return 0;
+ value = (value << 7) + (c & 0x7f);
+ } while (c & 0x80);
+ }
+ return (value);
+}
+
+static long
+to32bit(c1,c2,c3,c4)
+{
+ long value = 0L;
+
+ value = (c1 & 0xff);
+ value = (value<<8) + (c2 & 0xff);
+ value = (value<<8) + (c3 & 0xff);
+ value = (value<<8) + (c4 & 0xff);
+ return (value);
+}
+
+static int
+to16bit(c1,c2)
+int c1, c2;
+{
+ return ((c1 & 0xff ) << 8) + (c2 & 0xff);
+}
+
+static long
+read32bit()
+{
+ int c1, c2, c3, c4;
+
+ c1 = egetc(); if (midifile_error) return 0;
+ c2 = egetc(); if (midifile_error) return 0;
+ c3 = egetc(); if (midifile_error) return 0;
+ c4 = egetc(); if (midifile_error) return 0;
+ return to32bit(c1,c2,c3,c4);
+}
+
+static int
+read16bit()
+{
+ int c1, c2;
+ c1 = egetc(); if (midifile_error) return 0;
+ c2 = egetc(); if (midifile_error) return 0;
+ return to16bit(c1,c2);
+}
+
+static void
+mferror(s)
+char *s;
+{
+ if ( Mf_error )
+ (*Mf_error)(s);
+ midifile_error = 1;
+}
+
+/* The code below allows collection of a system exclusive message of */
+/* arbitrary length. The Msgbuff is expanded as necessary. The only */
+/* visible data/routines are msginit(), msgadd(), msg(), msgleng(). */
+
+#define MSGINCREMENT 128
+static char *Msgbuff = 0; /* message buffer */
+static int Msgsize = 0; /* Size of currently allocated Msg */
+static int Msgindex = 0; /* index of next available location in Msg */
+
+static void
+msginit()
+{
+ Msgindex = 0;
+}
+
+static char *
+msg()
+{
+ return(Msgbuff);
+}
+
+static int
+msgleng()
+{
+ return(Msgindex);
+}
+
+static void
+msgadd(c)
+int c;
+{
+ /* If necessary, allocate larger message buffer. */
+ if ( Msgindex >= Msgsize )
+ msgenlarge();
+ Msgbuff[Msgindex++] = c;
+}
+
+static void
+msgenlarge()
+{
+ char *newmess;
+ char *oldmess = Msgbuff;
+ int oldleng = Msgsize;
+
+ Msgsize += MSGINCREMENT;
+ newmess = MALLOC((sizeof(char)*Msgsize) );
+
+ /* copy old message into larger new one */
+ if ( oldmess != 0 ) {
+ register char *p = newmess;
+ register char *q = oldmess;
+ register char *endq = &oldmess[oldleng];
+
+ for ( ; q!=endq ; p++,q++ )
+ *p = *q;
+ free(oldmess);
+ }
+ Msgbuff = newmess;
+}
diff --git a/cmt/midifile.h b/cmt/midifile.h
new file mode 100644
index 0000000..a759d76
--- /dev/null
+++ b/cmt/midifile.h
@@ -0,0 +1,27 @@
+extern void midifile(void);
+extern int (*Mf_getc)(void);
+extern void (*Mf_header)(int,int,int);
+extern void (*Mf_starttrack)(void);
+extern void (*Mf_endtrack)(void);
+extern void (*Mf_on)(int,int,int);
+extern void (*Mf_off)(int,int,int);
+extern void (*Mf_pressure)(int,int,int);
+extern void (*Mf_controller)(int,int,int);
+extern void (*Mf_pitchbend)(int,int,int);
+extern void (*Mf_program)(int,int);
+extern void (*Mf_chanpressure)(int,int);
+extern void (*Mf_sysex)(int,char*);
+extern void (*Mf_metamisc)(int,int,char*);
+extern void (*Mf_sqspecific)(int,char*);
+extern void (*Mf_seqnum)(int);
+extern void (*Mf_text)(int,int,char*);
+extern void (*Mf_eot)(void);
+extern void (*Mf_timesig)(int,int,int,int);
+extern void (*Mf_smpte)(int,int,int,int,int);
+extern void (*Mf_tempo)(int);
+extern void (*Mf_keysig)(int,int);
+extern void (*Mf_arbitrary)(int,char*);
+extern void (*Mf_error)(char *);
+extern long Mf_currtime;
+extern int Mf_nomerge;
+extern int Mf_skipinit;
diff --git a/cmt/midifns.c b/cmt/midifns.c
new file mode 100644
index 0000000..e8f7482
--- /dev/null
+++ b/cmt/midifns.c
@@ -0,0 +1,1886 @@
+/*****************************************************************************
+* midifns.c
+* Copyright 1989 Carnegie Mellon University
+* Date | Change
+*-----------+-----------------------------------------------------------------
+* 29-Mar-88 | Created from IBM PC version of mpu.c
+* | Added settime()
+* 02-May-88 | AMIGA 2000 version. portable version.
+* 12-Oct-88 | JCD : Exclusive AMIGA Version.
+* 13-Apr-89 | JCD : New portable version.
+* 19-Apr-89 | JCD : Amiga CAMD Version added.
+* 5-Apr-91 | JDW : Further modification
+* 17-Feb-92 | GWL : incorporate JMN's new mpu.c
+* 8-Jun-92 | JDZ : add support for ITC midi interface
+* 16-Dec-92 | RBD : replace JMN's mpu.c with LMS's mpu.c
+* 11-Mar-94 | PLu : port to IRIX
+* 25-Apr-97 | RBD : it looks like SGI changed their interface. I
+* | made it compile again, but MIDI does not work, so
+* | took out calls to actually send/recv MIDI data
+* 28-Apr-03 | DM : Renamed random -> cmtrand, true->TRUE, false->FALSE
+* | Use features rather than system names in #ifdef's
+*****************************************************************************/
+
+#include "switches.h"
+
+#ifdef UNIX
+#include <sys/resource.h>
+#include <sys/param.h>
+#ifndef OPEN_MAX
+/* this is here for compiling the UNIX version under AIX. This is a BSDism */
+#define OPEN_MAX 2000
+#endif /* OPEN_MAX */
+#endif /* UNIX */
+
+#ifdef UNIX_MACH
+#include "machmidi.h"
+#endif
+
+
+#ifdef AMIGA
+#ifdef AZTEC
+#include "functions.h"
+#endif /* AZTEC */
+#include "midi/camd.h"
+#include "clib/camd_protos.h"
+/* note: azt_camd_pragmas.h was produced by running MAPFD on the
+ * lib/camd_lib.fd file included in the CAMD disk from Commodore.
+ * The "CamdClient" calls are manually removed from
+ */
+#ifdef AZTEC
+#include "pragmas/azt_camd_pragmas.h"
+#else /* !AZTEC */
+#include "pragmas/lat_camd_pragmas.h"
+#endif /* AZTEC */
+#include "camdmidi.h"
+#include "ctype.h"
+#endif /* AMIGA */
+
+#ifdef UNIX_IRIX
+/* #define UNIX_IRIX_MIDIFNS -- this would enable actual midi I/O
+ * if the actual midi I/O code worked
+ */
+/* IRIX changed the MIDI interface,
+ * retain this for older systems:
+ */
+#ifdef UNIX_IRIX_MIDIFNS
+#include <dmedia/midi.h>
+#endif
+#endif
+
+#include "stdio.h"
+#include "cext.h"
+#include "midicode.h"
+#include "cmdline.h"
+#include "pitch.h"
+#include "midifns.h"
+#include "userio.h"
+#include "string.h"
+#ifdef MACINTOSH_OR_DOS
+#ifndef WINDOWS
+#include "midibuff.h"
+#endif
+#endif
+
+#ifdef UNIX_ITC /* was ITC */
+#include "sys/param.h"
+/* since boolean is defined, block its definition in midistruct.h.
+ * CMT defines boolean as ushort, but midistruct.h uses int.
+ * This is not a problem on RS/6000s, but beware!
+ */
+/* the following would be included if we had the BSD switch set. I think
+ we should try to avoid BSDisms; when fixed, the following should be
+ removed
+ */
+#define NBBY 8
+#include "sys/select.h" /* defines fd_set */
+#define MIDI_HAS_BOOLEAN
+#include "midistruct.h"
+#include "cmtio.h"
+#endif /* UNIX_ITC */
+
+#ifdef DOS
+#ifndef WINDOWS
+#include "timer.h"
+#include "mpu.h"
+#endif /* ifndef WINDOWS */
+#endif /* ifdef DOS */
+
+#ifndef BREAKTEST
+#define BREAKTEST
+#endif
+
+#ifdef __APPLE__
+#include <sys/types.h>
+#include <sys/time.h>
+#include <errno.h>
+#else
+#ifdef UNIX
+#ifndef UNIX_IRIX
+#include "sys/time.h"
+#include "sys/timeb.h"
+#include "cmtio.h"
+#else
+#include <sys/types.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#ifdef UNIX_IRIX_MIDIFNS
+#include <midi.h>
+#include <midiio.h>
+#endif /* UNIX_IRIX_MIDIFNS */
+#endif /* UNIX_IRIX */
+#endif /* UNIX */
+#endif /* __APPLE__ */
+
+#ifdef ITC
+static int ignore_realtime = 0;
+#endif /* ITC */
+
+#ifdef MACINTOSH
+
+/* added for ThinkC 7: */
+#include <OSUtils.h>
+
+/* port numbers are in the range 0..MAX_PORTS-1 */
+#define CHANNELS_PER_PORT 16
+#define MAX_PORTS ((MAX_CHANNELS + CHANNELS_PER_PORT - 1) / CHANNELS_PER_PORT)
+
+/* here are some MIDIMGR specific definitions */
+#ifdef MIDIMGR
+#include "MIDI.h"
+#include "midimgr.h"
+
+#define TICKS_TO_MS(t) t
+#define MS_TO_TICKS(t) t
+
+#else
+/* here are some non-MIDIMGR definitions for the Mac */
+/****************************************************************************
+*
+* DMH: constants from macmidi.c
+*
+****************************************************************************/
+
+/* the modem port, also called port A */
+#define portA 0
+
+/* the printer port, also called port B */
+#define portB 1
+
+/* a tick is 1/60 of a second
+ *
+ * the following tables and routines are used to convert
+ * between ticks and milliseconds
+ */
+#define TICKS_TO_MS(t) (((t) * 50) / 3)
+#define MS_TO_TICKS(t) (((t) * 3) / 50)
+#endif /* def MIDIMGR */
+#endif /* def MACINTOSH */
+
+#ifdef WINDOWS
+#define huge
+#endif
+
+/****************************************************************************
+*
+* exported flags
+*
+****************************************************************************/
+
+boolean miditrace = FALSE; /* enables printed trace of MIDI output */
+boolean musictrace = FALSE; /* enables printed trace of commands */
+#ifdef MACINTOSH_OR_DOS
+boolean ctrlFilter = TRUE; /* suppress continuous controller data */
+boolean exclFilter = TRUE; /* suppress exclusive messages */
+boolean realFilter = TRUE; /* suppress realtime messages */
+#endif
+
+/****************************************************************************
+*
+* exported variables
+*
+****************************************************************************/
+
+public int keyloud; /* set to velocity of last getkey event */
+/* public long error; */
+public short midi_error_flags = 0;
+
+/* The following midifns_syntax lists command line switches and options.
+ Since these are machine dependent, use conditional compilation.
+ Conditional compilation within a string is a bit tricky: you want to
+ write "\" for line continuation within the string, but "\" gets eaten
+ by the macro preprocessor.
+ That's why we define macros like AMIGAINPORT.
+ Regretably it doesn't work for all compilers.
+ */
+
+
+/* Lattice and RT/Unix aren't happy expanding the embedded macros below, so
+ I made a separate declaration of midifns_syntax for Unix
+ */
+#ifdef UNIX
+public char *midifns_syntax = "block<s>Turn off midi THRU;\
+ miditrace<s>Trace low-level midi functions;\
+ noalloff<s>Do not send alloff message when done;\
+ trace<s>Trace music operations;\
+ tune<o>Load a tuning file";
+#else
+#ifdef MACINTOSH
+#ifdef MIDIMGR
+public char *midifns_syntax = "miditrace<s>Trace low-level midi functions;\
+ noalloff<s>Do not send alloff message when done;\
+ patch<s>Remember/reuse Midi Mgr patches;\
+ trace<s>Trace music operations;\
+ keep<s>Keep other processes running;\
+ tune<o>Load a tuning file";
+#else /* no MIDIMGR */
+public char *midifns_syntax = "miditrace<s>Trace low-level midi functions;\
+ noalloff<s>Do not send alloff message when done;\
+ patch<s>Remember/reuse Midi Mgr patches;\
+ trace<s>Trace music operations;\
+ tune<o>Load a tuning file";
+#endif /* MIDIMGR */
+#else
+#ifdef AMIGA
+public char *midifns_syntax = "block<s>Turn off midi THRU;\
+ inport<o>Inpur port number;\
+ miditrace<s>Trace low-level midi functions;\
+ noalloff<s>Do not send alloff message when done;\
+ outport<o>Output port number;\
+ trace<s>Trace music operations;\
+ tune<o>Load a tuning file";
+#else /* not UNIX or MACINTOSH or MIDIMGR or AMIGA */
+#ifdef DOS
+public char *midifns_syntax = "miditrace<s>Trace low-level midi functions;\
+ noalloff<s>Do not send alloff message when done;\
+ trace<s>Trace music operations;\
+ tune<o>Load a tuning file";
+#endif /* DOS */
+#endif /* AMIGA */
+#endif /* MACINTOSH */
+#endif /* UNIX */
+
+#ifdef MACINTOSH
+boolean do_midi_thru = FALSE; /* exported: copy midi in to midi out */
+#endif
+
+
+/****************************************************************************
+*
+* local module variables
+*
+****************************************************************************/
+
+private int initialized = FALSE; /* set by musicinit, cleared by musicterm */
+private boolean tune_flag = FALSE; /* set by musicinit, never cleared */
+#ifdef DOS
+private boolean metroflag = FALSE; /* flag to turn on metronome */
+#endif
+private int user_scale = FALSE; /* TRUE if user-defined scale */
+private int bend[MAX_CHANNELS]; /* current pitch bend on channel */
+short cur_midi_prgm[MAX_CHANNELS];
+private pitch_table pit_tab[128]; /* scale definition */
+
+#ifdef DOS
+private ulong timeoffset = 0;
+public boolean exclerr = FALSE;
+public byte xcodemask; /* mask (00 or FF) */
+public byte xcode; /* mfr code */
+#endif
+
+#ifdef MACINTOSH_OR_DOS
+boolean sysex_pending = FALSE;
+#endif
+
+#ifdef AMIGA
+
+#define CONTCONT ((CMF_Ctrl & ~CMF_CtrlSwitch) | CMF_PitchBend | \
+ CMF_ChanPress)
+#endif /* def AMIGA */
+
+#ifdef UNIX
+private ulong timeoffset = 0;
+#endif
+
+#ifdef UNIX_IRIX_MIDIFNS
+static MIport *miport;
+static int ignore_realtime = 0;
+
+private byte *sysex_p;
+private int sysex_n;
+#endif
+
+#ifdef ITC
+mi_id midiconn;
+#endif
+
+#ifdef MACINTOSH
+private ulong ticksAtStart = 0L;
+ /* clock ticks at time of last musicinit or timereset
+ * ASSUME: tick clock never wraps. this is a good assumption, since
+ * the tick clock is set to zero when the power is turned on and the
+ * tick counter is 32 bits. the Macintosh would need to be on for
+ * 828.5 days for the tick counter to wrap around! */
+
+#endif /* def MACINTOSH */
+
+/****************************************************************************
+*
+* functions declared in this module
+*
+****************************************************************************/
+
+private void fixup();
+private void midi_init();
+extern boolean check_ascii(); /*userio.c*/
+private void musicterm();
+
+
+/****************************************************************************
+* alloff
+* Inputs:
+* none
+* Effect:
+* Sends MIDI all notes off command on every channel.
+****************************************************************************/
+
+#define ALL_NOTES_OFF 0x7B /*DMH: from macmidi.c*/
+
+void alloff()
+{
+ int c;
+
+ if (!initialized) fixup();
+ if (musictrace)
+ gprintf(TRANS,"alloff()\n");
+ for (c = 1; c <= MAX_CHANNELS; c++) {
+ midi_write(3, MIDI_PORT(c), (byte) (0xb0 | MIDI_CHANNEL(c)), ALL_NOTES_OFF, 0);
+ }
+}
+
+
+
+/***************************************************************
+* eventwait
+*
+* Input : wakeup time, -1 means forever
+* Output : none
+* Return: none
+* Effect: waits until ascii or midi input or timeout
+***************************************************************/
+
+#ifdef UNIX_ITC
+void eventwait(timeout)
+ long timeout;
+{
+ struct timeval unix_timeout;
+ struct timeval *waitspec = NULL;
+ fd_set readfds;
+ struct rlimit file_limit;
+
+ FD_ZERO(&readfds);
+ FD_SET(MI_CONNECTION(midiconn), &readfds);
+ FD_SET(fileno(stdin), &readfds);
+ if (timeout >= 0) {
+ timeout -= gettime(); /* convert to millisecond delay */
+ unix_timeout.tv_sec = timeout / 1000;
+ /* remainder become microsecs: */
+ unix_timeout.tv_usec = (timeout - (unix_timeout.tv_sec * 1000)) * 1000;
+ waitspec = &unix_timeout;
+ }
+ getrlimit(RLIMIT_NOFILE, &file_limit);
+ select(file_limit.rlim_max+1, &readfds, 0, 0, waitspec);
+ return;
+}
+#else /* !UNIX_ITC */
+#ifdef UNIX
+/* see machmidi.c for UNIX_MACH implementation */
+#ifndef UNIX_MACH
+#ifdef UNIX_IRIX_MIDIFNS
+void eventwait(timeout)
+ long timeout;
+{
+ struct timeval unix_timeout;
+ struct timeval *waitspec = NULL;
+ fd_set readfds;
+
+ FD_ZERO(&readfds);
+ FD_SET(mdGetFd(miport), &readfds);
+ FD_SET(fileno(stdin), &readfds);
+ if (timeout >= 0) {
+
+ timeout -= gettime(); /* convert to millisecond delay */
+ unix_timeout.tv_sec = timeout / 1000;
+ /* remainder become microsecs: */
+ unix_timeout.tv_usec = (timeout - (unix_timeout.tv_sec * 1000)) * 1000;
+ waitspec = &unix_timeout;
+ }
+ select(FD_SETSIZE, &readfds, 0, 0, waitspec);
+
+ return;
+}
+#else
+#ifdef BUFFERED_SYNCHRONOUS_INPUT
+void eventwait(timeout)
+ long timeout;
+{
+ struct timeval unix_timeout;
+ struct timeval *waitspec = NULL;
+ struct rlimit file_limit;
+
+ if (timeout >= 0) {
+ timeout -= gettime(); /* convert to millisecond delay */
+ unix_timeout.tv_sec = timeout / 1000;
+ /* remainder become microsecs: */
+ unix_timeout.tv_usec = (timeout - (unix_timeout.tv_sec * 1000)) * 1000;
+ waitspec = &unix_timeout;
+ getrlimit(RLIMIT_NOFILE, &file_limit);
+ select(file_limit.rlim_max+1, 0, 0, 0, waitspec);
+ } else {
+ int c = getc(stdin);
+ ungetc(c, stdin);
+ }
+ return;
+}
+#else
+void eventwait(timeout)
+ long timeout;
+{
+ struct timeval unix_timeout;
+ struct timeval *waitspec = NULL;
+ int readfds = 1 << IOinputfd;
+ struct rlimit file_limit;
+
+ if (timeout >= 0) {
+ timeout -= gettime(); /* convert to millisecond delay */
+ unix_timeout.tv_sec = timeout / 1000;
+ /* remainder become microsecs: */
+ unix_timeout.tv_usec = (timeout - (unix_timeout.tv_sec * 1000)) * 1000;
+ waitspec = &unix_timeout;
+ }
+ getrlimit(RLIMIT_NOFILE, &file_limit);
+ select(file_limit.rlim_max+1, &readfds, 0, 0, waitspec);
+ return;
+}
+#endif /* BUFFERED_SYNCHRONOUS_INPUT */
+#endif /* UNIX_IRIX */
+#endif /* UNIX_MACH */
+#endif /* UNIX */ /* I wanted to put an else here, but this confused a Unix C compiler */
+#endif /* UNIX_ITC */
+#ifdef AMIGA
+/* see camdmidi.c for Amiga implementation */
+#else
+#ifndef UNIX /* since I couldn't use an else above, have to check UNIX here */
+#ifdef WINDOWS
+void eventwait(timeout)
+ long timeout;
+{
+ if (timeout >= 0) {
+ gprintf(TRANS, "eventwait: not implemented\n");
+ return;
+ } else {
+ int c = getc(stdin);
+ ungetc(c, stdin);
+ }
+ return;
+}
+#else
+void eventwait(timeout)
+ long timeout;
+{
+ while (timeout > gettime() || timeout == -1) {
+ if (check_ascii() || check_midi()) return;
+ }
+}
+#endif /* WINDOWS */
+#endif /* UNIX */
+#endif /* AMIGA */
+
+
+/****************************************************************************
+* exclusive
+* Inputs:
+* boolean onflag -- set to TRUE to receive midi exclusive data
+* Effect:
+* Tells module to read exclusive messages into buffer
+****************************************************************************/
+
+void exclusive(boolean onflag)
+{
+ if (!initialized) fixup();
+ if (musictrace) gprintf(TRANS, "exclusive: %d\n", onflag);
+#ifdef AMIGA
+ if (onflag) SetMidiFilters(cmt_mi,
+ cmt_mi->PortFilter, cmt_mi->TypeFilter | CMF_SysEx, cmt_mi->ChanFilter);
+ else SetMidiFilters(cmt_mi,
+ cmt_mi->PortFilter, cmt_mi->TypeFilter & ~CMF_SysEx, cmt_mi->ChanFilter);
+#endif
+#ifdef MACINTOSH_OR_DOS
+ exclFilter = !onflag;
+#endif
+}
+
+
+/****************************************************************************
+* fixup
+* Effect:
+* Print error message and call musicinit
+****************************************************************************/
+
+private void fixup()
+{
+ gprintf(ERROR, "You forgot to call musicinit. I'll do it for you.\n");
+ musicinit();
+}
+
+#ifdef UNIX_IRIX_MIDIFNS
+private void flush_sysex(void);
+#endif
+
+long get_excl(byte *buffer, long len)
+{
+ long ret = 0;
+#ifdef UNIX_IRIX_MIDIFNS
+ byte *sxp = sysex_p;
+ long l = len;
+#endif
+#ifdef UNIX_ITC /* was ITC */
+ ret = mi_getx(midiconn, FALSE, len, (char *) buffer);
+#endif
+#ifdef UNIX_MACH
+ ret = mi_getx(midiconn, FALSE, len, (unsigned char *)buffer);
+#endif
+#ifdef UNIX_IRIX_MIDIFNS
+ if (!sysex_p) return 0;
+ if (len > sysex_n) len = sysex_n;
+ while (l--)
+ {
+ *buffer = *(sxp++);
+ if (*(buffer++) == CMT_MIDI_EOX)
+ {
+ flush_sysex();
+ break;
+ }
+ }
+ ret = len - l - 1;
+#endif
+#ifdef AMIGA
+ ret = GetSysEx(cmt_mi, (UBYTE *) buffer, len);
+ AMIGA_ERROR_CHECK;
+#endif
+#ifdef MACINTOSH_OR_DOS
+#ifndef WINDOWS
+ /* I'm not sure the following line is a good thing: it forces the
+ * caller to wait until a full sysex message is received and the
+ * 1st 4 bytes are fetched via getbuf() before a sysex message can
+ * be read via get_excl(). Without this, both mm.c and exget.c
+ * were fetching ahead and getting out of sync with getbuf(). I
+ * fixed mm.c and exget.c to work (by checking for EOX), but I added
+ * this line (which should never have any effect) just to make the
+ * DOS interface behave more like the Amiga and Mac interfaces. The
+ * drawback is that you can't fetch bytes until the EOX is seen,
+ * because nothing goes into the getbuf() buffer until then.
+ */
+ if (!sysex_pending) return 0;
+ while (len-- && (xbufhead != xbuftail)) {
+ *buffer = xbuff[xbufhead++];
+ ret++;
+ if (*buffer == MIDI_EOX) {
+ sysex_pending = FALSE;
+ break;
+ }
+ buffer++;
+ xbufhead &= xbufmask;
+ }
+#endif
+#endif
+ return ret;
+}
+
+/****************************************************************************
+* getbuf
+* Inputs:
+* boolean waitflag: TRUE if routine should wait for data
+* byte * p: Pointer to data destination
+* Result: boolean
+* TRUE if data was written to *p
+* FALSE if data not written to *p
+* Effect:
+* copies data from buffer to *p
+* will wait for buffer to become nonempty if waitflag is TRUE
+*
+* Modified 24 May 1988 for AMIGA (JCD)
+****************************************************************************/
+
+#ifdef UNIX_IRIX_MIDIFNS
+ private void setup_sysex(MDevent *event, u_char *buffer);
+#endif /* UNIX_IRIX */
+boolean getbuf(boolean waitflag, unsigned char * p)
+{
+#ifdef UNIX_IRIX_MIDIFNS
+ MDevent event;
+ int ret;
+#endif /* UNIX_IRIX */
+
+ if (!initialized) fixup();
+#ifdef UNIX
+#ifdef UNIX_IRIX_MIDIFNS
+/* current IRIX version ignores the waitflag (it never waits) */
+
+ if (sysex_p) flush_sysex();
+ if (ignore_realtime == 0) {
+ ret = mdReceive(miport, &event, 1);
+ if (ret) {
+ if (event.msg[0] != 0xF0) {
+ *((u_long*) p) = *((u_long*) event.msg);
+ } else {
+ setup_sysex(&event, p);
+ }
+ }
+ return ret;
+ } else {
+ do /* skip realtime messages */
+ {
+ ret = mdReceive(miport, &event, 1);
+ if (ret == -1) return ret;
+ } while (event.msg[0] == 0xf8);
+ if (event.msg[0] != 0xF0) {
+ *((u_long*) p) = *((u_long*) event.msg);
+ } else {
+ setup_sysex(&event, p);
+ }
+ return ret;
+ }
+#endif /* UNIX_IRIX */
+#ifdef UNIX_ITC
+ if (ignore_realtime == 0) {
+ return(mi_get(midiconn, waitflag, (char *) p));
+ }
+ else {
+ boolean ret=false;
+ /* filter out realtime msgs */
+ do {
+ ret = mi_get(midiconn, waitflag, (char *) p);
+ if (ret == FALSE)
+ return(ret);
+ } while(p[0] == 0xf8);
+ return(ret);
+ }
+#else /* UNIX_ITC */
+#ifndef UNIX_IRIX
+ if (waitflag) {
+ gprintf(ERROR, "getbuf called with waitflag!");
+ EXIT(1);
+ }
+ return FALSE;
+#endif /* UNIX_IRIX */
+#endif /* UNIX_ITC */
+#endif /* UNIX */
+
+#ifdef MACINTOSH_OR_DOS
+#ifndef WINDOWS
+ if (sysex_pending) { /* flush sysex to keep buffers in sync */
+ while (xbuff[xbufhead++] != MIDI_EOX) {
+ xbufhead &= xbufmask;
+ if (xbufhead == xbuftail) break;
+ }
+ sysex_pending = FALSE;
+ }
+ if (waitflag) while (buffhead == bufftail) /* wait */ ;
+ else if (buffhead == bufftail) return(false);
+ *(long *)p = *(long *)(((char *)buff)+buffhead);
+ buffhead = (buffhead + 4) & BUFF_MASK;
+ if (*p == MIDI_SYSEX) { /* if sys-ex, remember to fetch from xbuff */
+ sysex_pending = TRUE;
+ }
+ return(true);
+#else
+ return FALSE;
+#endif /* WINDOWS */
+#endif /* MACINTOSH_OR_DOS */
+
+#ifdef AMIGA
+ if (waitflag) {
+ do {
+ WaitMidi(cmt_mi, &cmt_msg);
+ AMIGA_ERROR_CHECK;
+ } while (amigaerrflags);
+ } else {
+ AMIGA_ERROR_CHECK;
+ if (!GetMidi(cmt_mi, &cmt_msg)) return(false);
+ }
+ *(long *)p = *(long *)&cmt_msg;
+ clearmsg(cmt_msg);
+ return(true);
+#endif /* AMIGA */
+}
+
+#ifdef UNIX_IRIX_MIDIFNS
+
+private void setup_sysex(MDevent *event, u_char *buffer)
+/* N.B. do not leak memory remember to call free(sysex_p) */
+{
+ u_char *sxp = (u_char *) event->sysexmsg;
+ int i;
+
+ for (i=0;i<4;i++)
+ *(buffer++) = *(sxp++);
+ sysex_p = event->sysexmsg;
+ sysex_n = event->msglen;
+}
+
+private void flush_sysex()
+{
+ mdFree(sysex_p);
+ sysex_p = 0;
+ sysex_n = 0;
+}
+#endif
+
+#ifdef MACINTOSH_OR_DOS
+#ifndef WINDOWS
+public boolean check_midi()
+{
+ if (buffhead == bufftail) return FALSE;
+ else return TRUE;
+}
+#endif
+#endif
+
+
+/****************************************************************************
+* getkey
+* Inputs:
+* boolean waitflag: TRUE if wait until key depression, FALSE if
+* return immediately
+* Result: int
+* key number of key which has been depressed
+* It returns -1 if waitflag is FALSE and no key has been pressed
+* If waitflag is TRUE this routine will block until a key is pressed
+* Effect:
+* reads a key
+****************************************************************************/
+
+/*DMH: in previous version, macmidi.c subtracted 12 from msg to get key at each occurence...*/
+
+short getkey(boolean waitflag)
+{
+ byte msg[4];
+ short k;
+
+ if (!initialized) fixup();
+
+ while (TRUE) { /* process data until you find a note */
+ /* look for data and exit if none found */
+ /* NOTE: waitflag will force waiting until data arrives */
+ if (!getbuf(waitflag, msg)) { /* nothing there */
+ k = -1;
+ break;
+ } else if ((msg[0] & MIDI_CODE_MASK) == MIDI_ON_NOTE) {
+ if (msg[2] == 0) { /* velocity 0 -> note off */
+ keyloud = 0;
+ k = msg[1] + 128;
+ } else {
+ keyloud = msg[2];
+ k = msg[1];
+ }
+ break;
+ } else if ((msg[0] & MIDI_CODE_MASK) == MIDI_OFF_NOTE) {
+ keyloud = 0;
+ k = msg[1] + 128;
+ break;
+ }
+ }
+ if (musictrace) {
+ if (k != -1) gprintf(TRANS,"getkey got %d\n", k);
+ }
+ return k;
+}
+
+
+/****************************************************************************
+* gettime
+* Result: ulong
+* current timestamp since the last call to
+* musicinit or timereset
+* Effect:
+* fakes it
+****************************************************************************/
+
+ulong gettime() /*DMH: ulong is from mpu->midifns conversion, for Mac*/
+{
+#if HAS_GETTIMEOFDAY
+ struct timeval timeval;
+#endif
+#if HAS_FTIME
+ struct timeb ftime_res;
+#endif
+ register ulong ticks = 0L;
+
+ BREAKTEST /* abort if user typed Ctrl Break */
+ if (!initialized) fixup();
+
+#ifdef MACINTOSH
+#ifdef MIDIMGR
+ ticks = MIDIGetCurTime(OutputRefNum) - ticksAtStart;
+#else
+ ticks = TickCount() - ticksAtStart;
+#endif
+ if (initialized) abort_check(); /* give user a chance to abort */
+ ticks = TICKS_TO_MS(ticks);
+#endif
+
+#ifdef AMIGA
+ ticks = (*camdtime - timeoffset) << 1; /* return milliseconds */
+#endif
+
+#ifdef DOS
+#ifndef WINDOWS
+ ticks = elapsedtime(timeoffset, readtimer()); /* return milliseconds */
+ /* gprintf(TRANS, "currtime = %ld, timeoffset = %ld\n", currtime, timeoffset); */
+#endif
+#endif /* ifdef DOS */
+
+#if HAS_GETTIMEOFDAY
+ gettimeofday(&timeval, 0);
+ ticks = timeval.tv_sec * 1000 + timeval.tv_usec / 1000 - timeoffset;
+#endif
+#if HAS_FTIME
+ ftime(&ftime_res);
+ ticks = ((ftime_res.time - timeoffset) * 1000) + ftime_res.millitm;
+#endif
+
+ /* if (miditrace) gprintf(TRANS, "."); */
+ return(ticks);
+}
+
+
+/****************************************************************************
+* l_rest
+* Inputs:
+* long time: Amount of time to rest
+* Effect:
+* Waits until the amount of time specified has lapsed
+****************************************************************************/
+
+void l_rest(time)
+long time;
+{
+ if (!initialized) fixup();
+ l_restuntil(time + gettime());
+}
+
+/****************************************************************************
+* l_restuntil
+* Inputs:
+* long time: Event time to rest until
+* Effect:
+* Waits until the specified time has been reached (absolute time)
+****************************************************************************/
+
+void l_restuntil(time)
+long time;
+{
+#ifdef MACINTOSH
+ ulong now = gettime();
+ ulong junk; /* changed from ulong for ThinkC 7, back to ulong for CW5 */
+#endif
+
+#ifdef AMIGA
+ while (time > gettime()) eventwait(time);
+#else
+ for(; (time_type) time > gettime(););
+#endif
+
+#ifdef MACINTOSH
+ now = gettime();
+
+ if (time > now) Delay(MS_TO_TICKS(time - now), &junk);
+ /* else time <= now, so return immediately */
+#endif
+
+}
+
+/****************************************************************************
+* metronome
+* Inputs:
+* boolean onflag: TRUE or FALSE
+* Effect:
+* enables (true) or disables (false) MPU-401 metronome function.
+* must be called before musicinit
+****************************************************************************/
+
+void metronome(boolean onflag)
+{
+#ifdef DOS
+metroflag = onflag;
+#endif
+}
+
+
+/****************************************************************************
+* midi_bend
+* Inputs:
+* int channel: midi channel on which to send data
+* int value: pitch bend value
+* Effect:
+* Sends a midi pitch bend message
+****************************************************************************/
+
+void midi_bend(int channel, int value)
+{
+ if (!initialized) fixup();
+ if (musictrace)
+ gprintf(TRANS,"midi_bend: ch %d, val %d\n", channel, value - (1 << 13));
+ bend[MIDI_CHANNEL(channel)] = value;
+
+ midi_write(3, MIDI_PORT(channel), (byte) (MIDI_BEND | MIDI_CHANNEL(channel)),
+ (byte) MIDI_DATA(value), (byte) MIDI_DATA(value >> 7));
+}
+
+
+/****************************************************************************
+* midi_buffer
+* Inputs:
+* byte * buffer: the buffer address
+* int size: number of bytes in buffer
+* Returns:
+* FALSE if size is less than 16 or buffer is NULL, otherwise TRUE
+* Effect: DOS, MAC:
+* tells interrupt routine to store system exclusive messages in
+* buffer. The largest power of 2 bytes less than size will be
+* used. xbufhead and xbuftail will be initialized to zero,
+* and xbuftail will be one greater than the index of the last
+* system exclusive byte read. Since there may already be a buffer
+* and therefore the normal midi message buffer may have the first
+* 4 bytes of some sysex messages, clear the normal midi buffer too.
+* AMIGA:
+* adds buffer to midi interface
+*
+****************************************************************************/
+
+boolean midi_buffer(byte huge *buffer, ulong size)
+{
+ if (!buffer) return FALSE;
+#ifdef AMIGA
+ if (!SetSysExQueue(cmt_mi, (UBYTE *) buffer, (ULONG) size)) return(false);
+ cu_register(remove_sysex_buffer, buffer);
+#endif
+
+#ifdef MACINTOSH_OR_DOS
+#ifndef WINDOWS
+ {
+ int mask = 0x000F;
+
+ if (size < 16) return(false);
+ while (mask < size && mask > 0) mask = ((mask << 1) | 1);
+ midi_flush();
+ xbuff = NULL; /* turn off buffering */
+ xbufmask = mask >> 1;
+ xbufhead = xbuftail = 0;
+ xbuff = buffer; /* set buffer, turn on buffering */
+ }
+#endif
+#endif
+#ifdef UNIX
+ return FALSE;
+#else
+ exclusive(TRUE);
+ return TRUE;
+#endif
+}
+
+
+/* midi_clock -- send a midi time clock message */
+/**/
+void midi_clock()
+{
+ if (!initialized) fixup();
+ if (musictrace) gprintf(TRANS, "+");
+ midi_write(1, 0, MIDI_TIME_CLOCK, 0, 0);
+}
+
+
+/****************************************************************************
+* midi_cont
+* Inputs:
+* boolean onflag: TRUE or FALSE
+* Effect:
+* enables (true) or disables (false) continuous control
+****************************************************************************/
+
+void midi_cont(boolean onflag)
+{
+ if (!initialized) fixup();
+ if (onflag) {
+#ifdef AMIGA
+ SetMidiFilters(cmt_mi, cmt_mi->PortFilter,
+ cmt_mi->TypeFilter | CONTCONT, cmt_mi->ChanFilter);
+#endif
+#ifdef DOS
+#ifndef WINDOWS
+ mPutCmd(BENDERON);
+#endif
+#endif
+ } else {
+#ifdef AMIGA
+ SetMidiFilters(cmt_mi, cmt_mi->PortFilter,
+ cmt_mi->TypeFilter & ~CONTCONT, cmt_mi->ChanFilter);
+#endif
+ }
+#ifdef MACINTOSH_OR_DOS
+ ctrlFilter = !onflag;
+#endif
+ if (musictrace) gprintf(TRANS,"midi_cont: %d\n", onflag);
+}
+
+
+/****************************************************************************
+* midi_ctrl
+* Inputs:
+* int channel: midi channel on which to send data
+* int control: control number
+* int value: control value
+* Effect:
+* Sends a midi control change message
+****************************************************************************/
+
+void midi_ctrl(int channel, int control, int value)
+{
+ if (!initialized) fixup();
+ if (musictrace)
+ gprintf(TRANS,"midi_ctrl: ch %d, ctrl %d, val %d\n",
+ channel, control, value);
+
+ midi_write(3, MIDI_PORT(channel), (byte) (MIDI_CTRL | MIDI_CHANNEL(channel)),
+ (byte) MIDI_DATA(control), (byte) MIDI_DATA(value));
+}
+
+
+/****************************************************************************
+* midi_exclusive
+* Inputs:
+* byte *msg: pointer to a midi exclusive message, terminated by 0xF7
+* Effect:
+* Sends a midi exclusive message
+* Bugs:
+* 18-mar-94 PLu : This function does not know which port to send to in
+* case of multiple midi-ports (MAC, IRIX)
+****************************************************************************/
+
+#ifdef MACINTOSH
+#define INTERBYTE_DELAY 10
+#endif
+
+void midi_exclusive(msg)
+unsigned char *msg; /* the data to be sent */
+{
+#ifdef ITC
+ int count, done, tosend, willsend;
+ unsigned char *m;
+ mi_status ret;
+#endif
+#ifdef UNIX_IRIX_MIDIFNS
+ unsigned char *m;
+ MDevent mdevent;
+#endif
+
+#ifdef MACINTOSH
+#ifndef NYQUIST
+ int i; /* for DX7 delay loop */
+ int count = 0; /* counter for formatting midi byte trace */
+ MIDIPacket TheMIDIPacket;
+ unsigned char prev = 0;
+ boolean first_packet = TRUE;
+#endif
+#endif
+
+ /*
+ * if user mistakenly called midi_exclusive instead of exclusive,
+ * the argument will be TRUE or FALSE, both of which are highly
+ * unlikely valid arguments for midi_exclusive:
+ */
+
+ if (msg == (byte *) FALSE || msg == (byte *) TRUE) {
+ gprintf(ERROR,"midi_exclusive: invalid argument %u.\n", msg);
+ EXIT(1);
+ }
+ if (!initialized) fixup();
+ if (musictrace) gprintf(TRANS,"midi_exclusive\n");
+
+#ifdef AMIGA
+ PutSysEx(cmt_mi, msg);
+#endif
+#ifdef MACINTOSH
+#ifndef NYQUIST /* if NYQUIST, do nothing */
+#ifdef MIDIMGR
+ while (prev != MIDI_EOX) {
+ int len = 0;
+ while (prev != MIDI_EOX && len < 249) {
+ TheMIDIPacket.data[len++] = prev = *msg++;
+ }
+ TheMIDIPacket.len = 6 + len;
+ TheMIDIPacket.tStamp = 0;
+ if (first_packet && (prev != MIDI_EOX)) {
+ TheMIDIPacket.flags = midiTimeStampCurrent + midiStartCont;
+ first_packet = FALSE;
+ } else if (first_packet) {
+ TheMIDIPacket.flags = midiTimeStampCurrent + midiNoCont;
+ } else if (prev == MIDI_EOX) {
+ TheMIDIPacket.flags = midiTimeStampCurrent + midiEndCont;
+ } else {
+ TheMIDIPacket.flags = midiTimeStampCurrent + midiMidCont;
+ }
+ MIDIWritePacket(OutputRefNum, &TheMIDIPacket);
+ }
+#else
+ while (*msg != MIDI_EOX) {
+ Xmit(0, *msg);
+ msg++;
+ count++;
+ /* this is a delay loop, without which your DX7 will crash */
+ for (i = INTERBYTE_DELAY; i > 0; i--)
+ abort_check();
+ }
+ Xmit(0, MIDI_EOX);
+#endif /* MIDIMGR */
+#endif /* NYQUIST */
+#endif /* MACINTOSH */
+
+#ifdef DOS
+#ifndef WINDOWS
+ do {
+ mPutData(*msg);
+ } while (*msg++ != MIDI_EOX);
+#endif
+#endif
+#ifdef ITC
+ for (m = msg, tosend = 1; (*m) != MIDI_EOX; m++, tosend++);
+ for (count = 0; count < tosend; count += done) {
+ willsend = min(16384, tosend);
+ ret = mi_exclusive(midiconn, 1, msg, (short) willsend);
+ if (ret != MI_SUCCESS) {
+ gprintf(GWARN, "Got %d from mi_exclusive\n", ret);
+ }
+ done = willsend;
+ }
+#endif
+#ifdef UNIX_IRIX_MIDIFNS
+/* we don't know which device to sent SYSEX messages to so port zero is
+ assumed. */
+ for (m = msg, mdevent.msglen = 1; (*m) != CMT_MIDI_EOX; m++, mdevent.msglen++);
+ mdevent.sysexmsg = msg;
+ if (mdSend(miport, &mdevent, 1) == -1) {
+ gprintf(GWARN, "could not send SYSEX message\n");
+ }
+#endif
+
+ if (miditrace) {
+ do { gprintf(TRANS, "~%2x", *msg);
+#ifdef UNIX_IRIX_MIDIFNS
+ } while (*msg++ != CMT_MIDI_EOX);
+#else
+ } while (*msg++ != MIDI_EOX);
+#endif
+ }
+}
+
+
+/****************************************************************************
+* midi_note
+* Inputs:
+* int channel: midi channel on which to send data
+* int pitch: midi pitch code
+* int velocity: velocity with which to sound it (0=> release)
+* Effect:
+* Sends a midi note-play request out
+****************************************************************************/
+
+void midi_note(int channel, int pitch, int velocity)
+{
+ if (!initialized) fixup();
+ if (musictrace) gprintf(TRANS,"midi_note: ch %d, key %d, vel %d\n",
+ channel, pitch, velocity);
+ if (user_scale) {
+ /* check for correct pitch bend */
+ if ((pit_tab[pitch].pbend != bend[MIDI_CHANNEL(channel)])
+ && (velocity != 0)) {
+ midi_bend(channel, pit_tab[pitch].pbend);
+ bend[channel] = pit_tab[pitch].pbend;
+ }
+ pitch = pit_tab[pitch].ppitch;
+ }
+ midi_write(3, MIDI_PORT(channel), (byte) (MIDI_ON_NOTE | MIDI_CHANNEL(channel)),
+ (byte) MIDI_DATA(pitch), (byte) MIDI_DATA(velocity));
+}
+
+
+/****************************************************************************
+* midi_program
+* Inputs:
+* int channel: Channel on which to send midi program change request
+* int program: Program number to send (decremented by 1 before
+* being sent as midi data)
+* Effect:
+* Sends a program change request out the channel
+****************************************************************************/
+
+void midi_program(int channel, int program)
+{
+#ifdef MACINTOSH
+ int port, midi_chan;
+#endif
+
+ if (!initialized) fixup();
+ if (musictrace) gprintf(TRANS,"midi_program: ch %d, prog %d\n",
+ channel, program);
+ channel = MIDI_CHANNEL(channel);
+ if (cur_midi_prgm[channel] != program) {
+ midi_write(2, MIDI_PORT(channel), (byte) (MIDI_CH_PROGRAM | channel),
+ (byte) (MIDI_PROGRAM(program)), 0);
+ cur_midi_prgm[channel] = program;
+ }
+}
+
+
+/****************************************************************************
+* midi_real
+* Inputs:
+* boolean onflag: TRUE or FALSE
+* Effect:
+* enables (true) or disables (false) midi realtime messages F8-FF
+****************************************************************************/
+
+void midi_real(boolean onflag)
+{
+ if (!initialized) fixup();
+#ifdef UNIX_ITC
+ {
+ mi_status ret;
+
+ ret = mi_realtime(midiconn, onflag);
+ if (ret != MI_SUCCESS) {
+ gprintf(ERROR, "Warning: bad ret = %d in midi_real\n", ret);
+ }
+ }
+#endif /* UNIX_ITC */
+#ifdef ITC
+ ignore_realtime = !onflag;
+#endif /* ITC */
+#ifdef AMIGA
+ if (onflag) {
+ SetMidiFilters(cmt_mi, cmt_mi->PortFilter,
+ cmt_mi->TypeFilter | CMF_RealTime, cmt_mi->ChanFilter);
+ } else {
+ SetMidiFilters(cmt_mi, cmt_mi->PortFilter,
+ cmt_mi->TypeFilter & ~CMF_RealTime, cmt_mi->ChanFilter);
+ }
+#endif
+#ifdef MACINTOSH_OR_DOS
+ realFilter = !onflag;
+#endif
+
+ if (musictrace) gprintf(TRANS,"midi_real: %d\n", onflag);
+}
+
+
+/* midi_start -- send a midi start message */
+/**/
+void midi_start()
+{
+ if (!initialized) fixup();
+ if (musictrace) gprintf(TRANS, "`");
+ midi_write(1, 0, MIDI_START, 0, 0);
+}
+
+
+/* midi_stop -- send a midi stop message */
+/**/
+void midi_stop()
+{
+ if (!initialized) fixup();
+ if (musictrace) gprintf(TRANS, "'");
+ midi_write(1, 0 /* ignored */, MIDI_STOP, 0, 0);
+}
+
+
+/****************************************************************************
+* midi_thru
+* Inputs:
+* boolean onflag: TRUE or FALSE
+* Effect:
+* DOS: enables (true) or disables (false) midi thru info from
+* MPU-401 to host. (Default is set; reset with cmdline -block.)
+* AMIGA: enables (true) or disables (false) midi route from AMIGA
+* midi input to AMIGA midi output.
+****************************************************************************/
+
+void midi_thru(boolean onflag) /* DMH: midi thru is not supported on the MAC or DOS */
+{
+ if (!initialized) fixup();
+#ifndef MIDI_THRU
+ gprintf(ERROR, "midi_thru called but not implemented\n");
+#else
+#ifdef AMIGA
+ MidiThru(0L, (long) onflag);
+#endif
+#ifdef MACINTOSH
+ /* this currently does not do anything - Mac driver doesn't
+ * support THRU
+ */
+ do_midi_thru = onflag;
+#endif
+#endif
+
+ if (musictrace) gprintf(TRANS,"midi_thru: %d\n", onflag);
+}
+
+
+/****************************************************************************
+* midi_touch
+* Inputs:
+* int channel: midi channel on which to send data
+* int value: control value
+* Effect:
+* Sends a midi after touch message
+****************************************************************************/
+
+void midi_touch(int channel, int value)
+{
+ if (!initialized) fixup();
+ if (musictrace) gprintf(TRANS,"midi_touch: ch %d, val %d\n",channel,value);
+ midi_write(2, MIDI_PORT(channel), (byte) (MIDI_TOUCH | MIDI_CHANNEL(channel)),
+ (byte) MIDI_DATA(value), 0);
+}
+
+
+/****************************************************************************
+* midi_write
+* Inputs:
+* UBYTE n: number of characters to send (1, 2 or 3);
+ int port: the port number (usually 0), on MAC, this may be 1
+* char c1,c2,c3: Character(s) to write to MIDI data port
+* Effect:
+* Writes the data to the serial interface designated by port
+****************************************************************************
+* Change log
+* Date | Change
+*-----------+----------------------------------------------------------------
+* 15-Mar-94 | PLu : Added IRIX version
+****************************************************************************/
+
+#ifdef UNIX
+#ifdef UNIX_IRIX_MIDIFNS
+void midi_write(int n, int port, unsigned char c1, unsigned char c2, unsigned char c3)
+{
+ MDevent event;
+
+ if (port < 0) return;
+
+ * ((u_long *) event.msg) = 0xe0000000 | ((port & 0x1f) << 24) | (c1 << 16) |
+ (c2 << 8) | c3;
+ if (mdSend(miport, &event, 1) == -1)
+ gprintf(ERROR, "Can not send midi message in midi_write");
+
+ midi_write_trace(n, port, c1, c2, c3);
+}
+#else
+
+#ifdef ITC
+void midi_write(int n, int port,
+ unsigned char c1, unsigned char c2, unsigned char c3)
+{
+ unsigned char outb[3];
+ mi_channel mch;
+ mi_status ret;
+
+ if (port < 0) return;
+ outb[0] = c1;
+ outb[1] = c2;
+ outb[2] = c3;
+ mch = (16*port)+((int)MI_CHANNEL(c1));
+ ret = mi_put(midiconn, mch, outb);
+ if (ret != MI_SUCCESS)
+ gprintf(ERROR, "Warning: bad ret = %d in midi_write\n", (int)ret);
+ midi_write_trace(n, port, c1, c2, c3);
+}
+#else
+void midi_write(int n, int port,
+ unsigned char c1, unsigned char c2, unsigned char c3)
+{
+ /* no output */
+ midi_write_trace(n, port, c1, c2, c3);
+}
+#endif /* ITC */
+#endif /* UNIX_IRIX */
+#endif /* UNIX */
+
+#ifdef DOS
+#ifndef WINDOWS
+void midi_write(int n, int port,
+ unsigned char c1, unsigned char c2, unsigned char c3)
+{
+ if (n >= 1) mPutData(c1);
+ if (n >= 2) mPutData(c2);
+ if (n >= 3) mPutData(c3);
+ midi_write_trace(n, port, c1, c2, c3);
+}
+#else
+void midi_write(int n, int port,
+ unsigned char c1, unsigned char c2, unsigned char c3)
+{
+ midi_write_trace(n, port, c1, c2, c3);
+}
+#endif
+#endif
+
+#ifdef MACINTOSH
+#ifdef MIDIMGR
+void midi_write(int n, int port,
+ unsigned char c1, unsigned char c2, unsigned char c3)
+{
+ MIDIPacket TheMIDIPacket;
+
+ TheMIDIPacket.flags = midiTimeStampCurrent;
+ TheMIDIPacket.len = 6 + n;
+ TheMIDIPacket.tStamp = 0;
+ TheMIDIPacket.data[0] = c1;
+ TheMIDIPacket.data[1] = c2;
+ TheMIDIPacket.data[2] = c3;
+ MIDIWritePacket(OutputRefNum, &TheMIDIPacket);
+ midi_write_trace(n, port, c1, c2, c3);
+}
+#else
+void midi_write(int n, int port, unsigned char c1, unsigned char c2, unsigned char c3)
+{
+#ifndef NYQUIST
+ Xmit(port, c1);
+ if (n >= 2) Xmit(port, c2);
+ if (n >= 3) Xmit(port, c3);
+#endif
+ midi_write_trace(n, port, c1, c2, c3);
+}
+#endif
+#endif
+
+void midi_write_trace(int n, int port,
+ unsigned char c1, unsigned char c2, unsigned char c3)
+{
+ if (miditrace) {
+ /* to indicate bytes going out on port 1, put message in brackets
+ * with the port number, e.g. [1:~90~3c~64]
+ */
+ if (port > 0) gprintf(TRANS, "[%d:", port);
+ if (n >= 1) gprintf(TRANS, "~%2x", c1);
+ if (n >= 2) gprintf(TRANS, "~%2x", c2);
+ if (n >= 3) gprintf(TRANS, "~%2x", c3);
+ if (port > 0) gprintf(TRANS, "]", port);
+ }
+}
+
+
+
+/*****************************************************************
+* set_pitch_default
+*****************************************************************/
+
+private void set_pitch_default()
+{
+ int i;
+
+ for (i = 0; i < 128; i++) {
+ pit_tab[i].pbend = 8192;
+ pit_tab[i].ppitch = i;
+ }
+}
+
+/*****************************************************************
+* read_tuning
+*****************************************************************/
+
+void read_tuning(filename)
+char *filename;
+{
+ int index, pit, lineno = 0;
+ float bend;
+ FILE *fpp;
+
+ user_scale = TRUE;
+ set_pitch_default();
+
+ fpp = fileopen(filename, "tun", "r", "Tuning definition file");
+ while ((fscanf(fpp, "%d %d %f\n", &index, &pit, &bend) > 2) &&
+ (lineno < 128)) {
+ lineno++;
+ if (index >= 0 && index <= 127) {
+ pit_tab[index].pbend = (int)(8192 * bend/100 + 8192);
+ pit_tab[index].ppitch = pit;
+ }
+ }
+}
+
+
+/****************************************************************************
+* musicinit
+* Effect:
+****************************************************************************/
+
+void musicinit()
+{
+ int i;
+ char *filename;
+
+ if (!tune_flag) { /* do this code only once */
+ miditrace = cl_switch("miditrace");
+ musictrace = cl_switch("trace");
+ }
+
+ if (!initialized) {
+ cu_register((cu_fn_type) musicterm, NULL);
+ midi_init();
+ }
+ initialized = TRUE;
+ /* this does some random cleanup activity */
+
+#ifndef APPLICATION
+ if (!tune_flag) { /* do this code only once */
+#ifdef DOS
+#ifndef WINDOWS
+#if 0
+ version = mPutGetCmd(GETMPUVER);
+ revision = mPutGetCmd(GETMPUREV);
+ gprintf(TRANS, "MPU version %d.%d%c\n", version >> 4, version & 0x0f,
+ revision + 'A' - 1);
+#endif
+ mPutCmd(UARTMODE);
+ mPutCmd(NOREALTIME); /* initially prevent Real Time MIDI info */
+ mPutCmd(EXCLUSIVOFF); /* initially prevent Sys-Ex data */
+#endif
+#endif
+ tune_flag = TRUE;
+ filename = cl_option("tune");
+ if (filename != NULL) read_tuning(filename);
+ }
+ /* now that flags are set, print the trace message */
+ if (musictrace) gprintf(TRANS, "musicinit()\n");
+
+ if (user_scale) {
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ midi_bend(i, 8192);
+ bend[i] = 8192;
+ }
+ }
+#endif /* ifndef APPLICATION */
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ /* initialize to impossible values so that the
+ * next call to midi_bend or midi_program will
+ * not match and therefore send an output:
+ */
+ bend[i] = -1;
+ cur_midi_prgm[i] = -1;
+ }
+#ifdef MIDI_THRU
+ midi_thru(!(cl_switch("block"))); /* set MIDI thru */
+#endif
+ timereset(); /* Reset clock */
+#ifdef AMIGA
+ event_mask |= (1L << ascii_signal()) | (1L << cmt_mi->AlarmSigBit) |
+ (1L << cmt_mi->RecvSigBit);
+#endif
+}
+
+
+/****************************************************************************
+* musicterm
+* Effect:
+* Miscellaneous cleanup of things done by musicinit.
+****************************************************************************/
+
+private void musicterm()
+{
+ if (musictrace) gprintf(TRANS, "musicterm()\n");
+ initialized = FALSE;
+}
+
+
+/****************************************************************************
+* cmtrand
+* Inputs:
+* int lo: Lower limit of value
+* int hi: Upper limit of value
+* Result: int
+* random number (lo <= result <= hi)
+****************************************************************************/
+
+long randseed = 1534781L;
+
+short cmtrand(short lo, short hi)
+{
+ randseed *= 13L;
+ randseed += 1874351L;
+ return((short)(lo + (((hi + 1 - lo) * ((0x00ffff00 & randseed) >> 8)) >> 16)));
+}
+
+
+#ifdef AMIGA
+/* remove_sysex_buffer -- a cleanup procedure for the Amiga */
+/**/
+void remove_sysex_buffer(void *obj)
+{
+ ClearSysExQueue(cmt_mi);
+}
+#endif /* AMIGA */
+
+
+/****************************************************************************
+* settime
+* Inputs: new time
+* Effect:
+* Sets the current time to the new time.
+* DMH: for MAC, sets the clock to absTime
+* implemented by adjusting ticksATStart
+****************************************************************************/
+
+void settime(newtime)
+ time_type newtime;
+{
+ if (musictrace) gprintf(TRANS, "settime(%lu)\n", newtime);
+#ifdef AMIGA
+ timeoffset = *camdtime - (newtime >> 1);
+#endif
+
+#ifdef MACINTOSH
+#ifdef MIDIMGR
+ ticksAtStart = MIDIGetCurTime(OutputRefNum);
+#else
+ ticksAtStart = TickCount() - MS_TO_TICKS(newtime);
+#endif
+#endif
+}
+
+/****************************************************************************
+* timereset
+* Effect:
+* Resets the time.
+* DMH: for MAC, implemented by setting ticksAtStart to
+* current value of system tick counter
+* JMN: for DOS, resets the time on the MPU-401. Ticks is reset to 0
+****************************************************************************/
+
+void timereset()
+{
+#if HAS_GETTIMEOFDAY
+ struct timeval timeval;
+#endif
+#if HAS_FTIME
+ struct timeb ftime_res;
+#endif
+
+ if (!initialized) fixup();
+ if (musictrace) gprintf(TRANS,"timereset()\n");
+
+#ifdef AMIGA
+ timeoffset = *camdtime;
+#endif
+
+#ifdef DOS
+#ifndef WINDOWS
+ timeoffset = (ulong) readtimer();
+#endif
+#endif
+
+#ifdef MACINTOSH
+#ifdef MIDIMGR
+ ticksAtStart = MIDIGetCurTime(OutputRefNum);
+#else
+ ticksAtStart = TickCount();
+#endif
+#endif
+
+#if HAS_GETTIMEOFDAY
+ gettimeofday(&timeval, 0);
+ timeoffset = timeval.tv_sec * 1000 + timeval.tv_usec / 1000 - timeoffset;
+#endif
+#if HAS_FTIME
+ ftime(&ftime_res);
+ timeoffset = ftime_res.time;
+#endif
+}
+
+
+/****************************************************************************
+* trace
+* Inputs:
+* boolean flag: TRUE for trace on
+* Effect:
+* turns tracing on (flag == TRUE) or off (flag == FALSE)
+****************************************************************************/
+
+void trace(boolean flag)
+{
+ musictrace = flag;
+}
+
+/****************************************************************************
+* tracemidi
+* Inputs:
+* boolean flag: TRUE for trace on
+* Effect:
+* turns midi tracing on (flag == TRUE) or off (flag == FALSE)
+****************************************************************************/
+
+void tracemidi(boolean flag)
+{
+ miditrace = flag;
+}
+
+
+
+/***********************************************************************
+*
+* midi and timer initialization
+*
+***********************************************************************/
+
+#ifdef DOS
+
+/* binary value of hex char */
+
+private int xval(int c)
+{
+ int i;
+ static char t[]="0123456789abcdef";
+
+ for (i=0; i<16; i++)
+ if(tolower(c)==t[i]) return(i);
+ return (-1);
+}
+
+/* binary value of hex string */
+
+private int atox(char *t)
+{
+ int i=0;
+ int x;
+ while(*t)
+ {
+ if ((x=xval(*t++))<0)return (0);
+ i=(i<<4)+x;
+ }
+ return (i);
+}
+#endif /* def DOS */
+
+
+private void midi_init()
+{
+#ifdef UNIX_IRIX_MIDIFNS
+#define PBUFLEN 4
+ MIconfig *config;
+ static u_int pbuf[] = { MI_STAMPING, MINOSTAMP, MI_BLOCKING, MINONBLOCKING};
+#endif
+
+#ifdef UNIX_MACH
+ mach_midi_init();
+#else
+#ifdef ITC
+ midiconn = mi_open(NULL);
+ if (midiconn == NULL) {
+ gprintf(FATAL, "could not open a MIDI device\n");
+ EXIT(1);
+ }
+ cu_register((cu_fn_type) mi_close, (void *) midiconn);
+#endif
+#endif
+#ifdef AMIGA
+ amiga_midi_init();
+#endif /* def AMIGA */
+#ifdef DOS
+#ifndef WINDOWS
+ int err;
+ int irq=SEARCHIRQ;
+ int base=MPUBASEADDR;
+ char *t;
+
+ if (t=getenv("MPUIRQ")) {
+ if (musictrace)
+ gprintf(TRANS,"MPUIRQ %s\n",t);
+ irq=atoi(t);
+ }
+ if (t=getenv("MPUBASE")) {
+ if (musictrace)
+ gprintf(TRANS,"MPUBASE %s\n",t);
+ base=atox(t);
+ }
+ if(err = mOpen(base, irq)) {
+ mClose(err);
+ EXIT(1);
+ }
+ cu_register((cu_fn_type) mClose, 0);
+ cu_register((cu_fn_type) mPutCmd, (cu_parm_type) MPURESET);
+ initializetimer();
+ cu_register((cu_fn_type) restoretimer, NULL);
+#endif
+#endif
+
+#ifdef MACINTOSH
+#ifndef NYQUIST /* if NYQUIST, do nothing */
+#ifdef MIDIMGR
+ setup_midimgr(); /* this registers itself for cleanup */
+#else
+ init_abort_handler();
+ cu_register(cleanup_abort_handler, NULL);
+ setupMIDI(portA, 0x80);
+ cu_register(restoreMIDI, (long) portA);
+ /* only initialize portB if necessary */
+ if (MAX_CHANNELS > CHANNELS_PER_PORT) {
+ setupMIDI(portB, 0x80);
+ cu_register(restoreMIDI, (long) portB);
+ }
+#endif
+#endif /* NYQUIST */
+#ifdef MIDIMGR
+ ticksAtStart = MIDIGetCurTime(OutputRefNum);
+#else
+ ticksAtStart = TickCount(); /* reset the clock */
+#endif
+#endif /* def MACINTOSH */
+
+ if (!(cl_switch("noalloff")))
+ cu_register((cu_fn_type) alloff, NULL);
+}
+
+#ifdef DOS
+/****************************************************************************
+* set_x_mfr
+* Inputs:
+* unsigned char mfr: Manufacturer ID for MIDI
+* Result: void
+*
+* Effect:
+* Sets the xcode and xcodemask to allow only these sysex messages
+****************************************************************************/
+
+void set_x_mfr(mfr)
+unsigned char mfr;
+{
+ xcode = mfr;
+ xcodemask = 0xFF;
+}
+
+/****************************************************************************
+* clear_x_mfr
+* Result: void
+*
+* Effect:
+* Clears sysex manufacturer code filter; accepts all sysex messages
+****************************************************************************/
+
+void clear_x_mfr()
+{
+ xcode = 0;
+ xcodemask = 0;
+}
+#endif /* DOS */
diff --git a/cmt/midifns.h b/cmt/midifns.h
new file mode 100644
index 0000000..99042d3
--- /dev/null
+++ b/cmt/midifns.h
@@ -0,0 +1,137 @@
+/* midifns.h -- definitions for users of midifns.c */
+
+/*****************************************************************************
+* Change Log
+* Date | Change
+*-----------+-----------------------------------------------------------------
+* 5-Mar-92 | GWL : insert definitions and logs from JMN's mpu.h
+* for LATTICE322, only variable type in prototypes
+* 28-Apr-03 | DM : random() is now named cmtrand() to avoid conflicts
+*****************************************************************************/
+
+#ifndef _MIDIFNS_H_
+#define _MIDIFNS_H_
+
+/* declaration types */
+
+typedef unsigned long time_type;
+typedef long sgnd_time_type;
+
+/* Maximum time value: */
+#define MAXTIME 0xFFFFFFFFL
+
+#define delay_type long
+
+/* controller numbers */
+
+#define MODWHEEL 1
+#define BREATH 2
+#define FOOT 4
+#define PORTARATE 5
+#define VOLUME 7
+#define SUSTAIN 64
+#define PORTASWITCH 65
+
+#include "midierr.h"
+
+extern char *midifns_syntax;
+
+/* support for allocating sysex buffer - examples in mm.c & exget.c */
+#ifdef DOS
+#define midibuff_alloc(size) (byte huge *) halloc(size, 1)
+#endif
+#ifndef midibuff_alloc
+#define midibuff_alloc (byte *) MALLOC
+#endif
+
+/* DMH: from mpu.h -- definitions for users of mpu.c */
+
+#ifdef OLD_PROTOTYPES
+
+void eventwait();
+void exclusive(boolean);
+boolean getbuf(boolean, unsigned char * );
+long get_excl();
+boolean getxbuf();
+boolean testxbuf();
+short getkey(boolean);
+ulong gettime(void); /*DMH: note- now unsigned*/
+void l_rest(long);
+void l_restuntil(long);
+void metronome(boolean);
+void midi_bend(short,short);
+boolean midi_buffer(byte * , ulong);
+void midi_cont(boolean);
+void midi_clock();
+void midi_ctrl(short, short, short);
+void midi_exclusive(unsigned char * );
+void midi_note(short, short, short);
+void midi_program(short, short);
+void midi_real();
+void midi_start();
+void midi_stop();
+#ifdef AMIGA
+/* MIDI_THRU defined means that it is really implemented. */
+#define MIDI_THRU
+#endif
+void midi_thru();/*boolean onflag*/
+void midi_touch(short, short);
+void midi_write();
+void musicinit();
+short cmtrand(short, short);
+void read_tuning();/*char *filename*/
+void settime();
+void synth_init();/*void*/
+void timereset();
+void trace();
+void tracemidi();
+boolean is_exclusive(void);
+unsigned char get_exclusive(void);
+
+#else
+
+void alloff(void);
+void eventwait(long timeout);
+void exclusive(boolean onflag);
+long get_excl(byte *buffer, long len);
+boolean getbuf(boolean waitflag, unsigned char * p);
+short getkey(boolean waitflag);
+ulong gettime(void);
+void l_rest(long time);
+void l_restuntil(long time);
+void metronome(boolean onflag);
+void midi_bend(int channel, int value);
+boolean midi_buffer(byte *buffer, ulong size);
+void midi_clock(void);
+void midi_cont(boolean onflag);
+void midi_ctrl(int channel, int control, int value);
+void midi_exclusive(unsigned char *msg);
+void midi_flush();
+void midi_note(int channel, int pitch, int velocity);
+void midi_program(int channel, int program);
+void midi_real(boolean onflag);
+void midi_start(void);
+void midi_stop(void);
+void midi_thru(boolean onflag);
+void midi_touch(int channel, int value);
+void read_tuning(char *filename);
+void midi_write(int n, int port, unsigned char c1, unsigned char c2, unsigned char c3);
+void midi_write_trace(int n, int port,
+ unsigned char c1, unsigned char c2, unsigned char c3);
+void musicinit(void);
+void settime(time_type newtime);
+void timereset(void);
+void trace(boolean flag);
+void tracemidi(boolean flag);
+
+
+boolean check_midi(void);
+#endif /* ifdef OLD_PROTOTYPES */
+
+
+#ifdef AMIGA
+byte *head_of_excl();
+byte *tail_of_excl();
+#endif
+
+#endif /* _MIDIFNS_H_ */
diff --git a/cmt/midimgr.c b/cmt/midimgr.c
new file mode 100644
index 0000000..e7db136
--- /dev/null
+++ b/cmt/midimgr.c
@@ -0,0 +1,784 @@
+/* midimgr.c -- this file contains interface code to support use of Apple Midi Manager */
+/*
+ * This code is based on code supplied with the Apple Midi Manager.
+ * Copyright 1991, Carnegie Mellon University
+ */
+
+/* BUGS:
+ * If exclusive() is called to turn exclusive messages on or off DURING the
+ * receipt of an exclusive message, incoming data will be garbled. The correct
+ * handling would be to record when receipt of an exclusive message is in
+ * progress, then properly remove any partial message when exclusive is turned
+ * off, and ignore any remaining message part when exclusive is turned on.
+ * The present code does neither.
+ */
+
+#include "cext.h"
+#undef round
+#ifdef THINK_C
+#include <pascal.h> /* for ThinkC 7 */
+#endif
+
+#include "stdio.h"
+#include "userio.h"
+#include "MIDI.h"
+#include "midifns.h"
+#include "midibuff.h"
+#include "midierr.h"
+#include "midimgr.h"
+#include "midicode.h"
+#include "cmdline.h"
+
+/* Needed for KillEverybody */
+#include <toolutils.h>
+#include <AppleEvents.h>
+#include <EPPC.h>
+#include <Gestalt.h>
+#include <PPCToolbox.h>
+#include <Processes.h>
+#include <Sound.h>
+
+
+#define CMTclientID 'CMT '
+/* note the following are in alphabetical order for Patcher display */
+#define timePortID 'Atim'
+#define inputPortID 'Bin '
+#define outputPortID 'Cout'
+#define noClient ' '
+
+#define noTimeBaseRefNum 0
+#define noReadHook 0L
+#define zeroTime 0L
+#define timePortBuffSize 0L
+#define inputPortBuffSize 2048
+#define outputPortBuffSize 0L
+#define refCon0 0L
+
+pascal short CMTreader(MIDIPacket *ThePacketPtr, long TheRefCon);
+
+/* "patch" switch from command line. This switch is cached in patch_flag and tells
+ whether to look in the resource fork for a patch, or just hook up to midi in and
+ out. If the resource fork is used, the patch will be saved upon exit. */
+private boolean patch_flag;
+extern boolean ctrlFilter;
+extern boolean exclFilter;
+extern boolean realFilter;
+
+private midi_read_lock = false; /* used to stop input during data structure manipulation */
+
+private void set_error(int bit);
+#ifndef NYQUIST
+void PatchPorts(void);
+void SavePatch(OSType PortID, short PortInfoResID, char *PortInfoResName);
+#endif
+
+/* exported: */
+public short InputRefNum; /* Input port reference number. */
+public short OutputRefNum; /* Output port reference number. */
+public short TimeRefNum; /* Time base port reference number. */
+
+Boolean GManualPatch; /* True if not launched by a PatchBay Config. File. */
+
+/****************************************************************************
+*
+* variables shared with other modules
+*
+****************************************************************************/
+
+/* midi input buffer */
+long buff[BUFF_SIZE/4]; /* data buffer, declared long to get 32-bit alignment */
+int buffhead = 0; /* buffer head and tail pointers */
+int bufftail = 0;
+
+/* user supplied system exclusive buffer */
+byte *xbuff = NULL; /* address of the user-supplied buffer */
+public long xbufmask; /* mask for circular buffer address calculation */
+long xbufhead = 0; /* buffer head and tail pointers */
+long xbuftail = 0;
+boolean xbuf_flush = true; /* says to flush remainder of sysex message */
+
+#ifdef SYSEXDEBUG
+int sysexcount = 0; /* for debugging */
+int sysexdone = 0;
+int sysexheadcount = 0;
+byte sysexfirst = 0;
+int sysexsysex = 0;
+#endif
+
+/* midi_flush -- empty out buffers */
+/**/
+void midi_flush()
+{
+ midi_read_lock = true;
+ buffhead = 0;
+ bufftail = 0;
+ xbufhead = 0;
+ xbuftail = 0;
+ xbuf_flush = true; /* in case sysex continuation messages are still coming */
+ midi_read_lock = false;
+}
+
+
+/* Nyquist only uses CMT for Midi and Adagio file IO */
+#ifndef NYQUIST
+/* Get String representation of MIDI Mgr Version Num.*/
+/* See Mac Tech Note #189 for details. */
+char *StdMacVerNumToStr(long VerNum, char *VerStr)
+{
+ char *RetVal;
+ char MajVer, MinVer, VerStage, VerRev, BugFixVer = 0;
+
+ if (VerNum == 0)
+ {
+ RetVal = NULL;
+ }
+ else
+ {
+ MajVer = (VerNum & 0xFF000000) >> 24;
+ MinVer = (VerNum & 0x00FF0000) >> 16;
+ VerStage = (VerNum & 0x0000FF00) >> 8;
+ VerRev = (VerNum & 0x000000FF) >> 0;
+ BugFixVer = MinVer & 0x0F;
+
+ switch (VerStage)
+ {
+ case 0x20:
+ VerStage = 'd';
+ break;
+ case 0x40:
+ VerStage = 'a';
+ break;
+ case 0x60:
+ VerStage = 'b';
+ break;
+ case 0x80:
+ VerStage = ' ';
+ break;
+ default:
+ VerStage = '?';
+ break;
+ }
+
+ if (BugFixVer == 0)
+ {
+ sprintf(VerStr,"%X.%X%c%X",
+ MajVer, MinVer>>4, VerStage, VerRev);
+ }
+ else
+ {
+ sprintf(VerStr,"%X.%X.%X%c%X",
+ MajVer, MinVer >> 4, MinVer & 0x0F, VerStage, VerRev);
+ }
+
+ RetVal = VerStr;
+ }
+
+ return(RetVal);
+}
+
+
+/* C2PStrCpy -- Convert a C String (from Cstr) into a Pascal string */
+/*
+ * NOTE: this is not the same code as shipped with midi manager example
+ */
+char *C2PStrCpy(char *Cstr, Str255 Pstr)
+{
+ char *c = Cstr;
+ char *p = ((char *) Pstr) + 1;
+
+ while (*c) *p++ = *c++;
+ *Pstr = c - Cstr;
+ return( (char *) Pstr );
+}
+
+/* This checks to see if THINK C is running under System 7,
+ and ONLY WORKS UNDER SYSTEM 7! Don't use unless you check! */
+boolean ThinkCRunning(void)
+{
+ ProcessSerialNumber processSN;
+ OSErr myErr;
+ ProcessInfoRec infoRec;
+
+ processSN.lowLongOfPSN = kNoProcess;
+ processSN.highLongOfPSN = kNoProcess;
+ do {
+ myErr = GetNextProcess(&processSN);
+
+ infoRec.processInfoLength = sizeof(ProcessInfoRec);
+ infoRec.processName = 0L;
+ infoRec.processAppSpec = 0L;
+ myErr = GetProcessInformation(&processSN, &infoRec);
+ if (!myErr) {
+ if (infoRec.processSignature == 'KAHL') {
+ return(true);
+ }
+ }
+ } while (myErr == noErr);
+ return(false);
+}
+
+/* This kills off all the other running processes...
+ ONLY WORKS UNDER SYSTEM 7! Don't use unless you check! */
+void KillEverybody(void)
+{
+ ProcessSerialNumber myProc, processSN;
+ ProcessSerialNumber finderPSN;
+ ProcessInfoRec infoRec;
+ Str31 processName;
+ FSSpec procSpec;
+
+ OSErr myErr = noErr;
+ OSErr otherError;
+ AppleEvent theEvent;
+ AEDesc theAddress;
+ Boolean ourFlag, notFinder;
+ Boolean finderFound = false;
+
+ GetCurrentProcess(&myProc);
+ /* Preset the PSN to no PSN, see IM VI, the Process Manager */
+ processSN.lowLongOfPSN = kNoProcess;
+ processSN.highLongOfPSN = kNoProcess;
+ finderPSN.lowLongOfPSN = 0UL; /* brk: was nil */
+ finderPSN.highLongOfPSN = 0UL; /* brk: was nil */
+
+ do {
+ myErr = GetNextProcess(&processSN);
+ /* See if it's us first */
+ notFinder = true;
+ SameProcess(&myProc, &processSN, &ourFlag);
+
+ infoRec.processInfoLength = sizeof(ProcessInfoRec);
+ infoRec.processName = (StringPtr) &processName;
+ infoRec.processAppSpec = &procSpec;
+ GetProcessInformation(&processSN, &infoRec);
+ if (!ourFlag && !finderFound) {
+ /* see if it's the Finder, we have to kill the finder LAST */
+ /* or else non-sys 7 apps won't get killed */
+ /* since the Finder must be there to convert the AppleEvent to Puppet Strings */
+ /* if the app is not APpleEvent aware */
+ /* Also, FileShare HAS to be killed before the Finder */
+ /* or your life will be unpleasant */
+
+ if (infoRec.processSignature == 'MACS' && infoRec.processType == 'FNDR') {
+ /* save this number for later */
+ finderPSN = processSN;
+ notFinder = false;
+ finderFound = true;
+
+ } else {
+ notFinder = true;
+ }
+ }
+ if (!myErr && !ourFlag && notFinder) {
+ otherError = AECreateDesc(typeProcessSerialNumber, (Ptr)&processSN, sizeof(processSN), &theAddress);
+ if (!otherError)
+ otherError = AECreateAppleEvent(kCoreEventClass, kAEQuitApplication, &theAddress, kAutoGenerateReturnID,
+ kAnyTransactionID, &theEvent);
+ if (!otherError)
+ AEDisposeDesc(&theAddress);
+ /* Again, the Finder will convert the AppleEvent to puppetstrings if */
+ /* the application is a System 6 or non-AE aware app. This ONLY */
+ /* happens for the 4 required (oapp,odoc,pdoc, and quit) AppleEvents */
+ /* and ONLY if you use the PSN for the address */
+ if (!otherError)
+ AESend(&theEvent, 0L, kAENoReply + kAEAlwaysInteract + kAECanSwitchLayer, kAENormalPriority, kAEDefaultTimeout,
+ 0L, 0L);
+ AEDisposeDesc(&theEvent);
+ }
+ } while (!myErr);
+
+ /* Now, if the finder was running, it's safe to kill it */
+ if (finderPSN.lowLongOfPSN || finderPSN.highLongOfPSN) {
+ otherError = AECreateDesc(typeProcessSerialNumber, (Ptr)&finderPSN, sizeof(processSN), &theAddress);
+ if (!otherError)
+ otherError = AECreateAppleEvent(kCoreEventClass, kAEQuitApplication, &theAddress, kAutoGenerateReturnID,
+ kAnyTransactionID, &theEvent);
+ if (!otherError)
+ AEDisposeDesc(&theAddress);
+ if (!otherError)
+ AESend(&theEvent, 0L, kAENoReply + kAEAlwaysInteract + kAECanSwitchLayer, kAENormalPriority, kAEDefaultTimeout, 0L,
+ 0L);
+ AEDisposeDesc(&theEvent);
+ }
+}
+
+/* Sign into the MIDI Manager. */
+/* Set up time, input, and output ports. */
+/* Start our time base clock. */
+void setup_midimgr(void)
+{
+ MIDIPortParams Init; /* MIDI Mgr Init data structure */
+ Handle TheIconHndl;
+ OSErr TheErr;
+ long MIDIMgrVerNum; /* MIDI Manager Ver (Std Mac Ver #) */
+ Str255 name = "\pCMU MIDI Toolkit";
+ char MIDIMgrVerStr[256]; /* MIDI Manager Ver (Std Mac Ver # String) */
+ long vers;
+ EventRecord theEvent;
+
+ Gestalt(gestaltSystemVersion, &vers);
+ vers = (vers >> 8) & 0xf; /* shift result over and mask out major version number */
+ if ((vers >= 7) && (!cl_switch("keep")) && (!ThinkCRunning())) {
+ gprintf(TRANS,"Killing other processes...\n");
+ KillEverybody();
+ for (vers=0; vers<100; ++vers) {
+ while (WaitNextEvent(everyEvent, &theEvent, 0L, 0L)) ;
+ }
+ }
+
+ /* Make sure MIDIMgr is installed and save version num. */
+ MIDIMgrVerNum = SndDispVersion(midiToolNum);
+ if (MIDIMgrVerNum == 0) {
+ gprintf(ERROR, "The MIDI Manager is not installed! Exiting...\n");
+ EXIT(1);
+ } else {
+ StdMacVerNumToStr(MIDIMgrVerNum, MIDIMgrVerStr);
+ gprintf(TRANS,"MIDI Manager Version %s\n", MIDIMgrVerStr);
+ }
+
+
+ /* Sign in to the MIDI Manager. */
+ TheIconHndl = GetResource('ICN#', 1);
+ TheErr = MIDISignIn(CMTclientID,
+ 0L,
+ TheIconHndl,
+ name);
+ if (TheErr) {
+ gprintf(ERROR, "Trouble signing into MIDI Manager! Aborting...");
+ EXIT(1);
+ }
+
+ /* Assume not a Patchbay configuration. */
+ GManualPatch = true;
+
+ /* Add time port. */
+ Init.portID = timePortID;
+ Init.portType = midiPortTypeTime;
+ Init.timeBase = noTimeBaseRefNum;
+ Init.readHook = noReadHook;
+ Init.initClock.syncType = midiInternalSync;
+ Init.initClock.curTime = zeroTime;
+ Init.initClock.format = midiFormatMSec;
+ Init.refCon = SetCurrentA5();
+ C2PStrCpy("TimeBase", Init.name);
+ TheErr = MIDIAddPort(CMTclientID, timePortBuffSize, &TimeRefNum, &Init);
+ /* Has a PatchBay connection been resolved? */
+ if (TheErr == midiVConnectMade) {
+ GManualPatch = false;
+ } else if (TheErr == memFullErr) {
+ gprintf(ERROR, "Not enough room in heap zone to add time port! Aborting...");
+ MIDISignOut(CMTclientID);
+ EXIT(1);
+ }
+
+ /* Add an input port. */
+ Init.portID = inputPortID;
+ Init.portType = midiPortTypeInput;
+ Init.timeBase = TimeRefNum;
+ Init.offsetTime = midiGetCurrent;
+ Init.readHook = NewMIDIReadHookProc(CMTreader);
+ Init.refCon = SetCurrentA5();
+ C2PStrCpy("InputPort", Init.name);
+ TheErr = MIDIAddPort(CMTclientID, inputPortBuffSize, &InputRefNum, &Init);
+ /* Has a PatchBay connection been resolved? */
+ if (TheErr == midiVConnectMade) {
+ GManualPatch = false;
+ } else if (TheErr == memFullErr) {
+ gprintf(ERROR, "Not enough room in heap zone to add input port! Aborting...");
+ MIDISignOut(CMTclientID);
+ EXIT(1);
+ }
+
+ /* Add an output port. */
+ Init.portID = outputPortID;
+ Init.portType = midiPortTypeOutput;
+ Init.timeBase = TimeRefNum;
+ Init.offsetTime = midiGetCurrent;
+ Init.readHook = NULL;
+ Init.refCon = refCon0;
+ C2PStrCpy("OutputPort", Init.name);
+ TheErr = MIDIAddPort(CMTclientID, outputPortBuffSize, &OutputRefNum, &Init);
+ /* Has a PatchBay connection been resolved? */
+ if (TheErr == midiVConnectMade) {
+ GManualPatch = false;
+ } else if (TheErr == memFullErr) {
+ printf("Not enough room in heap zone to add output port! Aborting...");
+ MIDISignOut(CMTclientID);
+ EXIT(1);
+ }
+
+ if (GManualPatch) {
+ PatchPorts(); /* connect ports as they were */
+ }
+ /* to clean this up (later) call finish_midimgr() */
+ cu_register((cu_fn_type) finish_midimgr, (cu_parm_type) finish_midimgr);
+
+ /* Start our Clock. */
+ MIDIStartTime(TimeRefNum);
+}
+
+
+/* The Read Hook Function. */
+
+/* 1st 4 bytes of sysex message get saved here and enqueued later */
+char save_sysex_head[4];
+int save_sysex_head_x = 0;
+
+void sysex_insert(unsigned char data) {
+ if (save_sysex_head_x < 4) {
+ save_sysex_head[save_sysex_head_x++] = data;
+ }
+ xbuff[xbuftail++] = data;
+ xbuftail &= xbufmask;
+ if (xbuftail == xbufhead) {
+ set_error(SYSEXOVFL);
+ }
+ if (data == MIDI_EOX) { /* we're done with the message */
+ *((long *) (((byte *) buff) + bufftail)) = *((long *)save_sysex_head);
+ bufftail = (bufftail + 4) & BUFF_MASK;
+ if (bufftail == buffhead) {
+ set_error(BUFFOVFL);
+ }
+ }
+}
+
+/* Read all incomming MIDI data. */
+
+pascal short CMTreader(MIDIPacket *ThePacketPtr, long TheRefCon)
+{
+ /* Set up our A5 world. */
+ long SysA5 = SetA5(TheRefCon);
+ short RetVal = midiMorePacket, i, j;
+ unsigned char *mm_data = ThePacketPtr->data;
+ register byte data1 = mm_data[1];
+
+ if (midi_read_lock) {
+ /* Don't want to read packet now, get it later */
+ /* DOES THIS REALLY WORK? WHAT WILL CAUSE AN INTERRUPT
+ * TO OCCUR LATER? THIS IS ONLY USED BY midi_flush, IS
+ * BASED ON THE MidiArp CODE FROM APPLE, AND IS UNTESTED - RBD
+ */
+ RetVal = midiKeepPacket;
+ goto alldone;
+ }
+
+ /* see if Packet is an error message */
+ if (((ThePacketPtr->flags & midiTypeMask) == midiMgrType) &&
+ *((short *) (&(ThePacketPtr->data))) < midiMaxErr) {
+ set_error(MIDIMGRERR);
+ goto alldone;
+ }
+
+ /* filter out control changes */
+ if (ctrlFilter) {
+ register int hibits = *mm_data & 0xF0;
+ if (hibits == 0xD0 || /* Chan Pressure */
+ hibits == 0xE0 || /* Pitch Bend */
+ hibits == 0xA0 || /* Poly Pressure */
+ ((hibits == 0xB0) && /* Control change (don't count switches) */
+ ((data1 < 64) || (data1 > 121)))) {
+ /* CONTROL MESSAGE HAS BEEN FILTERED */
+ goto alldone;
+ }
+ } else if (realFilter) {
+ register int hibits = *mm_data & 0xF0;
+ if (hibits >= 0xF8) goto alldone;
+ }
+
+
+ /* if not a continuation, copy the data into cmt_data */
+ /* The logic to detect a non-continued
+ * packet or a first packet is: "flags bit 1 is clear".
+ */
+ if ((((ThePacketPtr->flags & midiContMask) == midiNoCont)) &&
+ (*mm_data != MIDI_SYSEX)) {
+ register byte *cmt_data = ((byte *) buff) + bufftail;
+ *((long *) cmt_data) = *((long *) mm_data);
+
+ bufftail = (bufftail + 4) & BUFF_MASK;
+ if (bufftail == buffhead) {
+ /* filled buffer faster than client emptied it */
+ set_error(BUFFOVFL);
+ }
+ }
+
+ /* see if we have a sysex message to copy to buffer */
+ if (xbuff && !exclFilter &&
+ ((ThePacketPtr->flags & midiContMask) || *mm_data == MIDI_SYSEX)) {
+ int i;
+ register byte *x_data = xbuff + xbuftail;
+
+ /* iterate over data in message */
+ /* NOTE: in the previous implementation, I thought Sysex messages were
+ * always starting at the beginning of the buffer, but that didn't work.
+ * This implementation assumes nothing -- it is slower because of additional
+ * testing and parsing inside the loop, but seems to work.
+ */
+ for (i = ThePacketPtr->len - 6; i > 0; i--) {
+ if (xbuf_flush) { /* we're searching for beginning of message */
+ if (*mm_data == MIDI_SYSEX) {
+ xbuf_flush = false;
+ sysex_insert(MIDI_SYSEX);
+ }
+ } else { /* we're scanning to the end of the message */
+ if (*mm_data == MIDI_SYSEX) { /* found it, insert proper EOX */
+ sysex_insert(MIDI_EOX);
+ sysex_insert(MIDI_SYSEX);
+ } else if (*mm_data == MIDI_EOX) { /* found it */
+ sysex_insert(MIDI_EOX);
+ xbuf_flush = true;
+ } else sysex_insert(*mm_data);
+
+ }
+ mm_data++;
+ }
+ }
+alldone:
+
+ /* Restore the systems A5 world. */
+ SetA5(SysA5);
+
+ return(RetVal);
+}
+
+
+/* Sign out from the MIDI Manager. */
+void finish_midimgr(void)
+{
+ if (GManualPatch && patch_flag) {
+ SavePatch(timePortID, timePortResInfoID, "timePortInfo");
+ SavePatch(inputPortID, inputPortResInfoID, "inputPortInfo");
+ SavePatch(outputPortID, outputPortResInfoID, "outputPortInfo");
+ }
+ MIDISignOut(CMTclientID);
+}
+
+
+
+/* Alert user to Resource Manager Error. */
+void
+ReportResError(char *Msg)
+{
+ OSErr TheErr;
+ char Buf[256];
+
+ if ( (TheErr = ResError()) != noErr) {
+ gprintf(ERROR, "ResError %d: %s...Aborting.", TheErr, Msg);
+ EXIT(1);
+ } else {
+ /* gprintf(ERROR, "%s OK\n", Msg); */
+ }
+}
+
+
+/****************************************************************************
+* error handling
+* Effect:
+* various error conditions are flagged by setting bits in
+* the global midi_error_flags. it is up to the client to clear this
+* word when necessary.
+****************************************************************************/
+
+private void set_error(int bit)
+{
+ midi_error_flags |= (1 << bit);
+}
+
+
+void midi_show_errors()
+{
+ if (midi_error_flags & (1<<BUFFOVFL))
+ gprintf(ERROR, "Midi Buffer Overflow Error\n");
+ if (midi_error_flags & (1<<MIDIMGRERR))
+ gprintf(ERROR, "Midi Manager Error\n");
+ if (midi_error_flags & (1<<SYSEXOVFL))
+ gprintf(ERROR, "Midi Sysex Overflow Error\n");
+}
+
+
+/**************** PATCHING CODE ***************/
+
+/*
+ MIDIArp Time, Input, and Output Port
+ Info Record Resource ID's.
+*/
+
+/* Get previously saved port connections (port info records) */
+/* from application's 'port' resource. */
+void
+PatchPorts(void)
+{
+ MIDIPortInfoHdl PortInfoH; /* Handle to port info record. */
+ MIDIPortInfoPtr PortInfoP; /* Pointer to port info record. */
+ short i, TheErr;
+
+ patch_flag = cl_switch("patch");
+
+ /* SET UP TIME PORT CONNECTIONS. */
+ if (patch_flag)
+ PortInfoH = (MIDIPortInfoHdl) GetResource(portResType, timePortResInfoID);
+ if (!patch_flag || PortInfoH == NULL) {
+ MIDIIDListHdl clients, ports;
+ OSErr err;
+
+ gprintf(TRANS, "Connecting to MIDI IN and OUT\n");
+#ifdef MIDIMGR_VERBOSE
+ clients = MIDIGetClients();
+ gprintf(TRANS, "clients = %lx\n", clients);
+ HLock((Handle) clients);
+
+ for (i = 0; i < (*clients)->numIDs; i++) {
+ OSType id = (*clients)->list[i];
+ gprintf(TRANS, "%d: %c%c%c%c\n", i, (char) (id>>24),
+ (char) ((id >> 16) & 0xFF), (char) ((id >> 8) & 0xFF),
+ (char) (id & 0xFF));
+ }
+ ports = MIDIGetPorts('amdr');
+ HLock((Handle) ports);
+ for (i = 0; i < (*ports)->numIDs; i++) {
+ OSType id = (*ports)->list[i];
+ gprintf(TRANS, "%d: %c%c%c%c\n", i, (char) (id>>24),
+ (char) ((id >> 16) & 0xFF), (char) ((id >> 8) & 0xFF),
+ (char) (id & 0xFF));
+ }
+ HUnlock((Handle) ports);
+ HUnlock((Handle) clients);
+#endif
+ /* the work starts here */
+ err = MIDIConnectData('CMT ', 'Cout', 'amdr', 'Aout');
+ /* gprintf(TRANS, "Connected CMT.Cout to amdr.Aout: %d\n", err); */
+ err = MIDIConnectData('amdr', 'Ain ', 'CMT ', 'Bin ');
+ /* gprintf(TRANS, "Connected amdr.Ain to CMT.Bin: %d\n", err); */
+
+ return;
+ }
+ HLock((Handle) PortInfoH);
+ PortInfoP = *PortInfoH;
+ if (GetHandleSize((Handle) PortInfoH) != 0)
+ {
+ /* Were we supposed to be sync'd to another client? */
+ if (PortInfoP->timeBase.clientID != noClient)
+ {
+ /* Yes, so make that client our time base. */
+ TheErr = MIDIConnectTime(
+ PortInfoP->timeBase.clientID,
+ PortInfoP->timeBase.portID,
+ CMTclientID,
+ timePortID
+ );
+#ifdef IGNORE
+ /* Is the client still signed in? */
+ if (TheErr != midiVConnectErr)
+ {
+ /* Yes, so set our sync mode to external. */
+ MIDISetSync(ArpGlobals.TimeRefNum, midiExternalSync);
+ }
+#endif
+
+ }
+ /* Were we somebody else's time base? */
+ for (i=0; i<PortInfoP->numConnects; i++)
+ {
+ MIDIConnectTime(CMTclientID,
+ timePortID,
+ PortInfoP->cList[i].clientID,
+ PortInfoP->cList[i].portID);
+ }
+ }
+ HUnlock((Handle) PortInfoH);
+ ReleaseResource((Handle) PortInfoH);
+ ReportResError("PatchPorts/ReleaseResource()");
+
+ /* SET UP INPUT PORT CONNECTIONS. */
+ PortInfoH = (MIDIPortInfoHdl) GetResource(portResType, inputPortResInfoID);
+ if (PortInfoH == NULL)
+ {
+ ReportResError("PatchPorts/GetResource()");
+ }
+ HLock((Handle) PortInfoH);
+ PortInfoP = *PortInfoH;
+ if (GetHandleSize((Handle) PortInfoH) != 0)
+ {
+ /* Were we connected to anyone? */
+ for (i=0; i<PortInfoP->numConnects; i++)
+ {
+ MIDIConnectData(CMTclientID,
+ inputPortID,
+ PortInfoP->cList[i].clientID,
+ PortInfoP->cList[i].portID);
+ }
+ }
+ HUnlock((Handle) PortInfoH);
+ ReleaseResource((Handle) PortInfoH);
+ ReportResError("PatchPorts/GetResource()");
+
+ /* SET UP OUTPUT PORT CONNECTIONS. */
+ PortInfoH = (MIDIPortInfoHdl) GetResource(portResType, outputPortResInfoID);
+ if (PortInfoH == NULL)
+ {
+ ReportResError("PatchPorts/GetResource()");
+ }
+ HLock((Handle) PortInfoH);
+ PortInfoP = *PortInfoH;
+ if (GetHandleSize((Handle) PortInfoH) != 0) {
+ /* Were we connected to anyone? */
+ for (i=0; i<PortInfoP->numConnects; i++)
+ {
+ MIDIConnectData(CMTclientID,
+ outputPortID,
+ PortInfoP->cList[i].clientID,
+ PortInfoP->cList[i].portID);
+ }
+ }
+ HUnlock((Handle) PortInfoH);
+ ReleaseResource((Handle) PortInfoH);
+ ReportResError("PatchPorts/ReleaseResource()");
+
+}
+
+/* Save current port connections (port info records) */
+/* to application's 'port' resource. */
+void
+SavePatch(OSType PortID, short PortInfoResID, char *PortInfoResName)
+{
+ Handle PortResH; /* Handle to ptch resource. */
+ CursHandle WatchCurs;
+
+ WatchCurs = GetCursor(watchCursor);
+ HLock((Handle) WatchCurs);
+ SetCursor(*WatchCurs);
+ HUnlock((Handle) WatchCurs);
+
+
+ /* Remove existing port info resource. */
+ PortResH = GetResource(portResType, PortInfoResID);
+ /* gprintf(TRANS, "PortResH: %lx, *PortResH: %lx\n", PortResH, *PortResH); */
+ if (PortResH) {
+ ReportResError("SavePatch/GetResource()");
+ RmveResource(PortResH);
+ ReportResError("SavePatch/RmveResource()");
+ DisposHandle(PortResH);
+ UpdateResFile(CurResFile());
+ ReportResError("SavePatch/UpdateResFile()");
+ }
+
+ /* Get new configurateion. */
+ PortResH = (Handle) MIDIGetPortInfo(CMTclientID, PortID);
+
+ /* Save new configurateion. */
+ CtoPstr(PortInfoResName);
+ AddResource(PortResH, portResType, PortInfoResID,
+ (ConstStr255Param) PortInfoResName);
+ PtoCstr((unsigned char *) PortInfoResName);
+
+ ReportResError("SavePatch/AddResource()");
+ WriteResource(PortResH);
+ ReportResError("SavePatch/WriteResource()");
+ UpdateResFile(CurResFile());
+ ReportResError("SavePatch/UpdateResFile()");
+ ReleaseResource(PortResH);
+ ReportResError("SavePatch/ReleaseResource()");
+
+ InitCursor();
+}
+#endif /* NYQUIST */
diff --git a/cmt/midimgr.h b/cmt/midimgr.h
new file mode 100644
index 0000000..790fc06
--- /dev/null
+++ b/cmt/midimgr.h
@@ -0,0 +1,14 @@
+#define portResType 'port'
+#define timePortResInfoID 128
+#define inputPortResInfoID 129
+#define outputPortResInfoID 130
+
+
+
+extern short InputRefNum; /* Input port reference number. */
+extern short OutputRefNum; /* Output port reference number. */
+extern short TimeRefNum; /* Time base port reference number. */
+
+void setup_midimgr(void);
+void finish_midimgr(void);
+void midi_show_errors();
diff --git a/cmt/moxc.c b/cmt/moxc.c
new file mode 100644
index 0000000..f62d65d
--- /dev/null
+++ b/cmt/moxc.c
@@ -0,0 +1,669 @@
+/* MOXC -- a C version of Collinge's MOXIE language */
+/* Copyright 1989 Carnegie Mellon University */
+
+/*****************************************************************************
+* Change Log
+* Date | Change
+*-----------+-----------------------------------------------------------------
+* 31-Dec-85 | Modified for use with midi
+* 5-Feb-86 | Added m_rest and m_restuntil allowing rests at top level
+* 28-May-86 | Added command line parsing
+* 4-Jun-86 | changed keyevent to separate calls for each event type
+* 10-Jul-86 | put loop in mainscore with prompt to play and replay
+* 03-Jun-88 | modified for portability (AMIGA) -JCD
+* 07-Jul-89 | time bases -RBD
+* 31-Jan-90 | GWL : cleaned up for LATTICE
+* 30-Jun-90 | RBD : further changes
+* 2-Apr-91 | JDW : further changes
+* 4-Mar-91 | GWL : DOS allows odd inst addrs
+* 10-Oct-94 | nix : posicionador tridimensionale interface
+* 28-Apr-03 | DM : true->TRUE, false->FALSE
+*****************************************************************************/
+
+#include "switches.h"
+
+#ifdef AMIGA
+#ifdef AZTEC
+#include "functions.h"
+#else
+#include "amiga.h"
+#endif
+#include "exec/exec.h"
+#include "cmtcmd.h"
+
+extern long event_mask; /* imported from midifns.c */
+#endif
+extern int abort_flag; /*DMH: taken out of ifdef AMIGA for moxcrun*/
+
+#include "stdio.h"
+#include "cext.h"
+#include "userio.h"
+#include "midifns.h"
+
+#include "cmdline.h"
+#include "midicode.h"
+#include "timebase.h"
+#include "moxc.h"
+
+#ifdef AMIGA /*DMH: only AMIGA cares about AMIGA's "proportional controllers"*/
+#include "prop1.h"
+#endif
+#ifdef POSICIONADOR_3D
+#include "pos3d.h"
+#include "pos3dbuf.h"
+#endif /* POSICIONADOR_3D */
+
+extern char *app_syntax;
+
+/***************************************************************************
+*
+* IMPORTS:
+* asciievent(k) user-defined action for terminal input
+* bendchange(ch, val) user-defined pitch bend handler
+* ctrlchange(ch, c, val) user-defined control change handler
+* keydown(ch, p, v) user-defined MIDI note on handler
+* keyup(ch, p) user-defined MIDI note off handler
+* mainscore() user-defined first action(s)
+* musicfns lots of time and io functions
+* peddown(ch) user-defined pedal down handler
+* pedup(ch) user-defined pedal up handler
+* touchchange(ch, val) user-defined aftertouch handler
+* app_syntax string defining extra command line options
+*
+* EXPORTS:
+*
+* cause(delay, routine, p1, p2, ..., p8)
+* moxcdone -- set to TRUE to quit
+* eventtime -- ideallized current time
+*
+*****************************************************************************/
+
+#define SAFEMOXC TRUE
+#define BREAKKEY 0x03
+
+int moxcdone; /* flag to halt execution */
+time_type eventtime; /* time of current call -- used to avoid */
+ /* timing errors due to finite execution speed */
+time_type virttime; /* virtual time of current call */
+timebase_type timebase; /* time base of current call */
+int mididecode = TRUE; /* whether to decode messages or just call midievent */
+
+int debug = FALSE;
+int moxcdebug = FALSE;
+time_type next_wakeup;
+timebase_type default_base;
+
+#ifdef AMIGA
+int pub_port_signal;
+struct MsgPort pub_port;
+#endif
+
+/*****************************************************************************
+* Routines local to this module
+*****************************************************************************/
+
+private void callrun();
+private void decode();
+private void moxcterm();
+
+/****************************************************************************
+* callallcancel
+* Inputs:
+* timebase_queue
+* Effect:
+* return all calls to free list
+* Implementation:
+* If timebase_queue is not empty, there's a pending call. Remove the call
+* (not necessarily the timebase) and repeat.
+****************************************************************************/
+
+void callallcancel()
+{
+ if (moxcdebug) gprintf(GDEBUG, "cancel all calls\n");
+ while (timebase_queue) {
+ timebase = timebase_queue;
+ timebase_queue = timebase->next;
+ while (timebase->heap_size > 0) {
+ call_free(remove_call(timebase));
+ }
+ insert_base(timebase);
+ }
+}
+
+/* catchup -- bring current timebase up to date by running its calls */
+/**/
+void catchup()
+{
+ register call_type call;
+ /* Remember where we're going in virtual time because setting the
+ * rate will also modify timebase->virt_base. We don't want catchup
+ * to stop short:
+ */
+ time_type target_time = timebase->virt_base;
+ /* remember timebase here because it's possible that a call will do
+ * a timebase_use() and change it:
+ */
+ register timebase_type my_base = timebase;
+
+ while (my_base->heap_size != 0 &&
+ (my_base->heap[1]->u.e.time < target_time)) {
+ /* eventtime is the real time at which something was scheduled */
+ eventtime = (my_base->next_time) >> 8;
+ call = remove_call(my_base);
+ virttime = call->u.e.time;
+ (*(call->u.e.routine))(CALLARGS(call));
+ call_free(call);
+ }
+ /* now that we've possibly pulled events out of the timebase, adjust
+ * the position in the timebase queue (and possibly remove it).
+ */
+ remove_base(my_base);
+ insert_base(my_base);
+}
+
+
+
+/****************************************************************************
+* cause
+* Inputs:
+* delay_type (long) delay: time before this call should occur
+* int (*routine)(): routine that implements the call
+* int p1 through p8: parameters to pass to routine
+* Effect:
+* builds a call and puts it in pending queue for later scheduling
+****************************************************************************/
+
+#ifndef DOTS_FOR_ARGS
+void cause(delay, routine, p)
+ delay_type delay;
+ int (*routine)();
+ call_args_node p;
+#else
+#include <stdarg.h>
+
+void cause(delay_type delay, ...)
+/* note: the routine parameter is not checked because any routine type can
+ be passed as a parameter, but in the call struct it's an int (*)()
+ */
+#endif
+{
+ register call_type call = call_alloc();
+#ifdef DOTS_FOR_ARGS
+ va_list xp;
+#endif
+
+ if (!call) {
+ gprintf(ERROR, "cause: out of memory\n");
+ EXIT(1);
+ }
+
+#ifdef DOTS_FOR_ARGS
+ call->u.e.time = virttime + delay;
+ call->u.e.priority = 128; /* default priority */
+ va_start(xp, delay);
+ call->u.e.routine = (int (*)()) va_arg(xp, long *);
+ call->u.e.p = va_arg(xp, call_args_node);
+ va_end(xp);
+#else
+ call->u.e.time = virttime + delay;
+ call->u.e.priority = 128; /* default priority */
+ call->u.e.routine = routine;
+ call->u.e.p = p;
+#endif
+#ifdef SAFEMOXC
+ if (call->u.e.routine == 0) {
+ gprintf(ERROR,"cause called with NULL routine\n");
+ EXIT(1);
+#ifndef DOS /* IBM allows odd addresses */
+#if (__APPLE__ != 1 || __i386__ != 1) /* Intel Mac allows odd addresses */
+ } else if (((long) call->u.e.routine) & 1) {
+ gprintf(ERROR, "cause called with bad routine address: 0x%lx\n",
+ call->u.e.routine);
+#ifndef GCC_MODEL_CPU
+#define GCC_MODEL_CPU "GCC_MODEL_CPU is undefined for this compilation"
+#endif
+ gprintf(ERROR, GCC_MODEL_CPU);
+ EXIT(1);
+#endif
+#endif
+ }
+#endif
+ /* put call in default queue */
+ callinsert(timebase, call);
+ if (moxcdebug) {
+ gprintf(GDEBUG,"(cause) call is pending on timebase 0x%x:\n", timebase);
+ callshow(call);
+ }
+}
+
+/****************************************************************************
+* causepri
+* Inputs:
+* int delay: time before this call should occur
+* int pri: priority, lowest priority goes first
+* int (*routine)(): routine that implements the call
+* int p1 through p8: parameters to pass to routine
+* Effect:
+* builds a call and puts it in pending queue for later scheduling
+****************************************************************************/
+
+#ifndef DOTS_FOR_ARGS
+void causepri(delay, pri, routine, p)
+ delay_type delay;
+ int pri;
+ int (*routine)();
+ call_args_node p;
+#else
+/* already included stdarg.h */
+
+void causepri(delay_type delay, int pri, ...)
+#endif
+{
+ register call_type call = call_alloc();
+#ifdef DOTS_FOR_ARGS
+ va_list xp;
+#endif
+
+ if (!call) {
+ gprintf(ERROR, "cause: out of memory\n");
+ EXIT(1);
+ }
+
+#ifdef DOTS_FOR_ARGS
+ call->u.e.time = virttime + delay;
+ call->u.e.priority = pri; /* default priority */
+ va_start(xp, pri);
+ call->u.e.routine = (int (*)()) va_arg(xp, long *);
+ call->u.e.p = va_arg(xp, call_args_node);
+ va_end(xp);
+#else
+ call->u.e.time = virttime + delay;
+ call->u.e.priority = pri; /* default priority */
+ call->u.e.routine = routine;
+ call->u.e.p = p;
+#endif
+#ifdef SAFEMOXC
+ if (call->u.e.routine == 0) {
+ gprintf(ERROR,"cause called with NULL routine\n");
+ EXIT(1);
+#ifndef DOS /* IBM allows odd addresses */
+#if (__APPLE__ != 1 || __i386__ != 1) /* Intel Mac allows odd addresses */
+ } else if (((long) call->u.e.routine) & 1) {
+ gprintf(ERROR, "causepri called with bad routine address: 0x%lx\n",
+ call->u.e.routine);
+ EXIT(1);
+#endif
+#endif
+ }
+#endif
+ /* put call in default queue */
+ callinsert(timebase, call);
+ if (moxcdebug) {
+ gprintf(GDEBUG,"(cause) call is pending:");
+ callshow(call);
+ }
+}
+
+/****************************************************************************
+* callrun
+* Inputs:
+* call_type call: the call to execute
+* Effect:
+* executes the previously scheduled call call and deallocates it
+****************************************************************************/
+
+private void callrun()
+{
+ call_type call;
+ if (moxcdebug) {
+ gprintf(GDEBUG,"(callrun) running a call: \n");
+ }
+ /* remove from head of queue */
+ while (!timebase_queue) gprintf(TRANS, "callrun fatal error\n");
+ timebase = timebase_queue;
+ timebase_queue = timebase->next;
+
+ if (debug) gprintf(TRANS, "callrun time %ld\n", timebase->next_time);
+ eventtime = (timebase->next_time) >> 8; /* real time of the call */
+
+ /* remove first call from timebase */
+ call = remove_call(timebase);
+ if (debug) gprintf(TRANS, "callrun call %lx\n", (ulong)call);
+ insert_base(timebase);
+ virttime = call->u.e.time; /* virtual time of the call */
+ if (moxcdebug) callshow(call);
+ (*(call->u.e.routine))(CALLARGS(call));
+ call_free(call);
+}
+
+/****************************************************************************
+* m_restuntil
+* Inputs:
+* int time: call time to rest until
+* Effect:
+* Waits until the specified time has been reached (absolute time).
+* Other "caused" calls will take place during the rest provided
+* this routine is called from "mainscore" (see m_rest description).
+****************************************************************************/
+void m_restuntil(time)
+ time_type time;
+{
+ time = virt_to_real(timebase, time);
+ while(time > gettime()) {
+ moxcwait(time);
+ }
+}
+
+
+/****************************************************************************
+* m_rest
+* Inputs:
+* int time: Amount of time to rest
+* Effect:
+* Waits until the amount of time specified has lapsed
+* Assumes:
+* Must not be called from a "caused" routine. Must only be called
+* from "mainscore" or a routine called directly or indirectly from
+* "mainscore" without using "cause".
+****************************************************************************/
+
+void m_rest(time)
+ time_type time;
+{
+ m_restuntil(time + real_to_virt(timebase, gettime()));
+}
+
+/****************************************************************************
+* moxcinit
+* Inputs:
+* int argc: number of command line arguments
+* char * argv: command line argument array
+* Effect: initializes moxc system
+****************************************************************************/
+
+boolean moxcinit(argc, argv)
+ int argc;
+ char * argv[];
+{
+ meminit();
+ io_init();
+#ifdef AMIGA
+ pub_port_signal = AllocSignal(-1L);
+ pub_port.mp_Node.ln_Type = NT_MSGPORT;
+ pub_port.mp_SigBit = pub_port_signal;
+ pub_port.mp_SigTask = FindTask(0L);
+ pub_port.mp_Flags = PA_SIGNAL;
+ pub_port.mp_Node.ln_Name = "CMTcmdport";
+ pub_port.mp_MsgList.lh_Head =
+ (struct Node *)&pub_port.mp_MsgList.lh_Tail;
+ pub_port.mp_MsgList.lh_TailPred =
+ (struct Node *)&pub_port.mp_MsgList.lh_Head;
+ event_mask |= (1L << pub_port_signal);
+ AddPort(&pub_port);
+#endif
+ cu_register((cu_fn_type) moxcterm, NULL);
+ cl_syntax(midifns_syntax);
+ cl_syntax("debug<s>Enable verbose debugging;\
+ moxc<s>Enable moxc debug mode;");
+ cl_syntax(app_syntax);
+
+ if (!cl_init(argv, argc)) {
+ /* make sure user gets to read the error message(s): */
+ gprintf(TRANS, "Type anything to exit...");
+#ifdef DOS
+ wait_ascii();
+#else
+ ggetchar();
+#endif
+ return FALSE;
+ }
+ debug = cl_switch("debug");
+ moxcdebug = cl_switch("moxc");
+ timebase = default_base = timebase_create(100);
+ default_base->rate = 2560L;
+
+ eventtime = 0L;
+ next_wakeup = MAXTIME;
+ musicinit();
+#ifdef POSICIONADOR_3D
+ ptInit();
+#endif
+ moxcdone = 0;
+ return TRUE;
+}
+
+
+/****************************************************************************
+* moxcwait
+* Input:
+* -1 => wait for next keyboard or midi event or queued event
+* 0 => don't wait
+* T => wait up to T for next keyboard or midi event or queued event
+* (this is used by m_restuntil)
+* Assume: there is work to do (npending > 0 || evqueue) ??
+* Effect: dispatch on user inputs, cause calls
+****************************************************************************/
+
+void moxcwait(dateoftimeout)
+ time_type dateoftimeout;
+{
+ time_type maxtime = dateoftimeout;
+
+ if (timebase_queue) {
+ if ((timebase_queue->next_time >> 8) < maxtime)
+ maxtime = (timebase_queue->next_time) >> 8;
+ }
+ eventwait(maxtime);
+ decode();
+}
+
+
+/****************************************************************************
+* decode
+* Effect: dispatch on user inputs, cause calls
+****************************************************************************/
+
+private void decode()
+{
+ /* It is important that midi_data is on a word boundary because we
+ copy to it by doing a word transfer.
+ */
+ byte midi_data[4];
+ time_type now;
+ byte code;
+ char k;
+#ifdef AMIGA
+ struct cmd_msg *cmd;
+#endif
+
+ now = gettime();
+ timebase = default_base;
+ eventtime = now;
+ virttime = 0L;
+
+/* gprintf(GDEBUG, "decode at time %ld\n", now); */
+
+/**********************************************
+* poll for and decode midi keyboard input
+***********************************************/
+
+ while (getbuf(FALSE, midi_data)) {
+ /* only divide if necessary, divides take 100us on 8MHz 68000: */
+ if (virttime == 0)
+ virttime = real_to_virt(default_base, now);
+
+ /* short-circuit midi decoding */
+ if (!mididecode) {
+ midievent(midi_data);
+ continue;
+ }
+
+ code = midi_data[0] & MIDI_CODE_MASK;
+ if (code == MIDI_ON_NOTE) {
+ if (midi_data[2] == 0) { /* velocity 0 -> note off */
+ keyup(1+(midi_data[0] & MIDI_CHN_MASK), midi_data[1]);
+ } else {
+ keydown((midi_data[0] & MIDI_CHN_MASK)+1,
+ midi_data[1], midi_data[2]);
+ }
+ } else if (code == MIDI_OFF_NOTE) {
+ keyup((midi_data[0] & MIDI_CHN_MASK)+1, midi_data[1]);
+ } else if (code == MIDI_TOUCH) {
+ touchchange((midi_data[0] & MIDI_CHN_MASK)+1,midi_data[1]);
+ } else if (code == MIDI_BEND) {
+ bendchange((midi_data[0] & MIDI_CHN_MASK)+1,
+ midi_data[1] + (midi_data[2] << 7));
+ } else if (code == MIDI_CTRL && midi_data[1] == SUSTAIN) {
+ if (midi_data[2] == 0) pedup((midi_data[0] & MIDI_CHN_MASK) + 1);
+ else peddown((midi_data[0] & MIDI_CHN_MASK) + 1);
+ } else if (code == MIDI_CTRL) {
+ ctrlchange((midi_data[0] & MIDI_CHN_MASK) + 1,
+ midi_data[1], midi_data[2]);
+ } else if (code == MIDI_CH_PROGRAM) {
+ prgmchange((midi_data[0] & MIDI_CHN_MASK) + 1, midi_data[1] + 1);
+/* think C midi driver doesn't handle sysex the way the Amiga drivers do (yet) */
+#ifndef MACINTOSH
+ } else if (code == MIDI_SYSEX) {
+ sysex();
+#endif
+ }
+ }
+
+/**********************************************
+* poll for ASCII keyboard input
+***********************************************/
+ while (get_ascii(&k)) {
+ virttime = real_to_virt(default_base, now);
+ asciievent(k);
+ /* if user doesn't handle abort char in asciievent,
+ we should exit now to avoid an infinite loop */
+ if (abort_flag) EXIT(1);
+ }
+
+#ifdef POSICIONADOR_3D
+/**********************************************
+* poll for posicionador tridimensionale input
+**********************************************/
+ {
+ pt_value pt_data;
+ while (ptGetValue(&pt_data)) {
+ /* only divide if necessary, divides take 100us on 8MHz 68000: */
+ if (virttime == 0)
+ virttime = real_to_virt(default_base, now);
+ ptevent(&pt_data);
+ }
+ }
+#endif /* POSICIONADOR_3D */
+
+#ifdef AMIGA
+/**********************************************
+* poll for proportional controller port
+**********************************************/
+ if (prop_1_events) {
+ int events;
+
+ Disable();
+ events = prop_1_events;
+ prop_1_events = 0;
+ Enable();
+
+ if (events & BUTTON_1_RIGHT_CHANGE)
+ buttonchange(3, prop_1_right_button);
+ if (events & BUTTON_1_LEFT_CHANGE)
+ buttonchange(2, prop_1_left_button);
+ if (events & PROP_1_LEFT_CHANGE)
+ propchange(2, prop_1_left_data);
+ if (events & PROP_1_RIGHT_CHANGE)
+ propchange(3, prop_1_right_data);
+ }
+
+/**********************************************
+* poll for input from public command port
+***********************************************/
+
+ while (cmd = (struct cmd_msg *) GetMsg(&pub_port)) {
+ struct symb_descr *desc = &HASHENTRY(lookup(cmd->symbol_name));
+/* gprintf(TRANS, "got %lx (%s) from pub_port\n", cmd, cmd->symbol_name); */
+ virttime = real_to_virt(default_base, now);
+ if (!desc) {
+ gprintf(TRANS, "Error, symbol %s undefined.\n", cmd->symbol_name);
+ } else if (desc->symb_type != cmd->symb_type) {
+ gprintf(TRANS, "Error, wrong type for symbol %s\n",
+ cmd->symbol_name);
+ } else if (cmd->symb_type == fn_symb_type) {
+/* gprintf(TRANS, "Calling routine\n"); */
+ (*(desc->ptr.routine))(
+ (int) cmd->the_args[0], (int) cmd->the_args[1],
+ (int) cmd->the_args[2], (int) cmd->the_args[3],
+ (int) cmd->the_args[4], (int) cmd->the_args[5],
+ (int) cmd->the_args[6], (int) cmd->the_args[7]
+ );
+ } else if (cmd->symb_type == var_symb_type) {
+ *(desc->ptr.intptr) = (int) cmd->the_args[0];
+ } else if (cmd->symb_type == vec_symb_type) {
+ if (cmd->the_args[0] >= desc->size) {
+ gprintf(TRANS, "Error: Vector %s is of size %d\n",
+ cmd->symbol_name, desc->size);
+ } else {
+ (desc->ptr.intptr)[cmd->the_args[0]] = cmd->the_args[1];
+/* gprintf(TRANS, "vec: setting %lx\n",
+ &(desc->ptr.intptr)[cmd->the_args[0]]); */
+ }
+ } else gprintf(TRANS, "Symbol Type Error\n");
+ ReplyMsg(&(cmd->msg));
+ }
+#endif
+/**********************************************
+* poll for next call in queue
+***********************************************/
+ now = (now + 1) << 8; /* shift because next_time is also scaled,
+ * add 256 because next_time has added priority */
+ if (debug)
+ gprintf(TRANS, "now %ld next_time %ld\n",
+ now, (timebase_queue ? timebase_queue->next_time : 1234));
+ /* give pending events priority, but every 100 events, loop to allow
+ input processing (user may want to give a "quit" command) */
+ for (k = 0;
+ k < 100 && timebase_queue && (now > timebase_queue->next_time);
+ k++) {
+ callrun();
+ }
+/*******************
+* flush text output
+********************/
+#ifdef MACINTOSH_OR_UNIX
+ gflush();
+#endif
+}
+
+
+/****************************************************************************
+* quit
+* Effect: tells moxc to shut down
+****************************************************************************/
+
+void quit()
+{
+ moxcdone = TRUE;
+}
+
+/* moxcrun -- schedule events until done */
+/**/
+void moxcrun()
+{
+ moxcdone = FALSE;
+ while (!moxcdone && !abort_flag) { /* test for finish */
+ if (!timebase_queue) moxcdone = TRUE;
+ else moxcwait(MAXTIME); /* do work */
+ }
+}
+
+/* moxcterm -- clean up after moxcinit */
+/**/
+private void moxcterm()
+{
+#ifdef AMIGA
+ FreeSignal((long) pub_port_signal);
+ RemPort(&pub_port);
+#endif
+}
diff --git a/cmt/moxc.h b/cmt/moxc.h
new file mode 100644
index 0000000..975ebb9
--- /dev/null
+++ b/cmt/moxc.h
@@ -0,0 +1,44 @@
+/* moxc.h -- functions exported by moxie.c */
+/* Copyright 1989 Carnegie Mellon University */
+
+#define maxparms 8
+
+extern timebase_type timebase;
+extern time_type eventtime, virttime;
+extern int debug;
+extern int mididecode;
+extern int moxcdone;
+
+void catchup(void);
+void callallcancel(void);
+#ifdef DOTS_FOR_ARGS
+void cause(delay_type delay, ...);
+void causepri(delay_type delay, int pri, ...);
+#else
+void cause();
+void causepri();
+#endif
+void m_rest(time_type time);
+void m_restuntil(time_type time);
+void quit(void);
+boolean moxcinit(int argc, char * argv[]);
+void moxcrun(void);
+void moxcwait(time_type dateoftimeout);
+
+void asciievent(char k);
+void bendchange(int chan, int value);
+void coda(void);
+void ctrlchange(int chan, int ctrl, int value);
+void keydown(int chan, int key, int vel);
+void keyup(int chan, int key);
+void mainscore(void);
+void midievent(byte midi_data[4]);
+void peddown(int chan);
+void pedup(int chan);
+void prgmchange(int chan, int prgm);
+void touchchange(int chan, int value);
+#ifdef AMIGA
+void buttonchange(int number, int value);
+void propchange(int number, int value);
+#endif
+void sysex(void);
diff --git a/cmt/musiprog.h b/cmt/musiprog.h
new file mode 100644
index 0000000..ddd9f95
--- /dev/null
+++ b/cmt/musiprog.h
@@ -0,0 +1,31 @@
+/* musiprog.h -- include file for cmt application programs */
+
+#include "stdio.h"
+#include "cext.h"
+#include "midifns.h"
+#include "userio.h"
+#include "timebase.h"
+#include "moxc.h"
+
+/*
+ * override the definition of l_rest - l_rest is not recommended because
+ * it stops polling for input. If you really want to use it, use #undef
+ * to make it visible.
+ */
+#define l_rest(d) m_rest(d)
+#define l_restuntil(t) m_restuntil(t)
+
+/*
+ * The default implementation of rest() and restuntil() poll for
+ * input during the rest. You might call rest() or restuntil() from
+ * mainscore(), but it is generally a bad idea to rest at all. If
+ * you are in a rest(), you get an event, e.g. keydown(), and you
+ * make a nested call to rest(), the original rest will be locked out
+ * until the nested one returns. It's better to use cause().
+ */
+#define rest(x) l_rest( (long) x )
+#define restuntil(x) l_restuntil( (long) x)
+
+#define repeat(var, count) {int var; for (var=1; var <= count; var++) {
+#define endrep ;}}
+
diff --git a/cmt/pitch.h b/cmt/pitch.h
new file mode 100644
index 0000000..ab6dd9c
--- /dev/null
+++ b/cmt/pitch.h
@@ -0,0 +1,9 @@
+/* mbc code */
+/* Copyright 1989 Carnegie Mellon University */
+
+typedef struct pitch_struct {
+ int ppitch;
+ int pbend;
+}
+pitch_table;
+/* end */
diff --git a/cmt/record.c b/cmt/record.c
new file mode 100644
index 0000000..44f2373
--- /dev/null
+++ b/cmt/record.c
@@ -0,0 +1,638 @@
+/* record.c -- keyboard to adagio recorder
+ * Copyright 1989 Carnegie Mellon University
+ *
+ * the interface consists of three routines:
+ * rec_init() -- initialization
+ * rec_event(byte *data) -- called to insert (record) midi data,
+ * -- returns FALSE if no more space
+ * rec_final() -- called to finish up
+ */
+
+/*****************************************************************************
+* Change Log
+* Date | Change
+*-----------+-----------------------------------------------------------------
+* 27-Feb-86 | Created changelog
+* | Use pedal information when computing durations (code taken
+* | from transcribe.c)
+* 23-Mar-86 | Determine size of transcription when rec_init is called.
+* 21-May-86 | Major rewrite to use continuous controls (code taken
+* | from transcribe.c)
+* 1-Aug-87 | F.H. Changed rec_init() to new memory handling.
+* 17-Oct-88 | JCD : portable version.
+* 31-Jan-90 | GWL : cleaned up for LATTICE
+* 30-Jun-90 | RBD : further changes
+* 2-Apr-91 | JDW : further changes
+* 28-Apr-03 | DM : changed for portability; true->TRUE, false->FALSE
+*****************************************************************************/
+
+#include "switches.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cext.h"
+#include "midifns.h"
+#include "userio.h"
+#include "midicode.h"
+#include "record.h"
+#include "cmdline.h"
+
+extern long space; /* how much space is left? */
+
+int debug_rec = FALSE; /* verbose debug flag for this module */
+
+long max_notes = -1L; /* -1 is flag that space must be allocated */
+
+time_type previous_time;
+
+/****************************************************************
+* data structure notes: the midi stream is stored as an array
+* of 4-byte records, each of which is either a time or midi
+* data. Midi data always begins with a control byte (high
+* order bit set), and it is assumed times are positive (high
+* order bit clear), so the two are easy to distinguish
+* IF THE COMPILER PUTS THESE BITS IN THE SAME PLACE. It looks
+* like the high order byte of the time lines up with the last
+* byte of a 4 byte array, so we will always set the high order
+* bit of the last array byte when the first 3 bytes are filled
+* with MIDI data. This is refered to as the "tag" bit.
+* WARNING: Lattice C longs are UNSIGNED, therefore always
+* positive. Test the high order bit with a mask.
+****************************************************************/
+
+#define MIDI_CMD_BIT 0x80
+#define HIGH_BIT 0x80000000
+#define istime(note) (!(((note)->when) & HIGH_BIT))
+
+typedef union note_struct {
+ byte n[4];
+ long when;
+}
+*note_type, note_node;
+
+private note_type event_buff; /* pointer to allocated buffer */
+private note_type next; /* pointer to next entry in buffer */
+private note_type last; /* pointer to last entry in buffer */
+private int pile_ups; /* inner loop iteration count */
+private int max_pile_up; /* maximum of pile_ups */
+private boolean fixed_octave; /* used to avoid many error messages */
+
+/****************************************************************************
+* Routines local to this module
+****************************************************************************/
+private void bend_filter();
+private void byteorder();
+private void ctrl_filter();
+private int event_bend();
+private void filter();
+private long getdur();
+private long getnext();
+private char map_ctrl();
+private void output();
+
+/****************************************************************************
+* bend_filter
+* Inputs:
+* note_type note: the current note
+* note_type last: the last recorded event
+* long now: the current time
+* Effect:
+* remove pitch bend events in same 0.01 sec time slot
+* Implementation:
+* If the current event is a pitch bend that bends again
+* in the same time slot, make it a no-op by replacing it with
+* the time.
+****************************************************************************/
+
+private void bend_filter(note, last, now)
+note_type note; /* current note */
+note_type last; /* the last recorded event */
+long now; /* the current time */
+{
+ /* first see if there is another bend in this time
+ * slot.
+ */
+ note_type note2 = note + 1;
+ while (note2 < last) {
+ if (istime(note2) && (note2->when > now)) {
+ break; /* new time slot */
+ }
+ else if (note->n[0] == note2->n[0]) {
+ note->when = now;
+ return; /* found another bend */
+ }
+ note2++;
+ }
+}
+
+/****************************************************************************
+* byteorder
+* Effect:
+* check out assumptions about byte order and placement
+****************************************************************************/
+
+private void byteorder()
+{
+ note_node test_event;
+ if ((sizeof(test_event) != 4) ||
+ (sizeof(test_event.when) != 4) ||
+ (sizeof(test_event.n[0]) != 1)) {
+ gprintf(ERROR, "implementation error: size problem\n");
+ EXIT(1);
+ }
+ test_event.n[0] = 0x12;
+ test_event.n[1] = 0x34;
+ test_event.n[2] = 0x56;
+ test_event.n[3] = 0x78;
+ if ((test_event.when != 0x78563412) &&
+ (test_event.when != 0x12345678)) {
+ gprintf(ERROR, "implementation error: layout problem\n");
+ EXIT(1);
+ }
+}
+
+/****************************************************************************
+* ctrl_filter
+* Inputs:
+* note_type note: the current note
+* note_type last: the last recorded event
+* long now: the current time
+* Effect:
+* remove ctrl change events in same 0.01 sec time slot
+* Implementation:
+* If the current event is a control change that changes again
+* in the same time slot, make it a no-op by replacing it with
+* the time.
+****************************************************************************/
+
+private void ctrl_filter(note, last, now)
+note_type note; /* the current note */
+note_type last; /* the last recorded event */
+long now; /* the current time */
+{
+ /* see if there is another control change in this time
+ * slot.
+ */
+ note_type note2 = note+1;
+ while (note2 < last) {
+ if (istime(note2) && (note2->when > now)) {
+ break; /* new time slot */
+ }
+ else if ((note->n[0] == note2->n[0]) &&
+ (note->n[1] == note2->n[1])) {
+ note->when = now;
+ return; /* found another change */
+ }
+ note2++;
+ }
+}
+
+/****************************************************************************
+* event_bend
+* Inputs:
+* note_type note: pointer to a pitch bend event
+* Outputs:
+* returns int: an 8 bit pitch bend number
+****************************************************************************/
+
+private int event_bend(note)
+note_type note;
+{
+ return((int) (((note->n[1]) >> 6) + ((note->n[2]) << 1)));
+}
+
+/****************************************************************************
+* filter
+* Inputs:
+* note_type last: the last note recorded
+* Effect: allow only one control change per time slot (0.01 sec)
+* Implementation:
+* call ctrl_filter and bend_filter to overwrite control changes with
+* noop data (the current time is used as a noop)
+****************************************************************************/
+
+private void filter(last)
+note_type last;
+{
+ note_type note; /* loop control variable */
+ long now=0; /* last time seen */
+ int command; /* command pointed to by note */
+
+ for (note = event_buff; note <= last; note++) {
+ if (istime(note)) {
+ now = note->when;
+ }
+ else {
+ command = note->n[0] & MIDI_CODE_MASK;
+
+ if (command == MIDI_CTRL &&
+ note->n[1] == SUSTAIN) {
+ /* do nothing */;
+ }
+ else if (command == MIDI_CTRL) {
+ ctrl_filter(note, last, now);
+ }
+ else if (command == MIDI_TOUCH) {
+ bend_filter(note, last, now); /* bend and touch use the */
+ }
+ else if (command == MIDI_BEND) { /* same filter routines */
+ bend_filter(note, last, now);
+ }
+ }
+ }
+}
+
+
+/****************************************************************************
+* getdur
+* Inputs:
+* int i: index of the note
+* note_type last: pointer to the last event recorded
+* int ped: TRUE if pedal is down at event i
+* long now: the time at event i
+* Outputs:
+* returns long: the duration of note i
+* Assumes:
+* assumes i is a note
+* Implementation:
+* This is tricky because of pedal messages. The note is kept on by
+* either the key or the pedal. Keep 2 flags, key and ped. Key is
+* turned off when a key is released, ped goes off and on with pedal.
+* Note ends when (1) both key and ped are FALSE, (2) key is
+* pressed (this event will also start another note).
+****************************************************************************/
+
+private long getdur(i, last, ped, now)
+int i;
+note_type last;
+int ped;
+long now;
+{
+ int key = TRUE; /* flag that says if note is on */
+ long start = now;
+ int chan = event_buff[i].n[0] & MIDI_CHN_MASK;
+ int pitch = event_buff[i].n[1];
+ note_type note = &(event_buff[i+1]);
+ int noteon; /* TRUE if a noteon message received on chan */
+ int keyon; /* TRUE if noteon message had non-zero velocity */
+
+ /* search from the next event (i+1) to the end of the buffer:
+ */
+ for (; note < last; note++) {
+ if (istime(note)) {
+ now = note->when;
+ }
+ else {
+ noteon = keyon = FALSE;
+ if ((note->n[0] & MIDI_CHN_MASK) == chan) {
+ noteon = ((note->n[0] & MIDI_CODE_MASK) == MIDI_ON_NOTE) &&
+ (note->n[1] == pitch);
+ keyon = noteon && (note->n[2] != 0);
+ if ((noteon && (note->n[2] == 0)) ||
+ (((note->n[0] & MIDI_CODE_MASK) == MIDI_OFF_NOTE) &&
+ (note->n[1] == pitch))) key = FALSE;
+ if (((note->n[0] & MIDI_CODE_MASK) == MIDI_CTRL) &&
+ note->n[1] == SUSTAIN && note->n[2] == 127) ped = TRUE;
+ if (((note->n[0] & MIDI_CODE_MASK) == MIDI_CTRL) &&
+ note->n[1] == SUSTAIN && note->n[2] == 0) ped = FALSE;
+
+ if ((!key && !ped) || keyon)
+ return(now - start);
+ }
+ }
+ }
+ return(last->when - start);
+}
+
+/****************************************************************************
+* getnext
+* Inputs:
+* int i: the index of the current note
+* note_type last: pointer to last valid data
+* long now: the current time
+* Outputs:
+* returns long: the time of the next note, program, or control change
+* (returns time of last event if nothing else is found)
+****************************************************************************/
+
+private long getnext(i, last, now)
+int i; /* the index of the current note */
+note_type last; /* pointer to last valid data */
+long now; /* the current time */
+{
+ i++; /* advance to next item */
+ for (; event_buff + i < last; i++) {
+ note_type note = &(event_buff[i]);
+ int cmd = note->n[0] & MIDI_CODE_MASK;
+
+ if (istime(note)) {
+ now = note->when;
+ }
+ else if (((cmd == MIDI_ON_NOTE) &&
+ (note->n[2] != 0)) /* note on */ ||
+ (cmd == MIDI_CH_PROGRAM) /* program change */ ||
+ ((cmd == MIDI_CTRL) &&
+ (note->n[1] != SUSTAIN) /* control change */ ) ||
+ (cmd == MIDI_TOUCH) ||
+ (cmd == MIDI_BEND)) {
+ return(now);
+ }
+ }
+ return(last->when);
+}
+
+/****************************************************************************
+* map_ctrl
+* Inputs:
+* int control: a midi control number
+* Outputs:
+* returns char: an adagio control change command letter, EOS if
+* control change is not one of PORTARATE, PORTASWITCH,
+* MODWHEEL, FOOT
+****************************************************************************/
+
+private char map_ctrl(control)
+int control;
+{
+ switch (control) {
+/* 'J' is no longer code for PORTARATE
+ case PORTARATE:
+ return 'J'; */
+ case PORTASWITCH:
+ return 'K';
+ case MODWHEEL:
+ return 'M';
+ case VOLUME:
+ return 'X';
+ default:
+ return EOS;
+ }
+#ifdef LATTICE322
+ return EOS; /* make Lattice C type checker happy */
+#endif
+}
+
+/****************************************************************************
+* output
+* Inputs:
+* FILE *fp: an opened file pointer
+* note_type last: the last data in the buffer
+* boolean absflag: set to TRUE if first line of the adagio score should
+* include the absolute time
+* Effect:
+* write adagio file using data in event_buff
+* Implementation:
+* NOTE: put all program changes in rests
+* use N(ext) notation for all timing
+* output no more than one continuous parameter change per
+* clock tick for each continuous change parameter
+****************************************************************************/
+
+private void output(fp, last, absflag)
+FILE *fp;
+note_type last;
+boolean absflag;
+{
+ int i; /* loop counter */
+ int command; /* the current command */
+ int voice; /* the midi channel of the current event */
+ int last_velocity = -1; /* used to filter repeated Lnn attributes */
+ int last_voice = 0; /* the default adagio channel (1) */
+ int ped = FALSE; /* flag maintains state of pedal */
+ int how_many = last - event_buff;
+ long now=0; /* the time of the next event */
+
+ if (fp == NULL) {
+ gprintf(ERROR, "internal error: output called with NULL file.\n");
+ EXIT(1);
+ }
+
+ if (debug_rec)
+ gprintf(GDEBUG,"hint: if file is not being closed, decrease MAXSPACE\n");
+
+
+ fprintf(fp, "!MSEC\n"); /* times will be in milliseconds */
+ /* set the initial absolute time, all other times are relative */
+
+ if (absflag) {
+ now = event_buff[0].when;
+ if (now < 0) {
+ fprintf(fp, "* First event took place at Adagio time %d,\n",
+ (int)now);
+ fprintf(fp, "* but Adagio cannot represent negative times,\n");
+ fprintf(fp, "* so this entire score will be %d ms late\n",
+ (int)-now);
+ gprintf(TRANS, "First event took place at Adagio time %d!\n",
+ (int)now);
+ gprintf(TRANS, "All events times will be %d ms late\n",
+ (int)-now);
+ now = 0L;
+ }
+ fprintf(fp, "T%ld ", now);
+ }
+
+ for (i = 0; i < how_many; i++) {
+ if (debug_rec) {
+ gprintf(GDEBUG,"ev %d: %x %x %x (%ld)\n", i, event_buff[i].n[0],
+ event_buff[i].n[1], event_buff[i].n[2], event_buff[i].when);
+ }
+
+ if (istime(event_buff+i)) {
+ now = event_buff[i].when;
+ if (debug_rec) gprintf(GDEBUG,"i = %d, now = %ld\n", i, now);
+ } else {
+ boolean needs_voice = TRUE;
+ command = event_buff[i].n[0] & MIDI_CODE_MASK;
+ voice = event_buff[i].n[0] & MIDI_CHN_MASK;
+
+ if (command == MIDI_ON_NOTE && event_buff[i].n[2] != 0) {
+ int velocity = event_buff[i].n[2];
+ write_pitch(fp, event_buff[i].n[1]);
+ fprintf(fp, " U%ld", getdur(i, last, ped, now));
+ if (last_velocity != velocity) {
+ fprintf(fp, " L%d", velocity);
+ last_velocity = velocity;
+ }
+ } else if (command == MIDI_CH_PROGRAM) {
+ fprintf(fp, "Z%d", event_buff[i].n[1] + 1);
+ } else if (command == MIDI_CTRL &&
+ event_buff[i].n[1] == SUSTAIN) {
+ ped = (event_buff[i].n[2] != 0);
+ needs_voice = FALSE;
+ } else if (command == MIDI_CTRL) {
+ char c = map_ctrl(event_buff[i].n[1]);
+ if (c != EOS) fprintf(fp, "%c%d", c, event_buff[i].n[2]);
+ else fprintf(fp, "~%d(%d)", event_buff[i].n[1], event_buff[i].n[2]);
+ } else if (command == MIDI_TOUCH) {
+ fprintf(fp, "O%d", event_buff[i].n[1]);
+ } else if (command == MIDI_BEND) {
+ fprintf(fp, "Y%d", event_bend(&event_buff[i]));
+ } else if (command == MIDI_ON_NOTE || command == MIDI_OFF_NOTE) {
+ needs_voice = FALSE; /* ignore note-offs */
+ } else {
+ gprintf(ERROR, "Command 0x%x ignored\n", command);
+ needs_voice = FALSE;
+ }
+ if (needs_voice) {
+ if (last_voice != voice) {
+ fprintf(fp, " V%d", voice + 1);
+ last_voice = voice;
+ }
+ fprintf(fp, " N%d", (int)(getnext(i, last, now) - now));
+ fprintf(fp, "\n");
+ }
+ }
+ }
+}
+
+
+/****************************************************************************
+* write_pitch
+* Inputs:
+* FILE *fp: an open file
+* int p: a pitch number
+* Effect: write out the pitch name for a given number
+****************************************************************************/
+
+void write_pitch(FILE *fp, int p)
+{
+ static char *ptos[] = {
+ "C", "CS", "D", "EF", "E", "F", "FS", "G",
+ "GS", "A", "BF", "B" };
+ /* avoid negative numbers: adagio can't express lowest octave: */
+ while (p < 12) {
+ if (!fixed_octave) {
+ gprintf(ERROR, "%s%s%s",
+ "A low note was transposed up an octave\n",
+ "(Adagio cannot express the lowest MIDI octave).\n",
+ "This message will appear only once.\n");
+ fixed_octave = TRUE;
+ }
+ p += 12;
+ }
+ fprintf(fp, "%s%d", ptos[p % 12], (p / 12) - 1);
+}
+
+/**********************************************************************
+* rec_final
+* Inputs:
+* boolean absflag: output absolute time of first note if TRUE
+* Effect:
+* Write recorded data to a file
+**********************************************************************/
+
+void rec_final(FILE *fp, boolean absflag)
+{
+ next->when = gettime();
+ last = next;
+ if (debug_rec) gprintf(GDEBUG,"max_pile_up = %d, ", max_pile_up);
+ gprintf(TRANS,"%ld times and events recorded.\n",
+ (long) (last - event_buff));
+ filter(last);
+ output(fp, last, absflag);
+ fclose(fp);
+ FREE(event_buff);
+ max_notes = -1;
+}
+
+/****************************************************************************
+* rec_init
+* Inputs:
+* char *file: pointer to file name from command line (if any)
+* boolean bender: TRUE if pitch bend should be enabled
+* Outputs:
+* return TRUE if initialization succeeds
+* Effect:
+* prepares module to record midi input
+****************************************************************************/
+
+/* ENOUGH_ROOM says if we have room for 10000 events + 10000 timestamps =
+ * 20000 note_struct's, then that's "enough room" for recording a sequence.
+ * If more ram is available, it won't be used. If less is available, we'll
+ * use as much as we can get, minus "SPACE_FOR_PLAY", which leaves a little
+ * bit of spare ram in case Moxc or stdio need to allocate some space.
+ * For DOS, we limit recording space to 64K.
+ */
+#ifdef DOS
+#define ENOUGH_ROOM 64000L
+#else
+#define ENOUGH_ROOM (20000L * sizeof(union note_struct))
+#endif
+
+
+boolean rec_init(boolean bender)
+{
+ size_t biggestChunk, spaceForRecord;
+
+ debug_rec = cl_switch("debug");
+ byteorder();
+ pile_ups = 0;
+ max_pile_up = 0;
+ previous_time = (unsigned) -1L; /* this will force putting in initial timestamp */
+ fixed_octave = FALSE;
+
+ if (max_notes == -1) { /* allocate space 1st time rec_init called */
+ biggestChunk = AVAILMEM;
+ if (biggestChunk <= SPACE_FOR_PLAY) {
+ /* not enough memory; give up */
+ return(FALSE);
+ }
+ else {
+ spaceForRecord =
+ MIN((biggestChunk - SPACE_FOR_PLAY), ENOUGH_ROOM);
+ /* leave SPACE_FOR_PLAY contiguous bytes of memory */
+ }
+ max_notes = spaceForRecord / sizeof(note_node);
+ /* gprintf(GDEBUG,"max_notes = %d\n", max_notes);*/
+ event_buff = (note_type) MALLOC(spaceForRecord);
+ if (event_buff == NULL) {
+ /* should never happen */
+ gprintf(FATAL, "Implementation error (record.c): getting memory.");
+ return FALSE;
+ }
+ }
+ next = event_buff;
+ last = event_buff + max_notes - 2; /* it is critical that last not point
+ * to the very last storage loc */
+ midi_cont(bender);
+ return((boolean)(max_notes > 10));
+ /* it would be silly to record with only room enough for 10 notes! */
+}
+
+
+/****************************************************************************
+* rec_event
+* Inputs:
+* long time: the current time
+* long data: midi data to record
+* Outputs:
+* returns FALSE if there is no more memory
+* Effect: reads and stores any input
+* Implementation:
+* time stamps and midi events share the same buffer of 4-byte events
+* save time at most once per call to rec_poll
+* save time only if it changes
+****************************************************************************/
+
+boolean rec_event(long *data, time_type time)
+{
+ /* can't allow negative time because sign bit distinguishes
+ * data from time: */
+ if (time < 0) time = 0;
+
+ if (previous_time != time) {
+ next++->when = previous_time = time;
+ if (next >= last) goto overflow;
+ }
+
+ next->when = *data;
+ next++->n[3] = MIDI_CMD_BIT; /* set tag bit */
+ if (next >= last) goto overflow;
+ return TRUE;
+
+overflow:
+ next = last; /* last doesn't really point to last storage */
+ gprintf(ERROR, "No more memory.\n");
+ return FALSE;
+}
diff --git a/cmt/record.h b/cmt/record.h
new file mode 100644
index 0000000..de1479d
--- /dev/null
+++ b/cmt/record.h
@@ -0,0 +1,6 @@
+/* Copyright 1989 Carnegie Mellon University */
+
+boolean rec_init(boolean bender);
+boolean rec_event(long *data, time_type time);
+void rec_final(FILE *fp, boolean absflag);
+void write_pitch(FILE *fp, int p);
diff --git a/cmt/seq.c b/cmt/seq.c
new file mode 100644
index 0000000..de54e47
--- /dev/null
+++ b/cmt/seq.c
@@ -0,0 +1,1199 @@
+/* seq.c -- implement adagio scores as abstract data type */
+
+/*****************************************************************************
+* Change Log
+* Date | Change
+*-----------+-----------------------------------------------------------------
+* 2-Apr-91 | JDW : further changes
+* 16-Feb-92 | GWL : use reg_timebase in seq_play()
+* 28-Apr-03 | DM : false->FALSE, true->TRUE, portability changes
+* 19-May-03 | RBD : no longer assume seq->current remains untouched between
+* | note inserts
+*****************************************************************************/
+
+#include "stdio.h"
+#include "cext.h"
+#include "userio.h"
+#include "midicode.h"
+#include "midifns.h"
+#include "timebase.h"
+#include "moxc.h"
+#include "seq.h"
+#include "string.h"
+
+extern int moxcdebug;
+extern timebase_type default_base;
+
+boolean seq_print = FALSE; /* debugging print switch */
+
+seq_type sequence; /* this is a global to be accessed by routines called
+ * from the sequence */
+
+/* clock state: */
+time_type clock_ticksize; /* millisec per tick shifted 16 bits */
+boolean clock_running = FALSE; /* TRUE if clock is running */
+boolean external_midi_clock = FALSE;
+boolean suppress_midi_clock = FALSE;
+
+private void insert_event(seq_type, register event_type);
+private void process_event(seq_type);
+
+private char *chunk_alloc(seq_type seq, int size);
+private void clock_tick(seq_type seq, time_type fraction);
+private void ramp_event(seq_type seq, event_type event, unsigned int value,
+ unsigned int to_value, int increment, time_type step, int n);
+/*private*/ void send_macro(register unsigned char *ptr, int voice,
+ short parameter[], int parm_num, int value, int nline);
+
+/* chunk_alloc -- allocate data for a sequence */
+/*
+ * NOTE: This assumes one chunk is already allocated.
+ * The first chunk holds shared sequence information in
+ * the info struct, and by convention this is always in
+ * the first chunk.
+ */
+private char *chunk_alloc(seq_type seq, int size)
+{
+ chunk_type chunk = seq->chunklist->u.info.last_chunk;
+ /* gprintf(TRANS, "chunk_alloc: seq %lx size %d\n", seq, size); */
+ if (size & 1) size++; /* make it even */
+ if (chunk->free + size >= CHUNK_SIZE) {
+ chunk_type new_chunk = chunk_create(FALSE);
+ if (!new_chunk)
+ {
+ gprintf(FATAL, "Out of memory while reading seq\n");
+ return NULL;
+ }
+ /* add new_chunk to chunk chain */
+ seq->chunklist->u.info.last_chunk = new_chunk;
+ chunk->next = new_chunk;
+ chunk = new_chunk;
+ }
+ chunk->free += size;
+ return &(chunk->u.data[chunk->free - size]);
+}
+
+
+/* chunk_create -- create a new chunk for seq data */
+/*
+ * If this is the first chunk, set first_flag to reserve
+ * space for the info structure.
+ */
+chunk_type chunk_create(boolean first_flag)
+{
+ chunk_type result = (chunk_type) memget(sizeof(chunk_node));
+ if (result) {
+ result->next = NULL;
+ result->u.info.refcount = 1; /* pre-initialize for caller */
+ result->free = 0;
+ if (first_flag) {
+ result->free = sizeof(struct info_struct);
+ result->u.info.last_chunk = result;
+ result->u.info.dictionary = NULL;
+ result->u.info.eventlist = NULL;
+ result->u.info.ctrlcount = 0;
+ result->u.info.notecount = 0;
+ result->u.info.duration = 0;
+ result->u.info.used_mask = 0;
+ }
+ }
+ /* gprintf(TRANS, "chunk_create: got %lx (size %d)\n", */
+ /* result, sizeof(chunk_node)); */
+ return result;
+}
+
+/* clock_tick -- advance the clock and send a tick */
+/**/
+private void clock_tick(seq_type seq, time_type fraction)
+{
+ int delay;
+ fraction += clock_ticksize;
+ delay = fraction >> 16;
+ fraction &= 0xFFFF;
+ if (seq->runflag && clock_ticksize && seq->note_enable) {
+ midi_clock();
+ cause((delay_type)delay, clock_tick, seq, fraction);
+ } else {
+ clock_running = FALSE;
+ midi_stop();
+ midi_clock(); /* stop takes effect on next clock, so provide one */
+ }
+}
+
+private void cycle(seq_type seq)
+{
+ seq_reset(seq);
+ seq_play(seq);
+}
+
+
+
+/****************************************************************************
+* event_create
+* Inputs:
+* seq_type seq: the seq to hold the event
+* int size: the size of the event in bytes
+* time_type etime: the time of the event
+* int eline: the line number of the event
+* Returns:
+* event_type: a new event structure or
+* NULL if there is not enough memory left
+* Effect:
+* allocates memory from the chunk, then heap as needed
+* Implementation:
+* to reduce the per block storage overhead, we allocate memory in
+* large chunks and do our own allocation. Allocate from first
+* chunk first. If full, allocate a new chunk.
+* WARNING: this implementation assumes that individual events are never freed!!
+****************************************************************************/
+
+private event_type event_create(seq, size, etime, eline)
+ seq_type seq;
+ int size;
+ time_type etime;
+ int eline;
+{
+ event_type result = (event_type) chunk_alloc(seq, size);
+ if (result) {
+ result->ntime = etime;
+ result->nline = eline;
+ /* since we know the time, we can insert now: */
+ insert_event(seq, result);
+ seq_duration(seq) = MAX(seq_duration(seq), etime);
+ }
+ return result;
+}
+
+
+/* insert_call -- add a call event to the seq */
+/**/
+event_type insert_call(seq, ctime, cline, voice, addr, value, n)
+ seq_type seq;
+ time_type ctime;
+ int cline;
+ int voice;
+ int (*addr)();
+ long value[SEQ_MAX_PARMS];
+ int n;
+{
+ int i;
+ register event_type event = event_create(seq, callsize, ctime, cline);
+ if (seq_print) {
+ gprintf(TRANS,
+ "call(%lx): time %ld, line %d, voice %d, fn %lx,\n\tvalues:",
+ event, ctime, cline, voice, addr);
+ for (i = 0; i < n; i++) gprintf(TRANS, " %ld", value[i]);
+ gprintf(TRANS, "\n");
+ }
+ if (event) {
+ seq_used_mask(seq) |= 1 << (voice - 1);
+ event->nvoice = ctrl_voice(ESC_CTRL, voice);
+ event->value = CALL_VALUE;
+ event->u.call.routine = addr;
+ /* save the arguments */
+ for (i = 0; i < n; i++) event->u.call.args.a[i] = value[i];
+ seq_ctrlcount(seq)++;
+ }
+ return event;
+}
+
+
+/* insert_clock -- add a clock cmd to the seq */
+/**/
+event_type insert_clock(seq, ctime, cline, ticksize)
+ seq_type seq;
+ time_type ctime;
+ int cline;
+ time_type ticksize;
+{
+ register event_type event = event_create(seq, clocksize, ctime, cline);
+
+ if (seq_print) {
+ gprintf(TRANS, "clock(%lx): time %ld, line %d\n", event, ctime, cline);
+ }
+ if (event) {
+ event->nvoice = ctrl_voice(ESC_CTRL, 1);
+ event->value = CLOCK_VALUE;
+ event->u.clock.ticksize = ticksize;
+ seq_ctrlcount(seq)++;
+ }
+ return event;
+}
+
+
+/* insert_ctrl -- add a control to the seq */
+/**/
+event_type insert_ctrl(seq, ctime, cline, ctrl, voice, value)
+ seq_type seq;
+ time_type ctime;
+ int cline;
+ int ctrl;
+ int voice;
+ int value;
+{
+ register event_type event = event_create(seq, ctrlsize, ctime, cline);
+ if (seq_print) {
+ gprintf(TRANS,
+ "ctrl(%lx): time %ld, line %d, ctrl %d, voice %d, value %d\n",
+ event, ctime, cline, ctrl, voice, value);
+ }
+ if (event) {
+ seq_used_mask(seq) |= 1 << (voice - 1);
+ event->nvoice = ctrl_voice(ctrl, voice);
+ event->value = value;
+ seq_ctrlcount(seq)++;
+ }
+ return event;
+}
+
+
+/* insert_ctrlramp -- add a control ramp event to the seq */
+/**/
+event_type insert_ctrlramp(seq, rtime, rline, voice, step, dur, ctrl, v1, v2)
+ seq_type seq;
+ time_type rtime;
+ int rline;
+ int voice;
+ time_type step;
+ time_type dur;
+ int ctrl;
+ int v1, v2;
+{
+ register event_type event = event_create(seq, ctrlrampsize, rtime, rline);
+ if (seq_print) {
+ gprintf(TRANS,
+ "ctrlramp(%lx): time %ld, line %d, step %ld, dur %ld, ctrl %d, voice %d\n",
+ event, rtime, rline, step, dur, ctrl, voice);
+ gprintf(TRANS, "\tfrom %d to %d\n", v1, v2);
+ }
+
+ if (event) {
+ seq_used_mask(seq) |= 1 << (voice - 1);
+ event->nvoice = ctrl_voice(ESC_CTRL, voice);
+ event->value = CTRLRAMP_VALUE;
+ if (dur <= 0) dur = 1L; /* don't allow zero duration */
+ event->u.ramp.dur = dur;
+ event->u.ramp.ctrl = ctrl;
+ if (step <= 0) step = 1; /* don't allow zero step size */
+ event->u.ramp.step = (short) step;
+ event->u.ramp.u.ctrl.from_value = v1;
+ event->u.ramp.u.ctrl.to_value = v2;
+ seq_ctrlcount(seq)++;
+ seq_duration(seq) = MAX(seq_duration(seq), rtime + dur);
+ }
+ return event;
+}
+
+
+/* insert_def -- add a definition to the dictionary */
+/**/
+def_type insert_def(seq, symbol, definition, deflen)
+ seq_type seq;
+ char *symbol;
+ unsigned char *definition;
+ int deflen;
+{
+ int i;
+ def_type defn = (def_type) chunk_alloc(seq, sizeof(def_node));
+ defn->symbol = chunk_alloc(seq, strlen(symbol) + 1);
+ defn->definition = (unsigned char *) chunk_alloc(seq, deflen);
+ strcpy(defn->symbol, symbol);
+ for (i = 0; i < deflen; i++) {
+ defn->definition[i] = definition[i];
+ }
+ defn->next = seq_dictionary(seq);
+ seq_dictionary(seq) = defn;
+ if (seq_print) {
+ gprintf(TRANS, "def(%ld): symbol %s defn \n", defn, symbol);
+ for (i = 0; i < deflen; i++) gprintf(TRANS, "%x", definition[i]);
+ gprintf(TRANS, "\n");
+ }
+ return defn;
+}
+
+
+/* insert_deframp -- add a def ramp event to the seq */
+/**/
+event_type insert_deframp(seq, rtime, rline, voice, step, dur,
+ def, nparms, parms, parm_num, to_value)
+ seq_type seq;
+ time_type rtime;
+ int rline;
+ int voice;
+ time_type step;
+ time_type dur;
+ def_type def;
+ int nparms; /* number of parameters for macro */
+ short parms[]; /* actual parameter vector */
+ int parm_num; /* which of the actual parameters to ramp */
+ int to_value; /* final destination of ramp */
+{
+ register event_type event = event_create(seq, deframpsize, rtime, rline);
+ if (seq_print) {
+ int i;
+ gprintf(TRANS,
+ "deframp(%ld): time %ld, line %d, voice %d, step %ld, dur %ld\n",
+ event, rtime, rline, voice, step, dur);
+ gprintf(TRANS, "def %ld, parms");
+ for (i = 0; i < nparms; i++) gprintf(TRANS, " %d", parms[i]);
+ gprintf(TRANS, "parm_num %d to %d\n", parm_num, to_value);
+ }
+ if (event) {
+ int i;
+ seq_used_mask(seq) |= 1 << (voice - 1);
+ event->nvoice = ctrl_voice(ESC_CTRL, voice);
+ event->value = DEFRAMP_VALUE;
+ if (dur <= 0) dur = 1L; /* don't allow zero duration */
+ event->u.ramp.dur = dur;
+ event->u.ramp.ctrl = 0;
+ if (step <= 0) step = 1; /* don't allow zero step size */
+ event->u.ramp.step = (short) step;
+ event->u.ramp.u.def.definition = def->definition;
+ for (i = 0; i < nmacroparms; i++) {
+ event->u.ramp.u.def.parameter[i] = (i < nparms ? parms[i] : 0);
+ }
+ event->u.ramp.u.def.parm_num = parm_num;
+ event->u.ramp.u.def.to_value = to_value;
+ seq_ctrlcount(seq)++;
+ seq_duration(seq) = MAX(seq_duration(seq), rtime + dur);
+ }
+ return event;
+}
+
+
+/****************************************************************************
+* insert_event
+* Inputs:
+* seq_type seq: where to put the event
+* event_type event: the event to insert
+* Effect:
+* inserts event into the event list
+* NOTE: it is inserted *after* previously inserted events with the same time
+* Implementation:
+* adagio files often contain many independent voices. Although each voice
+* consists of events in sequence, the voices need not be inter-twined in
+* the input file. Rather, all the events of voice 1 appear followed by all
+* the events of voice 2, and so forth. As phase one merges these event
+* sequences, it must make many passes over an increasingly long list of
+* events: expensive if we always start from the beginning of the list!
+* we can exploit the fact that each voice is sequential by starting the
+* search for the proper point of insertion at the last event inserted.
+* the variable "last_event" is used to remember this hint. We could
+* also snapshot "last_event" in "ref_event" when a !tempo or !rate
+* command occurs as another hint, but we don't.
+****************************************************************************/
+
+private void insert_event(seq, event)
+ seq_type seq;
+ register event_type event;
+{
+ event_type *evlptr = &(seq_eventlist(seq));
+ if ((*evlptr == NULL) ||
+ (event->ntime < (*evlptr)->ntime)) {
+ /* insert at the head of the list */
+ event->next = *evlptr;
+ *evlptr = event;
+ seq->current = event;
+ } else {
+ /* insert somewhere after the head of the list
+ * do not assume: current is not NULL. Although we always leave
+ * it set, the client may access the sequence before the next
+ * insert.
+ */
+ register event_type previous;
+ register event_type insert_before;
+
+ if (!seq->current) {
+ seq->current = seq_eventlist(seq);
+ }
+ if (event->ntime >= seq->current->ntime) {
+ /* insertion point is after current */
+ previous = seq->current;
+ insert_before = previous->next;
+ } else {
+ /* insertion point is before current; start at beginning */
+ /* assume: not inserting at very head of list; that would
+ * have been taken care of above */
+ previous = seq_events(seq);
+ insert_before = previous->next;
+ }
+
+ while ((insert_before != NULL) &&
+ (event->ntime >= insert_before->ntime)) {
+ previous = insert_before;
+ insert_before = insert_before->next;
+ }
+ previous->next = event;
+ event->next = insert_before;
+ seq->current = event;
+ }
+}
+
+
+/* insert_macctrl -- add a control to the seq */
+/**/
+event_type insert_macctrl(seq, ctime, cline, ctrl, voice, value)
+ seq_type seq;
+ time_type ctime;
+ int cline;
+ int ctrl;
+ int voice;
+ int value;
+{
+ register event_type event = event_create(seq, macctrlsize, ctime, cline);
+ if (seq_print) {
+ gprintf(TRANS,
+ "macctrl(%lx): time %ld, line %d, ctrl %d, voice %d, value %d\n",
+ event, ctime, cline, ctrl, voice, value);
+ }
+ if (event) {
+ seq_used_mask(seq) |= 1 << (voice - 1);
+ event->nvoice = ctrl_voice(ESC_CTRL, voice);
+ event->value = MACCTRL_VALUE;
+ event->u.macctrl.ctrl_number = ctrl;
+ event->u.macctrl.value = value;
+ seq_ctrlcount(seq)++;
+ }
+ return event;
+}
+
+
+/* insert_macro -- insert a macro call seq */
+/**/
+event_type insert_macro(seq, ctime, cline, def, voice, nparms, parms)
+ seq_type seq;
+ time_type ctime;
+ int cline;
+ def_type def;
+ int voice;
+ int nparms;
+ short *parms;
+{
+ register event_type event = event_create(seq, macrosize, ctime, cline);
+ if (seq_print) {
+ int i;
+ gprintf(TRANS,
+ "macro(%lx): time %ld, line %d, def %ld, voice %d, parms",
+ event, ctime, cline, def, voice);
+ for (i = 0; i < nparms; i++) gprintf(TRANS, " %d", parms[i]);
+ gprintf(TRANS, "\n");
+ }
+ if (event) {
+ seq_used_mask(seq) |= 1 << (voice - 1);
+ event->nvoice = ctrl_voice(ESC_CTRL, voice);
+ event->value = MACRO_VALUE;
+ event->u.macro.definition = def->definition;
+ while (nparms-- > 0) {
+ event->u.macro.parameter[nparms] = parms[nparms];
+ }
+ seq_ctrlcount(seq)++;
+ }
+ return event;
+}
+
+
+/* insert_note -- add a note to the seq */
+/**/
+event_type insert_note(seq, ntime, nline, voice, pitch, dur, loud)
+ seq_type seq;
+ time_type ntime;
+ int nline;
+ int voice;
+ int pitch;
+ time_type dur;
+ int loud;
+{
+ register event_type event = event_create(seq, notesize, ntime, nline);
+
+ if (seq_print) {
+ gprintf(TRANS,
+ "note(%lx): time %ld, line %d, dur %ld, pitch %d, voice %d, loudness %d\n",
+ event, ntime, nline, dur, pitch, voice, loud);
+ }
+
+ if (event) {
+ seq_used_mask(seq) |= 1 << (voice - 1);
+ event->nvoice = voice - 1;
+ event->value = pitch;
+ event->u.note.ndur = (dur << 8) + loud;
+ seq_notecount(seq)++;
+ seq_duration(seq) = MAX(seq_duration(seq), ntime + dur);
+ }
+ return event;
+}
+
+
+/* insert_seti -- add a seti event to the seq */
+/**/
+event_type insert_seti(seq, stime, sline, voice, addr, value)
+ seq_type seq;
+ time_type stime;
+ int sline;
+ int voice;
+ int *addr;
+ int value;
+{
+ register event_type event = event_create(seq, setisize, stime, sline);
+ if (seq_print) {
+ gprintf(TRANS,
+ "seti(%ld): time %ld, line %d, voice %d, addr %ld, value %d\n",
+ event, stime, sline, voice, addr, value);
+ }
+ if (event) {
+ event->nvoice = ctrl_voice(ESC_CTRL, voice);
+ event->value = SETI_VALUE;
+ event->u.seti.int_to_set = addr;
+ event->u.seti.value = value;
+ seq_ctrlcount(seq)++;
+ }
+ return event;
+}
+
+
+/* noop -- just returns, the default stopfunc for sequences */
+/**/
+void noop(seq_type seq) {}
+
+
+private void process_event(seq)
+ seq_type seq;
+{
+ register event_type event;
+ if (!seq->runflag) return;
+ while ((event = seq->current) && (event->ntime <= virttime)) {
+ int voice;
+ /* process all current (and earlier) events */
+ if (is_note(event)) { /*** play a note or rest ***/
+ /* if this note is not a rest, play it and schedule an off event */
+ if (event->value != NO_PITCH &&
+ (seq_channel_mask(seq) &
+ (1 << ((voice = vc_voice(event->nvoice)) - 1)))) {
+ seq_noteon(seq, voice, event->value,
+ (int) event->u.note.ndur & 0xFF);
+ if (debug) {
+ gprintf(TRANS, "play pitch %d at %ld\n",
+ event->value, event->ntime);
+ }
+ seq_cause_noteoff(seq, (event->u.note.ndur) >> 8,
+ voice, event->value);
+
+ }
+ } else { /*** send a control command ***/
+ int n;
+ time_type step;
+ int delta;
+ long increment;
+ int voice = vc_voice(event->nvoice);
+ ulong enabled = seq_channel_mask(seq) & (1 << (voice - 1));
+
+ switch (vc_ctrl(event->nvoice)) {
+ case PSWITCH_CTRL:
+ if (!enabled) break;
+ seq_midi_ctrl(seq, voice, PORTASWITCH, event->value);
+ break;
+ case MODWHEEL_CTRL:
+ if (!enabled) break;
+ seq_midi_ctrl(seq, voice, MODWHEEL, event->value);
+ break;
+ case TOUCH_CTRL:
+ if (!enabled) break;
+ seq_midi_touch(seq, voice, event->value);
+ break;
+ case VOLUME_CTRL:
+ if (!enabled) break;
+ seq_midi_ctrl(seq, voice, VOLUME, event->value);
+ break;
+ case BEND_CTRL:
+ if (!enabled) break;
+ seq_midi_bend(seq, voice, (event->value << 6));
+ break;
+ case PROGRAM_CTRL:
+ if (!enabled) break;
+ seq_midi_program(seq, voice, event->value + 1);
+ break;
+ case ESC_CTRL:
+ switch (event->value) {
+ case CALL_VALUE:
+ sequence = seq;
+ (*(event->u.call.routine))(event->u.call.args);
+ break;
+ case CLOCK_VALUE:
+ clock_ticksize = event->u.clock.ticksize;
+ if (!clock_running && !suppress_midi_clock &&
+ !external_midi_clock) {
+ clock_running = TRUE;
+ midi_start();
+ clock_tick(seq, 0L);
+ }
+ break;
+ case MACCTRL_VALUE:
+ if (!enabled) break;
+ seq_midi_ctrl(seq, voice, event->u.macctrl.ctrl_number,
+ event->u.macctrl.value);
+ break;
+ case MACRO_VALUE: {
+ if (!enabled) break;
+ send_macro(event->u.macro.definition, voice,
+ event->u.macro.parameter, -1, 0,
+ event->nline);
+ break;
+ }
+ case CTRLRAMP_VALUE:
+ case DEFRAMP_VALUE: {
+ int from, to;
+ if (!enabled) break;
+
+ step = event->u.ramp.step;
+ if (event->value == CTRLRAMP_VALUE) {
+ from = event->u.ramp.u.ctrl.from_value;
+ to = event->u.ramp.u.ctrl.to_value;
+ } else {
+ from = event->u.ramp.u.def.parameter[
+ event->u.ramp.u.def.parm_num];
+ to = event->u.ramp.u.def.to_value;
+ }
+ delta = to - from;
+ increment = delta;
+ if (delta < 0) delta = -delta;
+ /* Note: Step is always non-zero */
+ n = event->u.ramp.dur / step;
+ increment = (increment << 8) / n;
+ ramp_event(seq, event, from << 8, to << 8,
+ (int) increment, step, n);
+ seq->noteoff_count++;
+ break;
+ }
+ case SETI_VALUE:
+ *(event->u.seti.int_to_set) = event->u.seti.value;
+ break;
+ default:
+ gprintf(TRANS, "unexpected ESC_CTRL value\n");
+ break;
+ }
+ break;
+ default:
+ gprintf(TRANS, "unexpected seq data\n");
+ break;
+ }
+ }
+ seq->current = event->next;
+ }
+ if (seq->current) {
+ cause((delay_type)(event->ntime - virttime), process_event, seq);
+ } else if (seq->noteoff_count == 0 && seq->note_enable) {
+ /* if we're just advancing to a start point, note_enable will be
+ * FALSE and this won't get called:
+ */
+ if (seq->stopfunc) {
+ (*(seq->stopfunc))(seq);
+ }
+ }
+}
+
+
+/* ramp_event -- generate a ramp */
+/**/
+private void ramp_event(seq, event, value, to_value, increment, step, n)
+ seq_type seq;
+ register event_type event;
+ unsigned int value;
+ unsigned int to_value;
+ int increment;
+ time_type step;
+ int n;
+{
+ if (seq->runflag) {
+ int voice = vc_voice(event->nvoice);
+/* printf("ramp_event: value %d to_value %d increment %d step %d n %d time %d\n",
+ value, to_value, increment, step, n, virttime); */
+ if (n == 0) value = to_value;
+ else {
+ causepri((delay_type)step, 5, ramp_event, seq, event, value + increment,
+ to_value, increment, step, n - 1);
+ }
+ if (event->value == CTRLRAMP_VALUE) {
+ int ctrl = event->u.ramp.ctrl;
+ if (ctrl == -TOUCH_CTRL) midi_touch(voice, value >> 8);
+ else if (ctrl == -BEND_CTRL) midi_bend(voice, value >> 2);
+ else midi_ctrl(voice, ctrl, value >> 8);
+ } else { /* must be DEFRAMP_VALUE */
+ send_macro(event->u.ramp.u.def.definition,
+ vc_voice(event->nvoice),
+ event->u.ramp.u.def.parameter,
+ event->u.ramp.u.def.parm_num, value >> 8,
+ event->nline);
+ }
+ if (n == 0) seq_end_event(seq);
+ }
+}
+
+
+/* report_enabled_channels -- print out concise listing of channels */
+/*
+ * to fit on one line, write out ranges, e.g. 1-5 9-11
+ */
+void report_enabled_channels(seq)
+ seq_type seq;
+{
+ ulong mask = seq_channel_mask(seq);
+ int i, range_open_at = 0;
+
+ for (i = 1; i <= MAX_CHANNELS; i++) {
+ if (!range_open_at && (mask & 1)) {
+ gprintf(TRANS, " %d", i);
+ range_open_at = i;
+ } else if (range_open_at && !(mask & 1)) {
+ if (i > (range_open_at + 1)) {
+ gprintf(TRANS, "-%d", i - 1);
+ }
+ range_open_at = 0; /* FALSE */
+ }
+ mask = mask >> 1;
+ }
+ if (range_open_at) gprintf(TRANS, "-%d", MAX_CHANNELS);
+}
+
+
+/* send_macro -- instantiate macro and send it */
+/*
+ * note: to support ramping, "value" is used in place of
+ * parameter["parm_num"]
+ */
+/*private*/
+void send_macro(ptr, voice, parameter, parm_num, value, nline)
+ register unsigned char *ptr;
+ int voice;
+ short parameter[];
+ int parm_num;
+ int value;
+ int nline;
+{
+ register unsigned char code, *loc;
+ while ((code = *ptr++)) {
+ loc = ptr + *ptr;
+ ptr++;
+ if (code <= nmacroparms) {
+ code--;
+ *loc = (code == parm_num ? value : parameter[code]) & 0x7f;
+ } else if (code == nmacroparms + 1) {
+ /* take old high order bits and OR in 4 voice bits */
+ *loc = (*loc & 0xF0) | ((voice - 1) & 0xF);
+ } else {
+ code -= (nmacroparms + 2);
+ *loc = ((code == parm_num ? value : parameter[code]) >> 7) & 0x7F;
+ }
+ }
+ if (ptr[1] == MIDI_SYSEX) {
+ midi_exclusive(ptr + 1);
+ } else {
+ /* make sure user didn't try to send more than 3 bytes. This test
+ * could be done at sequence read time, but it's tricky because the
+ * first byte could be a parameter, so in general you need to
+ * plug the actual parameters into the message and then do the test.
+ * Currently, this is the only place parameters are plugged in.
+ */
+ if (*ptr > 3) {
+ gprintf(ERROR,
+ "Non-sysex macro longer than 3 bytes ignored, line %d.\n",
+ nline);
+ } else {
+ midi_write((int) *ptr, MIDI_PORT(voice), ptr[1], ptr[2], ptr[3]);
+ }
+ }
+}
+
+
+/* seq_alloc -- a utility function to allocate a seq struct */
+/**/
+seq_type seq_alloc()
+{
+ seq_type seq;
+ seq = (seq_type) memget(sizeof(seq_node));
+ return seq;
+}
+
+
+/* seq_at_end -- set the function to be called at sequence end */
+/**/
+void seq_at_end(seq, fn)
+ seq_type seq;
+ void (*fn)(seq_type);
+{
+ if (!fn) fn = noop;
+ seq->stopfunc = fn;
+}
+
+
+/* seq_cause_noteoff_meth -- turn off a note in the future */
+/**/
+void seq_cause_noteoff_meth(seq_type seq, time_type delay, int voice, int pitch)
+{
+ if (seq->note_enable) {
+ pitch += seq->transpose;
+ while (pitch < 0) pitch += 12;
+ while (pitch > 127) pitch -= 12;
+ seq->noteoff_count++;
+ causepri((delay_type) delay, 10, seq->noteoff_fn,
+ seq, voice, pitch);
+ }
+}
+
+/* seq_copy -- copy a sequence, share the eventlist */
+/**/
+seq_type seq_copy(from_seq)
+ seq_type from_seq;
+{
+ register seq_type seq = seq_init(seq_alloc(), FALSE);
+ if (!seq) return NULL;
+ seq->chunklist = from_seq->chunklist;
+ seq->current = seq_events(seq);
+ seq->chunklist->u.info.refcount++;
+ seq->transpose = from_seq->transpose;
+ seq->loudness = from_seq->loudness;
+ seq->rate = from_seq->rate;
+ seq->paused = from_seq->paused;
+ seq->noteoff_count = 0;
+ return seq;
+}
+
+
+/* seq_create -- create a seq structure and an initial event chunk */
+/**/
+seq_type seq_create()
+{
+ return seq_init(seq_alloc(), TRUE);
+}
+
+
+/* seq_cycle -- set parameters for cycling a sequence */
+/**/
+void seq_cycle(seq_type seq, boolean flag, time_type dur)
+{
+ seq->cycleflag = flag;
+ seq->cycledur = dur;
+}
+
+
+/* seq_end_event -- call this when an score-generated event ends */
+/*
+ * Assumes that noteoff_count was incremented when event started.
+ */
+void seq_end_event(seq)
+ seq_type seq;
+{
+ /*gprintf(TRANS, "nd");*/
+ seq->noteoff_count--;
+ if (seq->current == NULL /* finished seq */ &&
+ seq->noteoff_count == 0 /* finished noteoff's */ &&
+ seq->runflag /* we've not been stopped */) {
+ if (seq->cycleflag) {
+ cause((delay_type) (seq->cycledur - virttime), cycle, seq);
+ } else if (seq->stopfunc) {
+ (*(seq->stopfunc))(seq);
+ }
+ }
+}
+
+
+
+/****************************************************************************
+* seq_free_meth
+* Input: a seq_type
+* Effect:
+* frees storage occupied by a seq
+****************************************************************************/
+
+private void seq_free_meth(seq)
+ seq_type seq;
+{
+ seq_free_chunks(seq);
+ if (seq->timebase) timebase_free(seq->timebase);
+ memfree((void *) seq, sizeof(seq_node));
+}
+
+
+/* seq_free_chunks -- free storage for note list */
+/*
+ * NOTE: in its original form, this routine was perhaps more readable,
+ * but would not compile under Microsoft C V7.00 due to a compiler bug.
+ * I rewrote the code until the bug disappeared, hopefully without
+ * changing the semantics! If you change this code, make sure it still
+ * compiles under Microsoft C.
+ *
+ * This module frees chunks from a seq_type in preparation for freeing
+ * the seq_type itself. Reference counts are checked and chunks are
+ * only freed when the last reference is removed.
+ */
+public void seq_free_chunks(seq)
+ seq_type seq;
+{
+ chunk_type tail;
+ chunk_type head;
+
+ head = seq->chunklist;
+ if (((head->u.info.refcount)--) != 0) return;
+
+ while (head != NULL) {
+ tail = head->next;
+ memfree((void *) head, sizeof(chunk_node));
+ head = tail;
+ seq->chunklist = head;
+ }
+}
+
+
+seq_type seq_init(seq, create_chunk)
+ seq_type seq;
+ int create_chunk;
+{
+ if (!seq || !(seq->timebase = timebase_create(50))) {
+ return NULL;
+ }
+ seq->chunklist = NULL;
+ if (create_chunk) {
+ seq->chunklist = chunk_create(TRUE);
+ if (!seq->chunklist) {
+ seq_free(seq);
+ return NULL;
+ }
+ }
+ seq->cause_noteoff_fn = seq_cause_noteoff_meth;
+ seq->midi_bend_fn = seq_midi_bend_meth;
+ seq->midi_ctrl_fn = seq_midi_ctrl_meth;
+ seq->midi_program_fn = seq_midi_program_meth;
+ seq->midi_touch_fn = seq_midi_touch_meth;
+ seq->noteoff_fn = seq_noteoff_meth;
+ seq->noteon_fn = seq_noteon_meth;
+ seq->free_fn = seq_free_meth;
+ seq->reset_fn = seq_reset_meth;
+
+ seq->current = NULL;
+ seq->transpose = 0;
+ seq->loudness = 0;
+ seq->cycleflag = FALSE;
+ seq->cycledur = 0L;
+ seq->rate = 256L;
+ seq->paused = FALSE;
+ seq->stopfunc = noop;
+ seq->channel_mask = 0xFFFFFFFFL;
+ seq->runflag = seq->note_enable = FALSE;
+ return seq;
+}
+
+
+/* seq_midi_bend_meth -- send a midi bend */
+/**/
+void seq_midi_bend_meth(seq_type seq, int voice, int value)
+{
+ midi_bend(voice, value);
+}
+
+
+/* seq_midi_ctrl_meth -- send a midi ctrl change */
+/**/
+void seq_midi_ctrl_meth(seq_type seq, int voice, int ctrl, int value)
+{
+ midi_ctrl(voice, ctrl, value);
+}
+
+
+/* seq_midi_program_meth -- send a midi program change */
+/**/
+void seq_midi_program_meth(seq_type seq, int voice, int prog)
+{
+ midi_bend(voice, prog);
+}
+
+
+/* seq_midi_touch_meth -- send a midi touch */
+/**/
+void seq_midi_touch_meth(seq_type seq, int voice, int value)
+{
+ midi_touch(voice, value);
+}
+
+
+/* seq_noteoff_meth -- turn a seq note off */
+/**/
+void seq_noteoff_meth(seq_type seq, int voice, int pitch)
+{
+ midi_note(voice, pitch, 0);
+ /*gprintf(TRANS, "_e");*/
+ seq_end_event(seq);
+}
+
+
+/* seq_noteon_meth -- play a note with transformations */
+/**/
+void seq_noteon_meth(seq_type seq, int chan, int pitch, int vel)
+{
+ if (seq->note_enable) {
+ pitch += seq->transpose;
+ while (pitch < 0) pitch += 12;
+ while (pitch > 127) pitch -= 12;
+
+ vel += seq->loudness;
+ if (vel <= 0) vel = 1;
+ else if (vel > 127) vel = 127;
+
+ midi_note(chan, pitch, vel);
+ }
+}
+
+
+/* seq_pause -- stop playing momentarily or resume playing */
+/**/
+time_type seq_pause(seq_type seq, boolean flag)
+{
+ if (!seq->paused && flag) {
+ seq->paused = TRUE;
+ seq->rate = seq->timebase->rate;
+ set_rate(seq->timebase, STOPRATE);
+ } else if (seq->paused && !flag) {
+ seq_play(seq);
+ }
+ return (time_type) seq->timebase->virt_base;
+}
+
+
+/* seq_play -- play a sequence from the current event forward */
+/**/
+void seq_play(seq)
+ seq_type seq;
+{
+ timebase_type prev_timebase = timebase;
+ register timebase_type reg_timebase = seq->timebase;
+
+ if (!seq->runflag) {
+ seq_reset(seq);
+ }
+ if (!seq->paused) return;
+ eventtime = gettime();
+
+ /* assume that virt_base is correct virtual time as the result
+ of seq_start_time or seq_reset
+ */
+ timebase = reg_timebase;
+ virttime = reg_timebase->virt_base;
+ /* note that set_rate will set reg_timebase->real_base to eventtime */
+ set_rate(reg_timebase, seq->rate);
+ seq->paused = FALSE; /* in case the score had been paused; note that
+ seq_pause() has no effect if paused is TRUE */
+ seq->runflag = TRUE;
+ seq->note_enable = TRUE;
+
+ /* restore previous timebase */
+ timebase_use(prev_timebase);
+}
+
+
+/* seq_reset_meth -- reset a sequence to start back at the first event */
+/**/
+void seq_reset_meth(seq_type seq)
+{
+ timebase_type old_timebase = timebase;
+
+ if (seq->runflag) {
+ /* maybe this seq is already reset, and process_event is
+ * already scheduled. If so, don't schedule another one.
+ */
+ if ((seq->timebase->virt_base == 0) &&
+ (seq->timebase->rate == STOPRATE)) {
+ /* in case the reader just iterated through the list without
+ * cause'ing events, reset the event list
+ */
+ seq->current = seq_events(seq);
+ return;
+ }
+ /* Otherwise, the seq is running, so stop it. */
+ seq_stop(seq);
+ }
+
+ timebase_use(seq->timebase);
+ set_rate(seq->timebase, STOPRATE);
+ set_virttime(seq->timebase, 0L);
+ seq->current = seq_events(seq);
+ seq->noteoff_count = 0L;
+ seq->runflag = TRUE;
+ seq->paused = TRUE;
+ if (seq->current)
+ cause((delay_type)(seq->current->ntime - virttime), process_event, seq);
+ timebase_use(old_timebase);
+}
+
+
+/* seq_set_loudness -- set the loudness offset of a sequence */
+/**/
+void seq_set_loudness(seq, loud)
+ seq_type seq;
+ int loud;
+{
+ seq->loudness = loud;
+}
+
+/* seq_set_rate -- set the rate of a sequence */
+/**/
+void seq_set_rate(seq, rate)
+ seq_type seq;
+ time_type rate;
+{
+ seq->rate = rate;
+ if (!seq->paused) set_rate(seq->timebase, rate);
+}
+
+
+/* seq_set_transpose -- set the sequence transposition */
+/**/
+void seq_set_transpose(seq, trans)
+ seq_type seq;
+ int trans;
+{
+ seq->transpose = trans;
+}
+
+
+/* seq_start_time -- set the current pointer so the sequence starts here */
+/**/
+void seq_start_time(seq, start_time)
+ seq_type seq;
+ time_type start_time;
+{
+ timebase_type prev_timebase = timebase;
+ if (!seq->runflag) {
+ seq_reset(seq);
+ }
+ if (real_to_virt(seq->timebase, eventtime) > start_time) {
+ seq_reset(seq);
+ }
+ timebase_use(seq->timebase);
+ seq->note_enable = FALSE;
+ /* prime the pump */
+ set_rate(timebase, STOPRATE);
+ set_virttime(timebase, start_time);
+ catchup();
+ seq->note_enable = TRUE;
+ seq->paused = TRUE;
+ /* restore previous timebase */
+ timebase_use(prev_timebase);
+}
+
+
+/* seq_stop -- stop a sequence, clear out all pending events */
+/**/
+void seq_stop(seq)
+ seq_type seq;
+{
+ timebase_type prev_timebase = timebase;
+
+ if (seq->runflag) {
+ if (moxcdebug)
+ gprintf(TRANS, "seq_reset swap from timebase 0x%x to 0x%x\n",
+ timebase, seq->timebase);
+ timebase = seq->timebase;
+ seq->runflag = FALSE;
+ set_rate(timebase, STOPRATE);
+ set_virttime(timebase, MAXTIME);
+ catchup();
+ }
+ timebase_use(prev_timebase);
+}
diff --git a/cmt/seq.h b/cmt/seq.h
new file mode 100644
index 0000000..4177d2c
--- /dev/null
+++ b/cmt/seq.h
@@ -0,0 +1,295 @@
+/* seq.h -- definitions for seq, the MIDI Toolkit sequence data type */
+
+#define minpitch 0
+#define maxpitch 127
+#define NO_PITCH (maxpitch+1)
+#define minprogram 1
+#define maxprogram 128
+
+/* keep these two lines in sync */
+#define nmacroparms 4
+#define parm_expected_error "Parameter number [1-4] expected"
+
+#define seq_dflt_loud 127
+#define seq_dflt_voice 1
+#define seq_dflt_pitch 60
+
+struct clock_struct {
+ time_type ticksize;
+};
+
+
+struct ctrlramp_struct {
+ unsigned char from_value;
+ unsigned char to_value;
+};
+
+
+struct deframp_struct {
+ unsigned char *definition;
+ short parameter[nmacroparms];
+ unsigned char parm_num;
+ short to_value;
+};
+
+
+struct macctrl_struct {
+ unsigned char ctrl_number;
+ unsigned char value;
+};
+
+
+struct macro_struct {
+ unsigned char *definition;
+ short parameter[nmacroparms];
+};
+
+
+struct note_struct {
+ long ndur; /* duration */
+ /* char nloud; loudness (MIDI velocity) now stored as low order byte
+ * of ndur
+ */
+};
+
+
+struct ramp_struct {
+ time_type dur;
+ short ctrl; /* encode pitch bend and after touch as negative */
+ short step;
+ union {
+ struct ctrlramp_struct ctrl;
+ struct deframp_struct def;
+ } u;
+};
+
+
+struct seti_struct {
+ int *int_to_set;
+ int value;
+};
+
+
+#define SEQ_MAX_PARMS 8
+struct cause_struct {
+ int (*routine)();
+ /* make a structure so we can copy by value */
+ struct seq_arg_struct {
+ long a[SEQ_MAX_PARMS];
+ } args;
+};
+
+
+typedef struct event_struct {
+ struct event_struct *next;
+ time_type ntime; /* start time */
+ short nline; /* line number from source code */
+ unsigned char nvoice; /* adagio voice (MIDI Channel)
+ * if this is a control change, high order 4 bits
+ * contain the control number, otherwise high order
+ * 4 bits are 0 (see is_note macro below)
+ */
+ unsigned char value;
+ /* this is a note pitch or a control value. It goes
+ * here rather than in the union to achieve word
+ * alignment (!). Also, value is used for extra commands
+ * such as call, seti, setv.
+ */
+ union {
+ struct cause_struct call;
+ struct clock_struct clock;
+ struct macctrl_struct macctrl;
+ struct macro_struct macro;
+ struct note_struct note;
+ struct ramp_struct ramp;
+ struct seti_struct seti;
+ } u;
+} event_node, *event_type;
+
+#define PSWITCH_CTRL 1
+#define MODWHEEL_CTRL 2
+#define TOUCH_CTRL 3
+#define VOLUME_CTRL 4
+#define BEND_CTRL 5
+#define PROGRAM_CTRL 6
+#define ESC_CTRL 7
+
+#define CALL_VALUE 0
+#define CLOCK_VALUE 1
+#define MACCTRL_VALUE 2
+#define MACRO_VALUE 3
+#define CTRLRAMP_VALUE 4
+#define DEFRAMP_VALUE 5
+#define SETI_VALUE 6
+
+#define commonsize (sizeof(struct event_struct) - sizeof(struct cause_struct))
+#define rampcommon (sizeof(struct ramp_struct) - sizeof(struct deframp_struct))
+#define ctrlsize commonsize
+#define callsize (commonsize + sizeof(struct cause_struct))
+#define clocksize (commonsize + sizeof(struct clock_struct))
+#define ctrlrampsize (commonsize + rampcommon + sizeof(struct ctrlramp_struct))
+#define deframpsize (commonsize + sizeof(struct ramp_struct))
+#define macctrlsize (commonsize + sizeof(struct macctrl_struct))
+#define macrosize (commonsize + sizeof(struct macro_struct))
+#define notesize (commonsize + sizeof(struct note_struct))
+#define setisize (commonsize + sizeof(struct seti_struct))
+#define ctrl_voice(c, v) (((c) << 5) + ((v) - 1))
+#define vc_ctrl(v) ((v) >> 5)
+#define vc_voice(v) (((v) & 0x1F) + 1)
+
+#define is_note(n) (((n)->nvoice & 0xE0) == 0)
+
+#define CHUNK_SIZE 2000
+
+typedef struct def_struct {
+ struct def_struct *next;
+ char *symbol;
+ unsigned char *definition;
+} def_node, *def_type;
+
+
+typedef struct chunk_struct {
+ struct chunk_struct *next;
+ short free;
+ union {
+ char data[CHUNK_SIZE];
+ struct info_struct {
+ short refcount;
+ struct chunk_struct *last_chunk; /* where to allocate memory */
+ def_type dictionary; /* macro defns, routine addresses */
+ event_type eventlist; /* first event in sequence */
+ ulong used_mask; /* tells what channels are actually present */
+ long ctrlcount;
+ long notecount;
+ time_type duration; /* time of the last event+dur in score */
+ } info;
+ } u;
+} chunk_node, *chunk_type;
+
+
+typedef struct seq_struct {
+ void (*cause_noteoff_fn)(struct seq_struct *, time_type, int, int);
+ void (*midi_bend_fn)(struct seq_struct * seq, int voice, int value);
+ void (*midi_ctrl_fn)(struct seq_struct * seq, int voice, int ctrl, int value);
+ void (*midi_program_fn)(struct seq_struct * seq, int voice, int prog);
+ void (*midi_touch_fn)(struct seq_struct * seq, int voice, int value);
+ void (*noteoff_fn)(struct seq_struct * seq, int voice, int pitch);
+ void (*noteon_fn)(struct seq_struct * seq, int chan, int pitch, int vel);
+ void (*free_fn)(struct seq_struct * seq);
+ void (*reset_fn)(struct seq_struct * seq);
+ void (*stopfunc)(struct seq_struct *);
+ chunk_type chunklist;
+ /* event_type eventlist;
+ seq->eventlist is now seq->eventlist->chunklist */
+ event_type current;
+ boolean runflag; /* normally true, set to false as a flag for
+ * processes started by score to terminate */
+ boolean note_enable; /* normally true, set to false as a flag to
+ * suppress note output, e.g. when indexing
+ * to a particular time point */
+ boolean cycleflag; /* normally false, set to true to make the
+ * sequence cycle every cycledur */
+ int transpose;
+ int loudness;
+ time_type cycledur;
+ timebase_type timebase;
+ time_type rate; /* remembers rate across pauses */
+ boolean paused; /* remembers if seq has been stopped or not */
+ short noteoff_count; /* keeps track of pending events, such as
+ * note off commands. When this count goes
+ * to zero, the score is finished */
+ ulong channel_mask;
+} seq_node, *seq_type;
+
+extern seq_type sequence;
+
+chunk_type chunk_create(boolean first_flag);
+
+#define seq_cause_noteoff(seq, delay, voice, pitch) \
+ (*(((seq_type) seq)->cause_noteoff_fn))(seq, delay, voice, pitch)
+#define seq_midi_bend(seq, voice, value) \
+ (*(((seq_type) seq)->midi_bend_fn))(seq, voice, value)
+#define seq_midi_ctrl(seq, voice, ctrl, value) \
+ (*(((seq_type) seq)->midi_ctrl_fn))(seq, voice, ctrl, value)
+#define seq_midi_program(seq, voice, prog) \
+ (*(((seq_type) seq)->midi_program_fn))(seq, voice, prog)
+#define seq_midi_touch(seq, voice, value) \
+ (*(((seq_type) seq)->midi_touch_fn))(seq, voice, value)
+#define seq_noteoff(seq, voice, pitch) \
+ (*(((seq_type) seq)->noteoff_fn))(seq, voice, pitch)
+#define seq_noteon(seq, voice, pitch, vel) \
+ (*(((seq_type) seq)->noteon_fn))(seq, voice, pitch, vel)
+#define seq_free(seq) (*(((seq_type) seq)->free_fn))(seq)
+#define seq_register(seq) \
+ cu_register((cu_fn_type) (((seq_type) seq)->free_fn), seq)
+#define seq_reset(seq) (*(((seq_type) seq)->reset_fn))(seq)
+ /* LISP: void (SEQ-RESET SEQ) */
+
+extern boolean seq_print; /* debugging switch */
+
+void seq_extensions(void); /* to be defined outside of seq -- user dependent */
+event_type insert_call(seq_type seq, time_type ctime, int cline,
+ int voice, int (*addr)(), long value[], int n);
+event_type insert_clock(seq_type seq, time_type ctime, int cline,
+ time_type ticksize);
+event_type insert_ctrl(seq_type seq, time_type ctime, int cline, int ctrl,
+ int voice, int value);
+ /* LISP: (SEQ-INSERT-CTRL SEQ FIXNUM FIXNUM FIXNUM FIXNUM FIXNUM) */
+event_type insert_ctrlramp(seq_type seq, time_type rtime, int rline, int voice,
+ time_type step, time_type dur, int ctrl, int v1, int v2);
+ /* LISP: (SEQ-INSERT-RAMP SEQ FIXNUM FIXNUM FIXNUM FIXNUM FIXNUM FIXNUM FIXNUM FIXNUM) */
+def_type insert_def(seq_type seq, char *symbol, unsigned char *definition,
+ int deflen);
+event_type insert_deframp(seq_type seq, time_type rtime, int rline, int voice,
+ time_type step, time_type dur, def_type def,
+ int nparms, short parms[], int parm_num, int to_value);
+event_type insert_macctrl(seq_type seq, time_type ctime, int cline, int ctrl,
+ int voice, int value);
+ /* LISP: (SEQ-INSERT-MACCTRL SEQ FIXNUM FIXNUM FIXNUM FIXNUM FIXNUM) */
+event_type insert_macro(seq_type seq, time_type ctime, int cline,
+ def_type def, int voice, int nparms, short *parms);
+event_type insert_note(seq_type seq, time_type ntime, int nline, int voice,
+ int pitch, time_type dur, int loud);
+ /* LISP: (SEQ-INSERT-NOTE SEQ FIXNUM FIXNUM FIXNUM FIXNUM FIXNUM FIXNUM) */
+event_type insert_seti(seq_type seq, time_type stime, int sline, int voice,
+ int *addr, int value);
+void noop(seq_type seq);
+seq_type seq_alloc(void);
+void seq_at_end(seq_type seq, void (*fn)(seq_type));
+void seq_cause_noteoff_meth(seq_type seq, time_type delay, int voice, int pitch);
+#define seq_channel_mask(seq) ((seq)->channel_mask)
+seq_type seq_copy(seq_type from_seq); /* LISP: (SEQ-COPY SEQ) */
+seq_type seq_create(void); /* LISP: (SEQ-CREATE) */
+void seq_cycle(seq_type seq, boolean flag, time_type dur);
+#define seq_duration(seq) (((seq_type) seq)->chunklist->u.info.duration)
+void seq_end_event(seq_type seq);
+#define seq_events(seq) (((seq_type) seq)->chunklist ? \
+ (((seq_type) seq)->chunklist->u.info.eventlist) : NULL)
+#define seq_dictionary(seq) (seq)->chunklist->u.info.dictionary
+#define seq_eventlist(seq) (seq)->chunklist->u.info.eventlist
+#define seq_ctrlcount(seq) (seq)->chunklist->u.info.ctrlcount
+#define seq_notecount(seq) (seq)->chunklist->u.info.notecount
+#define seq_used_mask(seq) (seq)->chunklist->u.info.used_mask
+void seq_free_chunks(seq_type seq);
+seq_type seq_init(seq_type seq, int create_chunk);
+#define seq_loudness(seq) (((seq_type) seq)->loudness)
+void seq_midi_bend_meth(seq_type seq, int voice, int value);
+void seq_midi_ctrl_meth(seq_type seq, int voice, int ctrl, int value);
+void seq_midi_program_meth(seq_type seq, int voice, int prog);
+void seq_midi_touch_meth(seq_type seq, int voice, int value);
+void seq_noteon_meth(seq_type seq, int voice, int pitch, int vel);
+void seq_noteoff_meth(seq_type seq, int chan, int pitch);
+time_type seq_pause(seq_type seq, boolean flag);
+void seq_play(seq_type seq);
+#define seq_rate(seq) ((seq_type) seq)->rate
+void seq_reset_meth(seq_type seq);
+#define seq_runflag(seq) ((seq_type) seq)->runflag
+#define seq_set_channel_mask(seq, cm) ((seq)->channel_mask) = (cm)
+void seq_set_loudness(seq_type seq, int offset);
+void seq_set_rate(seq_type seq, time_type rate);
+#define seq_set_timebase(seq, tb) ((seq_type) seq)->timebase = (tb)
+void seq_set_transpose(seq_type seq, int trans);
+void seq_start_time(seq_type seq, time_type start_time);
+void seq_stop(seq_type seq);
+#define seq_timebase(seq) ((seq_type) seq)->timebase
+#define seq_transpose(seq) ((seq_type) seq)->transpose
diff --git a/cmt/seqdecls.h b/cmt/seqdecls.h
new file mode 100644
index 0000000..654d12a
--- /dev/null
+++ b/cmt/seqdecls.h
@@ -0,0 +1,11 @@
+/* This .h file declares everything you need to include seq.h */
+/*
+ * This file gets included by an interface to seq generated by intgen
+ */
+#include "switches.h"
+#include "stdio.h"
+#include "cext.h"
+#include "userio.h"
+#include "midifns.h"
+#include "timebase.h"
+#include "moxc.h"
diff --git a/cmt/seqmread.c b/cmt/seqmread.c
new file mode 100644
index 0000000..52f0c57
--- /dev/null
+++ b/cmt/seqmread.c
@@ -0,0 +1,448 @@
+/*
+ * seqmread.c
+ *
+ * Convert a MIDI file to a seq.
+ */
+
+/* Copyright 1989 Carnegie Mellon University */
+
+/*****************************************************************************
+* Change Log
+* Date | who : Change
+*-----------+-----------------------------------------------------------------
+* 17-Feb-92 | GWL : only one stdio.h
+* : fix to satisfy compiler:
+ void returns, time_type giotime(), int filegetc()
+*****************************************************************************/
+
+#include "switches.h"
+#include "stdio.h"
+#include "cext.h"
+#include "cmdline.h"
+#include "midifns.h" /* to get time_type */
+#include "timebase.h"
+#include "moxc.h" /* to get debug declared */
+#include "seq.h"
+#include "seqread.h" /* to get scale */
+#include "seqmread.h"
+#include "userio.h"
+#include "ctype.h"
+
+#include "midifile.h"
+#include "tempomap.h"
+
+int filegetc();
+void initfuncs();
+void prtime();
+void snding_free();
+
+typedef struct snding_struct {
+ struct snding_struct *next;
+ event_type event_ptr;
+ int pitch;
+ int channel;
+} snding_node, *snding_type;
+
+#define snding_alloc() (snding_type) memget(sizeof(snding_node))
+#define snding_free(s) memfree(s, sizeof(snding_node))
+
+snding_type snding_list = NULL;
+
+tempomap_type the_tempomap;
+event_type initial_clock; /* remember the first clock event */
+long prev_ticksize; /* remember the previous ticksize */
+int sysex_id = 0;
+
+
+void smf_noteoff();
+void smf_error();
+void smf_header();
+void smf_trackstart();
+void smf_trackend();
+void smf_noteon();
+void smf_pressure();
+void smf_parameter();
+void smf_pitchbend();
+void smf_program();
+void smf_chanpressure();
+void smf_sysex();
+void smf_metamisc();
+void smf_metaseq();
+void smf_metaeot();
+void smf_timesig();
+void smf_smpte();
+void smf_tempo();
+void smf_keysig();
+void smf_metaspecial();
+void smf_metatext();
+void smf_arbitrary();
+
+private seq_type the_score;
+
+static FILE *F;
+
+int filegetc()
+{
+/* int temp = getc(F);
+ printf(" %x ", temp);*/
+ return(int)(getc(F));
+}
+
+void seq_read_smf(seq, fp)
+ seq_type seq;
+ FILE *fp;
+{
+ F = fp;
+ initfuncs();
+ sysex_id = 0; /* sysex in seq has to correspond to a symbol */
+ the_score = seq; /* current sequence is a global within this module */
+ if (!seq) return;
+ the_tempomap = tempomap_create();
+ /* insert an initial clock to correspond to the default midifile tempo
+ (tempomap_create creates a corresponding initial entry in the tempomap)
+ (see smf_tempo for explanation of the scale() call)
+ */
+ initial_clock = insert_clock(the_score, 0L, 0, 500L << 16);
+ /* scale(24 * 500000, 1 << 16, 24000) */
+ if (!the_tempomap) return;
+ Mf_getc = filegetc;
+ midifile();
+ /* fmac_close(F); -- do not close the file because the caller might try to
+ * close it (in fact XLISP insists on closing it as a side effect of
+ * garbage collection.
+ */
+ gprintf(TRANS, "\nLoaded Midi file with %ld note(s), %ld ctrl(s).\n\n",
+ seq_notecount(seq), seq_ctrlcount(seq));
+ seq_reset(seq);
+ while (snding_list) {
+ snding_type snding = snding_list;
+ snding_list = snding_list->next;
+ gprintf(TRANS, "Note-on (key %d, chan %d) has no matching noteoff\n",
+ snding->pitch, snding->channel + 1);
+ snding_free(snding);
+ }
+ tempomap_free(the_tempomap);
+}
+
+
+/* gio_time -- get the time in millisec for Adagio */
+/*
+ * Since Adagio times are (in their precise form) 1/256 ms, we want
+ * a similar time for midifiles, whose natural unit would be microseconds.
+ * We'll shift the microsecond time by 2 to get 1/250 ms = 4 us units
+ * and convert using the scale function when necessary.
+ * Real time is the time of the last tempo change (last_tempo_time)
+ * which is in 4us units + elapsed time.
+ * Elapsed time is the elapsed beats times the beat duration.
+ * Elapsed beats is Mf_currtime - last_tempo_beat.
+ * Beat duration is the specified tempo / division, where specified tempo
+ * is in microseconds, and division is parts per quarternote.
+ */
+unsigned long divisions = 24L;
+
+time_type gio_time()
+{
+ return (tempomap_lookup(the_tempomap, Mf_currtime) + 125L) / 250L;
+}
+
+
+void smf_header(format,ntrks,division)
+{
+/* gprintf(TRANS, "Header format=%d ntrks=%d division=%d\n",
+ format,ntrks,division); */
+ if (format > 1) gprintf(TRANS,
+ "Warning: format %d midi file may not work.\n",
+ format);
+ divisions = division;
+ /* adjust the initial tempochange */
+ the_tempomap->entries->tempo = 500000L / division;
+}
+
+
+void smf_trackstart()
+{
+/* gprintf(TRANS, "Track start\n"); */
+}
+
+void smf_trackend()
+{
+/* gprintf(TRANS, "Track end\n"); */
+}
+
+void smf_noteon(chan,pitch,vol)
+{
+ snding_type snding;
+ if (vol == 0) { /* convert to a noteoff */
+ smf_noteoff(chan, pitch, 0);
+ return;
+ }
+/* prtime();
+ gprintf(TRANS, "Note on, chan=%d pitch=%d vol=%d\n",chan+1,pitch,vol);
+*/
+ /* get ready to remember the sounding note */
+ snding = snding_alloc();
+ snding->next = snding_list;
+ snding_list = snding;
+ /* enter an event into score and remember it */
+ snding->event_ptr = insert_note(the_score, gio_time(), 0,
+ chan + 1, pitch, 0L, vol);
+ snding->pitch = pitch;
+ snding->channel = chan;
+}
+
+void smf_noteoff(chan,pitch,vol)
+{
+ snding_type *snding_ptr;
+ register snding_type snding;
+/* prtime();
+ gprintf(TRANS, "Note off, chan=%d pitch=%d vol=%d\n",chan+1,pitch,vol);
+*/ /* search for the snding record */
+ for (snding_ptr = &snding_list;
+ (snding = *snding_ptr) &&
+ ((snding->pitch != pitch) || (snding->channel != chan));
+ snding_ptr = &(snding->next)) /* printf("* search *\n") */;
+ if (!snding) {
+ gprintf(TRANS, "Note off %d, channel %d ignored: no note on\n",
+ pitch, chan + 1);
+ } else {
+ event_type event = snding->event_ptr;
+ event->u.note.ndur += (gio_time() - event->ntime) << 8;
+ /* free the snding record */
+ *snding_ptr = snding->next;
+ snding_free(snding);
+ }
+}
+
+
+void smf_pressure(chan,pitch,press)
+{
+ prtime();
+ gprintf(TRANS, "Pressure, chan=%d pitch=%d press=%d (IGNORED)\n",
+ chan + 1, pitch, press);
+}
+
+void smf_parameter(chan,control,value)
+{
+ int ctrl = 0;
+/* prtime();
+ gprintf(TRANS, "Parameter, chan=%d c1=%d c2=%d\n",chan+1,control,value);
+ */ /* see if the control is one of the standard Adagio controls that
+ can be encoded in a special way. If not, ctrl remains at zero.
+ */
+ switch (control) {
+ case PORTASWITCH: ctrl = PSWITCH_CTRL; break;
+ case MODWHEEL: ctrl = MODWHEEL_CTRL; break;
+ case VOLUME: ctrl = VOLUME_CTRL; break;
+ }
+ if (ctrl) /* then do special ctrl insert and save storage */
+ insert_ctrl(the_score, gio_time(), 0, ctrl, chan + 1, value);
+ else insert_macctrl(the_score, gio_time(), 0, control, chan + 1, value);
+}
+
+
+/* smf_pitchbend -- handle a pitch bend event */
+/*
+ * NOTE: the midifile code from Tim Thompson has the msb and lsb bytes swapped.
+ * Thus the parameter msb is really the low order byte and lsb is high order.
+ */
+void smf_pitchbend(chan,msb,lsb)
+{
+/* prtime();
+ gprintf(TRANS, "Pitchbend, chan=%d msb=%d lsb=%d\n",chan+1,msb,lsb); */
+ insert_ctrl(the_score, gio_time(), 0, BEND_CTRL, chan + 1,
+ ((lsb << 7) + msb) >> 6);
+}
+
+void smf_program(chan,program)
+{
+/* prtime();
+ gprintf(TRANS, "Program, chan=%d program=%d\n",chan+1,program); */
+ insert_ctrl(the_score, gio_time(), 0, PROGRAM_CTRL, chan + 1, program);
+}
+
+void smf_chanpressure(chan,press)
+{
+/* prtime();
+ gprintf(TRANS, "Channel pressure, chan=%d pressure=%d\n",chan+1,press);
+ */
+ insert_ctrl(the_score, gio_time(), 0, TOUCH_CTRL, chan + 1, press);
+}
+
+void smf_sysex(leng,mess)
+int leng;
+char *mess;
+{
+ char symb[10];
+ def_type defn;
+ int i;
+ sprintf(symb, "X%d", sysex_id++);
+ if (leng > 255) {
+ gprintf(TRANS, "sysex too long (%d bytes), ignored\n", leng - 2);
+ return;
+ }
+ /* need to end up with a prefix of [0][length], so add 2 to length;
+ note that this will copy past the end of the message -- this is
+ slightly dangerous and definitely crufty:
+ */
+ defn = insert_def(the_score, symb, (unsigned char *) mess, leng + 2);
+ /* now fix up the definition by inserting the prefix bytes: */
+ for (i = leng + 1; i > 1; i--)
+ defn->definition[i] = defn->definition[i - 2];
+ defn->definition[0] = 0;
+ defn->definition[1] = leng;
+ insert_macro(the_score, gio_time(), 0, defn, 1, 0, NULL);
+/* prtime();
+ gprintf(TRANS, "Sysex, leng=%d (IGNORED)\n",leng); */
+}
+
+void smf_metamisc(type,leng,mess)
+char *mess;
+{
+ prtime();
+ gprintf(TRANS,
+ "Meta event, unrecognized, type=0x%02x leng=%d (IGNORED)\n",
+ type, leng);
+}
+
+void smf_metaspecial(type,leng,mess)
+char *mess;
+{
+ prtime();
+ gprintf(TRANS,
+ "Meta event, sequencer-specific, type=0x%02x leng=%d (IGNORED)\n",
+ type, leng);
+}
+
+void smf_metatext(type,leng,mess)
+char *mess;
+{
+ static char *ttype[] = {
+ NULL,
+ "Text Event", /* type=0x01 */
+ "Copyright Notice", /* type=0x02 */
+ "Sequence/Track Name",
+ "Instrument Name", /* ... */
+ "Lyric",
+ "Marker",
+ "Cue Point", /* type=0x07 */
+ "Unrecognized"
+ };
+ int unrecognized = (sizeof(ttype)/sizeof(char *)) - 1;
+
+ if ( type < 1 || type > unrecognized )
+ type = unrecognized;
+}
+
+void smf_metaseq(num)
+{
+ prtime();
+ gprintf(TRANS, "Meta event, sequence number = %d (IGNORED)\n",num);
+}
+
+void smf_metaeot()
+{
+/* prtime();
+ gprintf(TRANS, "Meta event, end of track\n"); */
+}
+
+void smf_keysig(sf,mi)
+{
+/* prtime();
+ gprintf(TRANS, "Key signature, sharp/flats=%d minor=%d\n",sf,mi); */
+}
+
+/* smf_tempo -- handle a midifile tempo change */
+/*
+ * NOTE: if divisions is positive, it gives time units per quarter, and
+ * tempo is microsec per division. The product is microsec per quarter.
+ * To convert to ticksize (parameter to insert_clock), we divide by 24*1000
+ * to get units of millisec and 24ths of quarter notes. insert_clock
+ * expects this to have a 16 bit fractional part.
+ */
+void smf_tempo(tempo)
+long tempo;
+{
+ time_type ctime = gio_time();
+ long ticksize = scale(tempo, 1024L, 375L);
+/* (tempo / 24000) << 16; microsec/clock converted to ms/quarter, shifted 16*/
+
+/* prtime();
+ gprintf(TRANS, "Tempo, microseconds-per-MIDI-quarter-note = %ld\n",tempo);
+*/
+ tempomap_insert(the_tempomap, Mf_currtime, tempo / divisions);
+ if (ctime == 0) {
+ /* we already have a clock event at t=0 -> fix it */
+ initial_clock->u.clock.ticksize = ticksize;
+ } else { /* we need a new one */
+ /* NOTE: after the first clock, insert clock events 1/2 tick early
+ to make sure ticksize is set before clock_tick() wakes up and
+ reads it.
+ */
+ insert_clock(the_score, ctime - (prev_ticksize >> 17), 0, ticksize);
+ prev_ticksize = ticksize;
+ }
+}
+
+void smf_timesig(nn,dd,cc,bb)
+{
+/* int denom = 1;
+ while ( dd-- > 0 )
+ denom *= 2;
+ prtime();
+ gprintf(TRANS,
+"Time signature=%d/%d MIDI-clocks/click=%d 32nd-notes/24-MIDI-clocks=%d\n",
+ nn,denom,cc,bb); */
+}
+
+void smf_smpte(hr,mn,se,fr,ff)
+{
+ prtime();
+ gprintf(TRANS,
+ "SMPTE, hour=%d minute=%d second=%d frame=%d fract-frame=%d (IGNORED)\n",
+ hr, mn, se, fr, ff);
+}
+
+void smf_arbitrary(leng,mess)
+char *mess;
+{
+ prtime();
+ gprintf(TRANS, "Arbitrary bytes, leng=%d (IGNORED)\n",leng);
+}
+
+void smf_error(msg)
+ char *msg;
+{
+ gprintf(ERROR, msg);
+}
+
+
+void prtime()
+{
+ gprintf(TRANS, "Time=%ld/%ld ",Mf_currtime, gio_time());
+}
+
+void initfuncs()
+{
+ Mf_error = smf_error;
+ Mf_header = smf_header;
+ Mf_starttrack = smf_trackstart;
+ Mf_endtrack = smf_trackend;
+ Mf_on = smf_noteon;
+ Mf_off = smf_noteoff;
+ Mf_pressure = smf_pressure;
+ Mf_controller = smf_parameter;
+ Mf_pitchbend = smf_pitchbend;
+ Mf_program = smf_program;
+ Mf_chanpressure = smf_chanpressure;
+ Mf_sysex = smf_sysex;
+ Mf_metamisc = smf_metamisc;
+ Mf_seqnum = smf_metaseq;
+ Mf_eot = smf_metaeot;
+ Mf_timesig = smf_timesig;
+ Mf_smpte = smf_smpte;
+ Mf_tempo = smf_tempo;
+ Mf_keysig = smf_keysig;
+ Mf_sqspecific = smf_metaspecial;
+ Mf_text = smf_metatext;
+ Mf_arbitrary = smf_arbitrary;
+}
diff --git a/cmt/seqmread.h b/cmt/seqmread.h
new file mode 100644
index 0000000..a03da03
--- /dev/null
+++ b/cmt/seqmread.h
@@ -0,0 +1,4 @@
+/* seqmread.h -- midi file reader */
+
+void seq_read_smf(seq_type seq, FILE *fp); /* LISP: (SEQ-READ-SMF SEQ FILE) */
+
diff --git a/cmt/seqmwrite.c b/cmt/seqmwrite.c
new file mode 100644
index 0000000..4e0b918
--- /dev/null
+++ b/cmt/seqmwrite.c
@@ -0,0 +1,658 @@
+/* created by DMH (damonhorowitz): write seq_type as standard midifile */
+/***************************************************************************
+* Change Log
+* Date | Change
+*-----------+---------------------------------------------------------------
+* 11-Mar-94 | Created Change Log
+* 11-Mar-94 | PLu : Added private to function defs.
+* 28-Apr-03 | DM : Change #include's for portability
+****************************************************************************/
+
+#include "switches.h"
+
+#include <stdio.h>
+
+#include "cext.h"
+#include "userio.h"
+#include "midicode.h"
+#include "mfmidi.h"
+#include "midifns.h"
+#include "timebase.h"
+#include "moxc.h"
+#include "seq.h"
+#include "seqread.h" /* to get scale() */
+#include "seqmwrite.h"
+
+long chunk_size_marker;
+int seti_counter;
+extern time_type a_start_time;
+
+long last_event; /*time from last_clock_event to the last event*/
+time_type last_clock_event;
+time_type last_tick_size; /* millisec per tick shifted 16 bits */
+
+struct smf_write_seq {
+ seq_type seq;
+ int track;
+ FILE *outfile;
+ } smfw_seq;
+extern seq_type sequence; /* this is a global to be accessed by
+ * routines called from the sequence */
+/* clock state: */
+extern time_type clock_ticksize; /* millisec per tick shifted 16 bits */
+extern boolean clock_running; /* TRUE if clock is running */
+extern boolean use_midi_clock;
+
+private void smfw_bend();
+private void smfw_cause_noteoff();
+private void smfw_ctrl();
+private void smfw_deltatime();
+private void smfw_dotrack();
+private void smfw_exclusive();
+private void smfw_noteoff();
+private void smfw_noteon();
+private void smfw_process_event();
+private void smfw_ramp_event(seq_type seq, event_type event,
+ unsigned int value, unsigned int to_value, int increment,
+ time_type step, int n);
+private void smfw_send_macro();
+private void smfw_touch(seq_type seq, int voice, int value);
+private void writevarlen();
+
+
+/* smfw_bend -- write a pitch bend to a midi file */
+/**/
+private void smfw_bend(seq_type seq, int voice, int value)
+{
+ if(debug) gprintf(TRANS, "smfw_bend %d\n", value);
+ smfw_deltatime();
+ putc(MIDI_BEND | (voice - 1), smfw_seq.outfile);
+ putc(0xFF & ((value & 0x1) << 6) , smfw_seq.outfile);
+ putc(0xFF & (value >> 1), smfw_seq.outfile);
+}
+
+
+/* smfw_cause_noteoff -- schedule a noteoff for midi file */
+/*
+ * NOTE: this is called by smfw_process_event when it handles a note
+ * event node in a seq_type's event list. The ordinary moxc scheduler
+ * is used to "schedule" the noteoff in the future. In reality, the
+ * output is done as fast as possible (by attempting an infinite rate),
+ * so no real timing delays occur. The effect is to sort events by their
+ * specified time.
+ */
+private void smfw_cause_noteoff(seq, delay, voice, pitch)
+ seq_type seq;
+ time_type delay;
+ int voice;
+ int pitch;
+{
+ if(debug) gprintf(TRANS, "cause noteoff at %ld...", virttime + delay);
+ pitch += seq->transpose;
+ while (pitch <= 0) pitch += 12;
+ while (pitch >= 127) pitch -= 12;
+ seq->noteoff_count++;
+ causepri((delay_type) delay, 10, seq->noteoff_fn,
+ seq, voice, pitch);
+
+}
+
+private void smfw_clock_event(old_ticksize, new_ticksize)
+ time_type old_ticksize, new_ticksize;
+{
+ time_type temp_ticksize = new_ticksize;
+ new_ticksize = scale(new_ticksize, 375L, 1024L);
+/* (new_ticksize >> 16) * 24000 ms/clock becomes us/midiquarter */
+
+ if(debug) gprintf(TRANS, "smfw_clock: write %ld (time:%ld) ->->->tempo %ld\n",
+ new_ticksize, virttime, 2500L / (new_ticksize / 24000));
+
+ /*use old ticksize to write the delta for the clock event*/
+ last_tick_size = old_ticksize;
+ smfw_deltatime();
+ last_tick_size = temp_ticksize;/* reset to = new_tick_size */
+ putc(0xFF, smfw_seq.outfile);
+ putc(0x51, smfw_seq.outfile);
+ putc(0x03, smfw_seq.outfile);
+ putc((int) ((new_ticksize >> 16) & 0xFF), smfw_seq.outfile);
+ putc((int) ((new_ticksize >> 8) & 0xFF), smfw_seq.outfile);
+ putc((int) (new_ticksize & 0xFF), smfw_seq.outfile);
+
+ last_clock_event = virttime;
+ last_event = 0L;
+ /*no time expired between last clockevent and last event(they are the same).*/
+ /*next clock event will be exactly the next this_event from last_clock_event*/
+}
+
+
+/* smfw_ctrl -- write a control change to a midi file */
+/**/
+private void smfw_ctrl(seq_type seq, int voice, int ctrl_name, int value)
+{
+ if(debug) gprintf(TRANS, "smfw_ctrl %d: %d\n", ctrl_name, value);
+ smfw_deltatime();
+ putc(MIDI_CTRL | (voice - 1), smfw_seq.outfile);
+ putc(ctrl_name, smfw_seq.outfile);
+ putc(value, smfw_seq.outfile);
+}
+
+
+/* smfw_deltatime -- write the time difference between now an previous event */
+/**/
+private void smfw_deltatime()
+{
+ /* if last_ and clock_ are different, use last_ for clock deltatime*/
+ time_type use_ticksize = (clock_ticksize != last_tick_size) ?
+ last_tick_size : clock_ticksize;
+ time_type this_event = virttime - last_clock_event;
+ if(debug) gprintf(TRANS, "delta! ticksize: %lu Lastev: %ld ThisevScaled: %lu Thisev: %lu ",
+ clock_ticksize, last_event, (this_event * ((2500L << 16) / use_ticksize)) / 100,
+ this_event);
+
+ this_event = ((virttime - last_clock_event) * ((2500L << 16) / use_ticksize)) / 100;
+
+ if(debug) gprintf(TRANS, "--- deltatime: %lu\n", this_event - last_event);
+ writevarlen((long) (this_event - last_event));
+
+ last_event = this_event;
+}
+
+
+/* smfw_dotrack -- write the remainder of a track */
+private void smfw_dotrack(seq)
+ seq_type seq;
+{
+ long end_marker;
+ timebase_type old_timebase = timebase;
+ unsigned long chunk_size;
+
+ if (seq->runflag) {
+ if ((seq->timebase->virt_base == 0) &&
+ (seq->timebase->rate == STOPRATE))
+ /*we just set these last time through... do nothing*/;
+ seq_stop(seq);
+ }
+ timebase_use(seq->timebase);
+ set_rate(seq->timebase, STOPRATE);
+ set_virttime(seq->timebase, 0L);
+ seq->current = seq_events(seq);
+ seq->noteoff_count = 0L;
+ seq->runflag = TRUE;
+ seq->paused = TRUE;
+ last_clock_event = 0L;
+ last_event = 0L;
+ if(debug) gprintf(TRANS, "dotrack (reset) %d %ld (%lu) \n",
+ smfw_seq.track, last_event, virttime);
+
+ if (seq->current)
+ cause((delay_type)(seq->current->ntime - virttime), smfw_process_event,
+ seq);
+ set_virttime(timebase, MAXTIME);
+ catchup();
+
+ putc(0x00, smfw_seq.outfile);
+ putc(0xFF, smfw_seq.outfile);/*end of track chunk*/
+ putc(0x2F, smfw_seq.outfile);
+ putc(0x00, smfw_seq.outfile);
+ end_marker = ftell(smfw_seq.outfile);
+ fseek(smfw_seq.outfile, chunk_size_marker, 0);/*go back to enter chunksize*/
+ chunk_size = (end_marker - chunk_size_marker) - 4;/* - 4 for 4 size bytes*/
+ if(debug) gprintf(TRANS, "bytes written in previous track: %ld \n\n", chunk_size);
+ putc((int) ((0xFF & (chunk_size >> 24))), smfw_seq.outfile);
+ putc((int) ((0xFF & (chunk_size >> 16))), smfw_seq.outfile);
+ putc((int) ((0xFF & (chunk_size >> 8))), smfw_seq.outfile);
+ putc((int) ((0xFF & chunk_size)), smfw_seq.outfile);
+ fseek(smfw_seq.outfile, end_marker, 0);/*return file pointer to end of track*/
+ timebase_use(old_timebase);
+}
+
+
+/* smfw_exclusive -- write a system excl. msg to midi file */
+private void smfw_exclusive(length, msg)
+int length;
+unsigned char *msg;
+{
+ int length_count = 0;
+
+ if(debug) gprintf(TRANS, "SYSEX (time:%ld)\n", virttime);
+
+ smfw_deltatime();
+
+ while (length > length_count){ /* *(msg-1) != MIDI_EOX) { */
+ putc(*msg++, smfw_seq.outfile);
+ length_count++;
+ }
+ if(*(--msg) != MIDI_EOX) gprintf(TRANS, "ERROR: no end of sysex\n");
+}
+
+private void smfw_msg_write(n,c1,c2,c3)
+ int n;
+ unsigned char c1,c2,c3;
+{
+ if(debug) gprintf(TRANS, "MSGWRITE %d bytes (time:%ld)\n", n, virttime);
+ smfw_deltatime();
+ switch(n) {
+ case 1: putc(c1, smfw_seq.outfile);
+ break;
+ case 2: putc(c1, smfw_seq.outfile);
+ putc(c2, smfw_seq.outfile);
+ break;
+ case 3: putc(c1, smfw_seq.outfile);
+ putc(c2, smfw_seq.outfile);
+ putc(c3, smfw_seq.outfile);
+ break;
+ }
+}
+
+
+/* smfw_noteoff -- write noteoff to midi file */
+/**/
+private void smfw_noteoff(seq_type seq, int voice, int pitch)
+{
+ if(debug) gprintf(TRANS, "smfw_noteoff %d: %d (time:%ld)\n", voice, pitch, virttime);
+ smfw_deltatime();
+ putc(NOTEOFF | (voice - 1), smfw_seq.outfile);
+ putc(pitch, smfw_seq.outfile);
+ putc(0x40, smfw_seq.outfile);
+}
+
+
+/* smfw_noteon -- write noteon to midi file */
+/*
+ * NOTE: the seq parameter is not used here, but is passed in by the
+ * seq_noteon macro, so we have to have a placeholder for it.
+ */
+private void smfw_noteon(seq, voice, pitch, vel)
+ seq_type seq;
+ int voice, pitch, vel;
+{
+ if(debug) gprintf(TRANS, "smfw_noteon %d: %d %d(time:%ld)\n", voice, pitch, vel, virttime);
+ smfw_deltatime();
+ putc(NOTEON | (voice - 1), smfw_seq.outfile);
+ putc(pitch, smfw_seq.outfile);
+ putc(vel, smfw_seq.outfile);
+}
+
+
+/* smfw_process_event -- write a seq event to a midi file */
+/**/
+private void smfw_process_event(seq)
+ seq_type seq;
+{
+ register event_type event;
+ if (!seq->runflag) return;
+ while ((event = seq->current) && (event->ntime <= virttime)) {
+ unsigned int voice;
+ if ((vc_voice(event->nvoice) == smfw_seq.track) || /*if on current track*/
+ (((vc_voice(event->nvoice) - 16) == smfw_seq.track)
+ && (smfw_seq.track > 0)) ||
+ /* acknowledge clock change on all tracks*/
+ (event->value == CLOCK_VALUE && vc_ctrl(event->nvoice) == ESC_CTRL)) {
+
+ /* process all current (and earlier) events */
+ if (is_note(event)) { /*** play a note or rest ***/
+ /* if this note is not a rest, play it and schedule an off event */
+ if (event->value != NO_PITCH &&
+ (seq_channel_mask(seq) &
+ (1 << ((voice = vc_voice(event->nvoice)) - 1)))) {
+ seq_noteon(seq, voice, (0xFF & event->value),
+ (int) (event->u.note.ndur & 0xFF));
+
+ seq_cause_noteoff(seq, (event->u.note.ndur) >> 8,
+ voice, (0xFF & event->value));
+ }
+ } else { /*** send a control command ***/
+ int n;
+ time_type step;
+ int delta;
+ int increment;
+ int voice = vc_voice(event->nvoice);
+ ulong enabled = seq_channel_mask(seq) & (1 << (voice - 1));
+
+ switch (vc_ctrl(event->nvoice)) {
+ case PSWITCH_CTRL:
+ if (!enabled) break;
+ if(debug) gprintf(TRANS, "porta %d (time:%ld)... ", event->value, virttime);
+ seq_midi_ctrl(seq, voice, PORTASWITCH, 0xFF & event->value);
+ break;
+ case MODWHEEL_CTRL:
+ if (!enabled) break;
+ if(debug) gprintf(TRANS, "modw %d (time:%ld)...", event->value, virttime);
+ seq_midi_ctrl(seq, voice, MODWHEEL, 0xFF & event->value);
+ break;
+ case TOUCH_CTRL:
+ if (!enabled) break;
+ if(debug) gprintf(TRANS, "touch %d (time:%ld)... ", event->value, virttime);
+ seq_midi_touch(seq, voice, 0xFF & event->value);
+ break;
+ case VOLUME_CTRL:
+ if (!enabled) break;
+ if(debug) gprintf(TRANS, "ftvol %d (time:%ld)...", event->value, virttime);
+ seq_midi_ctrl(seq, voice, VOLUME, 0xFF & event->value);
+ break;
+ case BEND_CTRL:
+ if (!enabled) break;
+ if(debug) gprintf(TRANS, "bend %d (time:%ld)... ", event->value, virttime);
+ seq_midi_bend(seq, voice, event->value);
+ break;
+ case PROGRAM_CTRL:
+ if (!enabled) break;
+ if(debug) gprintf(TRANS, "prog %d (time:%ld)\n", event->value, virttime);
+ smfw_deltatime();
+ putc(MIDI_CH_PROGRAM | (voice - 1), smfw_seq.outfile);
+ putc(0xFF & event->value, smfw_seq.outfile);
+ break;
+ case ESC_CTRL:
+ switch (event->value) {
+ time_type this_event;
+ case CALL_VALUE: /*called routine will write to midifile in execution */
+ sequence = seq;
+ (*(event->u.call.routine))(event->u.call.args);
+ break;
+ case CLOCK_VALUE:
+ clock_ticksize = event->u.clock.ticksize;
+ if(debug) gprintf(TRANS, "clockevent! ticksize: %lu (time:%ld)\n",
+ clock_ticksize, virttime);
+
+ if (virttime > 0) { /* any clock before this is already recorded in the header */
+ if (smfw_seq.track == 0) { /* record clock event on tempo track = 0 */
+ /* cause clock write in half a newtick, because it was written .5 tick early*/
+ cause((delay_type) (clock_ticksize >> 17), smfw_clock_event,
+ last_tick_size, clock_ticksize);
+ last_tick_size = clock_ticksize; /*set new ticksize*/
+ } else { /*not on tempo track*/
+ this_event = ((virttime - last_clock_event) *
+ ((2500L << 16) / last_tick_size)) / 100;
+ if(debug) gprintf(TRANS, "track != 0: Lastev: %ld Thisev: %ld NewLast: %ld\n",
+ last_event, this_event, this_event - last_event);
+ last_event = 0L - (this_event - last_event);
+ last_clock_event = virttime;
+ /*last_event is negative, so will be ADDED to next this_event*/
+
+ last_tick_size = clock_ticksize;
+ }
+ } else if (debug) gprintf(TRANS, "IGNORED\n");/* if virttime <= 0 */
+ break;
+ case MACCTRL_VALUE:
+ if (!enabled) break;
+ if (debug) gprintf(TRANS, "MACCTRL %d: %d (time:%ld)\n",
+ event->u.macctrl.ctrl_number, event->u.macctrl.value, virttime);
+ smfw_deltatime();
+ putc(MIDI_CTRL | (voice - 1), smfw_seq.outfile);
+ putc(0xFF & event->u.macctrl.ctrl_number, smfw_seq.outfile);
+ putc(0xFF & event->u.macctrl.value, smfw_seq.outfile);
+ break;
+ case MACRO_VALUE:
+ if (!enabled) break;
+ if (debug) gprintf(TRANS, "MACRO sent to...\n");
+ smfw_send_macro(event->u.macro.definition,
+ voice, event->u.macro.parameter, -1, 0);
+ break;
+ case CTRLRAMP_VALUE:
+ case DEFRAMP_VALUE: {
+ int from, to;
+ if (!enabled) break;
+ step = event->u.ramp.step;
+ if (event->value == CTRLRAMP_VALUE) {
+ if(debug) gprintf(TRANS, "CTRLRAMP (time:%ld)...", virttime);
+ from = event->u.ramp.u.ctrl.from_value;
+ to = event->u.ramp.u.ctrl.to_value;
+ } else {
+ if (debug) gprintf(TRANS, "DEFRAMP (time:%ld)...", virttime);
+ from = event->u.ramp.u.def.parameter[
+ event->u.ramp.u.def.parm_num];
+ to = event->u.ramp.u.def.to_value;
+ }
+ delta = to - from;
+ increment = delta;
+ if (delta < 0) delta = -delta;
+ /* RBD - Note: Step is always non-zero */
+ n = event->u.ramp.dur / step;
+ increment = (increment << 8) / n;
+ smfw_ramp_event(seq, event, from << 8, to << 8,
+ increment, step, n);
+ seq->noteoff_count++;
+ break;
+ }
+ case SETI_VALUE:
+ seti_counter++; /*will be printed after writing is completed*/
+ *(event->u.seti.int_to_set) = event->u.seti.value;
+ break;
+ default:
+ gprintf(TRANS, "unexpected ESC_CTRL value\n");
+ break;
+ }
+ break;
+ default:
+ gprintf(TRANS, "unexpected seq data\n");
+ break;
+ }
+ }
+ }
+ seq->current = event->next;
+ }
+ if (seq->current) { /* if there is an event: delay, and then process again */
+ cause((delay_type)(event->ntime - virttime), smfw_process_event, seq);
+ }
+}
+
+/* smfw_ramp_event -- generate a ramp to write*/
+private void smfw_ramp_event(seq, event, value, to_value, increment, step, n)
+ seq_type seq;
+ register event_type event;
+ unsigned int value;
+ unsigned int to_value;
+ int increment;
+ time_type step;
+ int n;
+{
+ if(debug) gprintf(TRANS, "ramp of %d: %d to %d\n", event->u.ramp.ctrl, value >> 8,
+ to_value >> 8);
+ if (seq->runflag) {
+ int voice = vc_voice(event->nvoice);
+ if (n == 0) value = to_value;
+ else cause((delay_type)step, smfw_ramp_event, seq, event, value + increment,
+ to_value, increment, step, n - 1);
+ if (event->value == CTRLRAMP_VALUE) {
+ int ctrl = event->u.ramp.ctrl;
+ if (ctrl == -TOUCH_CTRL) smfw_touch(seq, voice, value >> 8);
+ else if (ctrl == -BEND_CTRL) smfw_bend(seq, voice, value >> 8);
+ else smfw_ctrl(seq, voice, ctrl, value >> 8);
+ } else { /* must be DEFRAMP_VALUE */
+ smfw_send_macro(event->u.ramp.u.def.definition,
+ vc_voice(event->nvoice),
+ event->u.ramp.u.def.parameter,
+ event->u.ramp.u.def.parm_num, value >> 8);
+ }
+ if (n == 0) seq_end_event(seq);
+ }
+}
+
+
+/* smfw_send_macro -- write msg to midi file from a seq "macro" event */
+/**/
+private void smfw_send_macro(ptr, voice, parameter, parm_num, value)
+ register unsigned char *ptr;
+ int voice;
+ short parameter[];
+ int parm_num;
+ int value;
+{
+ register unsigned char code, *loc;
+ while ((code = *ptr++)) {
+ loc = ptr + *ptr;
+ ptr++;
+ if (code <= nmacroparms) {
+ code--;
+ *loc = (code == parm_num ? value : parameter[code]) & 0x7f;
+ }
+ else if (code == nmacroparms + 1) {
+ *loc = ((voice - 1) & 0xF) | *loc;
+ }
+ else {
+ code -= (nmacroparms + 2);
+ *loc = ((code == parm_num ? value : parameter[code]) >> 7) & 0x7F;
+ }
+ }
+ if (ptr[1] == MIDI_SYSEX)
+ smfw_exclusive(*ptr, ptr + 1);
+ else
+ smfw_msg_write(*ptr, ptr[1], ptr[2], ptr[3]);
+}
+
+
+/* smfw_touch -- write aftertouch msg to midi file */
+/**/
+private void smfw_touch(seq_type seq, int voice, int value)
+{
+ if(debug) gprintf(TRANS, "smfw_touch %d\n", value);
+ smfw_deltatime();
+ putc(MIDI_TOUCH | (voice - 1), smfw_seq.outfile);
+ putc(value, smfw_seq.outfile);
+}
+
+
+void seq_write_smf(seq, outfile)
+ seq_type seq;
+ FILE *outfile;
+{
+ time_type put_tick_size;
+ int i;
+ /* ticksize is milliseconds << 16, and tickrate = 24*tempo
+ 60000ms/min / (24 * tempo) = 2500/tempo = 25
+ 25 << 16 = 1638400 */
+ time_type starting_ticksize = 1638400L; /*default midifile tempo 100*/
+ int track_count = 0;
+ long track_count_marker;
+ register event_type event;
+
+ seti_counter = 0;
+
+ /*initialize the smfw_seq struct*/
+ smfw_seq.outfile = outfile;
+ smfw_seq.seq = seq_copy(seq);
+
+ smfw_seq.seq->cause_noteoff_fn = smfw_cause_noteoff;
+ smfw_seq.seq->midi_bend_fn = smfw_bend;
+ smfw_seq.seq->midi_ctrl_fn = smfw_ctrl;
+ smfw_seq.seq->midi_touch_fn = smfw_touch;
+ smfw_seq.seq->noteoff_fn = smfw_noteoff;
+ smfw_seq.seq->noteon_fn = smfw_noteon;
+
+ event = seq_events(smfw_seq.seq);
+
+ /* search for clock events up till start of score */
+ /* careful: there may be no events at all */
+ while(event && event->ntime <= 0){
+ if(debug) gprintf(TRANS, "event (time:%ld)\n", event->ntime);
+ if(vc_ctrl(event->nvoice) == ESC_CTRL && event->value == CLOCK_VALUE) {
+ if(debug) gprintf(TRANS, "clock %lu at 0\n", event->u.clock.ticksize);
+ starting_ticksize = event->u.clock.ticksize;
+ break;
+ }
+ event = event->next;
+ }
+
+ putc(0x4D, smfw_seq.outfile); /*header: MThd*/
+ putc(0x54, smfw_seq.outfile);
+ putc(0x68, smfw_seq.outfile);
+ putc(0x64, smfw_seq.outfile);
+ putc(0x00, smfw_seq.outfile);
+ putc(0x00, smfw_seq.outfile);
+ putc(0x00, smfw_seq.outfile);
+ putc(0x06, smfw_seq.outfile);
+ putc(0x00, smfw_seq.outfile);
+ putc(0x01, smfw_seq.outfile); /*format 1 */
+ putc(0x00, smfw_seq.outfile);
+
+ track_count_marker = ftell(smfw_seq.outfile); /*number of tracks will be written later*/
+ putc(0x00, smfw_seq.outfile); /*will be filled by track_count_marker*/
+
+ putc(0x02, smfw_seq.outfile);/*division resolution of 600*/
+ putc(0x58, smfw_seq.outfile);
+
+ for (i = 0; i < 17; i++) { /* for each track... */
+ if (((seq_used_mask(smfw_seq.seq) >> (i - 1)) & 0x1) || (i == 0)) {
+ if (debug) gprintf(TRANS, "write track %d \n", i);
+ track_count++;
+ clock_ticksize = starting_ticksize;
+ last_tick_size = starting_ticksize;
+ putc(0x4D, smfw_seq.outfile);/*track header: MTrk*/
+ putc(0x54, smfw_seq.outfile);
+ putc(0x72, smfw_seq.outfile);
+ putc(0x6B, smfw_seq.outfile);
+
+ chunk_size_marker = ftell(smfw_seq.outfile);
+ /* size of chunk will be written later */
+ /* will be filled by chunk_size_marker */
+ putc(0x00, smfw_seq.outfile);
+ putc(0x00, smfw_seq.outfile);
+ putc(0x00, smfw_seq.outfile);
+ putc(0x00, smfw_seq.outfile);
+
+ if (i == 0) { /* tempo and time signature track */
+ putc(0x00, smfw_seq.outfile);/* default time sig stuff*/
+ putc(0xFF, smfw_seq.outfile);
+ putc(0x58, smfw_seq.outfile);
+ putc(0x04, smfw_seq.outfile);
+ putc(0x04, smfw_seq.outfile);
+ putc(0x02, smfw_seq.outfile);
+ putc(0x18, smfw_seq.outfile);
+ putc(0x08, smfw_seq.outfile);
+ putc(0x00, smfw_seq.outfile);
+
+ /* TEMPO: inserted here in case default is used */
+ putc(0xFF, smfw_seq.outfile);
+ putc(0x51, smfw_seq.outfile);
+ putc(0x03, smfw_seq.outfile);
+ /* ticksize is in ms<<16, so to get milliseconds per tick, it's
+ ticksize / 65536. To get beat durations, multiply by 24
+ to get ticksize * 24 / 65536. To get microseconds,
+ multiply by 1000: ticksize * 24000 / 65536. Divide both
+ constants by 64 to get ticksize * 375 / 1024 =
+ microseconds per quarter note.
+ */
+ put_tick_size = scale(clock_ticksize, 375L, 1024L);
+ putc((int) ((put_tick_size >> 16) & 0xFF), smfw_seq.outfile);
+ putc((int) ((put_tick_size >> 8) & 0xFF), smfw_seq.outfile);
+ putc((int) (put_tick_size & 0xFF), smfw_seq.outfile);
+ }
+ smfw_seq.track = i;
+ smfw_dotrack(smfw_seq.seq);
+ }
+ }
+ if (seti_counter)
+ gprintf(TRANS, "%d SETI events IGNORED!\n", seti_counter);
+ seq_stop(smfw_seq.seq);
+ /* go back and insert number of tracks */
+ fseek(smfw_seq.outfile, track_count_marker, 0);
+ putc(0xFF & track_count, smfw_seq.outfile);
+ fclose(smfw_seq.outfile);
+}
+
+
+/* writevarlen -- write a variable length integer to midi file */
+/**/
+private void writevarlen(value)
+ register long value;
+{
+ register ulong buffer;
+
+ if(debug) gprintf(TRANS, "variable length quantity...");
+
+ buffer = value & 0x7f;
+
+ while((value >>= 7) > 0) {
+ buffer <<= 8;
+ buffer |= 0x80;
+ buffer += (value & 0x7f);
+ }
+
+ for(;;) {
+ if(debug) gprintf(TRANS, " byte ");
+ putc((int) (buffer & 0xFF), smfw_seq.outfile);
+ if (buffer & 0x80) buffer >>= 8;
+ else break;
+ }
+ if(debug) gprintf(TRANS, "written!\n");
+}
diff --git a/cmt/seqmwrite.h b/cmt/seqmwrite.h
new file mode 100644
index 0000000..458336f
--- /dev/null
+++ b/cmt/seqmwrite.h
@@ -0,0 +1,4 @@
+/* seqmwrite.h -- midi file writer */
+
+void seq_write_smf(seq_type seq, FILE *outfile); /* LISP: (SEQ-WRITE-SMF SEQ FILE) */
+
diff --git a/cmt/seqread.c b/cmt/seqread.c
new file mode 100644
index 0000000..62d759d
--- /dev/null
+++ b/cmt/seqread.c
@@ -0,0 +1,1890 @@
+/****************************************************************************
+ seqread.c -- Phase 1 of adagio compilation...
+
+ this module parses adagio programs and builds a linked list structure
+ consisting of notes and control changes in time order.
+
+ Copyright 1989 Carnegie Mellon University
+*****************************************************************************/
+
+/*****************************************************************************
+* Change Log
+* Date | Change
+*-----------+-----------------------------------------------------------------
+* 31-Dec-85 | Created changelog
+* 31-Dec-85 | Add c:\ to include directives
+* 31-Dec-85 | Added standard command scanner, metronome variable, need to add
+* | cmdline_help procedure
+* 31-Dec-85 | Call intr_init
+* 31-Dec-85 | Set musictrace from command line via -trace
+* 31-Dec-85 | Added -poll
+* 1-Jan-86 | Put error messages out to stderr
+* 1-Jan-86 | Set IsAT. Can be later overridden by -at and -xt switches,
+* | currently only used for diagnostics (may be needed for
+* | compatibles, who knows? In which case remove the tests which
+* | confirm the type of processor)
+* 1-Jan-86 | <rgd/jmn> Removed dur-adjusted message
+* 1-Jan-86 | Added miditrace
+* 18-Jan-86 | Shortened durations by 1/200 s to avoid roundoff problems --
+* | see buildnote for details.
+* 3-Mar-86 | Allow octave and accidentals in either order after pitch name.
+* | Default octave is now one that gets nearest previous pitch,
+* | the tritone (half an octave) interval is descending by default.
+* | Special commands handled by table search, !Rate command added
+* | to scale all times by a percentage (50 = half speed).
+* 9-Mar-86 | Use space to limit amount of storage allocation. Otherwise
+* | exhausting storage in phase1 caused phase2 to fail.
+* 12-Mar-86 | Broke off command line parser into adagio.c, only parser remains
+* 24-Mar-86 | Changed representation from note_struct to event_struct
+* | Parse M, N, O, X, and Y as control change commands
+* 23-May-86 | Added , and ; syntax: "," means "N0\n", ";" means "\n"
+* 16-Jul-86 | modify to only call toupper/lower with upper/lower case as
+* | parameter to be compatible with standard C functions
+* 7-Aug-86 | fixed bug with default pitches and rests
+* 5-Jul-87 | F.H: Introduced new memory handling from Mac version.
+* | Changed: init()
+* | ins_event()
+* | ins_ctrl()
+* | ins_note()
+* | Deleted: reverse()
+* | nalloc()
+* | Introduced: event_alloc()
+* | phase1_FreeMem()
+* | system.h & system.c dependencies
+* 10-Feb-88 | fixed parseend to accept blanks and tabs,
+* | fixed rate scaling of durations
+* 11-Jun-88 | commented out gprintf of \n to ERROR after parsing finished.
+* 13-Oct-88 | JCD : exclusive AMIGA version.
+* 13-Apr-89 | JCD : New portable version.
+* 31-Jan-90 | GWL : Cleaned up for LATTICE
+* 30-Jun-90 | RBD : further changes
+* 2-Apr-91 | JDW : further changes
+* 30-Jun-91 | RBD : parse '+' and '/' in durations, * after space is comment
+* 28-Apr-03 | DM : changes for portability
+*****************************************************************************/
+
+#include "switches.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "cext.h"
+#include "cmdline.h"
+#include "midifns.h" /* to get time_type */
+#include "timebase.h"
+#include "moxc.h" /* to get debug declared */
+#include "seq.h"
+#include "seqread.h"
+#include "userio.h"
+/* ctype.h used to be included only by UNIX and AMIGA,
+ surely everyone wants this? */
+#include "ctype.h"
+
+#ifndef toupper
+/* we're already taking precautions, so inline version of toupper is ok: */
+#define toupper(c) ((c)-'a'+'A')
+/* CAUTION: AZTEC V5.0 defines an inline version of toupper called _toupper,
+ but they got it wrong!
+ */
+#endif
+
+/* cmtcmd.h references amiga message ports */
+#ifdef AMIGA
+#ifdef LATTICE
+#include "amiga.h"
+#endif
+#include "exec/exec.h"
+#endif
+#include "cmtcmd.h"
+
+/* public stuff */
+extern long space; /* remaining free bytes */
+extern int abort_flag;
+
+/****************************************************************************
+ The following are used to simulate fixed point with the radix point
+ 8 bits from the right:
+****************************************************************************/
+
+#define precise(x) (((time_type) x) << 8)
+#define seqround(x) ((((time_type) x) + 128) >> 8)
+#define trunc(x) (((time_type) x) >> 8)
+
+#define nullstring(s) (s[0] == EOS)
+
+
+/****************************************************************************
+* Routines local to this module:
+****************************************************************************/
+private void do_a_rest();
+private time_type doabsdur();
+private int doabspitch();
+private void doclock();
+private void docomment();
+private void doctrl();
+private void dodef();
+private time_type dodur();
+private void doerror();
+private int doloud();
+void domacro();
+private void donextdur();
+private int dopitch();
+private void doprogram();
+private void dorate();
+private void doset();
+private void dospecial();
+private time_type dosymdur();
+private void dotempo();
+private void dotime();
+private void dovoice();
+private void fferror();
+private void init();
+private int issymbol();
+private void marker();
+private void parseend();
+private void parsefield();
+private boolean parsenote();
+private boolean parseparm();
+private int scan();
+private int scan1();
+private long scanint();
+private void scansymb();
+private long scansgnint();
+
+/****************************************************************************
+* data structures for parser lookup tables
+****************************************************************************/
+
+struct durt { /* duration translation table */
+ char symbol;
+ time_type value;
+};
+
+#define durtable_len 7
+struct durt durtable[durtable_len] = {
+ {'W', 4800L},
+ {'H', 2400L},
+ {'Q', 1200L},
+ {'I', 600L},
+ {'S', 300L},
+ {'%', 150L},
+ {'^', 75L}
+};
+
+struct loudt { /* loudness translation table */
+ char symbol[4];
+ int value;
+};
+
+struct loudt loudtable[] = {
+ {"PPP", 20},
+ {"PP\0", 26},
+ {"P\0\0", 34},
+ {"MP\0", 44},
+ {"MF\0", 58},
+ {"F\0\0", 75},
+ {"FF\0", 98},
+ {"FFF", 127}
+};
+
+char too_many_error[] = "Too many parameters";
+
+private char *ssymbols[] = {"TEMPO", "RATE", "CSEC", "MSEC",
+ "SETI", "SETV", "CALL", "RAMP",
+ "CLOCK", "DEF", "END"};
+
+#define sym_tempo 0
+#define sym_rate 1
+#define sym_csec 2
+#define sym_msec 3
+#define sym_seti 4
+#define sym_setv 5
+#define sym_call 6
+#define sym_ramp 7
+#define sym_clock 8
+#define sym_def 9
+#define sym_end 10
+
+/* number of symbols */
+#define sym_n 11
+
+#define linesize 100
+private char line[linesize]; /* the input line */
+private char token[linesize]; /* a token scanned from the input line */
+
+private boolean pitch_flag; /* set when a pitch is indicated */
+/* (if controls changes are given, only allocate a note event if
+ * a pitch was specified -- i.e. when pitch_flag is set)
+ */
+private boolean rest_flag; /* set when a rest (R) is found */
+/* this flag is NOT inherited by the next line */
+
+private boolean symbolic_dur_flag;
+/* TRUE if last dur was not absolute
+ * (if this is set, then the default duration is changed
+ * accordingly when the tempo is changed.)
+ */
+
+
+#define nctrl 8
+
+private boolean ctrlflag[nctrl];
+/* TRUE if control change was present
+ * ctrlflag[0] TRUE if ANY control change
+ * was present
+ */
+private int ctrlval[nctrl];
+/* the new value of the control */
+#define nmacroctrl 10
+short macctrlx; /* index into the following: */
+short macctrlnum[nmacroctrl]; /* macro ctrl number, e.g. for ~4(67), or
+ * number of parameters for a symbolic macro */
+short macctrlparmx[nmacroctrl]; /* ctrl value for ctrl change, or index of
+ * parameters for symbolic macro */
+short macctrlparms[nmacroctrl*nmacroparms]; /* parameters for symbolic macros */
+short macctrlnextparm;
+def_type macctrldef[nmacroctrl]; /* definition for symbolic macro */
+
+private time_type time_scale; /* 1000 if centisec, 100 if millisec */
+/* note: user_specified_time * (time_scale / rate) = millisec */
+
+
+
+/****************************************************************************
+*
+* variables private to this module
+*
+****************************************************************************/
+
+private boolean end_flag = FALSE; /* set "true" when "!END" is seen */
+
+/****************************************************************************
+* state variables
+* Because each line of an Adagio score inherits properties from the previous
+* line, it makes sense to implement the parser as a collection of routines
+* that make small changes to some global state. For example, pitch is a
+* global variable. When the field G4 is encountered, the dopitch routine
+* assigns the pitch number for G4 to the variable pitch. After all fields
+* are processed, these variables describe the current note and contain the
+* default parameters for the next note as well.
+*
+* Global variables that are used in this way by the parsing rountines are:
+****************************************************************************/
+private int
+linex, /* index of the next character to be scanned */
+lineno, /* current line number */
+fieldx, /* index of the current character within a field */
+pitch, /* pitch of note */
+loud, /* loudness of note */
+voice, /* voice (midi channel) of note */
+artic; /* articulation (a percentage of duration) */
+
+private boolean ndurp; /* set when a next (N) is indicated */
+/* (next time defaults to the current time plus duration unless
+ * overridden by a next (N) command whose presence is signalled
+ * by ndurp.)
+ */
+
+private time_type
+thetime, /* the starting time of the note */
+rate, /* time rate -- scales time and duration, default = 100 */
+ntime, /* the starting time of the next note */
+dur, /* the duration of the note */
+tempo, /* the current tempo */
+start, /* the reference time (time of last !tempo or !rate cmd) */
+ticksize; /* set by !clock command, zero for no clock */
+
+private int pitchtable[7] = {
+ 69, 71, 60, 62, 64, 65, 67 };
+
+extern char score_na[name_length];
+
+private seq_type the_score; /* this is the score we are parsing */
+
+
+/* def_append -- append a byte to the current definition */
+/*
+ * The def data structure:
+ * [code][offset][code][offset]...[0][length][data][data][data]...
+ * where code is 1:nmacroparms for %n,
+ * nmacroparms+1 for %v,
+ * nmacroparms+2:nmacroparms*2+1 for ^n
+ * and offset is the byte offset (from the offset byte) to the data
+ * where the parameter should be substituted
+ * and length is the number of data bytes
+ */
+boolean def_append(def, nparms, data)
+ unsigned char def[];
+ int nparms;
+ int data;
+{
+ int base = (nparms << 1) + 1; /* this byte is the length */
+ /* first parameter has to be able to reference last byte: */
+ if ((def[base])++ >= (254 - (nparms << 1))) {
+ fferror("Data too long");
+ return FALSE;
+ }
+ def[base + def[base]] = data;
+ return TRUE;
+}
+
+
+def_type def_lookup(symbol)
+ char *symbol;
+{
+ def_type defn = seq_dictionary(the_score);
+ while (defn) {
+ if (strcmp(defn->symbol, symbol) == 0) {
+ return defn;
+ }
+ defn = defn->next;
+ }
+ return NULL;
+}
+
+
+void def_parm(def, nparms, code)
+ unsigned char def[];
+ int nparms;
+ int code;
+{
+ int i, j;
+ /* in order to insert a 2-byte parameter descriptor, the offsets from
+ * previous descriptors (that precede the data) need to be increased by 2:
+ */
+ for (i = 1; i < (nparms << 1); i += 2) {
+ def[i] += 2;
+ }
+ /* now i is index of length; work backwards from the last byte, moving
+ * everything up by 2 bytes to make room for the new descriptor:
+ */
+ for (j = i + def[i] + 2; j > i; j--) {
+ def[j] = def[j - 2];
+ }
+ /* now i is index of offset; insert the descriptor code (first byte)
+ * and the offset to the parameter location in the message (second byte)
+ */
+ def[i - 1] = code;
+ def[i] = def[i + 2] + 2;
+}
+
+/****************************************************************************
+* do_a_rest
+* Effect: parses a rest (R) command
+****************************************************************************/
+
+private void do_a_rest()
+{
+ if (token[fieldx])
+ fferror("Nothing expected after rest");
+ rest_flag = TRUE;
+}
+
+/****************************************************************************
+* doabsdur
+* Effect: parses an absolute dur (U) command
+****************************************************************************/
+
+private time_type doabsdur()
+{
+ time_type result=1000L;
+ register char c;
+ if (isdigit(token[fieldx])) {
+ result = precise(scanint());
+ /* allow comma or paren for use in parameter lists */
+ if ((c = token[fieldx]) && (c != ',') && (c != ')') && (c != '+')) {
+ fferror("U must be followed by digits only");
+ }
+ if (time_scale == 1000) result *= 10; /* convert to ms */
+ } else fferror("No digit after U");
+ return result;
+}
+
+/****************************************************************************
+* doabspitch
+* Effect: parses an absolute pitch (P) command
+****************************************************************************/
+
+private int doabspitch()
+{
+ int result = 60;
+ int startx = fieldx;
+ register char c;
+ int savex;
+ if (isdigit (token[fieldx])) {
+ result = (int) scanint();
+ /* allow comma or paren for abspitch in parameter */
+ if ((c = token[fieldx]) && c != ',' && c != ')')
+ fferror("P must be followed by digits only");
+ else if (result < minpitch) {
+ savex = fieldx;
+ fieldx = startx;
+ fferror("Minimum pitch of 0 will be used");
+ result = minpitch;
+ fieldx = savex;
+ } else if (result > maxpitch) {
+ savex = fieldx;
+ fieldx = startx;
+ fferror("Maximum pitch of 127 will be used");
+ result = maxpitch;
+ fieldx = savex;
+ }
+ } else fferror("No digits after P");
+ return result;
+}
+
+
+/* doartic -- compute an articulation factor */
+/*
+ NOTE: artic is a percentage that scales the duration
+ of notes but not the time to the next note onset. It
+ is applied to the final computed duration after all
+ other scaling is applied.
+ */
+private void doartic()
+{
+ if (isdigit(token[fieldx])) {
+ artic = (int) scanint();
+ if (token[fieldx])
+ fferror("Only digits were expected here");
+ } else fferror("No digits after /");
+}
+
+
+/* docall -- parse a call in the form !CALL fn(p1,p2,p3) */
+/**/
+private void docall()
+{
+ boolean error_flag = TRUE;
+ ndurp = FALSE;
+
+ linex += scan();
+
+ if (token[0] == 0) fferror("Function name expected");
+ else {
+ char symbol[100];
+ struct symb_descr *desc;
+ long value[SEQ_MAX_PARMS];
+ int i=0;
+
+ scansymb(symbol);
+ if (fieldx == 1) fferror("Routine name expected");
+ else if (token[fieldx] != '(') fferror("Open paren expected");
+ else {
+ desc = &HASHENTRY(lookup(symbol));
+ if (!desc->symb_type) {
+ fieldx = 0;
+ fferror("Function not defined");
+ } else if (desc->symb_type != fn_symb_type) {
+ fieldx = 0;
+ gprintf(TRANS, "desc->symb_type is %d\n", desc->symb_type);
+ fferror("This is not a function");
+ } else {
+ error_flag = FALSE;
+ fieldx++; /* skip over paren */
+ for (i = 0; i < SEQ_MAX_PARMS; i++) value[i] = 0;
+ i = 0;
+ /* note that no params "()" is legal */
+ while (i < SEQ_MAX_PARMS && token[fieldx] != ')' &&
+ parseparm(&value[i])) {
+ i++;
+ if (token[fieldx] == ',') {
+ fieldx++;
+ } else if (token[fieldx] != ')') {
+ fferror("Unexpected character");
+ error_flag = TRUE;
+ break;
+ }
+ }
+ fieldx++;
+ if (i > SEQ_MAX_PARMS) fferror("Too many parameters");
+ }
+ while (TRUE) {
+ linex += scan();
+ if (nullstring(token)) {
+ break;
+ }
+ switch (token[0]) {
+ case 'T':
+ fieldx = 1;
+ dotime();
+ break;
+ case 'V':
+ fieldx = 1;
+ dovoice();
+ break;
+ case 'N':
+ fieldx = 1;
+ donextdur();
+ break;
+ default:
+ fferror("Unexpected character");
+ }
+ }
+ if (!error_flag)
+ insert_call(the_score, seqround(thetime), lineno, voice,
+ desc->ptr.routine, value, i);
+ /* advance the time only if an N field was given */
+ if (ndurp) thetime += ntime;
+ }
+ }
+}
+
+
+/* doclock -- insert a clock command */
+/*
+ * derivation: if there is no previous clock running, then start the
+ * clock on time. Otherwise, start the clock half a tick early.
+ * ticksize = (beattime / 24) = ((60sec/tempo)/24) =
+ * ((60000ms/tempo)/24) = (60000/24)/tempo = 2500/tempo
+ */
+private void doclock()
+{
+ int oldticksize = ticksize;
+ ticksize = (2500L << 16) / tempo;
+ insert_clock(the_score, seqround(thetime) - (oldticksize >> 17),
+ lineno, ticksize);
+}
+
+
+/****************************************************************************
+* docomment
+* Effect: parses a comment (*) command
+****************************************************************************/
+
+private void docomment()
+{
+ line[linex] = '\n'; /* force end of line to skip comment line */
+ line[linex+1] = EOS;
+}
+
+/****************************************************************************
+* doctrl
+* Inputs:
+* n: control number
+* Effect: parses a control (K, M, O, X, or Y) command
+****************************************************************************/
+
+private void doctrl(n)
+int n;
+{
+ ctrlval[n] = (int) scanint();
+ if (token[fieldx]) {
+ fferror("Only digits expected here");
+ } else {
+ ctrlflag[n] = TRUE;
+ ctrlflag[0] = TRUE; /* ctrlflag[0] set if any flag is set */
+ }
+}
+
+
+private void dodef()
+{
+ /* maximum def size is 256 + 9 parms * 2 + 2 = 276 */
+ unsigned char def[280];
+ char symbol[100];
+ int nparms = 0;
+ int nibcount = 0;
+ int data = 0;
+ register char c;
+
+ linex += scan();
+
+ if (!token[0]) fferror("Symbol expected");
+ else {
+ strcpy(symbol, token);
+ def[0] = def[1] = 0;
+ while (TRUE) {
+ linex += scan1(&line[linex]);
+ c = token[0];
+ if (!c) {
+ linex--;
+ if (nibcount & 1) {
+ fferror("Expected pairs of hex digits: one missing");
+ return;
+ }
+ break;
+ } else if (c == ' ' || c == '\t' || c == '\n') continue;
+ else if (isdigit(c)) {
+ data = (data << 4) + (c - '0');
+ nibcount++;
+ if (!(nibcount & 1)) {
+ if (!def_append(def, nparms, data))
+ return;
+ data = 0;
+ }
+ } else if ('A' <= c && c <= 'F') {
+ data = (data << 4) + (c - 'A') + 10;
+ nibcount++;
+ if (!(nibcount & 1)) {
+ if (!def_append(def, nparms, data))
+ return;
+ data = 0;
+ }
+ } else if (c == 'V') {
+ data = data << 4;
+ nibcount++;
+ /* v without a leading nibble is equivalent to 0v: */
+ if (nibcount & 1) nibcount++;
+ if (!def_append(def, nparms, data))
+ return;
+ def_parm(def, nparms++, nmacroparms+1);
+ } else if (c == '%') {
+ linex += scan1(&line[linex]);
+ c = token[0];
+ if (c < '1' || c > ('0' + nmacroparms)) {
+ fferror(parm_expected_error);
+ break;
+ }
+ if (!def_append(def, nparms, 0))
+ return;
+ def_parm(def, nparms++, c - '0');
+ } else if (c == '^') {
+ linex += scan1(&line[linex]);
+ c = token[0];
+ if (c < '1' || c > ('0' + nmacroparms)) {
+ fferror(parm_expected_error);
+ break;
+ }
+ if (!def_append(def, nparms, 0))
+ return;
+ def_parm(def, nparms++, (c - '0') + nmacroparms + 1);
+ } else { /* something unexpected here -- just exit */
+ linex--;
+ fferror("Unexpected data");
+ return;
+ }
+ }
+ insert_def(the_score, symbol, def,
+ (nparms << 1) + def[(nparms << 1) + 1] + 2);
+ }
+}
+
+/****************************************************************************
+* dodur
+* Effect: parses a duration (sum of dosymdur and/or doabsdur)
+* sets symbolic_dur_flag (according to the first addend in mixed arithmetic)
+*
+* Returns: duration in "precise" units
+****************************************************************************/
+private time_type dodur()
+{
+ time_type result = 0L;
+ symbolic_dur_flag = TRUE;
+
+ if (token[fieldx-1] == 'U') {
+ result = doabsdur();
+ symbolic_dur_flag = FALSE;
+ } else result = dosymdur();
+ while (token[fieldx] == '+') {
+ fieldx += 2;
+ if (token[fieldx-1] == 'U') result += doabsdur();
+ else result += dosymdur();
+ }
+ return scale(result, 100L, rate);
+}
+
+/****************************************************************************
+* doerror
+* Effect: parse an unrecognized field by reporting an error
+****************************************************************************/
+
+private void doerror()
+{
+ fieldx = 0;
+ fferror("Bad field");
+}
+
+/****************************************************************************
+* doloud
+* Effect: parse a loudness (L) command
+****************************************************************************/
+
+private int doloud()
+{
+ int i, j;
+ int result;
+ int oldfieldx = fieldx;
+ int newfieldx;
+ char symbol[100];
+
+ if (!token[fieldx] || token[fieldx]==')' || token[fieldx]==',') {
+ fferror("L must be followed by loudness indication");
+ return 100;
+ }
+ if (isdigit(token[fieldx])) {
+ result = (int) scanint();
+ newfieldx = fieldx;
+ if (token[fieldx] && token[fieldx]!=')' && token[fieldx]!=',')
+ fferror("Digits expected after L");
+ else if (result > 127) {
+ fieldx = oldfieldx;
+ fferror("Maximum loudness of 127 will be used");
+ fieldx = newfieldx;
+ result = 127;
+ } else if (result == 0) {
+ fieldx = oldfieldx;
+ fferror("Minimum loudness of 1 will be used");
+ fieldx = newfieldx;
+ result = 1;
+ }
+ return result;
+ }
+ scansymb(symbol);
+ newfieldx = fieldx;
+ if ((i = strlen(symbol)) > 3 ) { /* maximum is 3, e.g. "ppp" */
+ fieldx = oldfieldx;
+ fferror("Loudness field too long");
+ fieldx = newfieldx;
+ return 100;
+ }
+ symbol[i + 1] = '\0'; /* pad short symbols with 0 */
+ /* e.g. "p\0" -> "p\0\0" */
+ for (i = 0; i <= 7; i++) { /* loop through possibilities */
+ for (j = 0; j <= 2; j++) { /* test 3 characters */
+ if (symbol[j] != loudtable[i].symbol[j])
+ break;
+ }
+ if (j == 3) {
+ return loudtable[i].value;
+ }
+ }
+ fieldx = oldfieldx;
+ fferror("Bad loudness indication");
+ fieldx = newfieldx;
+ return 100;
+}
+
+
+void domacro()
+{
+ int control_num;
+ int value;
+ if (isdigit(token[1])) {
+ control_num = (int) scanint();
+ if (token[fieldx] == '(') {
+ fieldx++;
+ if (!isdigit(token[fieldx])) {
+ fferror("Control value expected");
+ } else {
+ value = (int) scanint();
+ if (token[fieldx] != ')') {
+ fferror("Missing close paren");
+ } else {
+ fieldx++;
+ if (token[fieldx])
+ fferror("Nothing expected after paren");
+ else if (macctrlx < nmacroctrl - 1) {
+ macctrlnum[macctrlx] = control_num;
+ macctrlparmx[macctrlx] = value;
+ macctrldef[macctrlx] = NULL;
+ macctrlx++;
+ } else fferror("Too many controls");
+ }
+ }
+ } else fferror("Missing paren");
+ } else {
+ def_type def;
+ char symbol[100];
+ scansymb(symbol);
+ if (fieldx == 1) fferror("Macro name expected");
+ else if (token[fieldx] != '(') fferror("Open paren expected");
+ else {
+ fieldx++;
+ def = def_lookup(symbol);
+ if (!def) {
+ fieldx = 1;
+ fferror("Undefined macro");
+ } else {
+ long val;
+ macctrlnum[macctrlx] = 0;
+ macctrlparmx[macctrlx] = macctrlnextparm;
+ macctrldef[macctrlx] = def;
+ while (token[fieldx] != ')' && parseparm(&val)) {
+ macctrlparms[macctrlnextparm++] = (short) val;
+ macctrlnum[macctrlx]++;
+ if (token[fieldx] == ',') {
+ fieldx++;
+ } else if (token[fieldx] != ')') {
+ fferror("Unexpected character");
+ break;
+ }
+ }
+ fieldx++;
+ macctrlx++;
+ }
+ }
+ }
+}
+
+
+/****************************************************************************
+* donextdur
+* Effect: parse a next (N) command
+* Implementation:
+* The syntax is N followed by a duration, so save dur and use dosymdur()
+* to parse the duration field.
+* The form N<digits> is parsed directly with scanint().
+****************************************************************************/
+
+private void donextdur()
+{
+ ndurp = TRUE; /* flag that N was given */
+ if (isdigit(token[fieldx])) {
+ ntime = precise(scanint());
+ ntime = scale(ntime, (ulong)time_scale, rate);
+ if (token[fieldx])
+ fferror("Only digits were expected here");
+ } else {
+ fieldx++;
+ ntime = dodur();
+ }
+}
+
+/****************************************************************************
+* dopitch
+* Effect: parses a pitch command
+****************************************************************************/
+
+private int dopitch()
+{
+ int p, octave=0;
+ int octflag = FALSE; /* set if octave is specified */
+ int oldfieldx = fieldx;
+
+ p = pitchtable[token[fieldx-1]-'A'];
+ while (TRUE) {
+ if (token[fieldx] == 'S') { /* sharp */
+ p++;
+ fieldx++;
+ }
+ else if (token[fieldx] == 'N') { /* skip */
+ fieldx++;
+ }
+ else if (token[fieldx] == 'F') { /* flat */
+ p--;
+ fieldx++;
+ }
+ else if (isdigit(token[fieldx]) && !octflag) { /* octave */
+ octave = (int) scanint();
+ octflag = TRUE;
+ }
+ else break; /* none of the above */
+ }
+ if (octflag) p = (p-48) + 12 * octave; /* adjust p to given octave */
+ else { /* adjust p to note nearest the default pitch */
+ int octdiff = (p + 126 - pitch) / 12;
+ p = p + 120 - (octdiff * 12);
+ }
+ if (p > maxpitch) { /* pitch in range? */
+ int newfield = fieldx;
+ fieldx = oldfieldx;
+ fferror("Pitch too high");
+ fieldx = newfield;
+ p = maxpitch;
+ }
+ /* We really should test for end-of-field, but we don't know if we're
+ in a parameter list, so comma may or may not be legal */
+ return p;
+}
+
+/****************************************************************************
+* doprogram
+* Effect: parses a program change (Z) command
+****************************************************************************/
+
+private void doprogram()
+{
+ register int program = (int) scanint();
+ ctrlflag[PROGRAM_CTRL] = ctrlflag[0] = TRUE;
+ if (token[fieldx]) {
+ fferror("Z must be followed by digits only");
+ } else if (program < minprogram) {
+ fieldx = 1;
+ fferror("Minimum program of 1 will be used");
+ program = minprogram;
+ } else if (program > maxprogram) {
+ fieldx = 1;
+ fferror("Maximum program of 128 will be used");
+ program = maxprogram;
+ }
+ ctrlval[PROGRAM_CTRL] = program - 1;
+}
+
+
+private void doramp()
+{
+ int values[2];
+ time_type stepsize = 100L; /* default 10 per second */
+ int index = 0;
+ ndurp = FALSE;
+ values[0] = values[1] = 0;
+ while (TRUE) {
+ linex += scan();
+ fieldx = 1;
+ if (nullstring(token)) {
+ break;
+ } else if (index == 2) { /* must be stepsize in dur syntax */
+ stepsize = dodur();
+ } else {
+ int ctrlx = 0;
+ static int ctrl_map[] = { -BEND_CTRL, VOLUME, -TOUCH_CTRL, MODWHEEL };
+
+ switch (token[0]) {
+ case 'M': ctrlx++; /* modwheel */
+ case 'O': ctrlx++; /* aftertouch */
+ case 'X': ctrlx++; /* volume */
+ case 'Y': /* pitch bend */
+
+ if (index < 2) {
+ macctrlnum[index] = ctrl_map[ctrlx];
+ macctrlparmx[index] = (int) scanint();
+ if (token[fieldx])
+ fferror("Only digits expected here");
+ macctrldef[index] = NULL;
+ } else fferror("Unexpected control");
+ break;
+ case '~':
+ if (index < 2) {
+ domacro();
+ if (token[fieldx]) fferror("Unexpected character");
+ } else fferror("Unexpected control");
+ break;
+ case 'T':
+ if (index < 2) fferror("Control expected");
+ dotime();
+ break;
+ case 'V':
+ if (index < 2) fferror("Control expected");
+ dovoice();
+ break;
+ case 'N':
+ if (index < 2) fferror("Control expected");
+ donextdur();
+ break;
+ default:
+ if (index < 2) fferror("Control expected");
+ dur = dodur();
+ break;
+ }
+ if (index == 1 && (macctrlnum[0] != macctrlnum[1] ||
+ macctrldef[0] != macctrldef[1])) {
+ fferror("Controls do not match");
+ }
+ }
+ index++;
+ }
+ if (index < 3) fferror("Expected 2 controls and step size");
+ else {
+ if (macctrldef[0]) {
+ int i, j, n;
+ n = 0;
+ i = macctrlparmx[0];
+ j = macctrlparmx[1];
+ while (n < macctrlnum[0]) {
+ if (macctrlparms[i] != macctrlparms[j]) break;
+ n++; i++; j++;
+ }
+ if (n >= macctrlnum[0]) n = 0;
+ /* Note: duration shortened to prevent overlap with next ramp */
+ insert_deframp(the_score, seqround(thetime), lineno, voice,
+ seqround(stepsize), trunc(dur) - 1, macctrldef[0], macctrlnum[0],
+ macctrlparms + macctrlparmx[0], n, macctrlparms[j]);
+ } else {
+ /* Note: duration shortened to prevent overlap with next ramp */
+ insert_ctrlramp(the_score, seqround(thetime), lineno, voice,
+ seqround(stepsize), trunc(dur) - 1,
+ macctrlnum[0], macctrlparmx[0], macctrlparmx[1]);
+ }
+ }
+ /* advance the time only if an N field was given */
+ if (ndurp) thetime += ntime;
+ else thetime += dur;
+}
+
+
+/****************************************************************************
+* dorate
+* Effect: parses a !rate command
+****************************************************************************/
+
+private void dorate()
+{
+ linex += scan();
+ if (!token[0])
+ fferror("rate number expected");
+ else {
+ long oldrate = rate;
+ rate = (int) scanint();
+ if (token[fieldx])
+ fferror("Only digits expected here");
+ if (rate == 0) {
+ fieldx = 0;
+ fferror("Rate 100 will be used here");
+ rate = 100L;
+ }
+ start = thetime;
+ /* adjust dur in case it is inherited by next note */
+ dur = (dur * oldrate);
+ dur = dur / rate;
+ }
+}
+
+
+private void doset(vec_flag)
+ boolean vec_flag;
+{
+ ndurp = FALSE;
+ linex += scan();
+ if (!token[0]) fferror("Variable name expected");
+ else {
+ struct symb_descr *desc = &HASHENTRY(lookup(token));
+ if (!desc->symb_type) fferror("Called function not defined");
+ else if (vec_flag && (desc->symb_type != vec_symb_type)) {
+ fferror("This is not an array");
+ } else if (!vec_flag && (desc->symb_type != var_symb_type)) {
+ fferror("This is not a variable");
+ } else {
+ int numargs = 1 + vec_flag;
+ int value[2];
+ int i;
+ int *address = desc->ptr.intptr;
+ value[0] = value[1] = 0;
+ i = 0;
+ while (TRUE) {
+ linex += scan();
+ if (nullstring(token)) {
+ break;
+ } else if (isdigit(token[0]) || token[0] == '-' ||
+ token[0] == '+') {
+ if (i < numargs) {
+ value[i++] = (int) scansgnint();
+ if (token[fieldx])
+ fferror("Only digits expected here");
+ } else fferror(too_many_error);
+ } else {
+ switch (token[0]) {
+ case 'T':
+ fieldx = 1;
+ dotime();
+ break;
+ case 'V':
+ fieldx = 1;
+ dovoice();
+ break;
+ case 'N':
+ fieldx = 1;
+ donextdur();
+ break;
+ default:
+ fieldx++;
+ if (i < numargs) {
+ value[i++] = seqround(dodur());
+ } else fferror(too_many_error);
+ break;
+ }
+ }
+ }
+ if (vec_flag && i != 2) fferror("No index given");
+ if (vec_flag) {
+ if (value[0] >= desc->size) {
+ fferror("Subscript out of bounds");
+ return;
+ }
+ /* reduce to the seti case: */
+ address += value[0]; /* compute the vector address */
+ value[0] = value[1]; /* set up value[] and i as if */
+ i--; /* this were seti, not setv */
+ }
+ if (i != 1) fferror("No value given");
+ insert_seti(the_score, seqround(thetime), lineno, voice,
+ address, value[0]);
+ /* advance the time only if an N field was given */
+ if (ndurp) thetime += ntime;
+ }
+ }
+}
+
+/****************************************************************************
+* dospecial
+* Effect: parses special (those starting with "!") commands
+****************************************************************************/
+
+private void dospecial()
+{
+ switch (issymbol()) {
+ case sym_tempo:
+ dotempo();
+ break;
+ case sym_rate:
+ dorate();
+ break;
+ case sym_csec:
+ /* adjust dur for inheritance by next note */
+ dur = (dur * 1000L) / time_scale;
+ time_scale = 1000L;
+ break;
+ case sym_msec:
+ dur = (dur * 100L) / time_scale;
+ time_scale = 100L;
+ break;
+ case sym_seti:
+ doset(FALSE);
+ break;
+ case sym_setv:
+ doset(TRUE);
+ break;
+ case sym_call:
+ docall();
+ break;
+ case sym_ramp:
+ doramp();
+ break;
+ case sym_clock:
+ doclock();
+ break;
+ case sym_def:
+ dodef();
+ break;
+ case sym_end:
+ end_flag = TRUE;
+ break;
+ default:
+ fferror("Special command expected");
+ }
+ parseend(); /* flush the rest of the line */
+}
+
+/****************************************************************************
+* dosymdur
+* Effect: parses a duration (^, %, S, I, Q, H, or W) command
+****************************************************************************/
+
+private time_type dosymdur()
+{
+ int i, dotcnt = 0;
+ long dotfactor;
+ time_type result = 0;
+
+ for (i = 0; i < durtable_len; i++) {
+ if (durtable[i].symbol == token[fieldx-1]) {
+ /* the shift right is because durs are stored doubled because
+ * otherwise a 64th note would have the value 75/2: */
+ result = precise(durtable[i].value) >> 1;
+ break;
+ }
+ }
+ if (i == durtable_len) {
+ fieldx--;
+ fferror("Duration expected: one of W, H, Q, I, S, %, or ^");
+ return 0L;
+ }
+ while (token[fieldx]) {
+ if (token[fieldx] == 'T') { /* triplet notation */
+ result = (result * 2) / 3; /* lose a bit but avoid scale() */
+ fieldx++;
+ }
+ else if (token[fieldx] == '.') { /* dotted notation */
+ dotcnt++;
+ fieldx++;
+ }
+ else if (token[fieldx] == '/') {
+ long divisor;
+ fieldx++;
+ divisor = scanint();
+ if (divisor > 0) result = result / divisor;
+ else fferror("non-zero integer expected");
+ }
+ else if (isdigit(token[fieldx])) { /* numbers are multipliers */
+ result = result * scanint();
+ }
+ else break;
+ }
+ dotfactor = 1L;
+ for (i=1; i<=dotcnt; i++)
+ dotfactor = dotfactor * 2;
+ result = (2 * result) - (result / dotfactor);
+
+ return scale(result, 100L, tempo); /* time in milliseconds */
+}
+
+/****************************************************************************
+* dotempo
+* Effect: parses a !tempo command
+****************************************************************************/
+
+private void dotempo()
+{
+ linex += scan();
+ if (!token[0])
+ fferror("Tempo number expected");
+ else {
+ long oldtempo = tempo;
+ tempo = scanint();
+ if (token[fieldx])
+ fferror("Only digits expected here");
+ if (tempo == 0) {
+ fieldx = 0;
+ fferror("Tempo 100 will be used here");
+ tempo = 100L;
+ }
+ start = thetime;
+ /* adjust dur in case it is inherited by next note */
+ if (symbolic_dur_flag) {
+ dur = (dur * oldtempo);
+ dur = dur / tempo;
+ }
+ }
+}
+
+/****************************************************************************
+* dotime
+* Effect: parses a time (T) command
+* Implementation: see implementation of donextdur()
+****************************************************************************/
+
+private void dotime()
+{
+ if (isdigit(token[fieldx])) {
+ thetime = precise(scanint());
+ thetime = scale(thetime, (ulong)time_scale, rate);
+ if (token[fieldx] )
+ fferror("Only digits were expected here");
+ } else {
+ fieldx++;
+ thetime = dodur();
+ }
+ thetime += start; /* time is relative to start */
+}
+
+/****************************************************************************
+* dovoice
+* Effect: parse a voice (V) command (the voice is the MIDI channel)
+****************************************************************************/
+
+private void dovoice()
+{
+ if (isdigit(token[fieldx])) {
+ voice = (int) scanint();
+ if (token[fieldx])
+ fferror("V must be followed by digits only");
+ if (voice > MAX_CHANNELS) {
+ char msg[40];
+ sprintf(msg, "number too high, using %d instead", MAX_CHANNELS);
+ fferror(msg);
+ voice = MAX_CHANNELS;
+ }
+ else if (voice < 1) {
+ fferror("number too low, using 1 instead");
+ voice = 1;
+ }
+ }
+ else fferror("No digit after V");
+}
+
+/****************************************************************************
+* fferror
+* Inputs:
+* char *s: an error message string
+* Effect:
+* prints the line with the error
+* puts a cursor (^) at the error location
+* prints the error message (s)
+* Implementation:
+* this routine prints a carat under the character that
+* was copied into token[fieldx]. E.g. if fieldx = 0, the
+* carat will point to the first character in the field.
+****************************************************************************/
+
+private void fferror(s)
+ char *s;
+{
+ gprintf(ERROR, "%3d | %s", lineno, line);
+ marker(linex-strlen(token)+fieldx+1+6);
+ gprintf(ERROR, "Error: %s.\n", s);
+}
+
+/****************************************************************************
+* init
+* Effect:
+* initializes the state variables
+****************************************************************************/
+
+private void init()
+{
+ int i;
+
+ end_flag = FALSE;
+
+ /* initial (default) values for all state variables */
+ symbolic_dur_flag = TRUE; /* default dur is symbolic */
+ for (i = 0; i < nctrl; i++) {
+ /* no initial control changes */
+ ctrlflag[i] = FALSE;
+ ctrlval[i] = 0;
+ }
+
+ lineno = 0;
+ pitch = seq_dflt_pitch;
+ loud = seq_dflt_loud;
+ voice = seq_dflt_voice;
+ time_scale = 1000L;
+ tempo = 100L;
+ rate = 100L;
+ dur = precise(600); /* default dur is quarter note */
+ thetime = precise(0);
+ start = thetime;
+ ntime = 0L;
+ ticksize = 0L;
+ artic = 100;
+}
+
+/****************************************************************************
+* ins_a_note
+* Returns:
+* boolean: TRUE on success, FALSE if not enough memory
+* Effect:
+* note events (if any) corresponding to the current line are inserted
+* Implementation:
+* if a note on should occur after a note off and doesn't, and the
+* two notes have the same pitch, then the note off can cancel the
+* note on. to make it unlikely that roundoff will cause this situation,
+* dur is decreased by one half of a clock tick before rounding.
+* also, phase2 gives precedence to note-offs that are simultaneous
+* with note-ons.
+****************************************************************************/
+
+private boolean ins_a_note()
+{
+ long the_dur = (trunc(dur) * artic + 50) / 100;
+ int the_pitch = pitch;
+ event_type note;
+ if (rest_flag) the_pitch = NO_PITCH;
+ note = insert_note(the_score, seqround(thetime), lineno, voice,
+ the_pitch, the_dur, loud);
+ if (!note) return FALSE;
+ return TRUE; /* success! */
+}
+
+/****************************************************************************
+* ins_ctrls
+* Returns:
+* boolean: TRUE on success, FALSE if not enough memory
+* Effect:
+* control events corresponding to current line are inserted in score
+* Implementation:
+* ctrlflag[i] is TRUE if control i was specified in this line, so
+* insert one control change for each ctrlflag[i] that is TRUE
+****************************************************************************/
+
+private boolean ins_ctrls()
+{
+ int i;
+ event_type ctrl;
+
+ for (i = 1; i < nctrl; i++) {
+ if (ctrlflag[i]) {
+ ctrl = insert_ctrl(the_score, seqround(thetime), lineno, i, voice,
+ ctrlval[i]);
+ if (!ctrl) return FALSE;
+ ctrlflag[i] = FALSE;
+ ctrlval[i] = 0;
+ }
+ }
+ return TRUE; /* success! */
+}
+
+/****************************************************************************
+* issymbol
+* Outputs: returns symbol number, or -1 if no match
+* Assumes: token[1] has the symbol to look up (token[0] == '!')
+****************************************************************************/
+
+private int issymbol()
+{
+ int i, symb_num;
+ char *sym;
+
+ for (symb_num = 0; symb_num < sym_n; symb_num++) {
+ sym = ssymbols[symb_num];
+ i = 1;
+ while (TRUE) {
+ if (token[i] != *sym) break;
+ if (*sym == 0) return symb_num;
+ sym++;
+ i++;
+ }
+ }
+ return -1;
+}
+
+/****************************************************************************
+* marker
+* Inputs:
+* int count: the number of characters to indent
+* Effect:
+* prints a carat (^) at the position specified on file stderr
+****************************************************************************/
+
+private void marker(count)
+int count;
+{
+ int i;
+ char s[128];
+ for (i=0; i<(count-1); s[i++]=' ') /* */ ;
+ s[count-1] = '^';
+ s[count] = '\0';
+ gprintf(ERROR,"%s\n",s);
+}
+
+/*****************************************************************
+* parseend
+* Effect:
+* parse the note terminator, either ",", ";", EOS or "\n"
+*
+****************************************************************/
+
+private void parseend()
+{
+ boolean done = FALSE;
+ while (!done) {
+ linex += scan1(&line[linex]);
+ switch (token[0]) {
+ case ',':
+ ndurp = TRUE; /* switch that next time was specified */
+ ntime = 0L;
+ done = TRUE;
+ break;
+ case ';':
+ case '\n':
+ case EOS:
+ done = TRUE;
+ break;
+ case ' ':
+ case '\t':
+ break; /* skip over blanks and scan1 again */
+ default:
+ fferror("Unexpected token");
+ linex += scan(); /* flush the token */
+ break;
+ }
+ }
+}
+
+/****************************************************************************
+* parsefield
+* Effect: looks at first character of token and calls a parsing routine
+*
+****************************************************************************/
+
+private void parsefield()
+{
+ fieldx = 1;
+ switch (token[0]) {
+ case 'T' :
+ dotime();
+ break;
+ case 'U':
+ case 'W':
+ case 'H':
+ case 'Q':
+ case 'S':
+ case 'I':
+ case '%':
+ case '^':
+ dur = dodur();
+ break;
+ case 'R':
+ do_a_rest();
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ pitch = dopitch();
+ pitch_flag = TRUE;
+ break;
+ case 'P':
+ pitch = doabspitch();
+ pitch_flag = TRUE;
+ break;
+ case 'L':
+ loud = doloud();
+ break;
+ case 'N':
+ donextdur();
+ break;
+/* case 'J':
+ * doctrl(1);
+ * break;
+ */
+ case 'K':
+ doctrl(PSWITCH_CTRL);
+ break;
+ case 'M':
+ doctrl(MODWHEEL_CTRL);
+ break;
+ case 'O':
+ doctrl(TOUCH_CTRL);
+ break;
+ case 'X':
+ doctrl(VOLUME_CTRL);
+ break;
+ case 'Y':
+ doctrl(BEND_CTRL);
+ break;
+ case 'Z':
+ doprogram();
+ break;
+ case 'V':
+ dovoice();
+ break;
+ case '~':
+ domacro();
+ break;
+ case '*':
+ docomment();
+ break;
+ case '#':
+ doartic();
+ break;
+ default :
+ doerror();
+ break;
+ }
+}
+
+/****************************************************************************
+* parsenote
+* Effect:
+* parses a note line -- control events (if any) and note event (if
+* present) are inserted into score
+* Assumes:
+* line contains a string to be parsed
+****************************************************************************/
+
+private boolean parsenote()
+{
+ boolean out_of_memory = FALSE;
+ int i;
+
+ ndurp = FALSE;
+ rest_flag = FALSE;
+
+ /* this loop reads tokens for a note */
+ while (token[0]) {
+ parsefield();
+ linex += scan();
+ }
+
+ parseend(); /* take care of note terminator */
+
+ /*
+ * insert ctrl's first so that will come before the note.
+ */
+ if (ctrlflag[0]) {
+ out_of_memory |= !ins_ctrls();
+ /* don't reset ctrlflag[0], it's used below */
+ }
+
+ /*
+ * insert macro's
+ */
+ for (i = 0; i < macctrlx; i++) {
+ event_type ctrl;
+ if (macctrldef[i] == NULL) {
+ ctrl = insert_macctrl(the_score, seqround(thetime), lineno,
+ macctrlnum[i], voice, macctrlparmx[i]);
+ } else {
+ ctrl = insert_macro(the_score, seqround(thetime), lineno,
+ macctrldef[i], voice, macctrlnum[i],
+ &(macctrlparms[macctrlparmx[i]]));
+ }
+ out_of_memory |= (ctrl == NULL);
+ }
+
+ /* insert a note if
+ * (1) a pitch was specified OR
+ * (2) no control was specified and this is not a rest
+ * (it's a pitch by default)
+ *
+ * NOTE: program changes during rests are advised since
+ * synthesizers may not be able to process a program
+ * change followed immediately by a note-on. In fact, this
+ * is why we insert notes whose pitch is NO_PITCH -- so that
+ * the program change can be processed during the rest.
+ */
+ if (pitch_flag ||
+ (!ctrlflag[0] && !rest_flag && (macctrlx == 0))) {
+ out_of_memory |= !ins_a_note();
+ }
+
+ if (ndurp) thetime += ntime;
+ else thetime += dur;
+
+ return out_of_memory;
+}
+
+
+private boolean parseparm(valptr)
+ long *valptr;
+{
+ register char c = token[fieldx];
+ if (isdigit(c) || c == '-') {
+ *valptr = scansgnint();
+ return TRUE;
+ } else {
+ switch (c) {
+ case 'P':
+ fieldx++;
+ *valptr = doabspitch();
+ return TRUE;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ fieldx++;
+ *valptr = dopitch();
+ return TRUE;
+ case 'U':
+ case 'W':
+ case 'H':
+ case 'Q':
+ case 'I':
+ case 'S':
+ case '%':
+ case '^':
+ fieldx++;
+ *valptr = seqround(dodur());
+ return TRUE;
+ case 'L':
+ fieldx++;
+ *valptr = doloud();
+ return TRUE;
+ case '\'':
+ fieldx++;
+ *valptr = token[fieldx];
+ fieldx++;
+ if (token[fieldx] != '\'') {
+ fferror("single quote expected");
+ }
+ fieldx++;
+ return TRUE;
+ default:
+ fferror("Parameter expected");
+ return FALSE;
+ }
+ }
+}
+
+
+
+/****************************************************************************
+* scale
+* Inputs:
+* time_type x
+* int (ulong?) n, d
+* Outputs:
+* returns time_type: result of scaling x by n/d
+****************************************************************************/
+
+public time_type scale(x, n, d)
+ ulong x;
+ ulong n, d;
+{
+ ulong lo = (x & 0xFFFFL) * n;
+ ulong hi = (x >> 16) * n;
+ ulong res = hi / d;
+ lo = (((hi - (res * d)) << 16) + lo + (d >> 1)) / d;
+ return (time_type)( (res << 16) + lo );
+}
+
+/****************************************************************************
+* scan
+* Inputs:
+* char *start: the string to scan
+* Outputs:
+* returns int: the index of the next char in start to scan
+* Effect:
+* skips over leading blanks
+* copies characters from start into token, converting to upper case
+* scanning stops on delimiter: one of space, tab, newline, semicolon
+****************************************************************************/
+
+private int scan()
+{
+ char *start = line + linex;
+ register char c;
+ register int i = 0;
+ register int j = 0;
+ register int parens = 0;
+
+ while (((c = start[i]) == ' ') || (c == '\t')) i++;
+
+ while ((c = start[i]) != ' ' && c != '\n' && c != '\t' && c != EOS &&
+ (c != ',' || token[0] == '~' || parens > 0) && c != ';') {
+
+ if (islower(start[i])) token[j] = toupper(start[i]);
+ else token[j] = start[i];
+ if (c == '(') parens++;
+ else if (c == ')') parens--;
+ j++;
+ i++;
+ }
+ token[j] = '\0';
+
+ fieldx = 0;
+ if (parens) fferror("Unbalanced parens");
+
+ return i;
+}
+
+/****************************************************************************
+* scan1
+* Inputs:
+* char *start: the string to scan
+* Outputs:
+* returns int: the index of the next char in start to scan
+* Effect:
+* copies one char from start into token, converting to upper case
+****************************************************************************/
+
+private int scan1(start)
+char *start;
+{
+ int i = 0;
+
+ token[0] = *start;
+ if (islower(token[0])) token[0] = toupper(token[0]);
+
+ if (!nullstring(token)) {
+ token[1] = '\0';
+ i = 1;
+ }
+ fieldx = 0;
+ return i;
+}
+
+/****************************************************************************
+* scanint
+* Outputs:
+* returns long: the scanned integer
+* Effect:
+* scans an unsigned long from token, starting at fieldx
+* fieldx is incremented to end of the integer
+****************************************************************************/
+
+private long scanint()
+{
+ long i = 0;
+ char c;
+ while ((c = token[fieldx])) {
+ if (isdigit(c)) {
+ i = (i*10) + (c - '0');
+ fieldx++;
+ } else return i;
+ }
+ return i;
+}
+
+private long scansgnint()
+{
+ if (token[fieldx] == '-') {
+ fieldx++;
+ return -scanint();
+ } else {
+ if (token[fieldx] == '+') {
+ fieldx++;
+ }
+ return scanint();
+ }
+}
+
+
+/* scansymb -- scan a symbol from the token */
+/**/
+private void scansymb(str)
+ char *str;
+{
+ char c;
+ while ((c = token[fieldx])) {
+ if (isdigit(c) || isalpha(c) || c == '_') {
+ *str++ = c;
+ fieldx++;
+ } else break;
+ }
+ *str = EOS;
+}
+
+/****************************************************************************
+* seq_read
+* Inputs:
+* seq_type seq: the sequence to receive the score
+* FILE *fp: input file
+* Outputs:
+* none
+* Effect:
+* parses score from input file and builds score data structure
+****************************************************************************/
+
+void seq_read(seq, fp)
+ seq_type seq;
+ FILE *fp;
+{
+ boolean out_of_memory = FALSE; /* set when no more memory */
+ /* printf("seq_read: chunklist is 0x%x\n", seq->chunklist); */
+ the_score = seq; /* current sequence is a global within this module */
+ if (!seq) return;
+ init();
+ lineno = 0;
+
+ /* make sure line is well terminated or scan might run off the end */
+ line[linesize - 1] = EOS;
+ line[linesize - 2] = '\n';
+
+ /* this loop reads lines */
+ while ((fgets(line, linesize - 2, fp) != NULL) && !out_of_memory &&
+ !check_aborted() && !end_flag) {
+ lineno++;
+ linex = 0;
+ /* this loop reads notes from a line */
+ while ((line[linex] != EOS) && !out_of_memory) {
+ /* loop invariant: line[linex] is first char of next note */
+ ctrlflag[0] = FALSE; /* other ctrlflags are reset by ins_ctrls() */
+ macctrlx = 0;
+ macctrlnextparm = 0;
+ pitch_flag = FALSE;
+ linex += scan();
+ if (!nullstring(token)) {
+ if (token[0] == '*') docomment();
+ else if (token[0] == '!') dospecial();
+ else out_of_memory = parsenote();
+ }
+ else parseend();
+ }
+ }
+
+ if (out_of_memory) {
+ gprintf(ERROR, "Out of note memory at line %d,\n", lineno-1);
+ gprintf(ERROR, " the rest of your file will be ignored.\n");
+ }
+
+ if (check_aborted()) {
+ gprintf(ERROR, "User aborted score input,\n");
+ gprintf(ERROR, " the rest of your file will be ignored.\n");
+ if (abort_flag == BREAK_LEVEL) abort_flag = 0;
+ }
+
+ /* fclose(fp); -- don't close the file; if you do, Nyquist's garbage
+ collector will close Nyquist's copy, and closing the file twice
+ in Linux will crash Nyquist */
+
+ gprintf(TRANS, "\nLoaded Adagio file with %ld note(s), %ld ctrl(s).\n\n",
+ seq_notecount(the_score), seq_ctrlcount(the_score));
+}
diff --git a/cmt/seqread.h b/cmt/seqread.h
new file mode 100644
index 0000000..1348574
--- /dev/null
+++ b/cmt/seqread.h
@@ -0,0 +1,7 @@
+/* seqread.h -- header file for users of seqread.c */
+/* Copyright 1989 Carnegie Mellon University */
+
+#define name_length 255
+void seq_read(seq_type seq, FILE *fp); /* LISP: (SEQ-READ SEQ FILE) */
+time_type scale(ulong, ulong, ulong); /*now public for smf_write, smf_read*/
+
diff --git a/cmt/seqwrite.c b/cmt/seqwrite.c
new file mode 100644
index 0000000..fde0746
--- /dev/null
+++ b/cmt/seqwrite.c
@@ -0,0 +1,282 @@
+/* seqwrite -- write a seq in Adagio format to a file */
+/***************************************************************************
+* Change Log
+* Date | Change
+*-----------+--------------------------------------------------------------
+* 11-Mar-94 | Created Change Log
+* 11-Mar-94 | PLu : Added private to function defs.
+* 28-Apr-03 | DM : changed for portability, true->TRUE, false->FALSE
+****************************************************************************/
+
+#include "switches.h"
+
+#include <stdio.h>
+
+#include "cext.h"
+#include "midifns.h"
+#include "timebase.h"
+#include "seq.h"
+#include "seqwrite.h"
+#include "userio.h"
+#include "record.h"
+
+private boolean next_event_time();
+private void write_event();
+private void write_velocity(FILE *f, int velocity);
+private void write_voice();
+private void write_rest(FILE *f, event_type ev, boolean abs_flag);
+private void write_time();
+
+private boolean clock_started;
+private long clock_half_tick;
+private int new_tempo;
+/* non-zero indicates a pending tempo change... */
+private time_type tempo_change_at;
+
+private int macro_count;
+private int call_count;
+private int deframp_count;
+private int seti_count;
+
+private int last_velocity;
+private int last_voice = seq_dflt_voice;
+
+
+/* next_event_time -- get the time of the next event */
+/*
+ * NOTE: clock events are ignored (unless this is the first clock event)
+ */
+private boolean next_event_time(event, next_time)
+ event_type event;
+ time_type *next_time;
+{
+ while (event) {
+ if (vc_ctrl(event->nvoice) == ESC_CTRL &&
+ event->value == CLOCK_VALUE &&
+ clock_started) {
+ /* this is a clock event, ignore it: */
+ event = event->next;
+ } else {
+ *next_time = event->ntime;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/* seq_write -- write a seq to a file */
+/*
+ * NOTE: if abs_flag, then write using absolute times ('T'), otherwise use
+ * relative timing ('N'). Also, only selected channels are written.
+ */
+void seq_write(seq_type seq, FILE *f, boolean abs_flag)
+{
+ event_type ev = seq_events(seq);
+
+ last_velocity = seq_dflt_loud;
+ last_voice = seq_dflt_voice;
+
+ fprintf(f, "!MSEC\n");
+ clock_started = FALSE;
+ tempo_change_at = 0;
+
+ macro_count = 0;
+ call_count = 0;
+ deframp_count = 0;
+ seti_count = 0;
+
+ while (ev) {
+ write_event(seq, ev, f, abs_flag);
+ ev = ev->next;
+ }
+ if (macro_count) gprintf(TRANS, "%d macros ignored.\n", macro_count);
+ if (call_count) gprintf(TRANS, "%d calls ignored.\n", call_count);
+ if (deframp_count) gprintf(TRANS, "%d deframps ignored.\n", deframp_count);
+ if (seti_count) gprintf(TRANS, "%d setis ignored.\n", seti_count);
+}
+
+
+private char ctrl_letter[] = "?KMOXYZ";
+
+/* write_event -- write a single event to a file */
+/**/
+private void write_event(seq, event, f, abs_flag)
+ seq_type seq;
+ event_type event;
+ FILE *f;
+ boolean abs_flag;
+{
+ int voice = vc_voice(event->nvoice);
+
+ /* process all current (and earlier) events */
+ if (is_note(event)) { /*** a note or rest ***/
+ /* if this note is not a rest, write it */
+ if (event->value != NO_PITCH &&
+ (seq_channel_mask(seq) &
+ (1 << (voice - 1)))) {
+ write_pitch(f, event->value);
+ fprintf(f, " U%ld ", event->u.note.ndur >> 8);
+ write_velocity(f, (int) (event->u.note.ndur & 0xFF));
+ write_voice(f, voice);
+ write_time(f, event, abs_flag);
+ }
+ } else { /*** a control command ***/
+ switch (vc_ctrl(event->nvoice)) {
+ case PSWITCH_CTRL:
+ case MODWHEEL_CTRL:
+ case TOUCH_CTRL:
+ case VOLUME_CTRL:
+ case BEND_CTRL:
+ fprintf(f, "%c%d ", ctrl_letter[vc_ctrl(event->nvoice)],
+ event->value);
+ write_voice(f, voice);
+ write_time(f, event, abs_flag);
+ break;
+ case PROGRAM_CTRL:
+ fprintf(f, "Z%d ", event->value + 1);
+ write_voice(f, voice);
+ write_time(f, event, abs_flag);
+ break;
+ case ESC_CTRL:
+ switch (event->value) {
+ case CALL_VALUE:
+ call_count++;
+ write_rest(f, event, abs_flag);
+ /* !call's are not written because there isn't enough
+ * information. A future version of seq should store
+ * the hash table entry instead of the address of the
+ * routine (then we could get string name of the call)
+ * and the number of arguments.
+ */
+ break;
+ case CLOCK_VALUE:
+ /* design for !clock: for absolute (absflag) files, put the
+ clocks (if any) at the end where they can be edited out
+ easily. For relative files, keep the clocks in-line.
+ On each clock, write a !tempo command inferred by the
+ clock and followed by the !clock. Because the clock
+ event comes before the actual tempo change, the chnage
+ must be delayed by a half tick except for the first one.
+ */
+ if (abs_flag) break;
+ new_tempo = (2500L << 16) / event->u.clock.ticksize;
+ if (clock_started) {
+ tempo_change_at = event->ntime + clock_half_tick;
+ } else {
+ fprintf(f, "!tempo %d\n", new_tempo);
+ fprintf(f, "!clock\n");
+ clock_started = TRUE;
+ }
+ clock_half_tick = (event->u.clock.ticksize) >> 17;
+ break;
+ case MACCTRL_VALUE:
+ fprintf(f, "~%d(%d) ", event->u.macctrl.ctrl_number,
+ event->u.macctrl.value);
+ write_voice(f, voice);
+ write_time(f, event, abs_flag);
+ break;
+ case MACRO_VALUE:
+ macro_count++;
+ write_rest(f, event, abs_flag);
+ /* macros are not written because there isn't enough
+ * information. A future version of seq should store
+ * the number of arguments in the event, or better yet,
+ * in the definition. Send complaints to RBD!
+ */
+ break;
+ case CTRLRAMP_VALUE:
+ fprintf(f, "!ramp ~%d(%d) ~%d(%d) U%d U%d ",
+ event->u.ramp.ctrl, event->u.ramp.u.ctrl.from_value,
+ event->u.ramp.ctrl, event->u.ramp.u.ctrl.to_value,
+ (int)event->u.ramp.step,
+ (int)event->u.ramp.dur);
+ write_voice(f, voice);
+ write_time(f, event, abs_flag);
+ break;
+ case DEFRAMP_VALUE:
+ deframp_count++;
+ write_rest(f, event, abs_flag);
+ /* See MACRO_VALUE above for why this isn't implemented. */
+ break;
+ case SETI_VALUE:
+ seti_count++;
+ write_rest(f, event, abs_flag);
+ /* !seti and !setv are not implemented -- a future version
+ * of seq should save more information so we can reconstruct
+ * the Adagio source command.
+ */
+ break;
+ default:
+ gprintf(TRANS, "unexpected ESC_CTRL value\n");
+ break;
+ }
+ break;
+ default:
+ gprintf(TRANS, "unexpected seq data\n");
+ break;
+ }
+ }
+}
+
+
+/* write_rest -- write a rest (in place of an event) */
+/**/
+private void write_rest(FILE *f, event_type ev, boolean abs_flag)
+{
+ fprintf(f, "R ");
+ write_time(f, ev, abs_flag);
+}
+
+
+/* write_time -- write the final field on the line with N or T command */
+/**/
+private void write_time(f, event, abs_flag)
+ FILE *f;
+ event_type event;
+ boolean abs_flag;
+{
+ time_type next_time;
+
+ if (abs_flag) {
+ fprintf(f, "T%ld\n", event->ntime);
+ return;
+ }
+
+ if (next_event_time(event->next, &next_time)) {
+ if (tempo_change_at && (next_time >= tempo_change_at)) {
+ fprintf(f, "N%ld\n!TEMPO %d\n!CLOCK\nR U%ld\n",
+ tempo_change_at - event->ntime,
+ new_tempo, next_time - tempo_change_at);
+ tempo_change_at = 0;
+ } else {
+ fprintf(f, "N%ld\n", next_time - event->ntime);
+ }
+ } else {
+ fprintf(f, "\n");
+ }
+}
+
+
+/* write_velocity -- write the velocity field */
+/**/
+private void write_velocity(FILE *f, int velocity)
+{
+ if (last_velocity != velocity) {
+ last_velocity = velocity;
+ fprintf(f, "L%d ", velocity);
+ }
+}
+
+
+/* write_voice -- write the voice field */
+/**/
+private void write_voice(f, voice)
+ FILE *f;
+ int voice;
+{
+ if (last_voice != voice) {
+ last_voice = voice;
+ fprintf(f, "V%d ", voice);
+ }
+}
diff --git a/cmt/seqwrite.h b/cmt/seqwrite.h
new file mode 100644
index 0000000..3f9ab6b
--- /dev/null
+++ b/cmt/seqwrite.h
@@ -0,0 +1,6 @@
+/* seqwrite.h -- write adagio format file */
+/* Copyright 1994 Carnegie Mellon University */
+
+void seq_write(seq_type seq, FILE *f, boolean abs_flag);
+ /* LISP: (SEQ-WRITE SEQ FILE BOOLEAN) */
+
diff --git a/cmt/swlogic.h b/cmt/swlogic.h
new file mode 100644
index 0000000..e09bd6c
--- /dev/null
+++ b/cmt/swlogic.h
@@ -0,0 +1,197 @@
+/********************************************************************
+* swlogic.h - switch logic, loaded from various versions of switch.h
+*
+* Copyright 1989 Carnegie Mellon University
+*
+*********************************************************************/
+
+/*
+ * When included, one of the following should be defined:
+ * AZTEC (manx compiler, implies AMIGA)
+ * THINK_C (Think C compiler, implies Macintosh)
+ * __MWERKS__ (Metrowerks C compiler, implies Macintosh)
+ * LATTICE & DOS (Lattice compiler for IBM PC/AT/XT/CLONES)
+ * MICROSOFT & DOS (Microsoft compiler, implies IBM PC/AT/XT/CLONES)
+ * UNIX (emulator for UNIX)
+ * UNIX_ITC (ITC code for RS6000)
+ * UNIX_MACH (MACH ukernel system)
+ */
+
+/*------------------------------------------*/
+/* Other switches that might be defined in switches.h are as follows: */
+/* APPLICATION, SPACE_FOR_PLAY, MAX_CHANNELS */
+/*------------------------------------------*/
+
+/* We're moving toward the elimination of switches.h, so try to map
+ * predefined constants into our standard constants shown above:
+ */
+
+/* CHANGE LOG
+ * --------------------------------------------------------------------
+ * 28Apr03 dm new conditional compilation structure
+ * 28Apr03 rbd remove macro redefinitions: MICROSOFT
+ */
+
+
+
+/* Microsoft C compiler: */
+#ifdef _MSC_VER
+#endif
+#ifdef _MSDOS
+#define DOS
+#endif
+
+/* Quick C compiler: */
+#ifndef DOS
+#ifdef MICROSOFT
+#define DOS
+#endif
+#endif
+
+/* Borland C compiler: */
+#ifdef __BORLANDC__
+#define BORLAND
+#define DOS
+#endif
+
+/* Borland Turbo C compiler: */
+#ifdef __TURBOC__
+#define BORLAND]
+#define DOS
+#endif
+
+/* SGI systems */
+#ifdef sgi
+#ifndef UNIX
+#define UNIX
+#endif
+#define UNIX_IRIX
+#define MAX_CHANNELS 32
+#endif
+
+/* APPLICATION -- define APPLICATION if you want to disable
+ * looking for command line switches in the midi interface.
+ * I think this feature is here for the Piano Tutor project
+ * and you should not define APPLICATION for CMU Midi Toolkit
+ * projects (APPLICATION is a poor choice of terms):
+ */
+
+
+/* memory space management (system dependent):
+ * SPACE_FOR_PLAY must be enough space to allow
+ * seq to play a score. This may include space for
+ * note-off events, I/O buffers, etc.
+ */
+#ifndef SPACE_FOR_PLAY
+#define SPACE_FOR_PLAY 10000L
+#endif
+
+
+/* How many MIDI channels are there? MACINTOSH can use 2 ports,
+ * so it supports 32 channels. Others have one port, 16 channels.
+ * On the other hand, if you don't have all the MIDI ports plugged
+ * into MIDI interfaces, CMT will just hang, so I'll compile with
+ * just 16 channels. The 32 channel option for the Mac is untested.
+ */
+#ifndef MAX_CHANNELS
+#define MAX_CHANNELS 16
+#endif
+
+
+/*------------------------------------------*/
+/* Now we get to the "logic": define things as a function of what
+ * was defined in switches.h
+ */
+
+#ifdef THINK_C
+#define MACINTOSH
+#endif
+
+#ifdef __MWERKS__
+#define MACINTOSH
+#endif
+
+#ifdef MACINTOSH
+#define MACINTOSH_OR_DOS
+#define MACINTOSH_OR_UNIX
+/* I don't know if THINK_C defines this and we need it for a few prototypes... */
+#ifndef __STDC__
+#define __STDC__
+#endif
+#ifndef TAB_WIDTH
+#define TAB_WIDTH 4
+#endif
+#endif
+
+#ifndef TAB_WIDTH
+#define TAB_WIDTH 8
+#endif
+
+/*
+ * If MIDIMGR is defined, compile for the Apple MIDI Manager
+ * (Non MIDI manager code is no longer supported)
+ */
+#ifdef MACINTOSH
+/* under Nyquist, the MidiMgr is not used, so you can't
+ * receive or send Midi as in CMU MIDI Toolkit; however,
+ * much of CMU MIDI Toolkit is used for Midi file IO
+ */
+#ifndef NYQUIST
+#define MIDIMGR
+#endif
+#define USE_VSPRINTF
+#endif
+
+#ifdef BORLAND
+#define DOS
+#endif
+
+#ifdef LATTICE322
+#define DOS
+#define OLD_PROTOTYPES
+#endif
+
+#ifdef UNIX_ITC
+#define UNIX
+#define ITC
+#endif
+
+#ifdef UNIX_MACH
+#define UNIX
+#define ITC
+#endif
+
+/* USE_VSPRINTF says vsprintf() is defined */
+#ifdef ITC
+#define USE_VSPRINTF
+#endif
+#ifdef AZTEC
+#define USE_VSPRINTF
+#endif
+
+
+/* DOTS_FOR_ARGS says ANSI "..." notation is recognized */
+#ifdef __STDC__
+#define DOTS_FOR_ARGS
+#endif
+#ifdef UNIX_ITC
+#define DOTS_FOR_ARGS
+#endif
+#ifdef BORLAND
+#define DOTS_FOR_ARGS
+#endif
+#ifdef MICROSOFT
+#define DOTS_FOR_ARGS
+#endif
+
+#ifdef DOS
+#define MACINTOSH_OR_DOS
+#else
+#define huge
+#endif
+
+#ifdef UNIX
+#define MACINTOSH_OR_UNIX
+#endif
+
+#define SWITCHES
diff --git a/cmt/tempomap.c b/cmt/tempomap.c
new file mode 100644
index 0000000..e56b9bf
--- /dev/null
+++ b/cmt/tempomap.c
@@ -0,0 +1,124 @@
+/* tempomap.c -- used by midifile reader to record and lookup time maps */
+/*
+ * This module is designed to provide tempo change list insert and
+ * lookup facilities. The exact interpretation of beat and tempo are
+ * defined in one function, elapsed_time, which takes a tempo and a
+ * number of beats, and returns time.
+ */
+
+#include "cext.h"
+#include "midifns.h"
+#include "timebase.h"
+#include "moxc.h"
+#include "seq.h"
+#include "tempomap.h"
+
+static time_type elapsed_time();
+
+/* tempomap_create -- create a tempomap */
+/**/
+tempomap_type tempomap_create()
+{
+ tempochange_type tempochange = tempochange_alloc();
+ tempomap_type tempomap = tempomap_alloc();
+ tempomap->hint = tempomap->entries = tempochange;
+ tempochange->beat = 0L;
+ /* default tempo is 120 (= 50000microsec/beat) and 24 divisions/quarter */
+ /* this may be overridden by a tempomap_insert */
+ tempochange->tempo = 500000L / 24;
+ tempochange->rtime = 0L;
+ tempochange->next = NULL;
+ return tempomap;
+}
+
+
+/* tempomap_free -- deallocate storage for tempo map */
+/**/
+void tempomap_free(tm)
+ tempomap_type tm;
+{
+ while (tm->entries) {
+ tempochange_type tc = tm->entries;
+ tm->entries = tc->next;
+ tempochange_free(tc);
+ }
+ memfree(tm, sizeof(tempomap_node));
+}
+
+
+/* tempomap_insert -- insert a tempo change into the map */
+/**/
+void tempomap_insert(tempomap, beat, tempo)
+ tempomap_type tempomap;
+ long beat; /* beat division number */
+ long tempo; /* microseconds per beat division */
+{
+ tempochange_type tempochange = tempochange_alloc();
+ register tempochange_type prev;
+ register tempochange_type next;
+
+ tempochange->tempo = tempo;
+ tempochange->beat = beat;
+
+ if ((!(tempomap->hint->next)) ||
+ (tempomap->hint->beat > beat))
+ tempomap->hint = tempomap->entries;
+
+ /* find the insert point */
+ for (prev = tempomap->hint;
+ (next = prev->next) && (next->beat <= beat);
+ prev = next);
+
+ /* make the insert */
+ tempochange->next = next;
+ prev->next = tempochange;
+ tempomap->hint = prev;
+
+ /* update the real time of each change */
+ for (tempochange = prev;
+ tempochange->next;
+ tempochange = tempochange->next) {
+ tempochange->next->rtime =
+ tempochange->rtime +
+ elapsed_time(tempochange->tempo,
+ tempochange->next->beat - tempochange->beat);
+ }
+}
+
+
+/* tempomap_lookup -- convert beat to 4us time */
+/*
+ * The returned time is real time in units of 4us.
+ */
+time_type tempomap_lookup(tempomap, beat)
+ tempomap_type tempomap;
+ long beat;
+{
+ register tempochange_type prev;
+ register tempochange_type next;
+
+ if ((!(tempomap->hint->next)) ||
+ (tempomap->hint->beat > beat))
+ tempomap->hint = tempomap->entries;
+
+ /* find the last inflection point */
+ for (prev = tempomap->hint;
+ (next = prev->next) && (next->beat <= beat);
+ prev = next);
+
+ /* interpolate */
+ return prev->rtime +
+ elapsed_time(prev->tempo, beat - prev->beat);
+}
+
+
+/* elapsed_time -- compute the real elapsed time at a given tempo */
+/*
+ * the time returned is in units of 4us.
+ */
+static time_type elapsed_time(tempo, beat)
+ long tempo;
+ long beat;
+{
+ return (time_type)((tempo * beat) >> 2);
+}
diff --git a/cmt/tempomap.h b/cmt/tempomap.h
new file mode 100644
index 0000000..a437c25
--- /dev/null
+++ b/cmt/tempomap.h
@@ -0,0 +1,25 @@
+/* tempomap.h -- list of tempo changes */
+
+typedef struct tempochange_struct {
+ struct tempochange_struct *next;
+ time_type rtime;
+ long beat;
+ long tempo;
+} tempochange_node, *tempochange_type;
+
+
+typedef struct tempomap_struct {
+ tempochange_type entries;
+ tempochange_type hint;
+} tempomap_node, *tempomap_type;
+
+
+#define tempochange_alloc() (tempochange_type) memget(sizeof(tempochange_node))
+#define tempochange_free(tc) memfree(tc, sizeof(tempochange_node))
+#define tempomap_alloc() (tempomap_type) memget(sizeof(tempomap_node))
+tempomap_type tempomap_create(void);
+void tempomap_free(tempomap_type tm);
+void tempomap_insert(tempomap_type tempomap, long beat, long tempo);
+time_type tempomap_lookup(tempomap_type tempomap, long beat);
+
+
diff --git a/cmt/timebase.c b/cmt/timebase.c
new file mode 100644
index 0000000..0bb1713
--- /dev/null
+++ b/cmt/timebase.c
@@ -0,0 +1,322 @@
+/* timebase.c -- management of calls, time bases and heaps for moxc */
+
+/*****************************************************************************
+* Change Log
+* Date | Change
+*-----------+-----------------------------------------------------------------
+* 2-Apr-91 | JDW : further changes
+* 21-Mar-92 | GWL : abort recovery
+* 28-Apr-03 | DM : true->TRUE
+*****************************************************************************/
+
+
+#include "stdio.h"
+#include "cext.h"
+#include "userio.h"
+#include "midifns.h"
+#include "timebase.h"
+#include "moxc.h"
+
+
+timebase_type timebase_queue = NULL; /* waiting to run timebase queue */
+call_type callfree = NULL; /* free list */
+
+private void fatal();
+
+
+/****************************************************************************
+* timebase_create
+* Inputs:
+* maxsize is the initial size of the heap
+* Outputs:
+* returns an initialized timebase_type
+****************************************************************************/
+
+timebase_type timebase_create(maxsize)
+ int maxsize;
+{
+ static char *error_msg = "Out of memory in timebase_create()";
+ timebase_type base = (timebase_type) memget(sizeof(timebase_node));
+ if (!base) fatal(error_msg);
+ base->next = NULL;
+ base->next_time = MAXTIME;
+ base->virt_base = 0L;
+ base->real_base = 0L;
+ base->rate = 256L;
+ base->heap_size = 0;
+ base->heap_max = maxsize;
+ base->heap = (call_type *) memget(sizeof(call_type) * maxsize);
+ if (!base->heap) fatal(error_msg);
+ return base;
+}
+
+/****************************************************************************
+* callinsert
+* Inputs:
+* timebase_type base: the time base and heap
+* call_type call: the call to insert in heap
+* Outputs:
+ none
+* Implementation:
+* linear insertion; to be changed to heap
+****************************************************************************/
+
+void callinsert(base, call)
+ timebase_type base;
+ call_type call;
+{
+ int i;
+ register call_type *heap = base->heap;
+
+ /* handle overflow -- this should never be executed if the user
+ * gives the right initial heap size
+ */
+ base->heap_size++;
+ if (base->heap_size >= base->heap_max) {
+ call_type *new_heap = (call_type *)
+ memget((base->heap_max << 1) * sizeof(call_type));
+ int i;
+ call_type *oldptr;
+ call_type *newptr;
+ if (!new_heap) {
+ gprintf(TRANS, "Out of space, can't allocate new heap\n");
+ EXIT(1);
+ }
+
+ oldptr = base->heap;
+ newptr = new_heap;
+
+ for (i = base->heap_max; i > 0; i--) *newptr++ = *oldptr++;
+ memfree((char *) heap, base->heap_max * sizeof(call_type));
+ base->heap = heap = new_heap;
+ base->heap_max = (base->heap_max << 1);
+ }
+
+ /* now do the heap insert */
+ i = base->heap_size;
+ while (i > 1) {
+ int parent = i >> 1;
+ if (heap[parent]->u.e.time < call->u.e.time ||
+ (heap[parent]->u.e.time == call->u.e.time &&
+ heap[parent]->u.e.priority <= call->u.e.priority)) break;
+ heap[i] = heap[parent];
+ i = parent;
+ }
+ heap[i] = call;
+
+ /* if next_time might change, reinsert base into queue */
+ if (heap[1] == call) {
+ remove_base(base);
+ insert_base(base);
+ }
+}
+
+/****************************************************************************
+* callshow
+* Inputs:
+* calltype call: the call to show
+* Effect:
+* prints a description of call
+* Assumes:
+* call is not null
+****************************************************************************/
+void callshow(call)
+ call_type call;
+{
+ int i;
+ gprintf(TRANS,"address: %lx\n", (ulong)call);
+ gprintf(TRANS,"time: %ld\n", call->u.e.time);
+ gprintf(TRANS,"routine: %lx\n", (ulong)call->u.e.routine);
+ gprintf(TRANS,"parameters:");
+ for (i = 0; i < MAX_CALL_ARGS; i++) {
+ gprintf(TRANS, " %d", call->u.e.p.arg[i]);
+ }
+ gprintf(TRANS, "\n");
+}
+
+/***************************************************************
+* fatal
+*
+* Input : msg: a message to be displayed
+* Effect: print message and exit program
+***************************************************************/
+
+private void fatal(msg)
+ char *msg;
+{
+ gprintf(FATAL, msg);
+ EXIT(1);
+}
+
+/***************************************************************
+* timebase_free
+*
+* Input : a time base
+* Effect: deallocate the time base
+***************************************************************/
+
+void timebase_free(timebase)
+ timebase_type timebase;
+{
+ remove_base(timebase);
+ if (timebase->heap) {
+ memfree((char *) timebase->heap,
+ (timebase->heap_max * sizeof(call_type)));
+ }
+ memfree((char *) timebase, sizeof(timebase_node));
+}
+
+/***************************************************************
+* insert_base
+*
+* Input : a time base not in the list
+* Effect: insert timebase at the appropriate spot in the list
+* computes the next_time field from the top of the heap
+***************************************************************/
+
+void insert_base(timebase)
+ timebase_type timebase;
+{
+ register timebase_type *ptr = &timebase_queue;
+ register time_type next_time = MAXTIME;
+ /* compute next_time */
+ if (timebase->heap_size != 0) {
+ register call_type call = timebase->heap[1];
+ /* virt to real calculation */
+ next_time = (virt_to_real_256(timebase, call->u.e.time) &
+ 0xFFFFFF00) + call->u.e.priority;
+/* gprintf(TRANS,
+ "insert next_time is %ld, vt %ld, rb %ld, vb %ld rt %ld\n",
+ next_time, timebase->heap[1]->u.e.time,
+ timebase->real_base, timebase->virt_base, timebase->rate);
+ */
+ }
+ timebase->next_time = next_time;
+
+ if (next_time != MAXTIME) {
+ /* insert into the list */
+ while (TRUE) {
+ if (! *ptr) {
+ *ptr = timebase;
+ timebase->next = NULL;
+ return;
+ } else if ((*ptr)->next_time >= next_time) {
+ timebase->next = *ptr;
+ *ptr = timebase;
+ return;
+ } else ptr = &((*ptr)->next);
+ }
+ }
+}
+
+/***************************************************************
+* remove_base
+*
+* Input : timebase
+* Effect: if timebase is in the queue, remove it
+***************************************************************/
+
+void remove_base(timebase)
+ timebase_type timebase;
+{
+ timebase_type *ptr = &timebase_queue;
+ while (*ptr) {
+ if (*ptr == timebase) {
+ *ptr = timebase->next;
+ return;
+ } else ptr = &((*ptr)->next);
+ }
+}
+
+/***************************************************************
+* remove_call
+*
+* Input : a timebase -- passed as a global
+* Assumes: a_timebase->heap_size > 0
+* Returns: the earliest call in the queue
+* Effect: removes the earliest call in the queue
+***************************************************************/
+
+call_type remove_call(timebase_type a_timebase)
+{
+ register call_type *heap = a_timebase->heap;
+ call_type result = heap[1];
+ register call_type large;
+ int i = 1;
+ int child = i << 1;;
+ large = heap[a_timebase->heap_size--];
+ while (child <= a_timebase->heap_size) {
+ if (child + 1 <= a_timebase->heap_size) {
+ if (heap[child + 1]->u.e.time < heap[child]->u.e.time ||
+ (heap[child + 1]->u.e.time == heap[child]->u.e.time &&
+ heap[child + 1]->u.e.priority < heap[child]->u.e.priority))
+ child++;
+ }
+ /* child is now the index of the least child */
+ if (large->u.e.time < heap[child]->u.e.time ||
+ (large->u.e.time == heap[child]->u.e.time &&
+ large->u.e.priority <= heap[child]->u.e.priority)) break;
+ /* swap */
+ heap[i] = heap[child];
+ i = child;
+ child = i << 1;
+ }
+ heap[i] = large;
+ return result;
+}
+
+/***************************************************************
+* set_rate
+*
+* Input : timebase and new rate
+* Effect: makes the current rate of timebase be rate
+***************************************************************/
+
+void set_rate(base, rate)
+ timebase_type base;
+ time_type rate;
+{
+ if (base == timebase) base->virt_base = virttime;
+ else base->virt_base = real_to_virt(base, eventtime);
+ base->real_base = eventtime;
+ base->rate = rate;
+/* gprintf(TRANS, "new real_base %ld virt_base %ld\n",
+ base->real_base, base->virt_base);
+ */
+ remove_base(base);
+ insert_base(base);
+}
+
+/***************************************************************
+* set_virttime
+*
+* Input : virtual time
+* Effect: makes the current virtual time of timebase be vtime
+***************************************************************/
+
+void set_virttime(base, vtime)
+ timebase_type base;
+ time_type vtime;
+{
+ base->real_base = eventtime;
+ base->virt_base = vtime;
+ if (base == timebase) virttime = vtime;
+ remove_base(base);
+ insert_base(base);
+}
+
+/***************************************************************
+* timebase_use
+*
+* Input : a timebase to use for scheduling
+* Effect: sets up globals: timebase, virttime
+***************************************************************/
+
+void timebase_use(base)
+ register timebase_type base;
+{
+ if (timebase != base) {
+ timebase = base;
+ virttime = real_to_virt(base, eventtime);
+ }
+}
diff --git a/cmt/timebase.h b/cmt/timebase.h
new file mode 100644
index 0000000..9da4e90
--- /dev/null
+++ b/cmt/timebase.h
@@ -0,0 +1,85 @@
+/* timebase.h -- management of calls, time bases and heaps for moxc */
+
+#define STOPRATE 0xFFFFL
+
+/***************************************************************************
+* call structure
+****************************************************************************/
+
+/* ---NOTE!!! if you change MAX_CALL_ARGS, change CALLARGS macro below--- */
+#define MAX_CALL_ARGS 8
+typedef struct call_args_struct {
+ long arg[MAX_CALL_ARGS];
+} call_args_node;
+
+typedef struct call {
+ union {
+ struct {
+ time_type time; /* virtual time of this call */
+ int priority; /* an 8-bit the priority, low priority first */
+ int (*routine)(); /* who to call */
+ call_args_node p; /* what to pass */
+ } e;
+ struct call *p; /* used to link free calls */
+ } u;
+} *call_type, call_node;
+
+/* CALLARGS - given a call_type, this macro generates an argument list */
+/*
+ * NOTE: originally, I thought call->u.e.p (a structure), would do it, but
+ * Lattice C for the Amiga has a compiler bug, and even in places where the
+ * bug doesn't show up, the code generated for the structure passing is
+ * a sequence of two loops: one to copy data to a local area on the stack,
+ * and one to push this data (a byte at a time!) to the top of the stack.
+ * With Lattice (and perhaps others, I haven't checked), it's better to
+ * push the data in-line.
+ */
+#ifdef LATTICE
+#define CARG(n) call->u.e.p.arg[n]
+#define CALLARGS(call) CARG(0), CARG(1), CARG(2), CARG(3), \
+ CARG(4), CARG(5), CARG(6), CARG(7)
+#else
+#define CALLARGS(call) call->u.e.p
+#endif
+
+/***************************************************************************
+* timebase structure
+****************************************************************************/
+
+typedef struct timebase_struct {
+ struct timebase_struct *next; /* used for list */
+ time_type next_time;
+ time_type virt_base;
+ time_type real_base;
+ time_type rate; /* ratio of real/virt time, STOPRATE or more is infinity */
+ short heap_size;
+ short heap_max;
+ call_type *heap;
+} timebase_node, *timebase_type;
+
+extern timebase_type timebase_queue;
+
+#define call_alloc() ((call_type) memget(sizeof(call_node)))
+#define call_free(c) memfree((char *) (c), sizeof(call_node))
+
+timebase_type timebase_create(int maxsize);
+void callinsert(timebase_type base, call_type call);
+void callshow(call_type call);
+void timebase_free(timebase_type timebase);
+void insert_base(timebase_type timebase);
+void remove_base(timebase_type timebase);
+call_type remove_call(timebase_type a_timebase);
+void set_rate(timebase_type base, time_type rate);
+void set_virttime(timebase_type base, time_type vtime);
+void timebase_use(timebase_type base);
+
+#define real_to_virt(base, rtime) ((base)->rate == 0 ? MAXTIME : \
+ ((base)->virt_base + (((rtime) - (base)->real_base) << 8) / (base)->rate))
+
+#define virt_to_real(base, vtime) ((base)->rate >= STOPRATE ? \
+ ((base)->virt_base > vtime ? (base)->real_base : MAXTIME) : \
+ (base)->real_base + ((((vtime) - (base)->virt_base) * (base)->rate) >> 8))
+
+#define virt_to_real_256(base, vtime) ((base)->rate >= STOPRATE ? \
+ ((base)->virt_base > vtime ? (base)->real_base << 8 : MAXTIME) : \
+ ((base)->real_base << 8) + ((((vtime) - (base)->virt_base) * (base)->rate)))
diff --git a/cmt/userio.c b/cmt/userio.c
new file mode 100644
index 0000000..e8d4fdb
--- /dev/null
+++ b/cmt/userio.c
@@ -0,0 +1,1285 @@
+/* userio.c -- handy user interface functions */
+/* Copyright 1989 Carnegie Mellon University */
+
+/*****************************************************************************
+* Change Log
+* Date | Change
+*-----------+-----------------------------------------------------------------
+* 21-May-86 | Created
+* 11-Aug-87 | F.H: Added clear_abort(), stop()
+* May-88 | JCD : AMIGA VERSION
+* 11-Jun-88 | RBD: disable printing of GDEBUG messages
+* 12-Oct-88 | JCD : EXCLUSIVE AMIGA VERSION
+* 13-Apr-89 | JCD : New portable version.
+* 5-Apr | JDW : Further changes
+* 2-Mar-92 | GWL : Little changes to satisfy compiler
+* 19-Nov-92 | JDZ : Mach tty io threads
+* 28-Apr-03 | DM : portability changes. true->TRUE, false->FALSE
+*****************************************************************************/
+
+/* Notes on ascii input:
+
+Input is complicated because different systems have varying input models,
+especially with regard to handling ^C. The CMT model handles ^C and ^G as
+special characters, and these do not cause software interrupts. Also, the
+lowest level of the CMT model does not support line editing: Every character
+is acted upon immediately. This has two implications:
+(1) CMT must "read ahead" looking for ^C and ^G characters. This is handled
+by the check_aborted() procedure, which reads characters into the
+type_ahead[] array.
+(2) CMT must do its own line editing. This is handled by the ggets() routine.
+
+A number of functions support ascii input, only some of which are visible
+to the application programmer. Let's start at the top-level and work down;
+each of the following calls the routine below it:
+
+ggets() - gets a string with line editing support. This function is fairly
+machine independent, except for some backspace-and-erase control character
+code sequences.
+
+ggetchar() - gets a raw character. This function calls wait_ascii()
+ and echoes it. Note that it may return ABORT_CHAR or BREAK_CHAR.
+
+wait_ascii() - gets a raw character without echo and without character
+code translation. wait_ascii() either polls get_ascii() or uses some
+kind of system-dependent event waiting. Returns ABORT_CHAR or BREAK_CHAR
+immediately if abort_flag has been set, regardless of whether there is
+new ascii input.
+
+get_ascii() - checks to see if a character is available. (Using
+check_aborted().)
+The only dependency here is on the Amiga, we restart input when buffer goes
+from full to non-full.
+
+check_aborted() - looks for input by calling ascii_input. If found,
+put the input into the type_ahead[] buffer. Returns abort_flag.
+
+ascii_input() - lowest level of input; just gets a character if there is
+one. Does conversion from RETURN (\r) to EOL (\n). The Amiga handles
+this in-line directly in check_aborted().
+
+Here's a quick summary:
+
+ggets = ggetchar + line editing & string building
+ggetchar = wait_ascii + character echo
+wait_ascii = get_ascii + wait for character
+get_ascii = check_aborted + pull char from buffer
+check_aborted = ascii_input + test for ^C,^G + put in buffer
+ascii_input = poll for char + CR->EOL conversion
+
+*/
+
+#include "switches.h"
+
+#include <stdio.h>
+#include <string.h>
+#if HAS_STDLIB_H
+#include <stdlib.h> /* normal case */
+#endif
+
+
+#ifdef MACINTOSH
+# include "StandardFile.h"
+ /* added for ThinkC 7 */
+# ifdef THINK_C
+# include <pascal.h>
+# endif
+#endif
+
+#ifdef AMIGA
+
+# ifdef AZTEC
+# include "functions.h"
+# else /* LATTICE */
+# include "amiga.h"
+# include "stdarg.h"
+# endif
+
+# include "intuition/intuition.h"
+# include "devices/console.h"
+#endif
+
+#include "ctype.h"
+#include "stdio.h"
+#include "cext.h"
+#include "userio.h"
+
+#ifdef MICROSOFT
+#include "signal.h"
+#endif
+
+#ifdef UNIX_MACH
+#include <varargs.h>
+#include <midistruct.h>
+extern char a_in;
+extern int a_in_flag;
+extern int i_am_running;
+#ifdef RTMach
+extern itc_mutex_t a_mutex;
+extern itc_condition_t a_cond, a_in_cond;
+#define A_LOCK() itc_mutex_lock(&a_mutex)
+#define A_UNLOCK() itc_mutex_unlock(&a_mutex)
+#else /* RTMach */
+extern struct mutex a_mutex;
+extern struct condition a_cond, a_in_cond;
+#define A_LOCK() mutex_lock(&a_mutex)
+#define A_UNLOCK() mutex_unlock(&a_mutex)
+#endif /* RTMach */
+#endif
+
+#ifdef DOTS_FOR_ARGS
+#include <stdarg.h>
+#endif
+
+#ifdef UNIX
+#include <sys/param.h>
+#include <sys/resource.h>
+#include "cmtio.h"
+#ifdef _IBMR2
+#define NBBY 8
+#define OPEN_MAX 2000
+#endif
+#include <sys/select.h>
+#endif
+
+#ifdef linux
+#include <sys/time.h> /* for FD_ZERO / FD_SET */
+#endif
+
+extern int debug;
+
+#ifdef NYQUIST
+/* get definitions for stdputstr, etc. */
+#include "xlisp.h"
+#endif
+
+
+/****************************************************************************
+*
+* routines private to this module
+*
+****************************************************************************/
+
+int GetReadFileName();
+int GetWriteFileName();
+
+#ifdef MACINTOSH
+private void PtoC_StrCopy(char *p1, char *p2);
+#endif
+
+#ifdef AMIGA
+ char ConGetChar();
+ ConMayGetChar();
+private void ConRead();
+private void ConPutStr();
+private void ConPutChar();
+UBYTE ascii_signal();
+UBYTE KeybSig();
+#endif
+
+
+/****************************************************************************
+*
+* variables shared with other modules
+*
+****************************************************************************/
+
+public int abort_flag; /* control C or control G equivalent */
+public int redirect_flag; /* check whether the I/O has been redirected--
+ Added by Ning Hu Apr.2001*/
+/* extern void musicterm(); */ /*DMH: from macmidi.c, to allow abort_check*/
+public boolean ascii_input();
+
+/****************************************************************************
+*
+* variables private to this module
+*
+****************************************************************************/
+
+#ifdef AMIGA
+struct IntuitionBase *IntuitionBase;
+private struct IOStdReq *ConOutReq;
+private struct MsgPort *ConOutPort;
+private struct IOStdReq *ConInReq;
+private struct MsgPort *ConInPort;
+private char KeyBuff[16];
+private struct Window *Window;
+private struct NewWindow NewWindow = {
+ 0,11,640,189,
+ 0,1,
+ NULL,
+ SMART_REFRESH | ACTIVATE | WINDOWDRAG | WINDOWDEPTH |
+ WINDOWSIZING,
+ NULL,NULL,
+ (STRPTR) "Carnegie Mellon University MIDI Toolkit for Commodore AMIGA",
+ NULL,NULL,
+ 100,25,640,200,
+ WBENCHSCREEN };
+#endif
+
+#ifdef MACINTOSH
+private OSType io_file_type = 0x3F3F3F3F; /* '????' */
+private OSType io_file_creator = 0x3F3F3F3F; /* '????' */
+#endif
+
+#define type_ahead_max 100
+char type_ahead[100];
+int type_ahead_head = 0;
+int type_ahead_tail = 0;
+int type_ahead_count = 0;
+
+
+#ifdef DOS
+#ifdef BORLAND
+int c_break(void)
+{
+ gprintf(TRANS, " BREAK ");
+ abort_flag = ABORT_LEVEL;
+ return 1; /* non-zero means do not exit program */
+}
+#endif
+#ifdef MICROSOFT
+void c_break(int sig)
+{
+ abort_flag = ABORT_LEVEL;
+ /* The CTRL+C interrupt must be reset to our handler since
+ * by default it is reset to the system handler.
+ */
+ signal(SIGINT, c_break); /* assume this succeeds */
+}
+#endif
+#endif
+
+#ifdef MACINTOSH
+#ifdef NYQUIST
+void FlushOutput (void);
+#endif
+#endif
+
+/* gflush -- flush output produced by gprintf, etc. */
+/**/
+void gflush(void)
+{
+#ifdef MACINTOSH
+#ifdef NYQUIST
+ FlushOutput();
+#else
+ fflush(stdout); /* make sure any prompts or errors have been output */
+ fflush(STDERR);
+#endif /* NYQUIST */
+#endif /* MACINTOSH */
+#ifdef UNIX
+ fflush(stdout); /* make sure any prompts or errors have been output */
+ fflush(STDERR);
+#endif
+}
+
+
+/****************************************************************************
+* io_init
+*
+* I added this init function for the AMIGA version.
+*
+* io_init : opens a window
+* and exits if initialisation can not be done properly.
+* registers cleanup calls to carefully deallocate resources.
+*
+* io_init is not amiga specific : the simplest version
+* of io_init could be a clear screen statement for example, and a
+* printf("Good bye !\n") on exit.
+*
+* for the Mac, it seems that ascii_input doesn't work unless getchar() is
+* called first. I assume this is because getchar() initializes the ability
+* of the window to process type-in, so there is probably a way to set this
+* directly. If you figure it out, let me know. -RBD
+*
+*****************************************************************************/
+
+void
+io_init()
+{
+
+#ifdef AMIGA
+ int error;
+
+ /* Window and console initialisation */
+ IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",1L);
+ if (IntuitionBase == NULL) EXIT(1);
+ cu_register((cu_fn_type) CloseLibrary, IntuitionBase);
+
+ ConOutPort = CreatePort("conoutport", 0L);
+ if (ConOutPort == NULL) EXIT(1);
+ cu_register((cu_fn_type) DeletePort, ConOutPort);
+
+ ConOutReq = CreateStdIO(ConOutPort);
+ if (ConOutReq == NULL) EXIT(1);
+ cu_register((cu_fn_type) DeleteStdIO, ConOutReq);
+
+ ConInPort = CreatePort("coninport", 0L);
+ if (ConInPort == NULL) EXIT(1);
+ cu_register((cu_fn_type) DeletePort, ConInPort);
+
+ ConInReq = CreateStdIO(ConInPort);
+ if (ConInReq == NULL) EXIT(1);
+ cu_register((cu_fn_type) DeleteStdIO, ConInReq);
+
+ Window = OpenWindow(&NewWindow);
+ if (Window == NULL) EXIT(1);
+ cu_register((cu_fn_type) CloseWindow, Window);
+
+ ConOutReq->io_Data = (APTR)Window;
+ ConOutReq->io_Length = sizeof(*Window);
+ error = OpenDevice("console.device", 0L, (struct IORequest *) ConOutReq, 0L);
+ ConInReq->io_Device = ConOutReq->io_Device;
+ ConInReq->io_Unit = ConOutReq->io_Unit;
+ if (error != NULL) EXIT(1);
+ cu_register((cu_fn_type) CloseDevice, ConOutReq);
+
+ ConInReq->io_Command = CMD_READ;
+ ConInReq->io_Data = (APTR)KeyBuff;
+ ConInReq->io_Length = 1;
+ SendIO((struct IORequest *) ConInReq);
+#endif
+
+#ifdef UNIX
+#ifndef BUFFERED_SYNCHRONOUS_INPUT
+ IOsetup(0 /* standard input */);
+ cu_register((cu_fn_type) IOcleanup, NULL);
+#endif
+#endif
+
+#ifdef MACINTOSH
+#ifndef NYQUIST /* don't need this if we're in Nyquist */
+ char s[100];
+ printf("Type <return> to start: ");
+ fgets(s, 100, stdin);
+#endif /* NYQUIST */
+#endif
+
+#ifdef DOS
+#ifdef MICROSOFT
+ if (signal(SIGINT, c_break) == SIG_ERR) {
+ gprintf(ERROR, "Couldn't set Ctrl C handler\n");
+ EXIT(1);
+ }
+#else
+#ifdef BORLAND
+ ctrlbrk(c_break);
+#else
+ ... we are in DOS, but neither MICROSOFT nor BORLAND,
+ please set up a control C handler here...
+#endif
+#endif
+#endif
+}
+
+#ifdef MACINTOSH
+
+/****************************************************************************
+* abort_check
+* Effect:
+* exit nicely if the aborted flag is set
+****************************************************************************/
+
+public void abort_check()
+{
+ if (abort_flag) clean_exit();
+}
+
+
+/****************************************************************************
+* clean_exit
+* Effect:
+* clean up and exit
+****************************************************************************/
+
+public void clean_exit()
+{
+ gprintf(TRANS, "Exiting...\n");
+ EXIT(1);
+}
+
+#ifdef MPW
+/****************************************************************************
+* cleanup_abort_handler
+* Effect:
+* shuts down abort watcher
+****************************************************************************/
+
+public void cleanup_abort_handler()
+{
+ (void) sigset(SIGINT, SIG_DFL); /* deactivate abort watcher */
+}
+
+
+/****************************************************************************
+* init_abort_handler
+* Effect:
+* starts abort watcher
+* aborted flag is set to FALSE
+****************************************************************************/
+
+public void init_abort_handler()
+{
+ abort_flag = FALSE;
+ (void) sigset(SIGINT, abort_watcher); /* activate abort watcher */
+}
+#endif
+
+#endif
+
+
+/****************************************************************************
+* askbool
+* Inputs:
+* char *prompt: string to prompt for user input
+* int deflt: TRUE or FALSE default
+* Returns:
+* boolean: TRUE or FALSE as entered by user
+* Effect:
+* prompts user for yes or no input, returns result
+****************************************************************************/
+
+int askbool(prompt, deflt)
+char *prompt;
+int deflt;
+{
+#define undefined -1
+ char defchar; /* the default answer */
+ char c; /* user input */
+ char in_string[100];
+ int result = -1; /* the result: -1 = undefined, 0 = FALSE, 1 = TRUE */
+ if (deflt) defchar = 'y';
+ else defchar = 'n';
+ while (result == undefined) {
+ gprintf(TRANS, "%s? [%c]: ", prompt, defchar);
+ ggets(in_string);
+ c = in_string[0];
+ if (islower(c)) c = toupper(c);
+ if (c == 'Y') result = TRUE;
+ else if (c == 'N') result = FALSE;
+ else if (c == EOS) result = deflt;
+ else if (abort_flag) result = deflt;
+ /* space before Please to separate from user's type-in: */
+ else gprintf(TRANS, " Please type Y or N.\n");
+ }
+ if (abort_flag == BREAK_LEVEL) {
+ abort_flag = 0;
+ result = deflt;
+ gprintf(TRANS, "\n");
+ }
+ return result;
+}
+
+
+/****************************************************************************
+* fileopen
+* Inputs:
+* char *deflt: the default file name (e.g. from command line)
+* char *extension: default extension
+* char *mode: read ("r") or write ("w")
+* char *prompt: prompt for user
+* Returns:
+* opened file pointer
+* Effect:
+* opens file, prompts for user input if necessary and warns about
+* possible confusion. If deflt is a null string or NULL, the user will
+* be prompted for a name. The routine loops until a file is opened.
+* If the mode is "r", a check is made to see if the file exists
+* with and without the extension. If both exist a warning is given.
+* For mode "w", a check is made to see if the file will be overwritten.
+* The extension is automatically added if the default or user-typed
+* file has no "." At the bottom of the loop body, if no file has
+* been opened, the user is prompted for another file name.
+****************************************************************************/
+
+char fileopen_name[100]; /* name of the opened file */
+
+FILE *fileopen(deflt, extension, mode, prompt)
+ char *deflt;
+ char *extension; /* default extension */
+ char *mode; /* read "r" or write "w" */
+ char *prompt; /* prompt for user */
+{
+ char extname[100]; /* trial name with extension added */
+ FILE *fp = NULL; /* file corresponding to filename */
+ FILE *fpext; /* file corresponding to extname */
+ char *problem = NULL; /* tells user why he has to try again */
+
+ if (!deflt) deflt = ""; /* treat NULL as the empty string */
+ strcpy(fileopen_name, deflt);
+ /* keep trying until a good file is found: */
+ while (fp == NULL) {
+ /* avoid null file names: */
+ while (strlen(fileopen_name) == 0) {
+#ifndef MACINTOSH
+ gprintf(TRANS, "%s : ", prompt);
+ ggets(fileopen_name);
+ if (abort_flag) {
+ if (abort_flag == BREAK_LEVEL) {
+ abort_flag = 0;
+ /* type return since user didn't... */
+ gprintf(TRANS, "\n");
+ }
+ return NULL;
+ }
+#else /* use Macintosh file dialog */
+ if (mode[0] == 'r') {
+ if (!GetReadFileName(fileopen_name)) return NULL;
+ } else if (mode[0] == 'w') {
+ if (!(GetWriteFileName(fileopen_name, prompt))) return NULL;
+ } else {
+ gprintf(ERROR, "(fileopen) internal error: bad mode\n");
+ }
+#endif /* MACINTOSH */
+ }
+ if (mode[0] == 'r') {
+ strcpy(extname, fileopen_name);
+ strcat(extname, ".");
+ strcat(extname, extension);
+ fp = fopen(fileopen_name, mode);
+ fpext = fopen(extname, mode);
+ if (fp != NULL && fpext != NULL) {
+ gprintf(TRANS,
+ "warning: both %s and %s exist. %s will be used.\n",
+ fileopen_name, extname, fileopen_name);
+ fclose(fpext);
+ } else if (fpext != NULL) {
+ fp = fpext;
+ strcpy(fileopen_name, extname); /* remember what was opened */
+ }
+ if (fp == NULL) problem = "Couldn't find %s.\n";
+ } else if (mode[0] == 'w') {
+ boolean added_extension = FALSE;
+
+ /* add the extension if there is no '.' in the file name */
+ if (!strchr(fileopen_name, '.')) {
+ strcat(fileopen_name, ".");
+ strcat(fileopen_name, extension);
+ added_extension = TRUE;
+ }
+ if (TRUE
+#ifdef MACINTOSH
+ /* file open dialog already asked user to confirm unless we're
+ * adding an extension
+ */
+ && added_extension
+#endif
+ ) {
+ fp = fopen(fileopen_name, "r");
+ if (fp != NULL) {
+ char question[100];
+ fclose(fp);
+ strcpy(question, "OK to overwrite ");
+ strcat(question, fileopen_name);
+ if (!askbool(question, FALSE)) {
+ fp = NULL;
+ problem = "\n";
+ goto tryagain;
+ }
+ }
+ }
+ fp = fopen(fileopen_name, mode);
+ if (fp == NULL) problem = "Couldn't create %s.\n";
+ }
+ tryagain:
+ if (fp == NULL) {
+ gprintf(TRANS, problem, fileopen_name);
+ gprintf(TRANS,"Try again.\n");
+ fileopen_name[0] = EOS;
+ }
+ }
+ return fp;
+}
+
+
+#ifdef MACINTOSH
+
+static int GetReadFileName(name)
+char *name;
+{
+ static Point p = {100,100};
+ SFReply loadfile;
+ SFTypeList mytypes;
+
+ mytypes[0] = 0x54455854; /* 'TEXT' */
+ mytypes[1] = 0x4D696469; /* 'Midi' */
+ mytypes[2] = 0x3F3F3F3F; /* '????' */
+/* could put any filter here (i.e. giofilefileter) */
+ SFGetFile(p, "\p", NULL, 3, mytypes, 0L, &loadfile);
+ if (loadfile.good) {
+ SetVol(0L,loadfile.vRefNum);
+ PtoC_StrCopy((char *) &loadfile.fName, name);
+ return(true);
+ } else return(false);
+}
+
+
+static int GetWriteFileName(fn, str)
+char *fn, *str;
+{
+ static Point SFPwhere = { 106, 104 };
+ unsigned char Pstr[100], Pfn[100];
+ SFReply reply;
+
+ strcpy((char *)Pstr, str);
+ CtoPstr((char *)Pstr);
+ strcpy((char *)Pfn, fn);
+ CtoPstr((char *)Pfn);
+
+ SFPutFile(SFPwhere, (ConstStr255Param) Pstr, (ConstStr255Param) Pfn,
+ 0L, &reply);
+ if (reply.good) {
+ SetVol (0L,reply.vRefNum);
+ PtoC_StrCopy((char *) &reply.fName, fn);
+ return(true);
+ }
+ else return(false);
+}
+
+
+void PtoC_StrCopy(p1, p2)
+ register char *p1, *p2;
+/* copies a pascal string from p1 to p2 */
+{
+ register int len;
+
+ len = *p1++;
+ while (--len>=0) *p2++=*p1++;
+ *p2 = '\0';
+}
+
+
+boolean get_file_info(char *filename, OSType *file_type, OSType *file_creator)
+{
+ short rc; /* toolbox return code */
+ FInfo fi; /* toolbox file info */
+ char fn[101]; /* temporary file name */
+
+ strcpy(fn, filename);
+ CtoPstr(fn);
+ if (rc = GetFInfo((byte*)fn, 0, &fi)) {
+ gprintf(ERROR, "rc from GetFInfo=%d\n", rc);
+ gprintf(ERROR, "unable to get file type\n");
+ *file_type = 0x3F3F3F3F; /* '????' */
+ *file_creator = 0x3F3F3F3F; /* '????' */
+ return FALSE;
+ } else /* set file type & creator */ {
+ if (debug) gprintf(TRANS, "File Type: '%.4s' File Creator: '%.4s'\n",
+ &fi.fdType, &fi.fdCreator );
+ *file_type = fi.fdType;
+ *file_creator = fi.fdCreator;
+ }
+ return TRUE;
+}
+
+
+boolean put_file_info(char *filename, OSType file_type, OSType file_creator)
+{
+ short rc; /* toolbox return code */
+ FInfo fi; /* toolbox file info */
+ char fn[101]; /* temporary file name */
+
+ if (debug) gprintf(TRANS,"set file %s to become type '%.4s'\n", filename, &file_type);
+ strcpy(fn, filename);
+ CtoPstr(fn);
+ if (rc = GetFInfo((byte*)fn, 0, &fi)) {
+ gprintf(TRANS, "rc from GetFInfo=%d\n", rc);
+ gprintf(TRANS, "unable to set file type\n");
+ } else /* set file type & creator */ {
+ if (debug) gprintf(TRANS, "File Type: '%.4s' File Creator: '%.4s'\n",
+ &fi.fdType, &fi.fdCreator );
+ fi.fdType = file_type;
+ fi.fdCreator = file_creator;
+ if (rc=SetFInfo((byte*)fn, 0, &fi)) {
+ gprintf(TRANS, "rc from SetFInfo=%d\n", rc);
+ gprintf(TRANS, "unable to set file type\n");
+ } else if (rc=GetFInfo((byte*)fn, 0, &fi)) {
+ gprintf(TRANS, "rc from GetFInfo=%d\n", rc);
+ gprintf(TRANS, "unable to verify file type\n");
+ } else {
+ if (debug) gprintf(TRANS, "File Type: '%.4s' File Creator: '%.4s'\n",
+ &fi.fdType, &fi.fdCreator );
+ }
+ }
+}
+#endif /* MACINTOSH */
+
+
+
+#ifdef AMIGA
+/***************************************************************
+* ascii_signal
+*
+* Input : none
+* Ouput : none
+* Return: the signal that will be raised on ascii input
+* Effect: none
+***************************************************************/
+
+UBYTE ascii_signal()
+{
+ return ConInPort->mp_SigBit;
+}
+#endif
+
+/* check_aborted -- see if any characters are available, check for ctrl C */
+
+int check_aborted()
+{
+ char in_c;
+#ifdef AMIGA
+ if (GetMsg(ConInPort)) {
+ in_c = KeyBuff[0];
+ if (in_c == '\r') in_c = '\n';
+#endif
+#ifndef AMIGA /* DOS or MACINTOSH or UNIX */
+ if (type_ahead_count < type_ahead_max && ascii_input(&in_c)) {
+#endif
+ type_ahead[type_ahead_tail] = in_c;
+ if (in_c == ABORT_CHAR) abort_flag = ABORT_LEVEL;
+ else if (!abort_flag && in_c == BREAK_CHAR)
+ abort_flag = BREAK_LEVEL;
+
+ /* go ahead and insert anything into buffer, including ^C, ^G: */
+ type_ahead_count++;
+ type_ahead_tail++;
+ if (type_ahead_tail == type_ahead_max) type_ahead_tail = 0;
+
+#ifdef AMIGA
+ if (type_ahead_count < type_ahead_max) ConRead();
+#endif
+ }
+ return abort_flag;
+}
+
+
+/****************************************************************************
+* readln
+* Inputs:
+* FILE * fp: File to read from
+* Effect:
+* Reads and discards characters until a newline is seen
+****************************************************************************/
+
+void readln(fp)
+ register FILE *fp;
+{
+ register int c;
+ while (((c = getc(fp)) != '\n') && (c != EOF)) ;
+}
+
+
+/****************************************************************************
+* gprintf
+* Inputs:
+* int * handler: pointer to output handler (say, a window)
+* or one of {TRANS, ERROR, FATAL, GDEBUG} from userio.h
+* char * format: a null-terminated printf style format string
+* int arg0 through arg14: a variable number of arguments for printf
+* Effect:
+* formats and outputs the specified information to an output handler.
+* this is a system-independent approach to output. On
+* a simple machine, it is like printf. on a more complex machine,
+* output is directed to the appropriate window.
+* Implementation
+* Note that to handle the variable argument list, a number of different
+* approaches are implemented. The first part of the implementation selects
+* one of 4 ways to build temp1, a formatted string. The 4 ways arise from
+* use or non-use of vsprintf, and use or non-use of ... in the arg list.
+* After building temp1, non-Amiga systems write to stdout or stderr,
+* whereas AMIGA writes to a special console. Why? Because the Amiga
+* needs a new console so we can set up a signal upon character typein.
+****************************************************************************/
+
+#ifndef gprintf
+#define GPRINTF_MESSAGE_LEN 512
+#ifdef USE_VSPRINTF
+#ifdef DOTS_FOR_ARGS
+
+/* define with ... in arg list and use vsprintf to get temp1 */
+public void gprintf(long where, char *format, ...)
+{
+ char temp1[GPRINTF_MESSAGE_LEN];
+#ifdef AMIGA
+ char temp2[GPRINTF_MESSAGE_LEN];
+#endif
+ va_list ap;
+
+ va_start(ap, format);
+ vsprintf(temp1, format, ap);
+ va_end(ap);
+
+#else /* !DOTS_FOR_ARGS */
+
+/* define with va_alist and use vsprintf to get temp1 */
+public void gprintf(where, format, va_alist)
+long where;
+char *format;
+va_dcl
+{
+ char temp1[GPRINTF_MESSAGE_LEN];
+ va_list pvar;
+/* this is a syntax error - if you don't have to remove this, */
+/* then this whole section of code is unnecessary. */
+ va_start(pvar);
+ vsprintf(temp1, format, pvar);
+ va_end(pvar);
+
+#endif /* DOTS_FOR_ARGS */
+
+#else /* !USE_VSPRINTF */
+#define MAX_GPRINTF_ARGS 10
+typedef struct gp_args_struct {
+ long arg[MAX_GPRINTF_ARGS];
+} gp_args_node;
+
+#ifdef DOTS_FOR_ARGS
+/* use ... but not vsprintf */
+public void gprintf(long where, char *format, ...)
+{
+ char temp1[GPRINTF_MESSAGE_LEN];
+#ifdef AMIGA
+ char temp2[GPRINTF_MESSAGE_LEN];
+#endif
+ va_list ap;
+ gp_args_node args;
+ va_start(ap, format);
+ args = va_arg(ap, gp_args_node);
+ va_end(ap);
+#else /* !DOTS_FOR_ARGS */
+/* don't use ... and don't use vsprintf */
+public void gprintf(where, format, args)
+ long where;
+ char *format;
+ gp_args_node args;
+{
+ char temp1[GPRINTF_MESSAGE_LEN];
+#ifdef AMIGA
+ char temp2[GPRINTF_MESSAGE_LEN];
+#endif /* AMIGA*/
+#endif /* DOTS_FOR_ARGS */
+
+ sprintf(temp1, format, args);
+
+#endif /* USE_VSPRINTF */
+
+/*
+ * Now we've got formatted output in temp1. Write it out.
+ */
+#ifdef NYQUIST
+ switch ((long) where) {
+ case TRANS:
+ stdputstr(temp1);
+ break;
+ case ERROR:
+ errputstr(temp1);
+ break;
+ case FATAL:
+ errputstr("FATAL: ");
+ errputstr(temp1);
+ break;
+ case GDEBUG:
+ errputstr("DEBUG: ");
+ errputstr(temp1);
+ break;
+ default:
+ errputstr("UNKNOWN: ");
+ errputstr(temp1);
+ break;
+ }
+ gflush();
+#else /* not NYQUIST */
+#ifdef AMIGA
+
+ switch((long) where) {
+ case TRANS:
+ strcpy(temp2, temp1);
+ break;
+ case ERROR:
+ strcpy(temp2, temp1);
+ break;
+ case FATAL:
+ strcpy(temp2, "FATAL: ");
+ strcat(temp2, temp1);
+ break;
+ case GDEBUG:
+ strcpy(temp2,"DEBUG: ");
+ strcat(temp2, temp1);
+ break;
+ default:
+ strcpy(temp2, "UNKNOWN: ");
+ strcat(temp2, temp1);
+ break;
+ }
+ ConOutReq->io_Command = CMD_WRITE;
+ ConOutReq->io_Data = (APTR)temp2;
+ ConOutReq->io_Length = -1; /* NULL terminated string */
+ DoIO((struct IORequest *) ConOutReq);
+#else /* not NYQUIST or AMIGA */
+ switch(where) {
+ case TRANS:
+ printf("%s", temp1);
+ break;
+ case ERROR:
+ fprintf(STDERR, "%s", temp1);
+ break;
+ case GDEBUG:
+ fprintf(STDERR, "DEBUG %s", temp1);
+ break;
+ case FATAL:
+ fprintf(STDERR, "FATAL %s", temp1);
+ break;
+ default:
+ fprintf(STDERR, "UNKNOWN %s", temp1);
+ break;
+ }
+#endif /* AMIGA */
+#endif /* NYQUIST */
+}
+
+#endif /* ifndef gprintf */
+
+
+/**************************************************************************
+* gputchar
+* General putchar
+**************************************************************************/
+
+#ifndef gputchar
+
+#ifdef AMIGA
+public int gputchar(c)
+int c;
+{
+ ConPutChar((char)c);
+ return(c);
+}
+#else
+public int gputchar(c)
+int c;
+{
+ putchar((char)c);
+ return(c);
+}
+#endif
+
+#endif /* ifndef gputchar */
+
+/**************************************************************************
+* ggetchar
+* General getchar
+**************************************************************************/
+
+public int ggetchar()
+{
+#ifdef BUFFERED_SYNCHRONOUS_INPUT
+ return getchar();
+#else
+ int key = wait_ascii();
+ if (key != ABORT_CHAR && key != '\b') gputchar((char)key);
+ return(key);
+#endif
+}
+
+
+/**************************************************************************
+* ggets
+* General gets
+**************************************************************************/
+
+
+#ifndef ggets
+
+public char *ggets(str)
+ char *str;
+{
+ char *s = str;
+ int c;
+
+ do {
+ c = ggetchar();
+ if (c == '\b' /* backspace */) {
+ if (s != str) {
+ gputchar('\b');
+ gputchar((int)' ');
+ gputchar('\b');
+ s--;
+ } else {
+#ifdef AMIGA
+ gputchar((int)0x9b);
+ gputchar((int)0x43);
+#else
+ /* gputchar((int)' '); */
+#endif
+ gputchar((int)0x07);
+ }
+ } else *s++ = (char) c;
+ } while (c != (int) '\n' && !abort_flag);
+
+ *(s-1) = EOS;
+ if (abort_flag) *str = EOS;
+ return str;
+}
+
+#endif /* ifndef ggets */
+
+
+/****************************************************************************
+* get_ascii
+* Returns:
+* boolean: TRUE if a character was found
+* int * c: pointer to int into which to store the character, if any
+* Effect:
+* polls (doesn't wait) for an ascii character and says if it got one
+* the character is returned in *c.
+****************************************************************************/
+
+public boolean get_ascii(c)
+ char *c;
+{
+ check_aborted(); /* input buffer check */
+ if (type_ahead_count == 0) return FALSE;
+#ifdef AMIGA
+ /* if the buffer is full, then there is no outstanding read, restart it: */
+ if (type_ahead_count == type_ahead_max) ConRead();
+#endif
+ type_ahead_count--;
+ *c = type_ahead[type_ahead_head++];
+ if (type_ahead_head == type_ahead_max) type_ahead_head = 0;
+ return TRUE;
+}
+
+#ifdef MACINTOSH /** Macintosh direct ascii input**/
+public boolean ascii_input(c)
+char *c;
+{
+ EventRecord theEvent;
+
+ (void) GetNextEvent((keyDownMask | autoKeyMask), &theEvent);
+ if ((theEvent.what == keyDown) || (theEvent.what == autoKey)) {
+ *c = theEvent.message & charCodeMask;
+ if (*c == '\r') *c = '\n';
+ return(true);
+ }
+ else {
+ return(false);
+ }
+}
+#endif
+
+#ifdef WINDOWS
+#include "conio.h"
+#define kbhit _kbhit
+#define getch _getch
+#endif
+
+#ifdef DOS
+public boolean ascii_input(c)
+char *c;
+{
+ if (abort_flag == ABORT_LEVEL) {
+ *c=ABORT_CHAR;
+ return((boolean)TRUE);
+ }
+ if (kbhit()) { /* If the keyboard was hit */
+ *c = getch(); /* Don't echo it */
+// printf("now break");
+ if (*c == '\r') *c = '\n';
+ return((boolean)TRUE);
+ }
+ return((boolean)FALSE); /* Keeps Lattice compiler happy */
+}
+#endif
+
+#ifdef UNIX
+public boolean ascii_input(c)
+char *c;
+{
+#ifdef UNIX_MACH
+ /*
+ * we can't read from stdin directly, because the ascii
+ * input thread is already doing so, so instead we'll
+ * wait for that thread to read a character and then take
+ * it
+ */
+ boolean ret = FALSE;
+
+ A_LOCK();
+ if (a_in_flag) {
+ (*c) = a_in;
+ a_in_flag = 0;
+ ret = TRUE;
+ }
+ A_UNLOCK();
+ if (ret) {
+#ifdef RTMach
+ itc_condition_signal(&a_cond);
+#else /* RTMach */
+ condition_signal(&a_cond);
+#endif /* RTMach */
+ }
+ if ((*c) == '\r')
+ (*c) = '\n';
+ return(ret);
+#else /* UNIX_MACH */
+#ifndef BUFFERED_SYNCHRONOUS_INPUT
+ int input = IOgetchar();
+ if (input != IOnochar) {
+ *c = input;
+ if (*c == '\r') *c = '\n';
+ return TRUE;
+ }
+#endif /* BUFFERED_SYNCHRONOUS_INPUT */
+ return FALSE;
+#endif /* UNIX_MACH */
+}
+#endif
+
+#ifndef AMIGA /*DOS and MAC and UNIX */
+public void unget_ascii(char c)
+{
+ if (type_ahead_head == 0) type_ahead_head = type_ahead_max;
+ type_ahead_head--;
+ type_ahead[type_ahead_head] = c;
+ type_ahead_count++;
+}
+
+
+public boolean check_ascii()
+{
+ char c;
+
+ if(get_ascii(&c)) {
+ unget_ascii(c);
+ return TRUE;
+ }
+ else return FALSE;
+}
+#endif
+
+
+/****************************************************************************
+* wait_ascii
+* Returns:
+* int: character for key pressed
+* Effect:
+* waits for the user to type a key on the terminal keyboard
+* (versus the synthesizer keyboard) and returns the key typed
+****************************************************************************/
+
+#ifdef MACINTOSH
+public int wait_ascii()
+{
+ char key ; /* key typed */
+
+ if (abort_flag == ABORT_LEVEL) return ABORT_CHAR;
+ if (abort_flag == BREAK_LEVEL) return BREAK_CHAR;
+ gflush();
+ while (!get_ascii(&key)) ;
+ return(key);
+}
+#endif
+
+#ifdef DOS
+public int wait_ascii()
+{
+ char key ; /* key typed */
+
+ if (abort_flag == ABORT_LEVEL) return ABORT_CHAR;
+ if (abort_flag == BREAK_LEVEL) return BREAK_CHAR;
+ if (!get_ascii(&key)) {
+ key = _getch(); // block until we get an input
+ }
+ /* GWL - check for abort on previos line */
+ return (int)key;
+}
+#endif
+
+#ifndef MACINTOSH
+#ifndef DOS
+public int wait_ascii()
+{
+#ifdef UNIX /* was defined (UNIX) || defined(ITC) */
+#ifndef UNIX_MACH
+ fd_set readfds;
+#endif /* !UNIX_MACH */
+#endif
+ char c;
+ struct rlimit file_limit;
+
+ if (abort_flag == ABORT_LEVEL) return ABORT_CHAR;
+ if (abort_flag == BREAK_LEVEL) return BREAK_CHAR;
+ while (!get_ascii(&c)) {
+#ifdef AMIGA
+ WaitPort(ConInPort);
+#endif
+#ifdef UNIX
+ fflush(stdout);
+#ifdef UNIX_MACH
+ /*
+ * we can't select, since another thread is reading
+ * from stdin, and we don't want to have an input war
+ * so instead, the ascii input thread will signal
+ * a_in_cond when it gets input, so we just wait
+ * for that to happen
+ */
+ A_LOCK();
+#ifdef RTMach
+ itc_condition_wait(&a_in_cond, &a_mutex);
+#else /* RTMach */
+ condition_wait(&a_in_cond, &a_mutex);
+#endif /* RTMach */
+ A_UNLOCK();
+#else /* UNIX_MACH */
+ FD_ZERO(&readfds);
+ FD_SET(IOinputfd, &readfds);
+ gflush();
+ getrlimit(RLIMIT_NOFILE, &file_limit);
+ select(file_limit.rlim_max+1, &readfds, 0, 0, NULL);
+#endif /* !UNIX_MACH */
+#endif /* ifdef UNIX */
+ }
+ return (int) c;
+}
+#endif
+#endif
+
+#ifdef AMIGA
+/******************************************************************
+ AMIGA 2000.
+ Console IO Functions
+ JCD 25-Apr-88
+*******************************************************************/
+
+UBYTE KeybSig()
+{
+ return ConInPort->mp_SigBit;
+}
+
+private void ConPutChar(c)
+char c;
+{
+ ConOutReq->io_Command = CMD_WRITE;
+ ConOutReq->io_Data = (APTR)&c;
+ ConOutReq->io_Length = 1;
+ DoIO((struct IORequest *) ConOutReq);
+}
+
+private void ConPutStr(str)
+char *str;
+{
+ ConOutReq->io_Command = CMD_WRITE;
+ ConOutReq->io_Data = (APTR)str;
+ ConOutReq->io_Length = -1;
+ DoIO((struct IORequest *) ConOutReq);
+}
+
+private void ConRead()
+{
+ ConInReq->io_Command = CMD_READ;
+ ConInReq->io_Data = (APTR)KeyBuff;
+ ConInReq->io_Length = 1;
+ SendIO((struct IORequest *) ConInReq);
+}
+#endif
diff --git a/cmt/userio.h b/cmt/userio.h
new file mode 100644
index 0000000..7c05e96
--- /dev/null
+++ b/cmt/userio.h
@@ -0,0 +1,96 @@
+/* Copyright 1989 Carnegie Mellon University */
+
+/*****************************************************************************
+* Change Log
+* Date | Change
+*-----------+-----------------------------------------------------------------
+* 5-Apr |JDW : Further changes
+*****************************************************************************/
+
+/* classes of output for gprintf */
+#ifdef MACINTOSH
+#undef false
+#undef true
+#include <MacTypes.h>
+#define TRANS (long) 0
+#define ERROR (long) 1
+#define FATAL (long) 2
+#define GDEBUG (long) 3
+#endif
+
+#ifdef DONT_USE_CMT_IO
+#define TRANS stdout
+#define ERROR stdout
+#define FATAL stdout
+#define GDEBUG stdout
+#endif
+
+#ifndef TRANS /* default */
+#define TRANS 0
+#define ERROR 1
+#define FATAL 2
+#define GDEBUG 3
+#endif
+
+#define CR '\n'
+#define ABORT_CHAR 0x03
+#ifdef NYQUIST
+#define BREAK_CHAR 0x02
+#else
+#define BREAK_CHAR 0x07
+#endif
+
+#define BREAK_LEVEL 1
+#define ABORT_LEVEL 2
+
+#define read_to_eol(ch) if (ch != CR) { char temp[100]; ggets(temp); }
+
+extern char fileopen_name[];
+extern int abort_flag;
+extern int redirect_flag; /* added by Ning Hu, Apr 2001 */
+
+boolean get_ascii(char *c); /* polls for an ascii character */
+#ifdef DOTS_FOR_ARGS
+/* was (defined(ITC_MACH) && defined(__STDC__)) || defined(MACINTOSH) || defined(AZTEC) || (defined(AMIGA) && defined(LATTICE)) || defined(UNIX_ITC) */
+void gprintf(long where, char *format, ...); /* general printf */
+#else
+void gprintf();
+#endif
+
+char *ggets(char *str); /* general gets */
+int wait_ascii(void); /* a waiting version of get_ascii */
+void clean_exit(void); /* exit the program after cleaning up */
+void io_init(void); /* overall initialization */
+void abort_check(void); /* exit if aborted */
+
+int check_aborted(void); /* looks to see if user typed ctrl-C */
+
+int askbool(char *prompt, int deflt);
+FILE *fileopen(char *deflt, char *extension, char *mode, char *prompt);
+void readln(FILE *fp);
+void gflush(void);
+int gputchar(int c);
+int ggetchar();
+char *ggets(char *str);
+boolean ascii_input(char *c);
+void unget_ascii(char c);
+boolean check_ascii(void);
+
+#ifdef MACINTOSH
+boolean get_file_info(char *filename, OSType *file_type, OSType *file_creator);
+boolean put_file_info(char *filename, OSType file_type, OSType file_creator);
+#endif
+
+#ifdef DONT_USE_CMT_IO
+#define ggetchar getchar
+#define ggets gets
+#define gprintf fprintf
+#define gputchar putchar
+#define gprintf fprintf
+#define gputchar putchar
+#endif
+
+#ifdef MICROSOFT
+void c_break(int sig);
+#endif
+