aboutsummaryrefslogtreecommitdiff
path: root/shared/common
diff options
context:
space:
mode:
Diffstat (limited to 'shared/common')
-rw-r--r--shared/common/Makefile.sources6
-rw-r--r--shared/common/binport.c353
-rw-r--r--shared/common/binport.h2
-rw-r--r--shared/common/dict.c57
-rw-r--r--shared/common/dict.h5
-rw-r--r--shared/common/loud.c288
-rw-r--r--shared/common/loud.h29
-rw-r--r--shared/common/mifi.c1712
-rw-r--r--shared/common/mifi.h157
-rw-r--r--shared/common/port.c10
10 files changed, 1622 insertions, 997 deletions
diff --git a/shared/common/Makefile.sources b/shared/common/Makefile.sources
index 66d47e9..18847a5 100644
--- a/shared/common/Makefile.sources
+++ b/shared/common/Makefile.sources
@@ -1,12 +1,14 @@
OTHER_SOURCES = \
-bifi.c \
binport.c \
+clc.c \
dict.c \
+fi.c \
grow.c \
+lex.c \
loud.c \
mifi.c \
port.c \
props.c \
+qtree.c \
rand.c \
-sq.c \
vefl.c
diff --git a/shared/common/binport.c b/shared/common/binport.c
index e2c9d34..cb3d201 100644
--- a/shared/common/binport.c
+++ b/shared/common/binport.c
@@ -13,7 +13,7 @@
#define BINPORT_MAXSTRING 1000
#define BINPORT_SYMGROW 64
-#ifndef BINPORT_STANDALONE
+#ifndef MIXED_STANDALONE
/* load a max binary file into a Pd binbuf */
#include "m_pd.h"
@@ -22,44 +22,60 @@
/* make a max-textual listing from a max binary file */
/* This is a standalone version of a ``max binary to binbuf'' module.
- It uses certain Pd calls and structs, which are duplicated below.
- LATER should be linked to the Pd API library. */
+ It uses certain Pd calls and structs, which are duplicated in the
+ "standalone" module defined in shared/unstable.
+ LATER standalone binport should be linked to the Pd API library. */
+
+#include "unstable/standalone.h"
#define BINPORT_VERBOSE
//#define BINPORT_DEBUG
#endif
+#include "common/lex.h"
#include "binport.h"
static void binport_error(char *fmt, ...)
{
+ char buf[BINPORT_MAXSTRING];
va_list ap;
va_start(ap, fmt);
- fprintf(stderr, "ERROR (binport): ");
- vfprintf(stderr, fmt, ap);
- putc('\n', stderr);
+ vsprintf(buf, fmt, ap);
+#ifdef MIXED_STANDALONE
+ fprintf(stderr, "ERROR (binport): %s\n", buf);
+#else
+ post("ERROR (binport): %s", buf);
+#endif
va_end(ap);
}
static void binport_warning(char *fmt, ...)
{
-#if defined (BINPORT_STANDALONE) || defined(BINPORT_VERBOSE)
+#if defined (MIXED_STANDALONE) || defined(BINPORT_VERBOSE)
+ char buf[BINPORT_MAXSTRING];
va_list ap;
va_start(ap, fmt);
- fprintf(stderr, "warning (binport): ");
- vfprintf(stderr, fmt, ap);
- putc('\n', stderr);
+ vsprintf(buf, fmt, ap);
+#ifdef MIXED_STANDALONE
+ fprintf(stderr, "warning (binport): %s\n", buf);
+#else
+ post("warning (binport): %s", buf);
+#endif
va_end(ap);
#endif
}
static void binport_bug(char *fmt, ...)
{
+ char buf[BINPORT_MAXSTRING];
va_list ap;
va_start(ap, fmt);
- fprintf(stderr, "BUG (binport): ");
- vfprintf(stderr, fmt, ap);
- putc('\n', stderr);
+ vsprintf(buf, fmt, ap);
+#ifdef MIXED_STANDALONE
+ fprintf(stderr, "BUG (binport): %s\n", buf);
+#else
+ bug("(binport) %s", buf);
+#endif
va_end(ap);
}
@@ -74,118 +90,6 @@ static void binpold_failure(char *filename)
filename);
}
-#ifdef BINPORT_STANDALONE
-
-typedef int t_int;
-typedef float t_float;
-
-typedef struct _symbol
-{
- char *s_name;
- void *s_thing;
- struct _symbol *s_next;
-} t_symbol;
-
-typedef union word
-{
- t_float w_float;
- t_symbol *w_symbol;
- int w_index;
-} t_word;
-
-typedef enum
-{
- A_NULL,
- A_FLOAT,
- A_SYMBOL,
- A_POINTER,
- A_SEMI,
- A_COMMA,
- A_DEFFLOAT,
- A_DEFSYM,
- A_DOLLAR,
- A_DOLLSYM,
- A_GIMME,
- A_CANT
-} t_atomtype;
-
-typedef struct _atom
-{
- t_atomtype a_type;
- union word a_w;
-} t_atom;
-
-void *getbytes(size_t nbytes)
-{
- void *ret;
- if (nbytes < 1) nbytes = 1;
- ret = (void *)calloc(nbytes, 1);
- if (!ret)
- binport_error("getbytes() failed -- out of memory");
- return (ret);
-}
-
-void *resizebytes(void *old, size_t oldsize, size_t newsize)
-{
- void *ret;
- if (newsize < 1) newsize = 1;
- if (oldsize < 1) oldsize = 1;
- ret = (void *)realloc((char *)old, newsize);
- if (newsize > oldsize && ret)
- memset(((char *)ret) + oldsize, 0, newsize - oldsize);
- if (!ret)
- binport_error("resizebytes() failed -- out of memory");
- return (ret);
-}
-
-void freebytes(void *fatso, size_t nbytes)
-{
- free(fatso);
-}
-
-#define HASHSIZE 1024
-
-static t_symbol *symhash[HASHSIZE];
-
-t_symbol *dogensym(char *s, t_symbol *oldsym)
-{
- t_symbol **sym1, *sym2;
- unsigned int hash1 = 0, hash2 = 0;
- int length = 0;
- char *s2 = s;
- while (*s2)
- {
- hash1 += *s2;
- hash2 += hash1;
- length++;
- s2++;
- }
- sym1 = symhash + (hash2 & (HASHSIZE-1));
- while (sym2 = *sym1)
- {
- if (!strcmp(sym2->s_name, s)) return(sym2);
- sym1 = &sym2->s_next;
- }
- if (oldsym) sym2 = oldsym;
- else
- {
- sym2 = (t_symbol *)getbytes(sizeof(*sym2));
- sym2->s_name = getbytes(length+1);
- sym2->s_next = 0;
- sym2->s_thing = 0;
- strcpy(sym2->s_name, s);
- }
- *sym1 = sym2;
- return (sym2);
-}
-
-t_symbol *gensym(char *s)
-{
- return(dogensym(s, 0));
-}
-
-#endif /* end of Pd API */
-
enum {
BINPORT_NULLTYPE,
BINPORT_INTTYPE = 1, BINPORT_FLOATTYPE, BINPORT_SYMTYPE,
@@ -499,6 +403,7 @@ typedef struct _binport
int b_symsize;
t_symbol **b_symtable;
t_binpold *b_old;
+ t_lex *b_lex;
} t_binport;
static void binport_setint(t_atom *ap, int i)
@@ -566,134 +471,14 @@ static int binport_setbysymtable(t_binport *bp, t_atom *ap, int id)
return (s != 0);
}
-/* single pass of binbuf_text(), int-preserving version */
-static int maxtext_nextatom(FILE *fp, t_atom *ap)
-{
- char buf[BINPORT_MAXSTRING + 1], *bufp, *ebuf = buf + BINPORT_MAXSTRING;
- int ready;
- unsigned char ch;
- ap->a_type = A_NULL;
- while ((ready = binport_readbyte(fp, &ch)) &&
- (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'));
- if (!ready)
- return (0);
- if (ch == ';')
- ap->a_type = A_SEMI;
- else if (ch == ',')
- ap->a_type = A_COMMA;
- else
- {
- int floatstate = 0, slash = 0, lastslash = 0, firstslash = (ch == '\\');
- bufp = buf;
- do
- {
- *bufp = ch;
- lastslash = slash;
- slash = (ch == '\\');
-
- if (floatstate >= 0)
- {
- int digit = (ch >= '0' && ch <= '9'),
- dot = (ch == '.'), minus = (ch == '-'),
- plusminus = (minus || (ch == '+')),
- expon = (ch == 'e' || ch == 'E');
- if (floatstate == 0) /* beginning */
- {
- if (minus) floatstate = 1;
- else if (digit) floatstate = 2;
- else if (dot) floatstate = 3;
- else floatstate = -1;
- }
- else if (floatstate == 1) /* got minus */
- {
- if (digit) floatstate = 2;
- else if (dot) floatstate = 3;
- else floatstate = -1;
- }
- else if (floatstate == 2) /* got digits */
- {
- if (dot) floatstate = 4;
- else if (expon) floatstate = 6;
- else if (!digit) floatstate = -1;
- }
- else if (floatstate == 3) /* got '.' without digits */
- {
- if (digit) floatstate = 5;
- else floatstate = -1;
- }
- else if (floatstate == 4) /* got '.' after digits */
- {
- if (digit) floatstate = 5;
- else if (expon) floatstate = 6;
- else floatstate = -1;
- }
- else if (floatstate == 5) /* got digits after . */
- {
- if (expon) floatstate = 6;
- else if (!digit) floatstate = -1;
- }
- else if (floatstate == 6) /* got 'e' */
- {
- if (plusminus) floatstate = 7;
- else if (digit) floatstate = 8;
- else floatstate = -1;
- }
- else if (floatstate == 7) /* got plus or minus */
- {
- if (digit) floatstate = 8;
- else floatstate = -1;
- }
- else if (floatstate == 8) /* got digits */
- {
- if (!digit) floatstate = -1;
- }
- }
- if (!slash) bufp++;
- }
- while ((ready = binport_readbyte(fp, &ch)) && bufp != ebuf
- && (slash || (ch != ' ' && ch != '\n' && ch != '\r'
- && ch != '\t' && ch != ',' && ch != ';')));
- if (ready && (ch == ',' || ch == ';'))
- ungetc(ch, fp);
- *bufp = 0;
-#if 0
- fprintf(stderr, "buf %s\n", buf);
-#endif
- if (*buf == '$' && buf[1] >= '0' && buf[1] <= '9' && !firstslash)
- {
- for (bufp = buf+2; *bufp; bufp++)
- {
- if (*bufp < '0' || *bufp > '9')
- {
- ap->a_type = A_DOLLSYM;
- ap->a_w.w_symbol = gensym(buf+1);
- break;
- }
- }
- if (ap->a_type == A_NULL)
- {
- ap->a_type = A_DOLLAR;
- ap->a_w.w_index = atoi(buf+1);
- }
- }
- else if (floatstate == 2)
- binport_setint(ap, atoi(buf));
- else if (floatstate == 4 || floatstate == 5 || floatstate == 8)
- binport_setfloat(ap, (float)atof(buf));
- else
- binport_setsymbol(ap, gensym(buf));
- }
- return (1);
-}
-
static int binport_nextatom(t_binport *bp, t_atom *ap)
{
unsigned char opcode;
int opval;
char buf[64];
- if (bp->b_ftype == BINPORT_MAXTEXT)
- return (maxtext_nextatom(bp->b_fp, ap));
+ if (bp->b_ftype == BINPORT_MAXTEXT && bp->b_lex)
+ return (lex_nextatom(bp->b_lex, ap));
else if (bp->b_ftype == BINPORT_MAXOLD && bp->b_old)
return (binpold_nextatom(bp->b_old, ap));
@@ -823,6 +608,11 @@ static void binport_free(t_binport *bp)
bp->b_old->o_fp = 0;
binpold_free(bp->b_old);
}
+ if (bp->b_lex)
+ {
+ bp->b_lex->l_fp = 0;
+ lex_free(bp->b_lex);
+ }
freebytes(bp, sizeof(*bp));
}
@@ -851,6 +641,7 @@ static t_binport *binport_new(FILE *fp, int *ftypep)
bp->b_symtable = 0;
}
bp->b_old = 0;
+ bp->b_lex = 0;
}
else if (*ftypep != BINPORT_PDFILE)
binport_warning("unknown header: %02x%02x%02x%02x",
@@ -866,52 +657,6 @@ static t_binport *binport_new(FILE *fp, int *ftypep)
return (bp);
}
-static void binport_atomstring(t_atom *ap, char *buf, int bufsize)
-{
- char *sp, *bp, *ep;
- switch(ap->a_type)
- {
- case A_SEMI:
- strcpy(buf, ";"); break;
- case A_COMMA:
- strcpy(buf, ","); break;
- case A_INT:
- sprintf(buf, "%d", ap->a_w.w_index); break;
- case A_FLOAT:
- sprintf(buf, "%#f", ap->a_w.w_float);
- ep = buf + strlen(buf) - 1;
- while (ep > buf && *ep == '0') *ep-- = 0;
- break;
- case A_SYMBOL:
- sp = ap->a_w.w_symbol->s_name;
- bp = buf;
- ep = buf + (bufsize-5);
- while (bp < ep && *sp)
- {
- if (*sp == ';' || *sp == ',' || *sp == '\\' ||
- (*sp == '$' && bp == buf && sp[1] >= '0' && sp[1] <= '9'))
- *bp++ = '\\';
- if ((unsigned char)*sp < 127)
- *bp++ = *sp++;
- else
- /* FIXME this is temporary -- codepage horror */
- sprintf(bp, "\\%.3o", (unsigned char)*sp++), bp += 4;
- }
- if (*sp) *bp++ = '*';
- *bp = 0;
- break;
- case A_DOLLAR:
- sprintf(buf, "$%d", ap->a_w.w_index);
- break;
- case A_DOLLSYM:
- sprintf(buf, "$%s", ap->a_w.w_symbol->s_name);
- break;
- default:
- binport_bug("bad atom type");
- strcpy(buf, "???");
- }
-}
-
static void binport_print(t_binport *bp, FILE *fp)
{
char buf[BINPORT_MAXSTRING];
@@ -929,13 +674,13 @@ static void binport_print(t_binport *bp, FILE *fp)
else if (at.a_type != A_NULL)
{
if (cnt++) fputc(' ', fp);
- binport_atomstring(&at, buf, BINPORT_MAXSTRING);
+ lex_atomstring(&at, buf, BINPORT_MAXSTRING, A_INT);
fputs(buf, fp);
}
}
}
-#ifndef BINPORT_STANDALONE
+#ifndef MIXED_STANDALONE
static int binport_tobinbuf(t_binport *bp, t_binbuf *bb)
{
@@ -971,12 +716,16 @@ int binport_read(t_binbuf *bb, char *filename, char *dirname)
else if (ftype == BINPORT_MAXTEXT)
{
t_atom at;
- while (binport_nextatom(bp, &at))
- if (at.a_type == A_SEMI)
- break;
- binbuf_addv(bb, "ss;", gensym("max"), gensym("v2"));
- result = (binport_tobinbuf(bp, bb)
- ? BINPORT_OK : BINPORT_CORRUPT);
+ if (bp->b_lex = lex_new(fp, A_INT))
+ {
+ while (binport_nextatom(bp, &at))
+ if (at.a_type == A_SEMI)
+ break;
+ binbuf_addv(bb, "ss;", gensym("max"), gensym("v2"));
+ result = (binport_tobinbuf(bp, bb)
+ ? BINPORT_OK : BINPORT_CORRUPT);
+ }
+ else result = BINPORT_FAILED;
}
else if (ftype == BINPORT_MAXOLD)
{
@@ -1036,7 +785,7 @@ void binport_write(t_binbuf *bb, char *filename, char *dirname)
else if (ap->a_type != A_NULL)
{
if (cnt++) fputc(' ', fp);
- binport_atomstring(ap, buf, BINPORT_MAXSTRING);
+ lex_atomstring(ap, buf, BINPORT_MAXSTRING, A_INT);
fputs(buf, fp);
}
ap++;
diff --git a/shared/common/binport.h b/shared/common/binport.h
index 93120fa..f29d24d 100644
--- a/shared/common/binport.h
+++ b/shared/common/binport.h
@@ -8,7 +8,7 @@
enum { BINPORT_OK, BINPORT_MAXTEXT, BINPORT_MAXOLD, BINPORT_PDFILE,
BINPORT_INVALID, BINPORT_CORRUPT, BINPORT_FAILED };
-#ifndef BINPORT_STANDALONE
+#ifndef MIXED_STANDALONE
int binport_read(t_binbuf *bb, char *filename, char *dirname);
void binport_write(t_binbuf *bb, char *filename, char *dirname);
#endif
diff --git a/shared/common/dict.c b/shared/common/dict.c
index 33ee365..0871f81 100644
--- a/shared/common/dict.c
+++ b/shared/common/dict.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 1997-2003 Miller Puckette, krzYszcz, and others.
+/* Copyright (c) 1997-2004 Miller Puckette, krzYszcz, and others.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
@@ -216,33 +216,52 @@ void dict_unbind(t_dict *x, t_pd *obj, t_symbol *s)
else pd_error(obj, "%s: couldn't unbind", s->s_name);
}
-/* adapted pd_findbyclass() from m_pd.c */
-t_pd *dict_value(t_dict *x, t_symbol *s)
+t_pd *dict_firstvalue(t_dict *dict, t_symbol *s, void **nextp)
{
- if (!s->s_thing) return (0);
- if (*s->s_thing == x->d_bindlist_class)
+ if (s->s_thing)
{
- t_pd *x = 0;
- t_dict_bindelem *e;
- int warned = 0;
- for (e = ((t_dict_bindlist *)s->s_thing)->b_list; e; e = e->e_next)
- {
- if (x && !warned)
- {
- post("warning: %s: multiply defined", s->s_name);
- warned = 1;
- }
- x = e->e_who;
- }
- return (x);
+ if (*s->s_thing == dict->d_bindlist_class)
+ {
+ t_dict_bindelem *e = ((t_dict_bindlist *)s->s_thing)->b_list;
+ if (e)
+ {
+ if (nextp)
+ *nextp = e->e_next;
+ return (e->e_who);
+ }
+ else return (0);
+ }
+ else
+ {
+ if (nextp)
+ *nextp = 0;
+ return (s->s_thing);
+ }
+ }
+ else return (0);
+}
+
+t_pd *dict_nextvalue(t_dict *dict, t_symbol *s, void **nextp)
+{
+ if (s->s_thing)
+ {
+ if (*s->s_thing == dict->d_bindlist_class && *nextp)
+ {
+ t_dict_bindelem *e = (t_dict_bindelem *)*nextp;
+ *nextp = e->e_next;
+ return (e->e_who);
+ }
}
- return (s->s_thing);
+ else bug("dict_nextvalue");
+ return (0);
}
+#if 0
t_pd *dict_xvalue(t_dict *x, t_symbol *s)
{
return (s && s != &s_ ? dict_value(x, dict_key(x, s->s_name)) : 0);
}
+#endif
int dict_forall(t_dict *x, t_symbol *s, t_dict_hook hook, void *hookarg)
{
diff --git a/shared/common/dict.h b/shared/common/dict.h
index 807bf9b..4ab48c8 100644
--- a/shared/common/dict.h
+++ b/shared/common/dict.h
@@ -20,8 +20,11 @@ t_symbol *dict_dokey(t_dict *x, char *s, t_symbol *oldsym);
t_symbol *dict_key(t_dict *x, char *s);
void dict_bind(t_dict *x, t_pd *obj, t_symbol *s);
void dict_unbind(t_dict *x, t_pd *obj, t_symbol *s);
-t_pd *dict_value(t_dict *x, t_symbol *s);
+t_pd *dict_firstvalue(t_dict *dict, t_symbol *s, void **nextp);
+t_pd *dict_nextvalue(t_dict *dict, t_symbol *s, void **nextp);
+#if 0
t_pd *dict_xvalue(t_dict *x, t_symbol *s);
+#endif
int dict_forall(t_dict *x, t_symbol *s, t_dict_hook hook, void *hookarg);
#endif
diff --git a/shared/common/loud.c b/shared/common/loud.c
index d176eb0..4f64110 100644
--- a/shared/common/loud.c
+++ b/shared/common/loud.c
@@ -9,23 +9,92 @@
#include "m_pd.h"
#include "common/loud.h"
-#define LOUD_ERROR_DEFAULT "error (miXed): "
+/* The 'shared_' calls do not really belong here,
+ LATER find them a permanent home. */
-/* LATER move it somewhere else */
-t_symbol *loud_floatsym(void)
+/* FIXME compatibility mode should be a standard Pd feature */
+static t_symbol *shared_compatibility = 0;
+static t_class *sharedcompatibility_class = 0;
+static t_pd *sharedcompatibility_target = 0;
+static t_symbol *sharedps_hashcompatibility = 0;
+static t_symbol *sharedps_max = 0;
+
+static void sharedcompatibility_bang(t_pd *x)
{
- static t_symbol *s = 0;
- return (s ? s : (s = gensym("noninteger float")));
+ if (sharedps_hashcompatibility)
+ {
+ if (shared_compatibility && sharedps_hashcompatibility->s_thing)
+ pd_symbol(sharedps_hashcompatibility->s_thing,
+ shared_compatibility);
+ }
+ else bug("sharedcompatibility_bang");
}
-/* LATER move it somewhere else */
-char *loud_symbolname(t_symbol *s, char *nullname)
+static void sharedcompatibility_symbol(t_pd *x, t_symbol *s)
{
- return (s && s != &s_ ? s->s_name : nullname);
+ shared_compatibility = s;
+}
+
+static void sharedcompatibility_setup(t_symbol *s)
+{
+ if (sharedcompatibility_class || sharedcompatibility_target)
+ bug("sharedcompatibility_setup");
+ sharedps_hashcompatibility = gensym("#compatibility");
+ sharedps_max = gensym("max");
+ sharedcompatibility_class = class_new(sharedps_hashcompatibility,
+ 0, 0, sizeof(t_pd),
+ CLASS_PD | CLASS_NOINLET, 0);
+ class_addbang(sharedcompatibility_class, sharedcompatibility_bang);
+ class_addsymbol(sharedcompatibility_class, sharedcompatibility_symbol);
+ sharedcompatibility_target = pd_new(sharedcompatibility_class);
+ pd_bind(sharedcompatibility_target, sharedps_hashcompatibility);
+ if (s)
+ pd_symbol(sharedps_hashcompatibility->s_thing, s);
+ else
+ pd_bang(sharedps_hashcompatibility->s_thing);
}
-/* LATER move it somewhere else */
-int loud_matchignorecase(char *test, char *pattern)
+void shared_usecompatibility(void)
+{
+ if (!sharedcompatibility_class)
+ sharedcompatibility_setup(0);
+}
+
+void shared_setcompatibility(t_symbol *s)
+{
+ post("setting compatibility mode to '%s'", (s ? s->s_name : "none"));
+ if (sharedcompatibility_class)
+ {
+ if (sharedps_hashcompatibility->s_thing)
+ pd_symbol(sharedps_hashcompatibility->s_thing, s);
+ else
+ bug("shared_setcompatibility");
+ }
+ else sharedcompatibility_setup(s);
+}
+
+t_symbol *shared_getcompatibility(void)
+{
+ if (!sharedcompatibility_class)
+ sharedcompatibility_setup(0);
+ return (shared_compatibility);
+}
+
+void shared_setmaxcompatibility(void)
+{
+ if (!sharedcompatibility_class)
+ sharedcompatibility_setup(0);
+ shared_setcompatibility(sharedps_max);
+}
+
+int shared_getmaxcompatibility(void)
+{
+ if (!sharedcompatibility_class)
+ sharedcompatibility_setup(0);
+ return (shared_compatibility == sharedps_max);
+}
+
+int shared_matchignorecase(char *test, char *pattern)
{
char ct, cp;
for (ct = *test, cp = *pattern; ct && cp; ct = *++test, cp = *++pattern)
@@ -37,7 +106,20 @@ int loud_matchignorecase(char *test, char *pattern)
return (ct == cp);
}
-/* LATER move it somewhere else */
+struct _loudcontext
+{
+ t_pd *lc_caller; /* an object reporting trouble */
+ char *lc_callername;
+ int lc_cnsize;
+ /* during object creation, use the following: */
+ t_symbol *lc_selector; /* creation message selector (class name) */
+ int lc_ac; /* creation message arguments */
+ t_atom *lc_av; /* void out of creation context */
+ int lc_andindent;
+};
+
+#define LOUD_ERROR_DEFAULT "error (miXed):"
+
char *loud_ordinal(int n)
{
static char buf[16]; /* assuming 10-digit INT_MAX */
@@ -60,32 +142,27 @@ char *loud_ordinal(int n)
void loud_error(t_pd *x, char *fmt, ...)
{
+ char buf[MAXPDSTRING];
va_list ap;
va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
if (x)
{
- char buf[MAXPDSTRING];
- fprintf(stderr, "%s's ", class_getname(*x));
- vsprintf(buf, fmt, ap);
+ startpost("%s's ", class_getname(*x));
pd_error(x, buf);
}
- else
- {
- fputs(LOUD_ERROR_DEFAULT, stderr);
- vfprintf(stderr, fmt, ap);
- putc('\n', stderr);
- }
+ else post("%s %s", LOUD_ERROR_DEFAULT, buf);
va_end(ap);
}
void loud_errand(t_pd *x, char *fmt, ...)
{
+ char buf[MAXPDSTRING];
va_list ap;
va_start(ap, fmt);
- fprintf(stderr, "%*s", (int)(x ? strlen(class_getname(*x)) + 10
- : strlen(LOUD_ERROR_DEFAULT)), "");
- vfprintf(stderr, fmt, ap);
- putc('\n', stderr);
+ vsprintf(buf, fmt, ap);
+ post("%*s%s", (int)(x ? strlen(class_getname(*x)) + 10
+ : strlen(LOUD_ERROR_DEFAULT) + 1), "", buf);
va_end(ap);
}
@@ -113,11 +190,14 @@ int loud_checkint(t_pd *x, t_float f, int *valuep, t_symbol *mess)
return (1);
else
{
+ static t_symbol *floatsym = 0;
+ if (!floatsym)
+ floatsym = gensym("noninteger float");
if (mess == &s_float)
- loud_nomethod(x, loud_floatsym());
+ loud_nomethod(x, floatsym);
else if (mess)
loud_error(x, "\"%s\" argument invalid for message \"%s\"",
- loud_floatsym()->s_name, mess->s_name);
+ floatsym->s_name, mess->s_name);
return (0);
}
}
@@ -129,13 +209,13 @@ void loud_classarg(t_class *c)
void loud_warning(t_pd *x, char *who, char *fmt, ...)
{
+ char buf[MAXPDSTRING];
va_list ap;
va_start(ap, fmt);
- fprintf(stderr, "warning (%s): ",
- (x ? class_getname(*x) : (who ? who : "miXed")));
- vfprintf(stderr, fmt, ap);
+ vsprintf(buf, fmt, ap);
+ post("warning (%s): %s",
+ (x ? class_getname(*x) : (who ? who : "miXed")), buf);
va_end(ap);
- putc('\n', stderr);
}
void loud_notimplemented(t_pd *x, char *name)
@@ -148,13 +228,16 @@ void loud_notimplemented(t_pd *x, char *name)
void loud_incompatible(t_class *c, char *fmt, ...)
{
- va_list ap;
- va_start(ap, fmt);
- fprintf(stderr, "'%s' class incompatibility warning:\n\t",
- class_getname(c));
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- putc('\n', stderr);
+ if (shared_getmaxcompatibility())
+ {
+ char buf[MAXPDSTRING];
+ va_list ap;
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ post("'%s' class incompatibility warning:\n\t%s",
+ class_getname(c), buf);
+ va_end(ap);
+ }
}
void loud_incompatible_max(t_class *c, int maxmax, char *what)
@@ -222,3 +305,134 @@ int loud_floatarg(t_class *c, int which, int ac, t_atom *av,
}
return (result);
}
+
+void loudx_error(t_loudcontext *lc, char *fmt, ...)
+{
+ char buf[MAXPDSTRING];
+ va_list ap;
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ if (lc->lc_caller)
+ {
+ startpost("%s's ", (lc->lc_callername ?
+ lc->lc_callername : class_getname(*lc->lc_caller)));
+ pd_error(lc->lc_caller, buf);
+ }
+ else
+ {
+ if (lc->lc_callername)
+ post("error (%s): %s", lc->lc_callername, buf);
+ else if (lc->lc_selector)
+ post("error (%s): %s", lc->lc_selector->s_name, buf);
+ else
+ post("%s %s", LOUD_ERROR_DEFAULT, buf);
+ }
+ va_end(ap);
+}
+
+void loudx_errand(t_loudcontext *lc, char *fmt, ...)
+{
+ char buf[MAXPDSTRING];
+ va_list ap;
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ post("%*s%s", lc->lc_andindent, "", buf);
+ va_end(ap);
+}
+
+void loudx_nomethod(t_loudcontext *lc, t_symbol *s)
+{
+ loudx_error(lc, "doesn't understand \"%s\"", s->s_name);
+}
+
+void loudx_messarg(t_loudcontext *lc, t_symbol *s)
+{
+ loudx_error(lc, "bad arguments for message \"%s\"", s->s_name);
+}
+
+void loudx_warning(t_loudcontext *lc, char *fmt, ...)
+{
+ char buf[MAXPDSTRING];
+ va_list ap;
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ if (lc->lc_callername)
+ post("warning (%s): %s", lc->lc_callername, buf);
+ else if (lc->lc_selector)
+ post("warning (%s): %s", lc->lc_selector->s_name, buf);
+ else
+ post("warning (miXed): %s", buf);
+ va_end(ap);
+}
+
+void loudx_setcontext(t_loudcontext *lc, t_pd *caller, char *callername,
+ t_symbol *s, int ac, t_atom *av)
+{
+ if (lc->lc_callername)
+ freebytes(lc->lc_callername, lc->lc_cnsize);
+ lc->lc_caller = caller;
+ if (callername)
+ {
+ lc->lc_cnsize = strlen(callername) + 1;
+ lc->lc_callername = getbytes(lc->lc_cnsize);
+ strcpy(lc->lc_callername, callername);
+ }
+ else
+ {
+ lc->lc_callername = 0;
+ lc->lc_cnsize = 0;
+ }
+ lc->lc_selector = s;
+ lc->lc_ac = ac;
+ lc->lc_av = av;
+ if (callername)
+ lc->lc_andindent = lc->lc_cnsize + 9;
+ else if (caller)
+ lc->lc_andindent = strlen(class_getname(*caller)) + 10;
+ else if (s)
+ lc->lc_andindent = strlen(s->s_name) + 10;
+ else
+ lc->lc_andindent = strlen(LOUD_ERROR_DEFAULT) + 1;
+}
+
+/* must call before going out of creation context */
+void loudx_setcaller(t_loudcontext *lc, t_pd *caller, char *callerfmt, ...)
+{
+ va_list ap;
+ va_start(ap, callerfmt);
+ if (callerfmt)
+ {
+ char buf[MAXPDSTRING];
+ vsprintf(buf, callerfmt, ap);
+ loudx_setcontext(lc, caller, buf, lc->lc_selector, 0, 0);
+ }
+ else loudx_setcontext(lc, caller, 0, lc->lc_selector, 0, 0);
+ va_end(ap);
+}
+
+t_symbol *loudx_getselector(t_loudcontext *lc)
+{
+ return (lc->lc_selector);
+}
+
+t_atom *loudx_getarguments(t_loudcontext *lc, int *acp)
+{
+ *acp = lc->lc_ac;
+ return (lc->lc_av);
+}
+
+void loudx_freecontext(t_loudcontext *lc)
+{
+ if (lc->lc_callername)
+ freebytes(lc->lc_callername, lc->lc_cnsize);
+ freebytes(lc, sizeof(*lc));
+}
+
+t_loudcontext *loudx_newcontext(t_pd *caller, char *callername,
+ t_symbol *s, int ac, t_atom *av)
+{
+ t_loudcontext *lc = getbytes(sizeof(*lc));
+ lc->lc_callername = 0;
+ loudx_setcontext(lc, caller, callername, s, ac, av);
+ return (lc);
+}
diff --git a/shared/common/loud.h b/shared/common/loud.h
index 6073a85..3fdcefd 100644
--- a/shared/common/loud.h
+++ b/shared/common/loud.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002-2003 krzYszcz and others.
+/* Copyright (c) 2002-2004 krzYszcz and others.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
@@ -10,9 +10,16 @@
enum { LOUD_ARGOK, LOUD_ARGUNDER, LOUD_ARGOVER, LOUD_ARGTYPE, LOUD_ARGMISSING };
-t_symbol *loud_floatsym(void);
-char *loud_symbolname(t_symbol *s, char *nullname);
-int loud_matchignorecase(char *test, char *pattern);
+EXTERN_STRUCT _loudcontext;
+#define t_loudcontext struct _loudcontext
+
+void shared_usecompatibility(void);
+void shared_setcompatibility(t_symbol *s);
+t_symbol *shared_getcompatibility(void);
+void shared_setmaxcompatibility(void);
+int shared_getmaxcompatibility(void);
+int shared_matchignorecase(char *test, char *pattern);
+
char *loud_ordinal(int n);
void loud_error(t_pd *x, char *fmt, ...);
void loud_errand(t_pd *x, char *fmt, ...);
@@ -29,4 +36,18 @@ int loud_floatarg(t_class *c, int which, int ac, t_atom *av,
t_float *vp, t_float minval, t_float maxval,
int underaction, int overaction, char *what);
+void loudx_error(t_loudcontext *lc, char *fmt, ...);
+void loudx_errand(t_loudcontext *lc, char *fmt, ...);
+void loudx_nomethod(t_loudcontext *lc, t_symbol *s);
+void loudx_messarg(t_loudcontext *lc, t_symbol *s);
+void loudx_warning(t_loudcontext *lc, char *fmt, ...);
+void loudx_setcontext(t_loudcontext *lc, t_pd *caller, char *callername,
+ t_symbol *s, int ac, t_atom *av);
+void loudx_setcaller(t_loudcontext *lc, t_pd *caller, char *callerfmt, ...);
+t_symbol *loudx_getselector(t_loudcontext *lc);
+t_atom *loudx_getarguments(t_loudcontext *lc, int *acp);
+void loudx_freecontext(t_loudcontext *lc);
+t_loudcontext *loudx_newcontext(t_pd *caller, char *callername,
+ t_symbol *s, int ac, t_atom *av);
+
#endif
diff --git a/shared/common/mifi.c b/shared/common/mifi.c
index b2fea10..1b9d367 100644
--- a/shared/common/mifi.c
+++ b/shared/common/mifi.c
@@ -1,9 +1,7 @@
-/* Copyright (c) 2001-2003 krzYszcz and others.
+/* Copyright (c) 2004 krzYszcz and others.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
-/* reading/writing midifiles, a prototype version */
-
#ifdef NT
#include <io.h>
#else
@@ -11,220 +9,321 @@
#endif
#include <stdlib.h>
#include <stdio.h>
+#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include "m_pd.h"
-#include "shared.h"
-#include "common/sq.h"
-#include "common/bifi.h"
-#include "common/mifi.h"
+#include "mifi.h"
+
+#ifdef __linux__
+#include <sys/types.h>
+#ifndef uint32
+typedef u_int32_t uint32;
+#endif
+#ifndef uint16
+typedef u_int16_t uint16;
+#endif
+#ifndef uchar
+typedef u_int8_t uchar;
+#endif
+#elif defined(NT)
+#ifndef uint32
+typedef unsigned long uint32;
+#endif
+#ifndef uint16
+typedef unsigned short uint16;
+#endif
+#ifndef uchar
+typedef unsigned char uchar;
+#endif
+#elif defined(IRIX)
+#ifndef uint32
+typedef unsigned long uint32;
+#endif
+#ifndef uint16
+typedef unsigned short uint16;
+#endif
+#ifndef uchar
+typedef unsigned char uchar;
+#endif
+#elif defined(__FreeBSD__)
+#include <sys/types.h>
+#ifndef uint32
+typedef u_int32_t uint32;
+#endif
+#ifndef uint16
+typedef u_int16_t uint16;
+#endif
+#ifndef uchar
+typedef u_int8_t uchar;
+#endif
+#else /* MACOSX */
+#ifndef uint32
+typedef unsigned int uint32;
+#endif
+#ifndef uint16
+typedef unsigned short uint16;
+#endif
+#ifndef uchar
+typedef unsigned char uchar;
+#endif
+#endif
-#define MIFI_VERBOSE
#define MIFI_DEBUG
+#define MIFI_VERBOSE
+
+#define MIFI_SHORTESTEVENT 2 /* singlebyte delta and one databyte */
+#define MIFI_TICKEPSILON ((double).0001)
+
+#define MIFIHARD_HEADERSIZE 14 /* in case t_mifiheader is padded to 16 */
+#define MIFIHARD_HEADERDATASIZE 6
+#define MIFIHARD_TRACKHEADERSIZE 8
-#define MIFI_SHORTEST_EVENT 2 /* singlebyte delta and one databyte */
-#define MIFI_EVENT_NALLOC 32 /* LATER do some research (average max?) */
-#define MIFI_HEADER_SIZE 14 /* in case t_mifi_header is padded to 16 */
-#define MIFI_HEADERDATA_SIZE 6
-#define MIFI_TRACKHEADER_SIZE 8
+/* midi file standard defaults */
+#define MIFIHARD_DEFBEATTICKS 192
+#define MIFIHARD_DEFTEMPO 500000 /* 120 bpm in microseconds per beat */
-/* header structures for midifile and track */
+/* user-space defaults */
+#define MIFIUSER_DEFWHOLETICKS ((double)241920) /* whole note, 256*27*5*7 */
+#define MIFIUSER_DEFTEMPO ((double)120960) /* 120 bpm in ticks/sec */
-typedef struct _mifi_header
+#define MIFIEVENT_NALLOC 256 /* LATER do some research (average max?) */
+#define MIFIEVENT_INISIZE 2 /* always be able to handle channel events */
+
+typedef struct _mifievent
+{
+ uint32 e_delay;
+ uchar e_status;
+ uchar e_channel;
+ uchar e_meta; /* meta-event type */
+ uint32 e_length;
+ size_t e_datasize;
+ uchar *e_data;
+ uchar e_dataini[MIFIEVENT_INISIZE];
+} t_mifievent;
+
+/* midi file header */
+typedef struct _mifiheader
{
char h_type[4];
uint32 h_length;
uint16 h_format;
uint16 h_ntracks;
uint16 h_division;
-} t_mifi_header;
+} t_mifiheader;
-typedef struct _mifi_trackheader
+/* midi file track header */
+typedef struct _mifitrackheader
{
- char h_type[4];
- uint32 h_length;
-} t_mifi_trackheader;
+ char th_type[4];
+ uint32 th_length;
+} t_mifitrackheader;
-/* reading helpers */
-
-static void mifi_earlyeof(t_mifi_stream *sp)
+typedef struct _mifireadtx
+{
+ double rt_wholeticks; /* userticks per whole note (set by user) */
+ double rt_deftempo; /* userticks per second (default, adjusted) */
+ double rt_tempo; /* userticks per second (current) */
+ double rt_tickscoef; /* userticks per hardtick */
+ double rt_mscoef; /* ms per usertick (current) */
+ double rt_userbar; /* userticks per bar */
+ uint16 rt_beatticks; /* hardticks per beat or per frame */
+ double rt_hardbar; /* hardticks per bar */
+} t_mifireadtx;
+
+struct _mifiread
+{
+ t_pd *mr_owner;
+ FILE *mr_fp;
+ t_mifiheader mr_header;
+ t_mifievent mr_event;
+ uint32 mr_scoretime; /* current time in hardticks */
+ uint32 mr_tempo; /* microseconds per beat */
+ uint32 mr_meternum;
+ uint32 mr_meterden;
+ uchar mr_status;
+ uchar mr_channel;
+ int mr_nevents;
+ int mr_ntempi;
+ uint16 mr_hdtracks; /* ntracks, as declared in the file header */
+ uint16 mr_ntracks; /* as actually contained in a file */
+ uint16 mr_trackndx;
+ t_symbol **mr_tracknames;
+ uchar mr_nframes; /* fps if nonzero, else use metrical time */
+ uint16 mr_format; /* anything > 0 handled as 1, FIXME */
+ uint32 mr_bytesleft; /* nbytes remaining to be read from a track */
+ int mr_pass;
+ int mr_eof; /* set in case of early eof (error) */
+ int mr_newtrack; /* reset after reading track's first event */
+ t_mifireadtx mr_ticks;
+};
+
+typedef struct _mifiwritetx
{
- sp->s_bytesleft = 0;
- sp->s_eof = 1;
+ double wt_wholeticks; /* userticks per whole note (set by user) */
+ double wt_deftempo; /* userticks per second (default, adjusted) */
+ double wt_tempo; /* userticks per second (set by user, quantized) */
+ double wt_tickscoef; /* hardticks per usertick */
+ uint16 wt_beatticks; /* hardticks per beat or per frame (set by user) */
+ double wt_mscoef; /* hardticks per ms */
+} t_mifiwritetx;
+
+struct _mifiwrite
+{
+ t_pd *mw_owner;
+ FILE *mw_fp;
+ t_mifiheader mw_header;
+ t_mifievent mw_event;
+ uint32 mw_tempo; /* microseconds per beat */
+ uint32 mw_meternum;
+ uint32 mw_meterden;
+ uchar mw_status;
+ uchar mw_channel;
+ int mw_ntempi;
+ uint16 mw_ntracks;
+ uint16 mw_trackndx;
+ t_symbol **mw_tracknames;
+ uchar mw_nframes; /* fps if nonzero, else use metrical time */
+ uint16 mw_format; /* anything > 0 handled as 1, FIXME */
+ uint32 mw_trackbytes; /* nbytes written to a track so far */
+ int mw_trackdirty; /* after opentrack, before adjusttrack */
+ t_mifiwritetx mw_ticks;
+};
+
+static int mifi_swapping = 1;
+
+static void mifi_initialize(void)
+{
+ unsigned short s = 1;
+ unsigned char c = *(char *)(&s);
+ mifi_swapping = (c != 0);
}
-/* Get next byte from track data.
- On error: return 0 (which is a valid result) and set sp->s_eof.
-*/
-static uchar mifi_getbyte(t_mifi_stream *sp)
+static void mifi_error(t_pd *x, char *fmt, ...)
{
- if (sp->s_bytesleft)
+ char buf[MAXPDSTRING];
+ va_list ap;
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ if (x)
{
- int c;
- if ((c = fgetc(sp->s_fp)) == EOF)
- {
- mifi_earlyeof(sp);
- return (0);
- }
- else
- {
- sp->s_bytesleft--;
- return ((uchar)c);
- }
+ startpost("%s's ", class_getname(*x));
+ pd_error(x, buf);
}
- else return (0);
+ else post("mifi error: %s", buf);
+ va_end(ap);
}
-static uint32 mifi_readbytes(t_mifi_stream *sp, uchar *buf, uint32 size)
+static void mifi_warning(t_pd *x, char *fmt, ...)
{
- size_t res;
- if (size > sp->s_bytesleft)
- size = sp->s_bytesleft;
- if ((res = fread(buf, 1, (size_t)size, sp->s_fp)) == size)
- sp->s_bytesleft -= res;
+ char buf[MAXPDSTRING];
+ va_list ap;
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ if (x)
+ post("%s's warning: %s", class_getname(*x), buf);
else
- mifi_earlyeof(sp);
- return (res);
+ post("mifi warning: %s", buf);
+ va_end(ap);
}
-static int mifi_skipbytes(t_mifi_stream *sp, uint32 size)
+static uint32 mifi_swap4(uint32 n)
{
- if (size > sp->s_bytesleft)
- size = sp->s_bytesleft;
- if (size)
- {
- int res = fseek(sp->s_fp, size, SEEK_CUR);
- if (res < 0)
- mifi_earlyeof(sp);
- else
- sp->s_bytesleft -= size;
- return res;
- }
- else return (0);
+ if (mifi_swapping)
+ return (((n & 0xff) << 24) | ((n & 0xff00) << 8) |
+ ((n & 0xff0000) >> 8) | ((n & 0xff000000) >> 24));
+ else
+ return (n);
}
-/* helpers handling variable-length quantities */
-
-static size_t mifi_writevarlen(t_mifi_stream *sp, uint32 n)
+static uint16 mifi_swap2(uint16 n)
{
- uint32 buf = n & 0x7f;
- size_t length = 1;
- while ((n >>= 7) > 0)
- {
- buf <<= 8;
- buf |= 0x80;
- buf += n & 0x7f;
- length++;
- }
- return ((fwrite(&buf, 1, length, sp->s_fp) == length) ? length : 0);
+ if (mifi_swapping)
+ return (((n & 0xff) << 8) | ((n & 0xff00) >> 8));
+ else
+ return (n);
}
-static uint32 mifi_readvarlen(t_mifi_stream *sp)
+static int mifievent_initialize(t_mifievent *ep, size_t nalloc)
{
- uint32 n = 0;
- uchar c;
- uint32 count = sp->s_bytesleft;
- if (count > 4) count = 4;
- while (count--)
+ ep->e_length = 0;
+ if (ep->e_data = getbytes(nalloc))
{
- n = (n << 7) + ((c = mifi_getbyte(sp)) & 0x7f);
- if ((c & 0x80) == 0)
- break;
+ ep->e_datasize = nalloc;
+ return (1);
+ }
+ else
+ {
+ ep->e_data = ep->e_dataini;
+ ep->e_datasize = MIFIEVENT_INISIZE;
+ return (0);
}
- return (n);
}
-/* other helpers */
-
-static int mifi_read_start_track(t_mifi_stream *sp)
+static int mifievent_setlength(t_mifievent *ep, size_t length)
{
- t_mifi_trackheader header;
- long skip;
- int notyet = 1;
- do {
- if (fread(&header, 1,
- MIFI_TRACKHEADER_SIZE, sp->s_fp) < MIFI_TRACKHEADER_SIZE)
- goto nomoretracks;
- header.h_length = bifi_swap4(header.h_length);
- if (strncmp(header.h_type, "MTrk", 4))
- {
- char buf[5];
- strncpy(buf, header.h_type, 4);
- buf[5] = '\0';
- if (sp->s_anapass)
- post("unknown chunk %s in midifile -- skipped", buf);
- }
- else if (header.h_length < MIFI_SHORTEST_EVENT)
+ if (length > ep->e_datasize)
+ {
+ size_t newsize = ep->e_datasize;
+ while (newsize < length)
+ newsize *= 2;
+ if (ep->e_data = resizebytes(ep->e_data, ep->e_datasize, newsize))
+ ep->e_datasize = newsize;
+ else
{
- if (sp->s_anapass) post("empty track in midifile -- skipped");
+ ep->e_length = 0;
+ /* rather hopeless... */
+ newsize = MIFIEVENT_NALLOC;
+ if (ep->e_data = getbytes(newsize))
+ ep->e_datasize = newsize;
+ else
+ {
+ ep->e_data = ep->e_dataini;
+ ep->e_datasize = MIFIEVENT_INISIZE;
+ }
+ return (0);
}
- else notyet = 0;
- if (notyet && (skip = header.h_length) &&
- fseek(sp->s_fp, skip, SEEK_CUR) < 0)
- goto nomoretracks;
- } while (notyet);
-
- sp->s_track++;
- sp->s_newtrack = 1;
- sp->s_status = sp->s_channel = 0;
- sp->s_bytesleft = header.h_length;
- sp->s_time = 0;
-
+ }
+ ep->e_length = (uint32)length;
return (1);
-nomoretracks:
- if (sp->s_track == 0)
- if (sp->s_anapass) post("no valid miditracks");
- return (0);
}
-/* public interface */
-
-t_mifi_event *mifi_event_new(void)
+static int mifievent_settext(t_mifievent *ep, unsigned type, char *text)
{
- t_mifi_event *ep = getbytes(sizeof(*ep));
- if (ep && !(ep->e_data = getbytes(ep->e_bufsize = MIFI_EVENT_NALLOC)))
+ if (type > 127)
{
- freebytes(ep, sizeof(*ep));
+ bug("mifievent_settext");
return (0);
}
- return (ep);
-}
-
-void mifi_event_free(t_mifi_event *ep)
-{
- freebytes(ep->e_data, ep->e_bufsize);
- freebytes(ep, sizeof(*ep));
-}
-
-int mifi_event_settext(t_mifi_event *ep, int type, char *text)
-{
- ep->e_delay = 0;
- ep->e_status = MIFI_EVENT_META;
- ep->e_meta = type;
- ep->e_length = strlen(text);
- if (squb_checksize(ep, ep->e_length + 1, 1) <= ep->e_length)
+ if (mifievent_setlength(ep, strlen(text) + 1))
{
- ep->e_length = 0;
+ ep->e_status = MIFIEVENT_META;
+ ep->e_meta = (uchar)type;
+ strcpy(ep->e_data, text);
+ return (1);
+ }
+ else
+ {
+ ep->e_status = 0;
return (0);
}
- strcpy(ep->e_data, text);
- return (1);
}
#ifdef MIFI_DEBUG
-static void mifi_event_printsysex(t_mifi_event *ep)
+static void mifievent_printsysex(t_mifievent *ep)
{
int length = ep->e_length;
uchar *dp = ep->e_data;
startpost("sysex:");
- while (length--) postfloat((float)*dp++);
+ while (length--)
+ postfloat((float)*dp++);
endpost();
}
#endif
-void mifi_event_printmeta(t_mifi_event *ep)
+static void mifievent_printmeta(t_mifievent *ep)
{
- static int isprintable[MIFI_META_MAXPRINTABLE+1] =
+ static int isprintable[MIFIMETA_MAXPRINTABLE+1] =
{
#ifdef MIFI_DEBUG
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
@@ -232,173 +331,301 @@ void mifi_event_printmeta(t_mifi_event *ep)
0, 0, 1, 1, 1, 1, 1, 1
#endif
};
- static char *printformat[MIFI_META_MAXPRINTABLE+1] =
+ static char *printformat[MIFIMETA_MAXPRINTABLE+1] =
{
"", "text: %s", "copyright: %s", "track name: %s",
"instrument name: %s", "lyric: %s", "marker: %s", "cue point: %s"
};
- if (ep->e_meta <= MIFI_META_MAXPRINTABLE)
+ if (ep->e_meta <= MIFIMETA_MAXPRINTABLE)
{
+#if 0
if (isprintable[ep->e_meta] && printformat[ep->e_meta])
post(printformat[ep->e_meta], ep->e_data);
+#endif
}
-#ifdef MIFI_DEBUG /* in verbose mode tempo printout done only after sorting */
- else if (ep->e_meta == MIFI_META_TEMPO)
+#ifdef MIFI_DEBUG
+ else if (ep->e_meta == MIFIMETA_TEMPO)
{
- int tempo = bifi_swap4(*(uint32 *)ep->e_data);
- post("tempo %d after %d", tempo, ep->e_delay);
+ int tempo = mifi_swap4(*(uint32 *)ep->e_data);
+ post("tempo (hard) %d after %d", tempo, ep->e_delay);
+ }
+ else if (ep->e_meta == MIFIMETA_TIMESIG)
+ {
+ post("meter %d/%d after %d",
+ ep->e_data[0], (1 << ep->e_data[1]), ep->e_delay);
}
#endif
}
-void mifi_stream_reset(t_mifi_stream *sp)
+static void mifiread_earlyeof(t_mifiread *mr)
{
- sq_reset(sp);
- sp->s_status = sp->s_channel = 0;
- sp->s_timecoef = sq_msecs2ticks(sp, 0);
- sp->s_bytesleft = 0;
+ mr->mr_bytesleft = 0;
+ mr->mr_eof = 1;
}
-t_mifi_stream *mifi_stream_new(void)
+/* Get next byte from track data. On error: return 0 (which is a valid
+ result) and set mr->mr_eof. */
+static uchar mifiread_getbyte(t_mifiread *mr)
{
- t_mifi_stream *sp = sq_new();
- if (sp)
+ if (mr->mr_bytesleft)
{
- if (sp->s_auxeve = mifi_event_new())
+ int c;
+ if ((c = fgetc(mr->mr_fp)) == EOF)
{
- sp->s_hdtracks = 1;
- sp->s_alltracks = 0;
- mifi_stream_reset(sp); /* LATER avoid calling sq_reset() twice */
+ mifiread_earlyeof(mr);
+ return (0);
}
else
{
- mifi_stream_free(sp);
- return (0);
+ mr->mr_bytesleft--;
+ return ((uchar)c);
}
}
- return (sp);
+ else return (0);
}
-void mifi_stream_free(t_mifi_stream *sp)
+static uint32 mifiread_getbytes(t_mifiread *mr, uchar *buf, uint32 size)
{
- if (sp->s_auxeve)
- mifi_event_free(sp->s_auxeve);
- sq_free(sp);
+ size_t res;
+ if (size > mr->mr_bytesleft)
+ size = mr->mr_bytesleft;
+ if ((res = fread(buf, 1, (size_t)size, mr->mr_fp)) == size)
+ mr->mr_bytesleft -= res;
+ else
+ mifiread_earlyeof(mr);
+ return (res);
}
-/* Open midifile for reading, parse the header. May be used as t_mifi_stream
- allocator (if sp is a null pointer), to be freed by mifi_read_end() or
- explicitly.
-
- Return value: null on error, else sp if passed a valid pointer, else pointer
- to an allocated structure.
-*/
-t_mifi_stream *mifi_read_start(t_mifi_stream *sp,
- const char *filename, const char *dirname,
- int complain)
+static int mifiread_skipbytes(t_mifiread *mr, uint32 size)
{
- t_mifi_stream *result = sp;
- t_bifi bifi;
- t_bifi *bp = &bifi;
- t_mifi_header header;
- long skip;
+ if (size > mr->mr_bytesleft)
+ size = mr->mr_bytesleft;
+ if (size)
+ {
+ int res = fseek(mr->mr_fp, size, SEEK_CUR);
+ if (res < 0)
+ mifiread_earlyeof(mr);
+ else
+ mr->mr_bytesleft -= size;
+ return res;
+ }
+ else return (0);
+}
- bifi_new(bp, (char *)&header, MIFI_HEADER_SIZE);
- if (!bifi_read_start(bp, filename, dirname))
+static uint32 mifiread_getvarlen(t_mifiread *mr)
+{
+ uint32 n = 0;
+ uchar c;
+ uint32 count = mr->mr_bytesleft;
+ if (count > 4)
+ count = 4;
+ while (count--)
{
- bifi_error_report(bp);
- bifi_free(bp);
- return (0);
+ n = (n << 7) + ((c = mifiread_getbyte(mr)) & 0x7f);
+ if ((c & 0x80) == 0)
+ break;
}
- if (strncmp(header.h_type, "MThd", 4))
- goto badheader;
- header.h_length = bifi_swap4(header.h_length);
- if (header.h_length < MIFI_HEADERDATA_SIZE)
- goto badheader;
- if (skip = header.h_length - MIFI_HEADERDATA_SIZE)
+ return (n);
+}
+
+static size_t mifiwrite_putvarlen(t_mifiwrite *mw, uint32 n)
+{
+ uint32 buf = n & 0x7f;
+ size_t length = 1;
+ while ((n >>= 7) > 0)
{
- post("%ld extra bytes of midifile header -- skipped", skip);
- if (fseek(bp->b_fp, skip, SEEK_CUR) < 0)
- goto badstart;
+ buf <<= 8;
+ buf |= 0x80;
+ buf += n & 0x7f;
+ length++;
}
+ return ((fwrite(&buf, 1, length, mw->mw_fp) == length) ? length : 0);
+}
- /* since we will tolerate other incompatibilities, now we can allocate */
- if (sp) mifi_stream_reset(sp);
- else
+static void mifiread_updateticks(t_mifiread *mr)
+{
+ if (mr->mr_nframes)
{
- if (!(result = mifi_stream_new()))
- goto badstart;
- result->s_autoalloc = 1;
+ mr->mr_ticks.rt_userbar = mr->mr_ticks.rt_wholeticks;
+ /* LATER ntsc */
+ mr->mr_ticks.rt_tickscoef = mr->mr_ticks.rt_deftempo /
+ (mr->mr_nframes * mr->mr_ticks.rt_beatticks);
+ mr->mr_ticks.rt_hardbar = mr->mr_ticks.rt_userbar /
+ mr->mr_ticks.rt_tickscoef;
+ mr->mr_ticks.rt_tempo = mr->mr_ticks.rt_deftempo;
}
- result->s_fp = bp->b_fp;
- result->s_format = bifi_swap2(header.h_format);
- result->s_hdtracks = bifi_swap2(header.h_ntracks);
- result->s_nticks = bifi_swap2(header.h_division);
- if (result->s_nticks & 0x8000)
+ else
{
- result->s_nframes = (result->s_nticks >> 8);
- result->s_nticks &= 0xff;
+ mr->mr_ticks.rt_userbar =
+ (mr->mr_ticks.rt_wholeticks * mr->mr_meternum) / mr->mr_meterden;
+ mr->mr_ticks.rt_hardbar =
+ (mr->mr_ticks.rt_beatticks * 4. * mr->mr_meternum) /
+ mr->mr_meterden;
+ mr->mr_ticks.rt_tickscoef =
+ mr->mr_ticks.rt_wholeticks / (mr->mr_ticks.rt_beatticks * 4.);
+ mr->mr_ticks.rt_tempo =
+ ((double)MIFIHARD_DEFTEMPO * mr->mr_ticks.rt_deftempo) /
+ ((double)mr->mr_tempo);
+ if (mr->mr_ticks.rt_tempo < MIFI_TICKEPSILON)
+ {
+ bug("mifiread_updateticks");
+ mr->mr_ticks.rt_tempo = mr->mr_ticks.rt_deftempo;
+ }
}
- else result->s_nframes = 0;
- if (result->s_nticks == 0)
- goto badheader;
+ mr->mr_ticks.rt_mscoef = 1000. / mr->mr_ticks.rt_tempo;
+}
- return (result);
-badheader:
- if (complain)
- post("`%s\' is not a valid midifile", filename);
-badstart:
- if (result && !sp) mifi_stream_free(result);
- bifi_free(bp);
- return (0);
+static void mifiread_resetticks(t_mifiread *mr)
+{
+ mr->mr_ticks.rt_wholeticks = MIFIUSER_DEFWHOLETICKS;
+ mr->mr_ticks.rt_deftempo = MIFIUSER_DEFTEMPO;
+ mr->mr_ticks.rt_beatticks = MIFIHARD_DEFBEATTICKS;
}
-int mifi_read_restart(t_mifi_stream *sp)
+static void mifiread_reset(t_mifiread *mr)
{
- FILE *fp = sp->s_fp;
- mifi_stream_reset(sp);
- sp->s_anapass = 0;
- sp->s_fp = fp;
- return (fseek(fp, 0, SEEK_SET) ? 0 : 1);
+ mr->mr_eof = 0;
+ mr->mr_newtrack = 0;
+ mr->mr_fp = 0;
+ mr->mr_format = 0;
+ mr->mr_nframes = 0;
+ mr->mr_tempo = MIFIHARD_DEFTEMPO;
+ mr->mr_meternum = 4;
+ mr->mr_meterden = 4;
+ mr->mr_ntracks = 0;
+ mr->mr_status = 0;
+ mr->mr_channel = 0;
+ mr->mr_bytesleft = 0;
+ mr->mr_pass = 0;
+ mr->mr_hdtracks = 1;
+ mr->mr_tracknames = 0;
+ mifiread_updateticks(mr);
}
-/* Close midifile and free t_mifi_stream if it was allocated
- by mifi_read_start() */
-void mifi_read_end(t_mifi_stream *sp)
+/* Calling this is optional. The obligatory part is supplied elsewhere:
+ in the constructor (owner), and in the doit() call (hook function). */
+void mifiread_setuserticks(t_mifiread *mr, double wholeticks)
{
- if (sp->s_fp) fclose(sp->s_fp);
- if (sp->s_autoalloc) mifi_stream_free(sp);
+ mr->mr_ticks.rt_wholeticks = (wholeticks > MIFI_TICKEPSILON ?
+ wholeticks : MIFIUSER_DEFWHOLETICKS);
+ mr->mr_ticks.rt_deftempo = mr->mr_ticks.rt_wholeticks *
+ (MIFIUSER_DEFTEMPO / MIFIUSER_DEFWHOLETICKS);
+ mifiread_updateticks(mr);
}
-/* Read next event from midifile.
- Return value: see #defines in mifi.h.
-*/
-int mifi_read_event(t_mifi_stream *sp, t_mifi_event *ep)
+/* open a file and read its header */
+static int mifiread_startfile(t_mifiread *mr, const char *filename,
+ const char *dirname, int complain)
{
+ char errmess[MAXPDSTRING], path[MAXPDSTRING], *fnameptr;
+ int fd;
+ mr->mr_fp = 0;
+ if ((fd = open_via_path(dirname, filename,
+ "", path, &fnameptr, MAXPDSTRING, 1)) < 0)
+ {
+ strcpy(errmess, "cannot open");
+ goto rstartfailed;
+ }
+ close(fd);
+ if (path != fnameptr)
+ {
+ char *slashpos = path + strlen(path);
+ *slashpos++ = '/';
+ /* try not to be dependent on current open_via_path() implementation */
+ if (fnameptr != slashpos)
+ strcpy(slashpos, fnameptr);
+ }
+ sys_bashfilename(path, path);
+ if (!(mr->mr_fp = fopen(path, "rb")))
+ {
+ strcpy(errmess, "cannot open");
+ goto rstartfailed;
+ }
+ if (fread(&mr->mr_header, 1,
+ MIFIHARD_HEADERSIZE, mr->mr_fp) < MIFIHARD_HEADERSIZE)
+ {
+ strcpy(errmess, "missing header of");
+ goto rstartfailed;
+ }
+ return (1);
+rstartfailed:
+ if (complain)
+ mifi_error(mr->mr_owner, "%s file \"%s\" (errno %d: %s)",
+ errmess, filename, errno, strerror(errno));
+ if (mr->mr_fp)
+ {
+ fclose(mr->mr_fp);
+ mr->mr_fp = 0;
+ }
+ return (0);
+}
+
+static int mifiread_starttrack(t_mifiread *mr)
+{
+ t_mifitrackheader th;
+ long skip;
+ int notyet = 1;
+ do {
+ if (fread(&th, 1, MIFIHARD_TRACKHEADERSIZE,
+ mr->mr_fp) < MIFIHARD_TRACKHEADERSIZE)
+ goto nomoretracks;
+ th.th_length = mifi_swap4(th.th_length);
+ if (strncmp(th.th_type, "MTrk", 4))
+ {
+ char buf[8];
+ strncpy(buf, th.th_type, 4);
+ buf[4] = 0;
+ if (mr->mr_pass == 1)
+ mifi_warning(mr->mr_owner,
+ "unknown chunk %s in midi file... skipped", buf);
+ }
+ else if (th.th_length < MIFI_SHORTESTEVENT)
+ {
+ if (mr->mr_pass == 1)
+ mifi_warning(mr->mr_owner,
+ "empty track in midi file... skipped");
+ }
+ else notyet = 0;
+ if (notyet && (skip = th.th_length) &&
+ fseek(mr->mr_fp, skip, SEEK_CUR) < 0)
+ goto nomoretracks;
+ } while (notyet);
+ mr->mr_scoretime = 0;
+ mr->mr_newtrack = 1;
+ mr->mr_status = mr->mr_channel = 0;
+ mr->mr_bytesleft = th.th_length;
+ return (1);
+nomoretracks:
+ if (mr->mr_ntracks == 0 && mr->mr_pass == 1)
+ mifi_warning(mr->mr_owner, "no valid miditracks");
+ return (0);
+}
+
+static int mifiread_nextevent(t_mifiread *mr)
+{
+ t_mifievent *ep = &mr->mr_event;
uchar status, channel;
uint32 length;
-
- sp->s_newtrack = 0;
+ mr->mr_newtrack = 0;
nextattempt:
- if (sp->s_bytesleft < MIFI_SHORTEST_EVENT && !mifi_read_start_track(sp))
- return (MIFI_READ_EOF);
-
- sp->s_time += (ep->e_delay = mifi_readvarlen(sp));
-
- if ((status = mifi_getbyte(sp)) < 0x80)
+ if (mr->mr_bytesleft < MIFI_SHORTESTEVENT &&
+ !mifiread_starttrack(mr))
+ return (MIFIREAD_EOF);
+ mr->mr_scoretime += (mr->mr_event.e_delay = mifiread_getvarlen(mr));
+ if ((status = mifiread_getbyte(mr)) < 0x80)
{
- if (MIFI_IS_CHANNEL(sp->s_status))
+ if (MIFI_ISCHANNEL(mr->mr_status))
{
ep->e_data[0] = status;
ep->e_length = 1;
- status = sp->s_status;
- ep->e_channel = sp->s_channel;
+ status = mr->mr_status;
+ ep->e_channel = mr->mr_channel;
}
else
{
- if (sp->s_anapass)
- post("missing running status in midifile -- \
- skip to end of track");
+ if (mr->mr_pass == 1)
+ mifi_warning(mr->mr_owner,
+ "missing running status in midi file... skip to end of track");
goto endoftrack;
}
}
@@ -409,165 +636,257 @@ nextattempt:
{
if (ep->e_length == 0)
{
- ep->e_data[0] = mifi_getbyte(sp);
+ ep->e_data[0] = mifiread_getbyte(mr);
ep->e_length = 1;
- sp->s_status = status & 0xf0;
- sp->s_channel = ep->e_channel = status & 0x0f;
- status = sp->s_status;
+ mr->mr_status = status & 0xf0;
+ mr->mr_channel = ep->e_channel = status & 0x0f;
+ status = mr->mr_status;
}
- if (!MIFI_ONE_DATABYTE(status))
+ if (!MIFI_ONEDATABYTE(status))
{
- ep->e_data[1] = mifi_getbyte(sp);
+ ep->e_data[1] = mifiread_getbyte(mr);
ep->e_length = 2;
}
}
/* system exclusive */
- else if (status == MIFI_SYSEX_FIRST || status == MIFI_SYSEX_NEXT)
+ else if (status == MIFISYSEX_FIRST || status == MIFISYSEX_NEXT)
{
- length = mifi_readvarlen(sp);
- if (squb_checksize(ep, length, 1) < length)
- {
- if (mifi_skipbytes(sp, length) < 0)
- return (MIFI_READ_FATAL);
- goto nextattempt;
- }
- /* LATER make the allocation optional */
- if (mifi_readbytes(sp, ep->e_data, length) != length)
- return (MIFI_READ_FATAL);
- ep->e_length = length;
-#ifdef MIFI_DEBUG
- if (sp->s_anapass) mifi_event_printsysex(ep);
-#elif defined MIFI_VERBOSE
- if (sp->s_anapass) post("got %d bytes of sysex", length);
-#endif
+ length = mifiread_getvarlen(mr);
+ /* FIXME optional read */
+ if (mifiread_skipbytes(mr, length) < 0)
+ return (MIFIREAD_FATAL);
+ goto nextattempt;
}
/* meta-event */
- else if (status == MIFI_EVENT_META)
+ else if (status == MIFIEVENT_META)
{
- ep->e_meta = mifi_getbyte(sp);
- length = mifi_readvarlen(sp);
+ ep->e_meta = mifiread_getbyte(mr);
+ length = mifiread_getvarlen(mr);
if (ep->e_meta > 127)
{
/* try to skip corrupted meta-event (quietly) */
#ifdef MIFI_VERBOSE
- if (sp->s_anapass) post("bad meta: %d > 127", ep->e_meta);
+ if (mr->mr_pass == 1)
+ mifi_warning(mr->mr_owner, "bad meta: %d > 127", ep->e_meta);
#endif
- if (mifi_skipbytes(sp, length) < 0)
- return (MIFI_READ_FATAL);
+ if (mifiread_skipbytes(mr, length) < 0)
+ return (MIFIREAD_FATAL);
goto nextattempt;
}
switch (ep->e_meta)
{
- case MIFI_META_EOT:
+ case MIFIMETA_EOT:
if (length)
{
/* corrupted eot: ignore and skip to the real end of track */
#ifdef MIFI_VERBOSE
- if (sp->s_anapass) post("corrupted eot, length %d", length);
+ if (mr->mr_pass == 1)
+ mifi_warning(mr->mr_owner,
+ "corrupted eot, length %d", length);
#endif
goto endoftrack;
}
break;
- case MIFI_META_TEMPO:
+ case MIFIMETA_TEMPO:
if (length != 3)
{
- if (sp->s_anapass)
- post("corrupted event in midifile -- skip to end of track");
+ if (mr->mr_pass == 1)
+ mifi_warning(mr->mr_owner,
+ "corrupted tempo event in midi file... skip to end of track");
goto endoftrack;
}
- if (mifi_readbytes(sp, ep->e_data + 1, 3) != 3)
- return (MIFI_READ_FATAL);
+ if (mifiread_getbytes(mr, ep->e_data + 1, 3) != 3)
+ return (MIFIREAD_FATAL);
ep->e_data[0] = 0;
- sp->s_tempo = bifi_swap4(*(uint32 *)ep->e_data);
+ mr->mr_tempo = mifi_swap4(*(uint32 *)ep->e_data);
+ if (!mr->mr_tempo)
+ mr->mr_tempo = MIFIHARD_DEFTEMPO;
+ mifiread_updateticks(mr);
+ break;
+ case MIFIMETA_TIMESIG:
+ if (length != 4)
+ {
+ if (mr->mr_pass == 1)
+ mifi_warning(mr->mr_owner,
+ "corrupted time signature event in midi file... skip to end of track");
+ goto endoftrack;
+ }
+ if (mifiread_getbytes(mr, ep->e_data, 4) != 4)
+ return (MIFIREAD_FATAL);
+ mr->mr_meternum = ep->e_data[0];
+ mr->mr_meterden = (1 << ep->e_data[1]);
+ if (!mr->mr_meternum || !mr->mr_meterden)
+ mr->mr_meternum = mr->mr_meterden = 4;
+ mifiread_updateticks(mr);
+#ifdef MIFI_DEBUG
+ if (mr->mr_pass == 1)
+ post("barspan (hard) %g", mr->mr_ticks.rt_hardbar);
+#endif
break;
default:
- if (squb_checksize(ep, length + 1, 1) <= length)
+ if (length + 1 > MIFIEVENT_NALLOC)
{
- if (mifi_skipbytes(sp, length) < 0)
- return (MIFI_READ_FATAL);
+ if (mifiread_skipbytes(mr, length) < 0)
+ return (MIFIREAD_FATAL);
goto nextattempt;
}
- if (mifi_readbytes(sp, ep->e_data, length) != length)
- return (MIFI_READ_FATAL);
+ if (mifiread_getbytes(mr, ep->e_data, length) != length)
+ return (MIFIREAD_FATAL);
ep->e_length = length;
- if (ep->e_meta && ep->e_meta <= MIFI_META_MAXPRINTABLE)
+ if (ep->e_meta && ep->e_meta <= MIFIMETA_MAXPRINTABLE)
ep->e_data[length] = '\0'; /* text meta-event nultermination */
}
}
else
{
- if (sp->s_anapass)
- post("unknown event type in midifile -- skip to end of track");
+ if (mr->mr_pass == 1)
+ mifi_warning(mr->mr_owner,
+ "unknown event type in midi file... skip to end of track");
goto endoftrack;
}
+ return ((ep->e_status = status) == MIFIEVENT_META ? ep->e_meta : status);
+endoftrack:
+ if (mifiread_skipbytes(mr, mr->mr_bytesleft) < 0)
+ return (MIFIREAD_FATAL);
+ else
+ return (MIFIREAD_SKIP);
+}
- return ((ep->e_status = status) == MIFI_EVENT_META ? ep->e_meta : status);
+static int mifiread_restart(t_mifiread *mr, int complain)
+{
+ mr->mr_eof = 0;
+ mr->mr_newtrack = 0;
+ mr->mr_status = 0;
+ mr->mr_channel = 0;
+ mr->mr_bytesleft = 0;
+ mr->mr_pass = 0;
+ if (fseek(mr->mr_fp, 0, SEEK_SET))
+ {
+ if (complain)
+ mifi_error(mr->mr_owner,
+ "file error (errno %d: %s)", errno, strerror(errno));
+ return (0);
+ }
+ else return (1);
+}
-endoftrack:
- if (mifi_skipbytes(sp, sp->s_bytesleft) < 0)
- return (MIFI_READ_FATAL);
- return (MIFI_READ_SKIP);
+static int mifiread_doopen(t_mifiread *mr, const char *filename,
+ const char *dirname, int complain)
+{
+ long skip;
+ mifiread_reset(mr);
+ if (!mifiread_startfile(mr, filename, dirname, complain))
+ return (0);
+ if (strncmp(mr->mr_header.h_type, "MThd", 4))
+ goto badheader;
+ mr->mr_header.h_length = mifi_swap4(mr->mr_header.h_length);
+ if (mr->mr_header.h_length < MIFIHARD_HEADERDATASIZE)
+ goto badheader;
+ if (skip = mr->mr_header.h_length - MIFIHARD_HEADERDATASIZE)
+ {
+ mifi_warning(mr->mr_owner,
+ "%ld extra bytes of midi file header... skipped", skip);
+ if (fseek(mr->mr_fp, skip, SEEK_CUR) < 0)
+ goto badstart;
+ }
+ mr->mr_format = mifi_swap2(mr->mr_header.h_format);
+ mr->mr_hdtracks = mifi_swap2(mr->mr_header.h_ntracks);
+ if (mr->mr_hdtracks > 1000) /* a sanity check */
+ mifi_warning(mr->mr_owner, "%d tracks declared in midi file \"%s\"",
+ mr->mr_hdtracks, filename);
+ mr->mr_tracknames = getbytes(mr->mr_hdtracks * sizeof(*mr->mr_tracknames));
+ mr->mr_ticks.rt_beatticks = mifi_swap2(mr->mr_header.h_division);
+ if (mr->mr_ticks.rt_beatticks & 0x8000)
+ {
+ mr->mr_nframes = (mr->mr_ticks.rt_beatticks >> 8);
+ mr->mr_ticks.rt_beatticks &= 0xff;
+ }
+ else mr->mr_nframes = 0;
+ if (mr->mr_ticks.rt_beatticks == 0)
+ goto badheader;
+ mifiread_updateticks(mr);
+#ifdef MIFI_DEBUG
+ if (mr->mr_nframes)
+ post("midi file (format %d): %d tracks, %d ticks (%d smpte frames)",
+ mr->mr_format, mr->mr_hdtracks,
+ mr->mr_ticks.rt_beatticks, mr->mr_nframes);
+ else
+ post("midi file (format %d): %d tracks, %d ticks per beat",
+ mr->mr_format, mr->mr_hdtracks, mr->mr_ticks.rt_beatticks);
+#endif
+ return (1);
+badheader:
+ if (complain)
+ mifi_error(mr->mr_owner, "\"%s\" is not a valid midi file", filename);
+badstart:
+ fclose(mr->mr_fp);
+ mr->mr_fp = 0;
+ return (0);
}
/* Gather statistics (nevents, ntracks, ntempi), pick track names, and
allocate the maps. To be called in the first pass of reading.
-*/
-/* LATER consider optional reading of nonchannel events */
-int mifi_read_analyse(t_mifi_stream *sp)
-{
- t_mifi_event *ep = sp->s_auxeve;
- int evtype, result = MIFI_READ_FATAL;
- int isnewtrack = 0;
- int i;
+ LATER consider optional reading of nonchannel events. */
+/* FIXME complaining */
+static int mifiread_analyse(t_mifiread *mr, int complain)
+{
+ t_mifievent *ep = &mr->mr_event;
+ int i, evtype, isnewtrack = 0;
char tnamebuf[MAXPDSTRING];
- t_symbol *tnamesym = 0;
- t_squack *trp = 0;
+ t_symbol **tnamep = 0;
+ mr->mr_pass = 1;
*tnamebuf = '\0';
- sp->s_alltracks = sp->s_ntracks = 0;
- sp->s_nevents = 0;
- sp->s_ntempi = 0;
-
- while ((evtype = mifi_read_event(sp, ep)) >= MIFI_READ_SKIP)
+ mr->mr_ntracks = 0;
+ mr->mr_nevents = 0;
+ mr->mr_ntempi = 0;
+ while ((evtype = mifiread_nextevent(mr)) >= MIFIREAD_SKIP)
{
- if (evtype == MIFI_READ_SKIP)
+ if (evtype == MIFIREAD_SKIP)
continue;
- if (sp->s_newtrack)
+ if (mr->mr_newtrack)
{
#ifdef MIFI_VERBOSE
- post("track %d", sp->s_track);
+ post("track %d", mr->mr_ntracks);
#endif
isnewtrack = 1;
*tnamebuf = '\0';
- tnamesym = 0; /* set to nonzero for nonempty tracks only */
+ tnamep = 0; /* set to nonzero for nonempty tracks only */
}
- if (MIFI_IS_CHANNEL(evtype))
+ if (MIFI_ISCHANNEL(evtype))
{
if (isnewtrack)
{
isnewtrack = 0;
- sp->s_alltracks++;
- if (!(trp = squax_add(sp)))
+ tnamep = mr->mr_tracknames + mr->mr_ntracks;
+ mr->mr_ntracks++;
+ if (mr->mr_ntracks > mr->mr_hdtracks)
+ {
+ if (complain)
+ mifi_error(mr->mr_owner,
+ "midi file has more tracks than header-declared %d", mr->mr_hdtracks);
+ /* FIXME grow? */
goto anafail;
+ }
if (*tnamebuf)
{
- tnamesym = trp->tr_name = gensym(tnamebuf);
+ *tnamep = gensym(tnamebuf);
#ifdef MIFI_DEBUG
- post("nonempty track name %s", tnamesym->s_name);
+ post("nonempty track name %s", (*tnamep)->s_name);
#endif
}
- else tnamesym = trp->tr_name = &s_;
+ else *tnamep = &s_;
}
- sp->s_nevents++;
+ mr->mr_nevents++;
}
else if (evtype < 0x80)
{
- mifi_event_printmeta(ep);
- if (evtype == MIFI_META_TEMPO)
- sp->s_ntempi++;
- else if (evtype == MIFI_META_TRACKNAME)
+ mifievent_printmeta(ep);
+ if (evtype == MIFIMETA_TEMPO)
+ mr->mr_ntempi++;
+ else if (evtype == MIFIMETA_TRACKNAME)
{
char *p1 = ep->e_data;
if (*p1 &&
@@ -582,302 +901,581 @@ int mifi_read_analyse(t_mifi_stream *sp)
do if (*p2 == ' ' || *p2 == ',' || *p2 == ';')
*p2 = '-';
while (*++p2);
- if (tnamesym == &s_)
- { /* trackname after channel-event */
- if (trp) /* redundant check */
- tnamesym = trp->tr_name = gensym(p1);
+ if (tnamep)
+ {
+ if (*tnamep == &s_)
+ /* trackname after channel-event */
+ *tnamep = gensym(p1);
+ else
+ strcpy(tnamebuf, p1);
}
- else strcpy(tnamebuf, p1);
}
}
}
}
}
- if (evtype != MIFI_READ_EOF)
- goto anafail;
-
- i = sp->s_ntracks;
- while (--i >= 0)
+ if (evtype == MIFIREAD_EOF)
{
- if (!sp->s_track_name(i) || sp->s_track_name(i) == &s_)
+ for (i = 0, tnamep = mr->mr_tracknames; i < mr->mr_ntracks; i++, tnamep++)
{
- sprintf(tnamebuf, "%d-track", i);
- sp->s_track_name(i) = gensym(tnamebuf);
+ if (!*tnamep || *tnamep == &s_)
+ {
+ sprintf(tnamebuf, "%d-track", i);
+ *tnamep = gensym(tnamebuf);
+ }
}
+ return (MIFIREAD_EOF);
}
-
- /* now (re)allocate the buffers */
- if (squb_checksize(sp->s_mytempi,
- sp->s_ntempi, sizeof(t_squmpo)) < sp->s_ntempi)
- goto anafail;
- sp->s_track_nevents(0) = 0;
- sp->s_track_nevents(sp->s_ntracks) = sp->s_nevents; /* guard point */
-
- result = evtype;
+ else return (evtype);
anafail:
- return (result);
-}
-
-/* To be called in second pass of reading */
-/* LATER do not trust analysis: in case of inconsistency give up or checksize */
-int mifi_read_doit(t_mifi_stream *sp)
-{
- t_mifi_event *ep = sp->s_auxeve;
- t_squiter *it = sp->s_myiter;
- t_squiter_seekhook seekhook = squiter_seekhook(it);
- t_squiter_incrhook incrhook = squiter_incrhook(it);
- t_squiter_setevehook evehook = squiter_setevehook(it);
- t_squiter_settimhook timhook = squiter_settimhook(it);
- t_squiter_settarhook tarhook = squiter_settarhook(it);
- int evtype, result = MIFI_READ_FATAL;
- int nevents = sp->s_nevents; /* three proxies... */
- int ntracks = sp->s_ntracks;
- int ntempi = sp->s_ntempi;
- int trackno = 0;
- t_symbol *trackname = 0;
- int isnewtrack = 0;
- t_squmpo *tp = sp->s_tempomap;
-
- if (!it || !seekhook(it, 0))
- goto readfailed;
-
- while ((evtype = mifi_read_event(sp, ep)) >= MIFI_READ_SKIP)
- {
- if (evtype == MIFI_READ_SKIP)
+ return (MIFIREAD_FATAL);
+}
+
+/* to be called in the second pass of reading */
+int mifiread_doit(t_mifiread *mr, t_mifireadhook hook, void *hookdata)
+{
+ int evtype, ntracks = 0, isnewtrack = 0;
+ mr->mr_pass = 2;
+ mr->mr_trackndx = 0;
+ while ((evtype = mifiread_nextevent(mr)) >= MIFIREAD_SKIP)
+ {
+ if (evtype == MIFIREAD_SKIP)
continue;
- if (sp->s_newtrack)
+ if (mr->mr_newtrack)
isnewtrack = 1;
- if (MIFI_IS_CHANNEL(evtype))
+ if (isnewtrack && MIFI_ISCHANNEL(evtype))
{
- int ret;
- if (isnewtrack)
- {
- isnewtrack = 0;
- trackname = sp->s_track_name(trackno);
- trackno++;
- if (!trackname || trackname == &s_)
- {
- bug("mifi_read_doit: empty track name");
- trackname = gensym("bug-track");
- }
- }
- sp->s_track_nevents(trackno)++;
- if (ret = squiter_inrange(it))
+ isnewtrack = 0;
+ mr->mr_trackndx = ntracks++;
+ if (ntracks > mr->mr_ntracks)
{
- evehook(it, (t_squeve *)ep, &ret);
- /* We store onset times instead of delta times, because:
- 1) some deltas may represent delays since nonchannel events;
- 2) we'll need onsets while merging the tracks. */
- if (ret) timhook(it, (t_float)sp->s_time, &ret);
- if (ret) tarhook(it, trackname, &ret);
+ bug("mifiread_doit: too many tracks");
+ goto doitfail;
}
- if (ret)
- incrhook(it);
- else
- goto readfailed;
- }
- else if (evtype < 0x80)
- {
- if (evtype == MIFI_META_TEMPO)
+ if (!mr->mr_tracknames[mr->mr_trackndx] ||
+ mr->mr_tracknames[mr->mr_trackndx] == &s_)
{
- tp->te_onset = sp->s_time;
- tp->te_value = sp->s_tempo;
- tp++;
+ bug("mifiread_doit: empty track name");
+ mr->mr_tracknames[mr->mr_trackndx] = gensym("bug-track");
}
}
+ if (!hook(mr, hookdata, evtype))
+ goto doitfail;
}
- if (evtype != MIFI_READ_EOF)
- goto readfailed;
+ if (evtype == MIFIREAD_EOF)
+ {
+#ifdef MIFI_DEBUG
+ if (evtype == MIFIREAD_EOF)
+ post("finished reading %d events from midi file", mr->mr_nevents);
+#endif
+ return (MIFIREAD_EOF);
+ }
+doitfail:
+ return (MIFIREAD_FATAL);
+}
+
+/* mifiread_get... calls to be used in the main read routine */
- result = evtype;
-readfailed:
- return (result);
+int mifiread_getnevents(t_mifiread *mr)
+{
+ return (mr->mr_nevents);
}
-/* Open midifile for saving, write the header. May be used as t_mifi_stream
- allocator (if sp is a null pointer), to be freed by mifi_write_end() or
- explicitly.
+int mifiread_getntempi(t_mifiread *mr)
+{
+ return (mr->mr_ntempi);
+}
- Return value: null on error, else sp if passed a valid pointer, else pointer
- to allocated structure.
-*/
-t_mifi_stream *mifi_write_start(t_mifi_stream *sp,
- const char *filename, const char *dirname)
+int mifiread_gethdtracks(t_mifiread *mr)
{
- t_mifi_stream *result = sp;
- t_bifi bifi;
- t_bifi *bp = &bifi;
- t_mifi_header header;
+ return (mr->mr_hdtracks);
+}
- /* this must precede bifi_swap() calls */
- bifi_new(bp, (char *)&header, MIFI_HEADER_SIZE);
+int mifiread_getformat(t_mifiread *mr)
+{
+ return (mr->mr_format);
+}
- if (sp->s_format == 0)
- {
- if (sp->s_ntracks != 1)
- goto startfailure; /* LATER replace with a warning only? */
-#ifdef MIFI_VERBOSE
- post("writing singletrack midifile %s", filename);
-#endif
- }
-#ifdef MIFI_VERBOSE
- else post("writing midifile %s (%d tracks)", filename, sp->s_ntracks);
-#endif
+int mifiread_getnframes(t_mifiread *mr)
+{
+ return (mr->mr_nframes);
+}
- strncpy(header.h_type, "MThd", 4);
- header.h_length = bifi_swap4(MIFI_HEADERDATA_SIZE);
- if (sp)
- {
- if (!sp->s_hdtracks || !sp->s_nticks)
- goto startfailure;
- header.h_format = bifi_swap2(sp->s_format);
- header.h_ntracks = bifi_swap2(sp->s_hdtracks);
- if (sp->s_nframes)
- header.h_division = ((sp->s_nframes << 8) | sp->s_nticks) | 0x8000;
- else
- header.h_division = sp->s_nticks & 0x7fff;
- header.h_division = bifi_swap2(header.h_division);
- }
+int mifiread_getbeatticks(t_mifiread *mr)
+{
+ return (mr->mr_ticks.rt_beatticks);
+}
+
+double mifiread_getdeftempo(t_mifiread *mr)
+{
+ return (mr->mr_ticks.rt_deftempo);
+}
+
+/* mifiread_get... calls to be used in a mifireadhook */
+
+int mifiread_getbarindex(t_mifiread *mr)
+{
+ return (mr->mr_scoretime / (int)mr->mr_ticks.rt_hardbar);
+}
+
+double mifiread_getbarspan(t_mifiread *mr)
+{
+ return (mr->mr_ticks.rt_userbar);
+}
+
+double mifiread_gettick(t_mifiread *mr)
+{
+ return (mr->mr_ticks.rt_tickscoef *
+ (mr->mr_scoretime % (int)mr->mr_ticks.rt_hardbar));
+}
+
+double mifiread_getscoretime(t_mifiread *mr)
+{
+ return (mr->mr_ticks.rt_tickscoef * mr->mr_scoretime);
+}
+
+double mifiread_gettempo(t_mifiread *mr)
+{
+ return (mr->mr_ticks.rt_tempo);
+}
+
+double mifiread_getmscoef(t_mifiread *mr)
+{
+ return (mr->mr_ticks.rt_mscoef);
+}
+
+t_symbol *mifiread_gettrackname(t_mifiread *mr)
+{
+ if (mr->mr_pass == 2 &&
+ mr->mr_tracknames &&
+ mr->mr_trackndx < mr->mr_ntracks)
+ return (mr->mr_tracknames[mr->mr_trackndx]);
else
{
- header.h_format = 0;
- header.h_ntracks = bifi_swap2(1);
- /* LATER parametrize this somehow */
- header.h_division = bifi_swap2(192);
+ bug("mifiread_gettrackname");
+ return (0);
}
+}
- if (!bifi_write_start(bp, filename, dirname))
+unsigned mifiread_getstatus(t_mifiread *mr)
+{
+ if (mr->mr_pass != 2)
+ bug("mifiread_getstatus");
+ return (mr->mr_event.e_status);
+}
+
+unsigned mifiread_getdata1(t_mifiread *mr)
+{
+ if (mr->mr_pass != 2)
+ bug("mifiread_getdata1");
+ return (mr->mr_event.e_data[0]);
+}
+
+unsigned mifiread_getdata2(t_mifiread *mr)
+{
+ if (mr->mr_pass != 2)
+ bug("mifiread_getdata2");
+ if (mr->mr_event.e_length < 2)
+ bug("mifiread_getdata2");
+ return (mr->mr_event.e_data[1]);
+}
+
+unsigned mifiread_getchannel(t_mifiread *mr)
+{
+ if (mr->mr_pass != 2)
+ bug("mifiread_getchannel");
+ return (mr->mr_event.e_channel);
+}
+
+t_pd *mifiread_getowner(t_mifiread *mr)
+{
+ return (mr->mr_owner);
+}
+
+int mifiread_open(t_mifiread *mr, const char *filename,
+ const char *dirname, int complain)
+{
+ return (mifiread_doopen(mr, filename, dirname, complain) &&
+ (mifiread_analyse(mr, complain) == MIFIREAD_EOF) &&
+ mifiread_restart(mr, complain));
+}
+
+void mifiread_close(t_mifiread *mr)
+{
+ mr->mr_pass = 0;
+ if (mr->mr_fp)
{
- bifi_error_report(bp);
- bifi_free(bp);
- return (0);
+ fclose(mr->mr_fp);
+ mr->mr_fp = 0;
}
+ if (mr->mr_tracknames)
+ freebytes(mr->mr_tracknames,
+ mr->mr_hdtracks * sizeof(*mr->mr_tracknames));
+}
- if (sp) mifi_stream_reset(sp);
+void mifiread_free(t_mifiread *mr)
+{
+ mifiread_close(mr);
+ if (mr->mr_event.e_data != mr->mr_event.e_dataini)
+ freebytes(mr->mr_event.e_data, mr->mr_event.e_datasize);
+ freebytes(mr, sizeof(*mr));
+}
+
+t_mifiread *mifiread_new(t_pd *owner)
+{
+ t_mifiread *mr = getbytes(sizeof(*mr));
+ mifi_initialize();
+ mr->mr_owner = owner;
+ mifievent_initialize(&mr->mr_event, MIFIEVENT_NALLOC);
+ mifiread_resetticks(mr);
+ mifiread_reset(mr);
+ return (mr);
+}
+
+static void mifiwrite_updateticks(t_mifiwrite *mw)
+{
+ if (mw->mw_nframes)
+ {
+ /* LATER ntsc */
+ mw->mw_ticks.wt_tickscoef =
+ (mw->mw_nframes * mw->mw_ticks.wt_beatticks) /
+ mw->mw_ticks.wt_deftempo;
+ mw->mw_ticks.wt_tempo = mw->mw_ticks.wt_deftempo;
+ mw->mw_ticks.wt_mscoef =
+ .001 * (mw->mw_nframes * mw->mw_ticks.wt_beatticks);
+ }
else
{
- if (!(result = mifi_stream_new()))
- goto startfailure;
- result->s_autoalloc = 1;
+ mw->mw_ticks.wt_tickscoef =
+ (mw->mw_ticks.wt_beatticks * 4.) / mw->mw_ticks.wt_wholeticks;
+ mw->mw_ticks.wt_tempo =
+ ((double)MIFIHARD_DEFTEMPO * mw->mw_ticks.wt_deftempo) /
+ ((double)mw->mw_tempo);
+ if (mw->mw_ticks.wt_tempo < MIFI_TICKEPSILON)
+ {
+ bug("mifiwrite_updateticks");
+ mw->mw_ticks.wt_tempo = mw->mw_ticks.wt_deftempo;
+ }
+ mw->mw_ticks.wt_mscoef =
+ (1000. * mw->mw_ticks.wt_beatticks) / mw->mw_tempo;
}
- result->s_fp = bp->b_fp;
- result->s_track = 0;
+}
- return (result);
-startfailure:
- if (result && !sp) mifi_stream_free(result);
- bifi_free(bp);
- return (0);
+static void mifiwrite_resetticks(t_mifiwrite *mw)
+{
+ mw->mw_ticks.wt_wholeticks = MIFIUSER_DEFWHOLETICKS;
+ mw->mw_ticks.wt_deftempo = MIFIUSER_DEFTEMPO;
+ mw->mw_ticks.wt_beatticks = MIFIHARD_DEFBEATTICKS;
}
-/* Close midifile, free t_mifi_stream if it was allocated
- by mifi_write_start(). */
-void mifi_write_end(t_mifi_stream *sp)
+static void mifiwrite_reset(t_mifiwrite *mw)
{
- if (sp->s_autoalloc)
- {
- /* LATER adjust ntracks field in a file header, but do so only if
- a stream was autoallocated -- number of tracks must be known
- before calling mifi_write_start() for a preexisting stream. */
- }
- if (sp->s_fp) fclose(sp->s_fp);
- if (sp->s_autoalloc) mifi_stream_free(sp);
+ mw->mw_trackndx = 0;
+ mw->mw_trackdirty = 0;
+ mw->mw_fp = 0;
+ mw->mw_format = 1; /* LATER settable parameter */
+ mw->mw_nframes = 0;
+ mw->mw_meternum = 4;
+ mw->mw_meterden = 4;
+ mw->mw_status = 0;
+ mw->mw_channel = 0;
+ mw->mw_trackbytes = 0;
+ mifiwrite_updateticks(mw);
}
-int mifi_write_start_track(t_mifi_stream *sp)
+void mifiwrite_sethardticks(t_mifiwrite *mw, int beatticks)
{
- t_mifi_trackheader header;
- /* LATER check if (sp->s_track < sp->s_hdtracks)... after some thinking */
- strncpy(header.h_type, "MTrk", 4);
- header.h_length = 0;
- sp->s_trackid = sp->s_track_id(sp->s_track);
- sp->s_track++;
- sp->s_newtrack = 1;
- sp->s_status = sp->s_channel = 0;
- sp->s_bytesleft = 0;
- sp->s_time = 0;
- if (fwrite(&header, 1,
- MIFI_TRACKHEADER_SIZE, sp->s_fp) != MIFI_TRACKHEADER_SIZE)
- {
- post("unable to write midifile header");
- return (0);
- }
- return (1);
+ mw->mw_ticks.wt_beatticks =
+ (beatticks > 0 && beatticks < MIFI_MAXBEATTICKS ?
+ beatticks : MIFIHARD_DEFBEATTICKS);
+ mifiwrite_updateticks(mw);
}
-/* append eot meta and update length field in a track header */
-int mifi_write_adjust_track(t_mifi_stream *sp, uint32 eotdelay)
+void mifiwrite_setuserticks(t_mifiwrite *mw, double wholeticks)
{
- t_mifi_event *ep = sp->s_auxeve;
- long skip;
- uint32 length;
- ep->e_delay = eotdelay;
- ep->e_status = MIFI_EVENT_META;
- ep->e_meta = MIFI_META_EOT;
- ep->e_length = 0;
- if (!mifi_write_event(sp, ep))
- return (0);
- skip = sp->s_bytesleft + 4;
- length = bifi_swap4(sp->s_bytesleft);
-#ifdef MIFI_DEBUG
- post("adjusting track size to %d", sp->s_bytesleft);
-#endif
- /* LATER add sanity check (compare to saved filepos) */
- if (skip > 4 &&
- fseek(sp->s_fp, -skip, SEEK_CUR) < 0 ||
- fwrite(&length, 1, 4, sp->s_fp) != 4 ||
- fseek(sp->s_fp, 0, SEEK_END) < 0)
- {
- post("unable to adjust length field in midifile track header \
- (length %d)", sp->s_bytesleft);
- return (0);
- }
- return (1);
+ mw->mw_ticks.wt_wholeticks = (wholeticks > MIFI_TICKEPSILON ?
+ wholeticks : MIFIUSER_DEFWHOLETICKS);
+ mw->mw_ticks.wt_deftempo = mw->mw_ticks.wt_wholeticks *
+ (MIFIUSER_DEFTEMPO / MIFIUSER_DEFWHOLETICKS);
+ mifiwrite_updateticks(mw);
+}
+
+void mifiwrite_setusertempo(t_mifiwrite *mw, double tickspersec)
+{
+ mw->mw_tempo = (tickspersec > MIFI_TICKEPSILON ?
+ ((double)MIFIHARD_DEFTEMPO * mw->mw_ticks.wt_deftempo) /
+ tickspersec : MIFIHARD_DEFTEMPO);
+ mifiwrite_updateticks(mw);
}
/* LATER analyse shrinking effect caused by truncation */
-int mifi_write_event(t_mifi_stream *sp, t_mifi_event *ep)
+static int mifiwrite_putnextevent(t_mifiwrite *mw, t_mifievent *ep)
{
uchar buf[3], *ptr = buf;
- size_t size = mifi_writevarlen(sp, ep->e_delay);
+ size_t size = mifiwrite_putvarlen(mw, ep->e_delay);
if (!size)
return (0);
- sp->s_bytesleft += size;
- if (MIFI_IS_CHANNEL(ep->e_status))
+ mw->mw_trackbytes += size;
+ if (MIFI_ISCHANNEL(ep->e_status))
{
- if ((*ptr = ep->e_status | ep->e_channel) == sp->s_status)
+ if ((*ptr = ep->e_status | ep->e_channel) == mw->mw_status)
size = 1;
else
{
- sp->s_status = *ptr++;
+ mw->mw_status = *ptr++;
size = 2;
}
*ptr++ = ep->e_data[0];
- if (!MIFI_ONE_DATABYTE(ep->e_status))
+ if (!MIFI_ONEDATABYTE(ep->e_status))
{
*ptr = ep->e_data[1];
size++;
}
ptr = buf;
}
- else if (ep->e_status == MIFI_EVENT_META)
+ else if (ep->e_status == MIFIEVENT_META)
{
- sp->s_status = 0; /* sysex and meta-events cancel any running status */
+ mw->mw_status = 0; /* sysex and meta cancel any running status */
buf[0] = ep->e_status;
buf[1] = ep->e_meta;
- if (fwrite(buf, 1, 2, sp->s_fp) != 2)
+ if (fwrite(buf, 1, 2, mw->mw_fp) != 2)
return (0);
- sp->s_bytesleft += 2;
- size = mifi_writevarlen(sp, (uint32)(ep->e_length));
+ mw->mw_trackbytes += 2;
+ size = mifiwrite_putvarlen(mw, ep->e_length);
if (!size)
return (0);
- sp->s_bytesleft += size;
+ mw->mw_trackbytes += size;
size = ep->e_length;
ptr = ep->e_data;
}
else return (0);
- if (fwrite(ptr, 1, size, sp->s_fp) != size)
+ if (size)
+ {
+ if (fwrite(ptr, 1, size, mw->mw_fp) != size)
+ return (0);
+ mw->mw_trackbytes += size;
+ }
+ return (1);
+}
+
+/* open a midi file for saving, write the header */
+int mifiwrite_open(t_mifiwrite *mw, const char *filename,
+ const char *dirname, int ntracks, int complain)
+{
+ char errmess[MAXPDSTRING], fnamebuf[MAXPDSTRING];
+ if (ntracks < 1 || ntracks > MIFI_MAXTRACKS)
+ {
+ bug("mifiwrite_open 1");
+ complain = 0;
+ goto wopenfailed;
+ }
+ mw->mw_ntracks = ntracks;
+ mifiwrite_reset(mw);
+ if (mw->mw_format == 0)
+ {
+ if (mw->mw_ntracks != 1)
+ { /* LATER consider replacing with a warning */
+ bug("mifiwrite_open 2");
+ complain = 0;
+ goto wopenfailed;
+ }
+#ifdef MIFI_VERBOSE
+ post("writing single-track midi file \"%s\"", filename);
+#endif
+ }
+#ifdef MIFI_VERBOSE
+ else post("writing midi file \"%s\" (%d tracks)", filename, mw->mw_ntracks);
+#endif
+ strncpy(mw->mw_header.h_type, "MThd", 4);
+ mw->mw_header.h_length = mifi_swap4(MIFIHARD_HEADERDATASIZE);
+ mw->mw_header.h_format = mifi_swap2(mw->mw_format);
+ mw->mw_header.h_ntracks = mifi_swap2(mw->mw_ntracks);
+ if (mw->mw_nframes)
+ mw->mw_header.h_division =
+ ((mw->mw_nframes << 8) | mw->mw_ticks.wt_beatticks) | 0x8000;
+ else
+ mw->mw_header.h_division = mw->mw_ticks.wt_beatticks & 0x7fff;
+ mw->mw_header.h_division = mifi_swap2(mw->mw_header.h_division);
+ fnamebuf[0] = 0;
+ if (*dirname)
+ strcat(fnamebuf, dirname), strcat(fnamebuf, "/");
+ strcat(fnamebuf, filename);
+ sys_bashfilename(fnamebuf, fnamebuf);
+ if (!(mw->mw_fp = fopen(fnamebuf, "wb")))
+ {
+ strcpy(errmess, "cannot open");
+ goto wopenfailed;
+ }
+ if (fwrite(&mw->mw_header, 1,
+ MIFIHARD_HEADERSIZE, mw->mw_fp) < MIFIHARD_HEADERSIZE)
+ {
+ strcpy(errmess, "cannot write header of");
+ goto wopenfailed;
+ }
+ return (1);
+wopenfailed:
+ if (complain)
+ mifi_error(mw->mw_owner, "%s file \"%s\" (errno %d: %s)",
+ errmess, filename, errno, strerror(errno));
+ if (mw->mw_fp)
+ {
+ fclose(mw->mw_fp);
+ mw->mw_fp = 0;
+ }
+ return (0);
+}
+
+/* append eot meta and update length field in a track header */
+static int mifiwrite_adjusttrack(t_mifiwrite *mw, uint32 eotdelay, int complain)
+{
+ t_mifievent *ep = &mw->mw_event;
+ long skip;
+ uint32 length;
+ mw->mw_trackdirty = 0;
+ ep->e_delay = eotdelay;
+ ep->e_status = MIFIEVENT_META;
+ ep->e_meta = MIFIMETA_EOT;
+ ep->e_length = 0;
+ if (!mifiwrite_putnextevent(mw, ep))
return (0);
- sp->s_bytesleft += size;
+ skip = mw->mw_trackbytes + 4;
+ length = mifi_swap4(mw->mw_trackbytes);
+#ifdef MIFI_DEBUG
+ post("adjusting track size to %d", mw->mw_trackbytes);
+#endif
+ /* LATER add sanity check (compare to saved filepos) */
+ if (skip > 4 &&
+ fseek(mw->mw_fp, -skip, SEEK_CUR) < 0 ||
+ fwrite(&length, 1, 4, mw->mw_fp) != 4 ||
+ fseek(mw->mw_fp, 0, SEEK_END) < 0)
+ {
+ if (complain)
+ mifi_error(mw->mw_owner,
+ "unable to adjust length field to %d in a midi file\
+ track header (errno %d: %s)", mw->mw_trackbytes, errno, strerror(errno));
+ return (0);
+ }
return (1);
}
+
+int mifiwrite_opentrack(t_mifiwrite *mw, char *trackname, int complain)
+{
+ t_mifitrackheader th;
+ if (mw->mw_trackdirty && !mifiwrite_adjusttrack(mw, 0, complain))
+ return (0);
+ if (mw->mw_trackndx > mw->mw_ntracks)
+ return (0);
+ else if (mw->mw_trackndx++ == mw->mw_ntracks)
+ {
+ bug("mifiwrite_opentrack");
+ return (0);
+ }
+ strncpy(th.th_type, "MTrk", 4);
+ th.th_length = 0;
+ mw->mw_status = mw->mw_channel = 0;
+ mw->mw_trackbytes = 0;
+ if (fwrite(&th, 1, MIFIHARD_TRACKHEADERSIZE,
+ mw->mw_fp) != MIFIHARD_TRACKHEADERSIZE)
+ {
+ if (complain)
+ mifi_error(mw->mw_owner,
+ "unable to write midi file header (errno %d: %s)",
+ errno, strerror(errno));
+ return (0);
+ }
+ if (trackname)
+ {
+ if (!mifiwrite_textevent(mw, 0., MIFIMETA_TRACKNAME, trackname))
+ {
+ if (complain)
+ mifi_error(mw->mw_owner,
+ "unable to write midi file track name \"%s\" (errno %d: %s)",
+ trackname, errno, strerror(errno));
+ return (0);
+ }
+ }
+ mw->mw_trackdirty = 1;
+ return (1);
+}
+
+/* calling this is optional (if skipped, enddelay defaults to 0.) */
+int mifiwrite_closetrack(t_mifiwrite *mw, double enddelay, int complain)
+{
+ if (mw->mw_trackdirty)
+ {
+ uint32 eotdelay = (uint32)(enddelay * mw->mw_ticks.wt_mscoef);
+ return (mifiwrite_adjusttrack(mw, eotdelay, complain));
+ }
+ else
+ {
+ bug("mifiwrite_closetrack");
+ return (0);
+ }
+}
+
+int mifiwrite_textevent(t_mifiwrite *mw, double delay,
+ unsigned type, char *text)
+{
+ t_mifievent *ep = &mw->mw_event;
+ if (!mifievent_settext(ep, type, text))
+ return (0);
+ ep->e_delay = (uint32)(delay * mw->mw_ticks.wt_mscoef);
+ return (mifiwrite_putnextevent(mw, ep));
+}
+
+int mifiwrite_channelevent(t_mifiwrite *mw, double delay, unsigned status,
+ unsigned channel, unsigned data1, unsigned data2)
+{
+ t_mifievent *ep = &mw->mw_event;
+ int shorter = MIFI_ONEDATABYTE(status);
+ if (!MIFI_ISCHANNEL(status) || channel > 15 || data1 > 127
+ || (!shorter && data2 > 127))
+ {
+ bug("mifiwrite_channelevent");
+ return (0);
+ }
+ ep->e_delay = (uint32)(delay * mw->mw_ticks.wt_mscoef);
+ ep->e_status = (uchar)(status & 0xf0);
+ ep->e_channel = (uchar)channel;
+ ep->e_data[0] = (uchar)data1;
+ if (shorter)
+ ep->e_length = 1;
+ else
+ {
+ ep->e_data[1] = (uchar)data2;
+ ep->e_length = 2;
+ }
+ return (mifiwrite_putnextevent(mw, ep));
+}
+
+void mifiwrite_close(t_mifiwrite *mw)
+{
+ if (mw->mw_trackdirty)
+ mifiwrite_adjusttrack(mw, 0, 0);
+ if (mw->mw_fp)
+ {
+ fclose(mw->mw_fp);
+ mw->mw_fp = 0;
+ }
+}
+
+void mifiwrite_free(t_mifiwrite *mw)
+{
+ mifiwrite_close(mw);
+ if (mw->mw_event.e_data != mw->mw_event.e_dataini)
+ freebytes(mw->mw_event.e_data, mw->mw_event.e_datasize);
+ freebytes(mw, sizeof(*mw));
+}
+
+t_mifiwrite *mifiwrite_new(t_pd *owner)
+{
+ t_mifiwrite *mw = getbytes(sizeof(*mw));
+ mifi_initialize();
+ mw->mw_owner = owner;
+ mw->mw_ntracks = 0;
+ mw->mw_tempo = MIFIHARD_DEFTEMPO;
+ mifievent_initialize(&mw->mw_event, MIFIEVENT_NALLOC);
+ mifiwrite_resetticks(mw);
+ mifiwrite_reset(mw);
+ return (mw);
+}
diff --git a/shared/common/mifi.h b/shared/common/mifi.h
index 5524993..1163a5d 100644
--- a/shared/common/mifi.h
+++ b/shared/common/mifi.h
@@ -1,84 +1,97 @@
-/* Copyright (c) 2001-2003 krzYszcz and others.
+/* Copyright (c) 2004 krzYszcz and others.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
-/* reading/writing midifiles, a prototype version */
-
#ifndef __MIFI_H__
#define __MIFI_H__
-/* event types, as returned by mifi_read_event() */
-#define MIFI_READ_FATAL -3 /* unexpected eof, error in last track, or file error */
-#define MIFI_READ_EOF -2 /* regular eof */
-#define MIFI_READ_SKIP -1 /* error and successful skip to the next track */
-#define MIFI_META_SEQNUM 0
-#define MIFI_META_TEXT 1
-#define MIFI_META_COPYRIGHT 2
-#define MIFI_META_TRACKNAME 3
-#define MIFI_META_INSTRUMENT 4
-#define MIFI_META_LYRIC 5
-#define MIFI_META_MARKER 6
-#define MIFI_META_CUE 7
-#define MIFI_META_MAXPRINTABLE 15 /* 1..15 are various text meta-events */
-#define MIFI_META_CHANNEL 0x20 /* channel prefix */
-#define MIFI_META_EOT 0x2f /* end of track */
-#define MIFI_META_TEMPO 0x51
-#define MIFI_META_SMPTE 0x54 /* SMPTE offset */
-#define MIFI_META_TIMESIG 0x58 /* time signature */
-#define MIFI_META_KEYSIG 0x59 /* key signature */
+EXTERN_STRUCT _mifiread;
+#define t_mifiread struct _mifiread
+EXTERN_STRUCT _mifiwrite;
+#define t_mifiwrite struct _mifiwrite
+
+typedef int (*t_mifireadhook)(t_mifiread *mf, void *hookdata, int evtype);
+
+#define MIFI_MAXTRACKS 0x7fff
+#define MIFI_MAXBEATTICKS 0x7fff
+
+/* event types, as returned by mifiread_nextevent(), ... */
+
+#define MIFIREAD_FATAL -3 /* unexpected eof, last track's or file error */
+#define MIFIREAD_EOF -2 /* regular eof */
+#define MIFIREAD_SKIP -1 /* error and successful skip to the next track */
+
+#define MIFIMETA_SEQNUM 0
+#define MIFIMETA_TEXT 1
+#define MIFIMETA_COPYRIGHT 2
+#define MIFIMETA_TRACKNAME 3
+#define MIFIMETA_INSTRUMENT 4
+#define MIFIMETA_LYRIC 5
+#define MIFIMETA_MARKER 6
+#define MIFIMETA_CUE 7
+#define MIFIMETA_MAXPRINTABLE 15 /* 1..15 are various text meta-events */
+#define MIFIMETA_CHANNEL 0x20 /* channel prefix */
+#define MIFIMETA_EOT 0x2f /* end of track */
+#define MIFIMETA_TEMPO 0x51
+#define MIFIMETA_SMPTE 0x54 /* SMPTE offset */
+#define MIFIMETA_TIMESIG 0x58 /* time signature */
+#define MIFIMETA_KEYSIG 0x59 /* key signature */
+
/* ...channel status codes go here, too obvious to #define... */
-#define MIFI_SYSEX_FIRST 0xf0
-#define MIFI_SYSEX_NEXT 0xf7
-/* this code is not returned as an event type, but in e_status of t_mifi_event */
-#define MIFI_EVENT_META 0xff
+
+#define MIFISYSEX_FIRST 0xf0
+#define MIFISYSEX_NEXT 0xf7
+
+/* this code is not returned as an event type, but in e_status of t_mifievent */
+#define MIFIEVENT_META 0xff
/* true if one of channel messages */
-#define MIFI_IS_CHANNEL(status) (((status) & 0x80) && (status) < 0xf0)
+#define MIFI_ISCHANNEL(status) (((status) & 0x80) && (status) < 0xf0)
/* true if one of the two shorter channel messages */
-#define MIFI_ONE_DATABYTE(status) (((status) & 0xe0) == 0xc0)
-
-/* derived from t_squeve */
-typedef struct _mifi_event
-{
- uint32 e_length;
- uchar *e_data;
- size_t e_bufsize;
- uint32 e_delay;
- uchar e_status;
- uchar e_channel;
- uchar e_meta; /* meta-event type */
-} t_mifi_event;
-
-/* This structure holds midi data stream properties, i.e. both the info stored
- in midifile header, and the current state according to position in a stream. */
-/* LATER clean up t_sq and derive t_mifi_stream */
-typedef struct _sq t_mifi_stream;
-
-/* prototypes of public interface routines */
-
-t_mifi_event *mifi_event_new(void);
-void mifi_event_free(t_mifi_event *ep);
-int mifi_event_settext(t_mifi_event *ep, int type, char *text);
-void mifi_event_printmeta(t_mifi_event *ep);
-
-t_mifi_stream *mifi_stream_new(void);
-void mifi_stream_reset(t_mifi_stream *sp);
-void mifi_stream_free(t_mifi_stream *sp);
-
-t_mifi_stream *mifi_read_start(t_mifi_stream *sp,
- const char *filename, const char *dirname,
- int complain);
-int mifi_read_restart(t_mifi_stream *sp);
-void mifi_read_end(t_mifi_stream *sp);
-int mifi_read_event(t_mifi_stream *sp, t_mifi_event *ep);
-int mifi_read_analyse(t_mifi_stream *sp);
-int mifi_read_doit(t_mifi_stream *sp);
-
-t_mifi_stream *mifi_write_start(t_mifi_stream *sp,
- const char *filename, const char *dirname);
-void mifi_write_end(t_mifi_stream *sp);
-int mifi_write_start_track(t_mifi_stream *sp);
-int mifi_write_adjust_track(t_mifi_stream *sp, uint32 eotdelay);
-int mifi_write_event(t_mifi_stream *sp, t_mifi_event *ep);
+#define MIFI_ONEDATABYTE(status) (((status) & 0xe0) == 0xc0)
+
+int mifiread_getnevents(t_mifiread *mr);
+int mifiread_getntempi(t_mifiread *mr);
+int mifiread_gethdtracks(t_mifiread *mr);
+int mifiread_getformat(t_mifiread *mr);
+int mifiread_getnframes(t_mifiread *mr);
+int mifiread_getbeatticks(t_mifiread *mr);
+double mifiread_getdeftempo(t_mifiread *mr);
+
+int mifiread_getbarindex(t_mifiread *mr);
+double mifiread_getbarspan(t_mifiread *mr);
+double mifiread_gettick(t_mifiread *mr);
+double mifiread_getscoretime(t_mifiread *mr);
+double mifiread_gettempo(t_mifiread *mr);
+double mifiread_getmscoef(t_mifiread *mr);
+t_symbol *mifiread_gettrackname(t_mifiread *mr);
+unsigned mifiread_getstatus(t_mifiread *mr);
+unsigned mifiread_getdata1(t_mifiread *mr);
+unsigned mifiread_getdata2(t_mifiread *mr);
+unsigned mifiread_getchannel(t_mifiread *mr);
+t_pd *mifiread_getowner(t_mifiread *mr);
+
+t_mifiread *mifiread_new(t_pd *owner);
+void mifiread_setuserticks(t_mifiread *mr, double wholeticks);
+int mifiread_open(t_mifiread *mr, const char *filename,
+ const char *dirname, int complain);
+int mifiread_doit(t_mifiread *mr, t_mifireadhook hook, void *hookdata);
+void mifiread_close(t_mifiread *mr);
+void mifiread_free(t_mifiread *mr);
+
+t_mifiwrite *mifiwrite_new(t_pd *owner);
+void mifiwrite_sethardticks(t_mifiwrite *mw, int beatticks);
+void mifiwrite_setuserticks(t_mifiwrite *mw, double wholeticks);
+void mifiwrite_setusertempo(t_mifiwrite *mw, double tickspersec);
+int mifiwrite_open(t_mifiwrite *mw, const char *filename,
+ const char *dirname, int ntracks, int complain);
+int mifiwrite_opentrack(t_mifiwrite *mw, char *trackname, int complain);
+int mifiwrite_textevent(t_mifiwrite *mw, double delay,
+ unsigned type, char *text);
+int mifiwrite_channelevent(t_mifiwrite *mw, double delay, unsigned status,
+ unsigned channel, unsigned data1, unsigned data2);
+int mifiwrite_closetrack(t_mifiwrite *mw, double enddelay, int complain);
+void mifiwrite_close(t_mifiwrite *mw);
+void mifiwrite_free(t_mifiwrite *mw);
#endif
diff --git a/shared/common/port.c b/shared/common/port.c
index 5ea4dcd..99f8211 100644
--- a/shared/common/port.c
+++ b/shared/common/port.c
@@ -715,8 +715,14 @@ static int imaction_P6_pack(t_port *x, char *arg)
{
t_symbol *s = x->x_inmess[i].a_w.w_symbol;
if (s->s_name[1])
+ {
+ loud_warning(0, "import",
+ "%s's argument '%s' bashed to 's'",
+ port_getsymbol(x, 6)->s_name, s->s_name);
x->x_inmess[i].a_w.w_symbol = gensym("s");
- else switch (*s->s_name) {
+ }
+ else switch (*s->s_name)
+ {
case 'b': case 'f': case 's': case 'l':
break;
case 'i':
@@ -1135,7 +1141,7 @@ secondpass:
while (nslots--)
{
if (slot->s_symbol == insym
- || (inname && loud_matchignorecase(inname, slot->s_name)))
+ || (inname && shared_matchignorecase(inname, slot->s_name)))
{
if (slot->s_subtree)
{