diff options
Diffstat (limited to 'shared/common')
-rw-r--r-- | shared/common/Makefile.sources | 6 | ||||
-rw-r--r-- | shared/common/binport.c | 353 | ||||
-rw-r--r-- | shared/common/binport.h | 2 | ||||
-rw-r--r-- | shared/common/dict.c | 57 | ||||
-rw-r--r-- | shared/common/dict.h | 5 | ||||
-rw-r--r-- | shared/common/loud.c | 288 | ||||
-rw-r--r-- | shared/common/loud.h | 29 | ||||
-rw-r--r-- | shared/common/mifi.c | 1712 | ||||
-rw-r--r-- | shared/common/mifi.h | 157 | ||||
-rw-r--r-- | shared/common/port.c | 10 |
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) { |