From d5a39ff6469f8762218c00a34f4b0a120a56332b Mon Sep 17 00:00:00 2001 From: "N.N." Date: Wed, 8 Dec 2004 15:40:14 +0000 Subject: various bug-fixes, maxmode, toxy .#args svn path=/trunk/externals/miXed/; revision=2360 --- shared/common/Makefile.sources | 6 +- shared/common/binport.c | 353 ++------- shared/common/binport.h | 2 +- shared/common/dict.c | 57 +- shared/common/dict.h | 5 +- shared/common/loud.c | 288 ++++++- shared/common/loud.h | 29 +- shared/common/mifi.c | 1712 +++++++++++++++++++++++++++------------- shared/common/mifi.h | 157 ++-- shared/common/port.c | 10 +- shared/getridof.baddeps | 18 +- shared/hammer/gui.c | 224 ++++-- shared/hammer/gui.h | 14 +- shared/hammer/tree.c | 494 +++++++++--- shared/hammer/tree.h | 67 +- shared/toxy/scriptlet.c | 143 ++-- 16 files changed, 2338 insertions(+), 1241 deletions(-) (limited to 'shared') 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 #else @@ -11,220 +9,321 @@ #endif #include #include +#include #include #include #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 +#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 +#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) { diff --git a/shared/getridof.baddeps b/shared/getridof.baddeps index 9483b3b..478cf4e 100644 --- a/shared/getridof.baddeps +++ b/shared/getridof.baddeps @@ -3,16 +3,16 @@ Some are inevitable, but others can, and should be removed. unstable/fragile -> common/loud unstable/fringe -> unstable/forky -toxy/plusbob -> common/loud -toxy/scriptlet -> common/loud, common/grow, common/props -sickle/sic -> common/loud -sickle/arsic -> common/loud, common/vefl, sickle/sic, unstable/fragile -hammer/file -> unstable/forky -common/hyphen -> common/dict common/props -> common/grow common/vefl -> common/loud, unstable/fragile +common/binport -> common/lex common/port -> common/loud, common/grow, common/binport, unstable/forky, unstable/fragile, unstable/fringe -common/sofi -> common/bifi -common/mifi -> common/bifi common/sq -common/mfbb -> common/bifi, common/mifi, common/sq, common/squeal +hammer/file -> unstable/forky +sickle/sic -> common/loud +sickle/arsic -> common/loud, common/vefl, sickle/sic, unstable/fragile +toxy/plusbob -> common/loud +toxy/scriptlet -> common/loud, common/grow, common/props +xeq/squ* -> common/loud, common/fi, common/mifi, common/qtree, xeq/squ* +vex/hyphen -> common/dict +vex/sofi -> vex/bifi diff --git a/shared/hammer/gui.c b/shared/hammer/gui.c index ec6add7..7106a0a 100644 --- a/shared/hammer/gui.c +++ b/shared/hammer/gui.c @@ -5,6 +5,7 @@ /* FIXME use guiconnect */ #include +#include #include "m_pd.h" #include "g_canvas.h" #include "hammer/gui.h" @@ -12,7 +13,9 @@ //#define HAMMERGUI_DEBUG static t_class *hammergui_class = 0; -static t_hammergui *sink = 0; +static t_hammergui *hammergui_sink = 0; +static t_symbol *ps_hashhammergui; +static t_symbol *ps__hammergui; static t_symbol *ps__up; static t_symbol *ps__focus; static t_symbol *ps__vised; @@ -26,7 +29,7 @@ static void hammergui_anything(t_hammergui *snk, #ifdef HAMMERGUI_DEBUG startpost("%s", s->s_name); postatom(ac, av); - endpost(); + post(" (sink %x)", (int)snk); #endif } @@ -34,31 +37,36 @@ static void hammergui_anything(t_hammergui *snk, static void hammergui__up(t_hammergui *snk, t_floatarg f) { #ifdef HAMMERGUI_DEBUG - post("_up %g", f); + post("_up %g (sink %x)", f, (int)snk); #endif + if (!snk->g_psmouse) + { + bug("hammergui__up"); + return; + } if ((int)f) { - if (!snk->g_up) + if (!snk->g_isup) { - snk->g_up = 1; - if (snk->g_mouse->s_thing) + snk->g_isup = 1; + if (snk->g_psmouse->s_thing) { t_atom at; SETFLOAT(&at, 1); - pd_typedmess(snk->g_mouse->s_thing, ps__up, 1, &at); + pd_typedmess(snk->g_psmouse->s_thing, ps__up, 1, &at); } } } else { - if (snk->g_up) + if (snk->g_isup) { - snk->g_up = 0; - if (snk->g_mouse->s_thing) + snk->g_isup = 0; + if (snk->g_psmouse->s_thing) { t_atom at; SETFLOAT(&at, 0); - pd_typedmess(snk->g_mouse->s_thing, ps__up, 1, &at); + pd_typedmess(snk->g_psmouse->s_thing, ps__up, 1, &at); } } } @@ -67,28 +75,38 @@ static void hammergui__up(t_hammergui *snk, t_floatarg f) static void hammergui__focus(t_hammergui *snk, t_symbol *s, t_floatarg f) { #ifdef HAMMERGUI_DEBUG - if (s) post("_focus %s %g", s->s_name, f); + post("_focus %s %g (sink %x)", (s ? s->s_name : "???"), f, (int)snk); #endif - if (snk->g_focus->s_thing) + if (!snk->g_psfocus) + { + bug("hammergui__focus"); + return; + } + if (snk->g_psfocus->s_thing) { t_atom at[2]; SETSYMBOL(&at[0], s); SETFLOAT(&at[1], f); - pd_typedmess(snk->g_focus->s_thing, ps__focus, 2, at); + pd_typedmess(snk->g_psfocus->s_thing, ps__focus, 2, at); } } static void hammergui__vised(t_hammergui *snk, t_symbol *s, t_floatarg f) { #ifdef HAMMERGUI_DEBUG - if (s) post("_vised %s %g", s->s_name, f); + post("_vised %s %g (sink %x)", (s ? s->s_name : "???"), f, (int)snk); #endif - if (snk->g_vised->s_thing) + if (!snk->g_psvised) + { + bug("hammergui__vised"); + return; + } + if (snk->g_psvised->s_thing) { t_atom at[2]; SETSYMBOL(&at[0], s); SETFLOAT(&at[1], f); - pd_typedmess(snk->g_vised->s_thing, ps__vised, 2, at); + pd_typedmess(snk->g_psvised->s_thing, ps__vised, 2, at); } #if 0 /* How to be notified about changes of button state, prior to gui objects @@ -101,23 +119,31 @@ static void hammergui__vised(t_hammergui *snk, t_symbol *s, t_floatarg f) static void hammergui_dobindmouse(t_hammergui *snk) { +#ifdef HAMMERGUI_DEBUG + post("dobindmouse (sink %x)", (int)snk); +#endif #if 0 /* How to be notified about changes of button state, prior to gui objects in a canvas? LATER find a reliable way -- delete if failed */ sys_vgui("bind hammertag <> {pd [concat %s _up 0 \\;]}\n", - snk->g_gui->s_name); + snk->g_psgui->s_name); sys_vgui("bind hammertag <> {pd [concat %s _up 1 \\;]}\n", - snk->g_gui->s_name); + snk->g_psgui->s_name); #endif sys_vgui("bind all <> {pd [concat %s _up 0 \\;]}\n", - snk->g_gui->s_name); + snk->g_psgui->s_name); sys_vgui("bind all <> {pd [concat %s _up 1 \\;]}\n", - snk->g_gui->s_name); + snk->g_psgui->s_name); } static void hammergui__remouse(t_hammergui *snk) { - if (snk->g_mouse->s_thing) + if (!snk->g_psmouse) + { + bug("hammergui__remouse"); + return; + } + if (snk->g_psmouse->s_thing) { /* if a new master was bound in a gray period, we need to restore gui bindings */ @@ -132,15 +158,20 @@ static void hammergui_dobindfocus(t_hammergui *snk) { sys_vgui("bind Canvas <> \ {if {[hammergui_ispatcher %%W]} \ - {pd [concat %s _focus %%W 1 \\;]}}\n", snk->g_gui->s_name); + {pd [concat %s _focus %%W 1 \\;]}}\n", snk->g_psgui->s_name); sys_vgui("bind Canvas <> \ {if {[hammergui_ispatcher %%W]} \ - {pd [concat %s _focus %%W 0 \\;]}}\n", snk->g_gui->s_name); + {pd [concat %s _focus %%W 0 \\;]}}\n", snk->g_psgui->s_name); } static void hammergui__refocus(t_hammergui *snk) { - if (snk->g_focus->s_thing) + if (!snk->g_psfocus) + { + bug("hammergui__refocus"); + return; + } + if (snk->g_psfocus->s_thing) { /* if a new master was bound in a gray period, we need to restore gui bindings */ @@ -153,17 +184,25 @@ static void hammergui__refocus(t_hammergui *snk) static void hammergui_dobindvised(t_hammergui *snk) { +#ifdef HAMMERGUI_DEBUG + post("dobindvised (sink %x)", (int)snk); +#endif sys_vgui("bind Canvas <> \ {if {[hammergui_ispatcher %%W]} \ - {pd [concat %s _vised %%W 1 \\;]}}\n", snk->g_gui->s_name); + {pd [concat %s _vised %%W 1 \\;]}}\n", snk->g_psgui->s_name); sys_vgui("bind Canvas <> \ {if {[hammergui_ispatcher %%W]} \ - {pd [concat %s _vised %%W 0 \\;]}}\n", snk->g_gui->s_name); + {pd [concat %s _vised %%W 0 \\;]}}\n", snk->g_psgui->s_name); } static void hammergui__revised(t_hammergui *snk) { - if (snk->g_vised->s_thing) + if (!snk->g_psvised) + { + bug("hammergui__revised"); + return; + } + if (snk->g_psvised->s_thing) { /* if a new master was bound in a gray period, we need to restore gui bindings */ @@ -174,9 +213,35 @@ static void hammergui__revised(t_hammergui *snk) } } -static void hammergui_setup(void) +static int hammergui_setup(void) { - hammergui_class = class_new(gensym("_hammergui"), 0, 0, + ps_hashhammergui = gensym("#hammergui"); + ps__hammergui = gensym("_hammergui"); + ps__up = gensym("_up"); + ps__focus = gensym("_focus"); + ps__vised = gensym("_vised"); + if (ps_hashhammergui->s_thing) + { + char *cname = class_getname(*ps_hashhammergui->s_thing); +#ifdef HAMMERGUI_DEBUG + post("'%s' already registered as the global hammergui sink ", + (cname ? cname : "???")); +#endif + if (strcmp(cname, ps__hammergui->s_name)) + { + /* FIXME protect against the danger of someone else + (e.g. receive) binding to #hammergui */ + bug("hammergui_setup"); + return (0); + } + else + { + /* FIXME compatibility test */ + hammergui_class = *ps_hashhammergui->s_thing; + return (1); + } + } + hammergui_class = class_new(ps__hammergui, 0, 0, sizeof(t_hammergui), CLASS_PD | CLASS_NOINLET, 0); class_addanything(hammergui_class, hammergui_anything); @@ -186,13 +251,10 @@ static void hammergui_setup(void) gensym("_refocus"), 0); class_addmethod(hammergui_class, (t_method)hammergui__revised, gensym("_revised"), 0); - ps__up = gensym("_up"); class_addmethod(hammergui_class, (t_method)hammergui__up, ps__up, A_FLOAT, 0); - ps__focus = gensym("_focus"); class_addmethod(hammergui_class, (t_method)hammergui__focus, ps__focus, A_SYMBOL, A_FLOAT, 0); - ps__vised = gensym("_vised"); class_addmethod(hammergui_class, (t_method)hammergui__vised, ps__vised, A_SYMBOL, A_FLOAT, 0); @@ -255,21 +317,25 @@ static void hammergui_setup(void) sys_gui(" bind Canvas <> {}\n"); sys_gui(" pd [concat #hammergui _revised \\;]\n"); sys_gui("}\n"); + return (1); } static int hammergui_validate(int dosetup) { - if (dosetup) + if (dosetup && !hammergui_sink + && (hammergui_class || hammergui_setup())) { - if (!hammergui_class) hammergui_setup(); - if (!sink) + if (ps_hashhammergui->s_thing) + hammergui_sink = (t_hammergui *)ps_hashhammergui->s_thing; + else { - sink = (t_hammergui *)pd_new(hammergui_class); - sink->g_gui = gensym("#hammergui"); - pd_bind((t_pd *)sink, sink->g_gui); + hammergui_sink = (t_hammergui *)pd_new(hammergui_class); + hammergui_sink->g_psgui = ps_hashhammergui; + pd_bind((t_pd *)hammergui_sink, + ps_hashhammergui); /* never unbound */ } } - if (hammergui_class && sink) + if (hammergui_class && hammergui_sink) return (1); else { @@ -280,13 +346,13 @@ static int hammergui_validate(int dosetup) static int hammergui_mousevalidate(int dosetup) { - if (dosetup && !sink->g_mouse) + if (dosetup && !hammergui_sink->g_psmouse) { - sink->g_mouse = gensym("#hammermouse"); + hammergui_sink->g_psmouse = gensym("#hammermouse"); sys_gui("event add <> \n"); sys_gui("event add <> \n"); } - if (sink->g_mouse) + if (hammergui_sink->g_psmouse) return (1); else { @@ -297,12 +363,13 @@ static int hammergui_mousevalidate(int dosetup) static int hammergui_pollvalidate(int dosetup) { - if (dosetup && !sink->g_poll) + if (dosetup && !hammergui_sink->g_pspoll) { - sink->g_poll = gensym("#hammerpoll"); - pd_bind((t_pd *)sink, sink->g_poll); /* never unbound */ + hammergui_sink->g_pspoll = gensym("#hammerpoll"); + pd_bind((t_pd *)hammergui_sink, + hammergui_sink->g_pspoll); /* never unbound */ } - if (sink->g_poll) + if (hammergui_sink->g_pspoll) return (1); else { @@ -313,13 +380,13 @@ static int hammergui_pollvalidate(int dosetup) static int hammergui_focusvalidate(int dosetup) { - if (dosetup && !sink->g_focus) + if (dosetup && !hammergui_sink->g_psfocus) { - sink->g_focus = gensym("#hammerfocus"); + hammergui_sink->g_psfocus = gensym("#hammerfocus"); sys_gui("event add <> \n"); sys_gui("event add <> \n"); } - if (sink->g_focus) + if (hammergui_sink->g_psfocus) return (1); else { @@ -330,15 +397,15 @@ static int hammergui_focusvalidate(int dosetup) static int hammergui_visedvalidate(int dosetup) { - if (dosetup && !sink->g_vised) + if (dosetup && !hammergui_sink->g_psvised) { - sink->g_vised = gensym("#hammervised"); + hammergui_sink->g_psvised = gensym("#hammervised"); /* subsequent map events have to be filtered out at the caller's side, LATER investigate */ sys_gui("event add <> \n"); sys_gui("event add <> \n"); } - if (sink->g_vised) + if (hammergui_sink->g_psvised) return (1); else { @@ -349,20 +416,23 @@ static int hammergui_visedvalidate(int dosetup) void hammergui_bindmouse(t_pd *master) { +#ifdef HAMMERGUI_DEBUG + post("bindmouse, master %x", (int)master); +#endif hammergui_validate(1); hammergui_mousevalidate(1); - if (!sink->g_mouse->s_thing) - hammergui_dobindmouse(sink); - pd_bind(master, sink->g_mouse); + if (!hammergui_sink->g_psmouse->s_thing) + hammergui_dobindmouse(hammergui_sink); + pd_bind(master, hammergui_sink->g_psmouse); } void hammergui_unbindmouse(t_pd *master) { if (hammergui_validate(0) && hammergui_mousevalidate(0) - && sink->g_mouse->s_thing) + && hammergui_sink->g_psmouse->s_thing) { - pd_unbind(master, sink->g_mouse); - if (!sink->g_mouse->s_thing) + pd_unbind(master, hammergui_sink->g_psmouse); + if (!hammergui_sink->g_psmouse->s_thing) sys_gui("hammergui_remouse\n"); } else bug("hammergui_unbindmouse"); @@ -384,8 +454,9 @@ void hammergui_startpolling(t_pd *master) { if (hammergui_validate(0) && hammergui_pollvalidate(0)) { - int doinit = (sink->g_poll->s_thing == (t_pd *)sink); - pd_bind(master, sink->g_poll); + int doinit = + (hammergui_sink->g_pspoll->s_thing == (t_pd *)hammergui_sink); + pd_bind(master, hammergui_sink->g_pspoll); if (doinit) { /* visibility hack for msw, LATER rethink */ @@ -400,8 +471,8 @@ void hammergui_stoppolling(t_pd *master) { if (hammergui_validate(0) && hammergui_pollvalidate(0)) { - pd_unbind(master, sink->g_poll); - if (sink->g_poll->s_thing == (t_pd *)sink) + pd_unbind(master, hammergui_sink->g_pspoll); + if (hammergui_sink->g_pspoll->s_thing == (t_pd *)hammergui_sink) { sys_gui("after cancel hammergui_poll\n"); /* visibility hack for msw, LATER rethink */ @@ -415,18 +486,18 @@ void hammergui_bindfocus(t_pd *master) { hammergui_validate(1); hammergui_focusvalidate(1); - if (!sink->g_focus->s_thing) - hammergui_dobindfocus(sink); - pd_bind(master, sink->g_focus); + if (!hammergui_sink->g_psfocus->s_thing) + hammergui_dobindfocus(hammergui_sink); + pd_bind(master, hammergui_sink->g_psfocus); } void hammergui_unbindfocus(t_pd *master) { if (hammergui_validate(0) && hammergui_focusvalidate(0) - && sink->g_focus->s_thing) + && hammergui_sink->g_psfocus->s_thing) { - pd_unbind(master, sink->g_focus); - if (!sink->g_focus->s_thing) + pd_unbind(master, hammergui_sink->g_psfocus); + if (!hammergui_sink->g_psfocus->s_thing) sys_gui("hammergui_refocus\n"); } else bug("hammergui_unbindfocus"); @@ -434,20 +505,23 @@ void hammergui_unbindfocus(t_pd *master) void hammergui_bindvised(t_pd *master) { +#ifdef HAMMERGUI_DEBUG + post("bindvised, master %x", (int)master); +#endif hammergui_validate(1); hammergui_visedvalidate(1); - if (!sink->g_vised->s_thing) - hammergui_dobindvised(sink); - pd_bind(master, sink->g_vised); + if (!hammergui_sink->g_psvised->s_thing) + hammergui_dobindvised(hammergui_sink); + pd_bind(master, hammergui_sink->g_psvised); } void hammergui_unbindvised(t_pd *master) { if (hammergui_validate(0) && hammergui_visedvalidate(0) - && sink->g_vised->s_thing) + && hammergui_sink->g_psvised->s_thing) { - pd_unbind(master, sink->g_vised); - if (!sink->g_vised->s_thing) + pd_unbind(master, hammergui_sink->g_psvised); + if (!hammergui_sink->g_psvised->s_thing) sys_gui("hammergui_revised\n"); } else bug("hammergui_unbindvised"); diff --git a/shared/hammer/gui.h b/shared/hammer/gui.h index 13afd0a..3cab055 100644 --- a/shared/hammer/gui.h +++ b/shared/hammer/gui.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2003 krzYszcz and others. +/* Copyright (c) 2003-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. */ @@ -8,12 +8,12 @@ typedef struct _hammergui { t_pd g_pd; - t_symbol *g_gui; - t_symbol *g_mouse; - t_symbol *g_poll; - t_symbol *g_focus; - t_symbol *g_vised; - int g_up; + t_symbol *g_psgui; + t_symbol *g_psmouse; + t_symbol *g_pspoll; + t_symbol *g_psfocus; + t_symbol *g_psvised; + int g_isup; } t_hammergui; void hammergui_bindmouse(t_pd *master); diff --git a/shared/hammer/tree.c b/shared/hammer/tree.c index 549dd09..9957da7 100644 --- a/shared/hammer/tree.c +++ b/shared/hammer/tree.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2003 krzYszcz and others. +/* Copyright (c) 2003-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,8 +10,10 @@ /* LATER freelist */ +typedef t_hammernode *(*t_hammertree_inserthook)(t_hammernode *); + #ifdef HAMMERTREE_DEBUG -/* returns bh or 0 if failed */ +/* returns black-height or 0 if failed */ static int hammernode_verify(t_hammernode *np) { if (np) @@ -43,55 +45,129 @@ static int hammernode_verify(t_hammernode *np) else return (1); } -/* returns bh or 0 if failed */ +/* returns black-height or 0 if failed */ static int hammertree_verify(t_hammertree *tree) { return (hammernode_verify(tree->t_root)); } -static void hammernode_post(t_hammernode *np) +static int hammernode_checkmulti(t_hammernode *np1, t_hammernode *np2) { - startpost("%d %g %d (", np->n_index, np->n_value, np->n_black); + if (np1 && np2 && np1->n_key == np2->n_key) + { + if (np1 == np2) + bug("hammernode_checkmulti"); + else + return (1); + } + return (0); +} + +static void hammernode_post(t_hammertree *tree, t_hammernode *np, + t_hammernode_vshowhook hook, char *message) +{ + startpost("%d ", np->n_key); + if (tree->t_valuetype == HAMMERTYPE_FLOAT) + startpost("%g ", HAMMERNODE_GETFLOAT(np)); + else if (tree->t_valuetype == HAMMERTYPE_SYMBOL) + startpost("%s ", HAMMERNODE_GETSYMBOL(np)->s_name); + else if (tree->t_valuetype == HAMMERTYPE_ATOM) + { + t_atom *ap = HAMMERNODE_GETATOMPTR(np); + if (ap->a_type == A_FLOAT) + startpost("%g ", ap->a_w.w_float); + else if (ap->a_type == A_SYMBOL) + startpost("%s ", ap->a_w.w_symbol->s_name); + } + else if (hook) + { + char buf[MAXPDSTRING]; + (*hook)(np, buf, MAXPDSTRING); + startpost("%s ", buf); + } + else startpost("0x%08x ", (int)HAMMERNODE_GETSYMBOL(np)); + startpost("%s ", (np->n_black ? "black" : "red")); + + if (hammernode_checkmulti(np, np->n_parent) || + hammernode_checkmulti(np, np->n_left) || + hammernode_checkmulti(np, np->n_right) || + hammernode_checkmulti(np->n_parent, np->n_left) || + hammernode_checkmulti(np->n_parent, np->n_right) || + hammernode_checkmulti(np->n_left, np->n_right)) + startpost("multi "); + + if (np->n_parent) + startpost("(%d -> ", np->n_parent->n_key); + else + startpost("(nul -> "); if (np->n_left) - startpost("%d, ", np->n_left->n_index); + startpost("%d, ", np->n_left->n_key); else startpost("nul, "); if (np->n_right) - post("%d)", np->n_right->n_index); + startpost("%d)", np->n_right->n_key); else - post("nul)"); + startpost("nul)"); + if (message) + post(": %s", message); + else + endpost(); } -/* this is a standard stackless traversal, not the best one, obviously... - (used only for debugging) */ -static int hammertree_traverse(t_hammertree *tree, int postit) +/* Assert a standard stackless traversal producing the same sequence, + as the auxiliary list. */ +static int hammertree_checktraversal(t_hammertree *tree) { - t_hammernode *np = tree->t_root; + t_hammernode *treewalk = tree->t_root; + t_hammernode *listwalk = tree->t_first; int count = 0; - while (np) + while (treewalk) { - t_hammernode *prev = np->n_left; + t_hammernode *prev = treewalk->n_left; if (prev) { - while (prev->n_right && prev->n_right != np) prev = prev->n_right; + while (prev->n_right && prev->n_right != treewalk) + prev = prev->n_right; if (prev->n_right) { prev->n_right = 0; - if (postit) hammernode_post(np); count++; - np = np->n_right; + if (treewalk == listwalk) + listwalk = listwalk->n_next; + else + { + bug("hammertree_checktraversal 1"); + hammernode_post(tree, treewalk, 0, "treewalk"); + if (listwalk) + hammernode_post(tree, listwalk, 0, "listwalk"); + else + post("empty listwalk pointer"); + listwalk = treewalk; + } + treewalk = treewalk->n_right; } else { - prev->n_right = np; - np = np->n_left; + prev->n_right = treewalk; + treewalk = treewalk->n_left; } } else { - if (postit) hammernode_post(np); count++; - np = np->n_right; + if (treewalk == listwalk) + listwalk = listwalk->n_next; + else + { + bug("hammertree_checktraversal 2"); + hammernode_post(tree, treewalk, 0, "treewalk"); + if (listwalk) + hammernode_post(tree, listwalk, 0, "listwalk"); + else + post("empty listwalk pointer"); + listwalk = treewalk; + } + treewalk = treewalk->n_right; } } return (count); @@ -108,22 +184,39 @@ static int hammernode_height(t_hammernode *np) else return (0); } -void hammertree_debug(t_hammertree *tree, int level) +void hammertree_debug(t_hammertree *tree, int level, + t_hammernode_vshowhook hook) { t_hammernode *np; int count; post("------------------------"); - count = hammertree_traverse(tree, level); - if (level > 1) + count = hammertree_checktraversal(tree); + if (level) { - post("***"); - for (np = tree->t_last; np; np = np->n_prev) - startpost("%d ", np->n_index); - endpost(); + for (np = tree->t_first; np; np = np->n_next) + hammernode_post(tree, np, hook, 0); + if (level > 1) + { + post("************"); + for (np = tree->t_last; np; np = np->n_prev) + startpost("%d ", np->n_key); + endpost(); + } } - post("count %d, height %d, root %d:", - count, hammernode_height(tree->t_root), - (tree->t_root ? tree->t_root->n_index : 0)); + if (tree->t_root) + { + t_hammernode *first = tree->t_root, *last = tree->t_root; + while (first->n_left && first->n_left != tree->t_root) + first = first->n_left; + while (last->n_right && last->n_right != tree->t_root) + last = last->n_right; + post("count %d, height %d, root %d", + count, hammernode_height(tree->t_root), tree->t_root->n_key); + post("first %d, root->left* %d, last %d, root->right* %d", + (tree->t_first ? tree->t_first->n_key : 0), first->n_key, + (tree->t_last ? tree->t_last->n_key : 0), last->n_key); + } + else post("empty"); post("...verified (black-height is %d)", hammertree_verify(tree)); post("------------------------"); } @@ -161,33 +254,101 @@ static void hammertree_rrotate(t_hammertree *tree, t_hammernode *np) np->n_parent = target; } -/* returns a newly inserted or already existing node - (or 0 if allocation failed) */ -t_hammernode *hammertree_insert(t_hammertree *tree, int ndx) +static t_hammernode *hammertree_preinserthook(t_hammernode *np) +{ + while (np->n_prev && np->n_prev->n_key == np->n_key) + np = np->n_prev; + if (np->n_left) + { + np = np->n_prev; + if (np->n_right) + { + /* LATER revisit */ + bug("hammertree_preinserthook"); + return (0); /* do nothing */ + } + } + return (np); +} + +static t_hammernode *hammertree_postinserthook(t_hammernode *np) +{ + while (np->n_next && np->n_next->n_key == np->n_key) + np = np->n_next; + if (np->n_right) + { + np = np->n_next; + if (np->n_left) + { + /* LATER revisit */ + bug("hammertree_postinserthook"); + return (0); /* do nothing */ + } + } + return (np); +} + +/* Returns a newly inserted or already existing node (or 0 if allocation + failed). A caller is responsible for assigning a value. If hook is + supplied, it is called iff key is found. In case of key being found + (which means foundp returns 1), a new node is inserted, unless hook is + either empty, or returns null. Hook's nonempty return is the parent + for the new node. It is expected to have no more than one child. */ +static t_hammernode *hammertree_doinsert(t_hammertree *tree, int key, + t_hammertree_inserthook hook, + int *foundp) { t_hammernode *np, *parent, *result; + int leftchild; + *foundp = 0; if (!(np = tree->t_root)) { - if (!(np = getbytes(sizeof(*np)))) + if (!(np = getbytes(tree->t_nodesize))) return (0); - np->n_index = ndx; + np->n_key = key; np->n_black = 1; tree->t_root = tree->t_first = tree->t_last = np; return (np); } do - if (np->n_index == ndx) - return (np); - else - parent = np; - while (np = (ndx < np->n_index ? np->n_left : np->n_right)); - - if (!(np = getbytes(sizeof(*np)))) + { + if (np->n_key == key) + { + *foundp = 1; + if (hook && (parent = (*hook)(np))) + { + if (parent->n_left && parent->n_right) + { + bug("hammertree_insert, callback return 1"); + parent = parent->n_next; + } + if (leftchild = (key < parent->n_key)) + { + if (parent->n_left) + { + bug("hammertree_insert, callback return 2"); + leftchild = 0; + } + } + else if (parent->n_right) + leftchild = 1; + goto addit; + } + else return (np); /* a caller may then keep or replace the value */ + } + else parent = np; + } + while (np = (key < np->n_key ? np->n_left : np->n_right)); + leftchild = (key < parent->n_key); +addit: + /* parent has no more than one child, new node becomes + parent's immediate successor or predecessor */ + if (!(np = getbytes(tree->t_nodesize))) return (0); - np->n_index = ndx; + np->n_key = key; np->n_parent = parent; - if (ndx < parent->n_index) + if (leftchild) { parent->n_left = np; /* update the auxiliary linked list structure */ @@ -269,35 +430,63 @@ t_hammernode *hammertree_insert(t_hammertree *tree, int ndx) } /* assuming that requested node exists */ -void hammertree_delete(t_hammertree *tree, t_hammernode *np) +void hammertree_delete(t_hammertree *tree, t_hammernode *gone) { - t_hammernode *gone, *parent, *child; - /* gone is the actual node to be deleted - -- it has to be the parent of no more than one child: */ - if (np->n_left && np->n_right) + t_hammernode *parent; /* parent of gone, after relinking */ + t_hammernode *child; /* gone's only child (or null), after relinking */ + /* gone has to be the parent of no more than one child */ + if (gone->n_left && gone->n_right) { - gone = np->n_next; /* gone always exists */ - child = gone->n_right; /* there is no left child of gone */ - /* gone is not a requested node, so we replace fields to be - deleted with gone's fields: */ - np->n_index = gone->n_index; - np->n_value = gone->n_value; + /* Successor is the new parent of gone's children, and a new child + of gone's parent (if any). Successor always exists in this context, + and it has no left child. The simplistic scheme is to replace + gone's fields with successor's fields, and delete the successor. + We cannot do so, however, because successor may be pointed at... */ + t_hammernode *successor = gone->n_next; + child = successor->n_right; + successor->n_left = gone->n_left; + successor->n_left->n_parent = successor; + if (successor == gone->n_right) + parent = successor; + else + { + /* successor's parent always exists in this context, + successor is the left child of its parent */ + parent = successor->n_parent; + parent->n_left = child; + if (child) /* (sentinel not used) */ + child->n_parent = parent; + successor->n_right = gone->n_right; + successor->n_right->n_parent = successor; + } + if (gone->n_parent) + { + int swp; + if (gone == gone->n_parent->n_left) + gone->n_parent->n_left = successor; + else + gone->n_parent->n_right = successor; + successor->n_parent = gone->n_parent; + swp = gone->n_black; + gone->n_black = successor->n_black; + successor->n_black = swp; + } + else + { + tree->t_root = successor; + successor->n_parent = 0; + gone->n_black = successor->n_black; + successor->n_black = 1; /* LATER rethink */ + } + /* update the auxiliary linked list structure */ - /* np->n_prev is up-to-date */ - if (np->n_prev) - np->n_prev->n_next = np; - else tree->t_first = np; - if (np->n_next = gone->n_next) - np->n_next->n_prev = np; - else tree->t_last = np; + if (successor->n_prev = gone->n_prev) + gone->n_prev->n_next = successor; + else + tree->t_first = successor; } else { - gone = np; - if (gone->n_left) - child = gone->n_left; - else - child = gone->n_right; /* update the auxiliary linked list structure */ if (gone->n_prev) gone->n_prev->n_next = gone->n_next; @@ -307,25 +496,30 @@ void hammertree_delete(t_hammertree *tree, t_hammernode *np) gone->n_next->n_prev = gone->n_prev; else tree->t_last = gone->n_prev; - } - /* connect gone's child with gone's parent */ - if (!(parent = gone->n_parent)) - { - if (tree->t_root = child) + + /* connect gone's child with gone's parent */ + if (gone->n_left) + child = gone->n_left; + else + child = gone->n_right; + if (parent = gone->n_parent) { - child->n_parent = 0; - child->n_black = 1; /* LATER rethink */ + if (child) /* (sentinel not used) */ + child->n_parent = parent; + if (gone == parent->n_left) + parent->n_left = child; + else + parent->n_right = child; } - goto done; - } - else - { - if (child) /* (sentinel not used) */ - child->n_parent = parent; - if (gone == parent->n_left) - parent->n_left = child; else - parent->n_right = child; + { + if (tree->t_root = child) + { + child->n_parent = 0; + child->n_black = 1; /* LATER rethink */ + } + goto done; + } } if (gone->n_black) @@ -431,41 +625,149 @@ void hammertree_delete(t_hammertree *tree, t_hammernode *np) child->n_black = 1; } done: - freebytes(gone, sizeof(*gone)); + freebytes(gone, tree->t_nodesize); #ifdef HAMMERTREE_DEBUG hammertree_verify(tree); #endif } -t_hammernode *hammertree_search(t_hammertree *tree, int ndx) +t_hammernode *hammertree_search(t_hammertree *tree, int key) { t_hammernode *np = tree->t_root; - while (np && np->n_index != ndx) - np = (ndx < np->n_index ? np->n_left : np->n_right); + while (np && np->n_key != key) + np = (key < np->n_key ? np->n_left : np->n_right); return (np); } -t_hammernode *hammertree_closest(t_hammertree *tree, int ndx, int geqflag) +t_hammernode *hammertree_closest(t_hammertree *tree, int key, int geqflag) { t_hammernode *np, *parent; if (!(np = tree->t_root)) return (0); do - if (np->n_index == ndx) + if (np->n_key == key) return (np); else parent = np; - while (np = (ndx < np->n_index ? np->n_left : np->n_right)); + while (np = (key < np->n_key ? np->n_left : np->n_right)); if (geqflag) - return (ndx > parent->n_index ? parent->n_next : parent); + return (key > parent->n_key ? parent->n_next : parent); else - return (ndx < parent->n_index ? parent->n_prev : parent); + return (key < parent->n_key ? parent->n_prev : parent); +} + +t_hammernode *hammertree_insert(t_hammertree *tree, int key, int *foundp) +{ + return (hammertree_doinsert(tree, key, 0, foundp)); +} + +t_hammernode *hammertree_multiinsert(t_hammertree *tree, int key, int fifoflag) +{ + int found; + return (hammertree_doinsert(tree, key, (fifoflag ? + hammertree_postinserthook : + hammertree_preinserthook), &found)); +} + +t_hammernode *hammertree_insertfloat(t_hammertree *tree, int key, t_float f, + int replaceflag) +{ + int found; + t_hammernode *np = hammertree_doinsert(tree, key, 0, &found); + if (np && (!found || replaceflag)) + { + if (tree->t_valuetype == HAMMERTYPE_FLOAT) + { + t_hammernode_float *npf = (t_hammernode_float *)np; + npf->nf_value = f; + } + else if (tree->t_valuetype == HAMMERTYPE_ATOM) + { + t_hammernode_atom *npa = (t_hammernode_atom *)np; + t_atom *ap = &npa->na_value; + SETFLOAT(ap, f); + } + else bug("hammertree_insertfloat"); + } + return (np); +} + +t_hammernode *hammertree_insertsymbol(t_hammertree *tree, int key, t_symbol *s, + int replaceflag) +{ + int found; + t_hammernode *np = hammertree_doinsert(tree, key, 0, &found); + if (np && (!found || replaceflag)) + { + if (tree->t_valuetype == HAMMERTYPE_SYMBOL) + { + t_hammernode_symbol *nps = (t_hammernode_symbol *)np; + nps->ns_value = s; + } + else if (tree->t_valuetype == HAMMERTYPE_ATOM) + { + t_hammernode_atom *npa = (t_hammernode_atom *)np; + t_atom *ap = &npa->na_value; + SETSYMBOL(ap, s); + } + else bug("hammertree_insertsymbol"); + } + return (np); +} + +t_hammernode *hammertree_insertatom(t_hammertree *tree, int key, t_atom *ap, + int replaceflag) +{ + int found; + t_hammernode *np = hammertree_doinsert(tree, key, 0, &found); + if (np && (!found || replaceflag)) + { + if (tree->t_valuetype == HAMMERTYPE_ATOM) + { + t_hammernode_atom *npa = (t_hammernode_atom *)np; + npa->na_value = *ap; + } + else bug("hammertree_insertatom"); + } + return (np); } /* LATER preallocate 'freecount' nodes */ -void hammertree_init(t_hammertree *tree, int freecount) +static void hammertree_doinit(t_hammertree *tree, t_hammertype vtype, + size_t nodesize, int freecount) { tree->t_root = tree->t_first = tree->t_last = 0; + tree->t_valuetype = vtype; + tree->t_nodesize = nodesize; +} + +void hammertree_inittyped(t_hammertree *tree, + t_hammertype vtype, int freecount) +{ + size_t nsize; + switch (vtype) + { + case HAMMERTYPE_FLOAT: + nsize = sizeof(t_hammernode_float); + break; + case HAMMERTYPE_SYMBOL: + nsize = sizeof(t_hammernode_symbol); + break; + case HAMMERTYPE_ATOM: + nsize = sizeof(t_hammernode_atom); + break; + default: + bug("hammertree_inittyped"); + vtype = HAMMERTYPE_ILLEGAL; + nsize = sizeof(t_hammernode); + } + hammertree_doinit(tree, vtype, nsize, freecount); +} + +void hammertree_initcustom(t_hammertree *tree, + size_t nodesize, int freecount) +{ + hammertree_doinit(tree, HAMMERTYPE_CUSTOM, nodesize, freecount); } /* LATER keep and/or preallocate 'freecount' nodes (if negative, keep all) */ @@ -476,7 +778,7 @@ void hammertree_clear(t_hammertree *tree, int freecount) { np = next; next = next->n_next; - freebytes(np, sizeof(*np)); + freebytes(np, tree->t_nodesize); } - hammertree_init(tree, 0); + hammertree_doinit(tree, tree->t_valuetype, tree->t_nodesize, 0); } diff --git a/shared/hammer/tree.h b/shared/hammer/tree.h index fcbc036..368fed2 100644 --- a/shared/hammer/tree.h +++ b/shared/hammer/tree.h @@ -1,17 +1,24 @@ -/* Copyright (c) 2003 krzYszcz and others. +/* Copyright (c) 2003-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. */ #ifndef __HAMMERTREE_H__ #define __HAMMERTREE_H__ +#ifdef KRZYSZCZ #define HAMMERTREE_DEBUG +#endif + +typedef enum +{ + HAMMERTYPE_FLOAT, HAMMERTYPE_SYMBOL, HAMMERTYPE_ATOM, + HAMMERTYPE_CUSTOM, HAMMERTYPE_ILLEGAL +} t_hammertype; typedef struct _hammernode { - int n_index; - float n_value; - int n_black; + int n_key; + int n_black; struct _hammernode *n_left; struct _hammernode *n_right; struct _hammernode *n_parent; @@ -19,19 +26,61 @@ typedef struct _hammernode struct _hammernode *n_next; } t_hammernode; +typedef struct _hammernode_float +{ + t_hammernode nf_node; + t_float nf_value; +} t_hammernode_float; + +typedef struct _hammernode_symbol +{ + t_hammernode ns_node; + t_symbol *ns_value; +} t_hammernode_symbol; + +typedef struct _hammernode_atom +{ + t_hammernode na_node; + t_atom na_value; +} t_hammernode_atom; + typedef struct _hammertree { t_hammernode *t_root; t_hammernode *t_first; t_hammernode *t_last; + t_hammertype t_valuetype; + size_t t_nodesize; } t_hammertree; -t_hammernode *hammertree_insert(t_hammertree *tree, int ndx); +#define HAMMERNODE_GETFLOAT(np) (((t_hammernode_float *)(np))->nf_value) +#define HAMMERNODE_GETSYMBOL(np) (((t_hammernode_symbol *)(np))->ns_value) +#define HAMMERNODE_GETATOMPTR(np) (&((t_hammernode_atom *)(np))->na_value) + +typedef void (*t_hammernode_vshowhook)(t_hammernode *, char *, unsigned); + +t_hammernode *hammertree_search(t_hammertree *tree, int key); +t_hammernode *hammertree_closest(t_hammertree *tree, int key, int geqflag); + +t_hammernode *hammertree_insert(t_hammertree *tree, int key, int *foundp); +t_hammernode *hammertree_multiinsert(t_hammertree *tree, int key, int fifoflag); +t_hammernode *hammertree_insertfloat(t_hammertree *tree, int key, t_float f, + int replaceflag); +t_hammernode *hammertree_insertsymbol(t_hammertree *tree, int key, t_symbol *s, + int replaceflag); +t_hammernode *hammertree_insertatom(t_hammertree *tree, int key, t_atom *ap, + int replaceflag); void hammertree_delete(t_hammertree *tree, t_hammernode *np); -t_hammernode *hammertree_search(t_hammertree *tree, int ndx); -t_hammernode *hammertree_closest(t_hammertree *tree, int ndx, int geqflag); -void hammertree_init(t_hammertree *tree, int freecount); + +void hammertree_inittyped(t_hammertree *tree, + t_hammertype vtype, int freecount); +void hammertree_initcustom(t_hammertree *tree, + size_t nodesize, int freecount); void hammertree_clear(t_hammertree *tree, int freecount); -void hammertree_debug(t_hammertree *tree, int level); + +#ifdef HAMMERTREE_DEBUG +void hammertree_debug(t_hammertree *tree, int level, + t_hammernode_vshowhook hook); +#endif #endif diff --git a/shared/toxy/scriptlet.c b/shared/toxy/scriptlet.c index df94e90..be5ff41 100644 --- a/shared/toxy/scriptlet.c +++ b/shared/toxy/scriptlet.c @@ -17,12 +17,13 @@ #include "common/props.h" #include "scriptlet.h" -//#define SCRIPTLET_DEBUG +#define SCRIPTLET_DEBUG -#define SCRIPTLET_INISIZE 1024 -#define SCRIPTLET_MARGIN 64 -#define SCRIPTLET_MAXARGS 9 /* do not increase (parser's constraint) */ -#define SCRIPTLET_MAXPUSH 20000 /* cf SOCKSIZE in t_tkcmd.c, LATER revisit */ +#define SCRIPTLET_INISIZE 1024 +#define SCRIPTLET_INIDOTSIZE 256 +#define SCRIPTLET_MARGIN 64 +#define SCRIPTLET_DOTMARGIN 16 +#define SCRIPTLET_MAXPUSH 20000 /* cf SOCKSIZE in t_tkcmd.c, LATER revisit */ enum { SCRIPTLET_CVOK, SCRIPTLET_CVUNKNOWN, SCRIPTLET_CVMISSING }; @@ -36,15 +37,17 @@ struct _scriptlet t_scriptlet_cvfn s_cvfn; /* if empty, passing resolveall is a bug */ t_canvas *s_cv; /* as returned by cvfn */ int s_cvstate; - int s_locked; /* append lock, for filtering, when reading from file */ - int s_size; - char *s_buffer; - char s_bufini[SCRIPTLET_INISIZE]; - char *s_head; /* ptr to the command part of a scriptlet */ - char *s_tail; - char s_separator; /* current separator, set before a new token */ - int s_ac; /* the actual count */ - t_atom s_av[SCRIPTLET_MAXARGS]; /* always padded with zeros (if used) */ + int s_locked; /* append lock, for filtering, when reading from file */ + int s_size; + char *s_buffer; + char s_bufini[SCRIPTLET_INISIZE]; + char *s_head; /* ptr to the command part of a scriptlet */ + char *s_tail; + char s_separator; /* current separator, set before a new token */ + int s_dotsize; + int s_dotoffset; + char *s_dotbuffer; + char s_dotbufini[SCRIPTLET_INIDOTSIZE]; }; static t_canvas *scriptlet_canvasvalidate(t_scriptlet *sp, int visedonly) @@ -114,33 +117,85 @@ static int scriptlet_doappend(t_scriptlet *sp, char *buf) return (1); } -static char *scriptlet_dedot(t_scriptlet *sp, char *ibuf, char *obuf, +static int scriptlet_dotstring(t_scriptlet *sp, char *st) +{ + int len = strlen(st), + newsize = sp->s_dotoffset + len + SCRIPTLET_DOTMARGIN; + if (newsize > sp->s_dotsize) + { + int nrequested = newsize; + sp->s_dotbuffer = grow_withdata(&nrequested, &sp->s_dotoffset, + &sp->s_dotsize, sp->s_dotbuffer, + SCRIPTLET_INIDOTSIZE, sp->s_dotbufini, + sizeof(*sp->s_dotbuffer)); + if (nrequested != newsize) + { + sp->s_dotoffset = 0; + sp->s_dotbuffer[0] = 0; + return (0); + } + } + strcpy(sp->s_dotbuffer + sp->s_dotoffset, st); + sp->s_dotoffset += len; + return (1); +} + +static int scriptlet_dotfloat(t_scriptlet *sp, float f) +{ + char obuf[32]; + sprintf(obuf, "%g", f); + return (scriptlet_dotstring(sp, obuf)); +} + +static char *scriptlet_dedot(t_scriptlet *sp, char *ibuf, int resolveall, int visedonly, int ac, t_atom *av, t_props *argprops) { int len = 0; + char *obuf = sp->s_dotbuffer; + sp->s_dotoffset = 0; switch (*ibuf) { case '#': - /* ac is ignored -- assuming av is padded to SCRIPTLET_MAXARGS atoms */ if (resolveall) { int which = ibuf[1] - '1'; - if (which >= 0 && which < SCRIPTLET_MAXARGS) + if (which >= 0 && which < 9) { - if (av) + if (which < ac) { - if (av[which].a_type == A_FLOAT) - { - sprintf(obuf, "%g", av[which].a_w.w_float); - len = 2; - } - else if (av[which].a_type == A_SYMBOL) - { - strcpy(obuf, av[which].a_w.w_symbol->s_name); - len = 2; + av += which; + if (av->a_type == A_FLOAT) + sprintf(obuf, "%g", av->a_w.w_float); + else if (av->a_type == A_SYMBOL && av->a_w.w_symbol) + scriptlet_dotstring(sp, av->a_w.w_symbol->s_name); + else + obuf[0] = 0; /* LATER rethink */ + } + else strcpy(obuf, "0"); + len = 2; + } + else if (!strncmp(&ibuf[1], "args", 4)) + { + if (ac) while (1) + { + if (av->a_type == A_FLOAT) + scriptlet_dotfloat(sp, av->a_w.w_float); + else if (av->a_type == A_SYMBOL && av->a_w.w_symbol) + scriptlet_dotstring(sp, av->a_w.w_symbol->s_name); + else + { /* LATER rethink */ + obuf[0] = 0; + break; } + ac--; av++; + if (ac) + sp->s_dotbuffer[sp->s_dotoffset++] = ' '; + else + break; } + else obuf[0] = 0; + len = 5; } else if (argprops) { @@ -157,7 +212,7 @@ static char *scriptlet_dedot(t_scriptlet *sp, char *ibuf, char *obuf, } if (optr = props_getvalue(argprops, ibuf + 1)) { - strcpy(obuf, optr); + scriptlet_dotstring(sp, optr); len = cnt; } if (c) *iptr = c; @@ -379,20 +434,19 @@ static int scriptlet_addstring(t_scriptlet *sp, char *ibuf, { int result = 1; char *bp = ibuf, *ep = ibuf, *ep1; - char dotbuf[256]; /* FIXME requires a growable per-scriptlet buffer. */ if (!sp->s_separator) sp->s_separator = ' '; while (*ep) { if (*ep == '.' - && (ep1 = scriptlet_dedot(sp, ep + 1, dotbuf, - resolveall, visedonly, ac, av, argprops))) + && (ep1 = scriptlet_dedot(sp, ep + 1, resolveall, visedonly, + ac, av, argprops))) { *ep = 0; if (!(result = scriptlet_doappend(sp, bp))) break; *ep = '.'; - if (!(result = scriptlet_doappend(sp, dotbuf))) + if (!(result = scriptlet_doappend(sp, sp->s_dotbuffer))) break; bp = ep = ep1; } @@ -504,20 +558,6 @@ int scriptlet_evaluate(t_scriptlet *insp, t_scriptlet *outsp, int visedonly, int i; char *bp; char separator = 0; - insp->s_ac = ac; - for (i = 0, ap = insp->s_av; i < SCRIPTLET_MAXARGS; i++, ap++) - { - if (ac) - { - if (av->a_type == A_FLOAT || - (av->a_type == A_SYMBOL && av->a_w.w_symbol)) - *ap = *av; - else - SETFLOAT(ap, 0); - ac--; av++; - } - else SETFLOAT(ap, 0); - } /* FIXME pregrowing of the transient scriptlet */ scriptlet_reset(outsp); /* LATER abstract this into scriptlet_parse() */ @@ -546,8 +586,7 @@ int scriptlet_evaluate(t_scriptlet *insp, t_scriptlet *outsp, int visedonly, } } outsp->s_separator = separator; - scriptlet_addstring(outsp, bp, 1, visedonly, - ac, insp->s_av, argprops); + scriptlet_addstring(outsp, bp, 1, visedonly, ac, av, argprops); if (done) break; *ep = c; @@ -703,6 +742,7 @@ int scriptlet_read(t_scriptlet *sp, t_symbol *fn) FILE *fp; char buf[MAXPDSTRING]; post("loading scriptlet file \"%s\"", fn->s_name); + /* FIXME use open_via_path() */ if (sp->s_glist) canvas_makefilename(sp->s_glist, fn->s_name, buf, MAXPDSTRING); else @@ -789,6 +829,9 @@ void scriptlet_free(t_scriptlet *sp) { if (sp->s_buffer != sp->s_bufini) freebytes(sp->s_buffer, sp->s_size * sizeof(*sp->s_buffer)); + if (sp->s_dotbuffer != sp->s_dotbufini) + freebytes(sp->s_dotbuffer, + sp->s_dotsize * sizeof(*sp->s_dotbuffer)); freebytes(sp, sizeof(*sp)); } } @@ -810,6 +853,7 @@ t_scriptlet *scriptlet_new(t_pd *owner, t_symbol *rptarget, t_symbol *cbtarget, sys_gui(" pd [concat $target _rp $::toxy::reply \\;]\n"); sys_gui(" unset ::toxy::reply\n"); sys_gui("}\n"); + configured = 1; } sp->s_owner = owner; sp->s_glist = gl; @@ -819,6 +863,9 @@ t_scriptlet *scriptlet_new(t_pd *owner, t_symbol *rptarget, t_symbol *cbtarget, sp->s_cvfn = cvfn; sp->s_size = SCRIPTLET_INISIZE; sp->s_buffer = sp->s_bufini; + sp->s_dotsize = SCRIPTLET_INIDOTSIZE; + sp->s_dotoffset = 0; + sp->s_dotbuffer = sp->s_dotbufini; scriptlet_reset(sp); } return (sp); -- cgit v1.2.1