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 --- Makefile.common | 15 +- ViCious/cyclone/makefile | 21 +- ViCious/cyclone/objects | 20 +- ViCious/cyclone/snapfiles | 1 + ViCious/toxy/snapfiles | 3 +- cyclone/Makefile.objects | 5 +- cyclone/Makefile.sources | 7 +- cyclone/build_counter | 4 +- cyclone/cyclone-shared.include | 6 +- cyclone/hammer/Append.c | 121 ++- cyclone/hammer/Decode.c | 1 + cyclone/hammer/Makefile.objects | 3 +- cyclone/hammer/Table.c | 1 + cyclone/hammer/bangbang.c | 3 + cyclone/hammer/coll.c | 1 + cyclone/hammer/comment.c | 23 +- cyclone/hammer/counter.c | 17 +- cyclone/hammer/cycle.c | 1 + cyclone/hammer/funbuff.c | 83 +- cyclone/hammer/gate.c | 3 + cyclone/hammer/hammer.c | 7 +- cyclone/hammer/maximum.c | 1 + cyclone/hammer/minimum.c | 1 + cyclone/hammer/mtr.c | 13 +- cyclone/hammer/offer.c | 17 +- cyclone/hammer/past.c | 1 + cyclone/hammer/prepend.c | 255 ++++-- cyclone/hammer/prob.c | 1 + cyclone/hammer/seq.c | 574 +++++++------ cyclone/hammer/switch.c | 3 + cyclone/hammer/urn.c | 1 + cyclone/shadow/Makefile | 6 +- cyclone/shadow/Makefile.objects | 1 + cyclone/shadow/Makefile.sources | 1 + cyclone/shadow/cyclone.c | 7 +- cyclone/sickle/Makefile.objects | 2 + cyclone/sickle/buffir.c | 12 +- cyclone/sickle/curve.c | 128 +-- cyclone/sickle/sickle.c | 7 +- 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 ++-- test/cyclone/offer-test.pd | 29 +- test/toxy/setup.wid | 38 +- toxy/Makefile | 1 + toxy/plustot.print.c | 12 +- toxy/widget.c | 189 ++++- toxy/widgettype.c | 87 +- toxy/widgettype.h | 21 +- 62 files changed, 3434 insertions(+), 1899 deletions(-) diff --git a/Makefile.common b/Makefile.common index fd57c44..2184624 100644 --- a/Makefile.common +++ b/Makefile.common @@ -23,12 +23,16 @@ include Makefile.objects default: define_build all ifeq ($(OS_NAME),Linux) -# FIXME -CC = gcc-2.95 X_SUFFIX = pd_linux -DEFINES = -DUNIX -OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer +CC = gcc LFLAGS = -export_dynamic -shared +# FIXME strict-aliasing +OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer -fno-strict-aliasing +ifeq ($(shell whoami),krzYszcz) +DEFINES = -DUNIX -DKRZYSZCZ +else +DEFINES = -DUNIX +endif endif ifeq ($(OS_NAME),MinGW) @@ -40,7 +44,7 @@ LFLAGS = -shared $(PD_DIR)/../bin/pd.dll endif ifeq ($(OS_NAME),Darwin) -CC = gcc2 +CC = gcc X_SUFFIX = pd_darwin DEFINES = -DUNIX -DMACOSX OPT_CFLAGS = -O2 -funroll-loops -fomit-frame-pointer @@ -87,6 +91,7 @@ INCLUDES = -I. -I$(PD_DIR) -I$(SHARED_DIR) WARN_CFLAGS = -Wall -W -Wstrict-prototypes -Werror \ -Wno-unused -Wno-parentheses -Wno-switch + DBG_CFLAGS = CFLAGS = $(WARN_CFLAGS) $(OPT_CFLAGS) $(DEFINES) $(INCLUDES) diff --git a/ViCious/cyclone/makefile b/ViCious/cyclone/makefile index 68fc15c..6fcc05f 100644 --- a/ViCious/cyclone/makefile +++ b/ViCious/cyclone/makefile @@ -25,7 +25,7 @@ LIBS = $(VCLIBDIR)\libc.lib \ !INCLUDE snapfiles !INCLUDE $(SRCDIR)\build_counter -cyclone: cyclone.dll hammer.dll sickle.dll dummies.dll cyclist.exe +cyclone: cyclone.dll hammer.dll sickle.dll dummies.dll maxmode.dll cyclist.exe @cd $(ROOTDIR) @$(ZIPCOMMAND) cyclone-$(CYCLONE_SNAPSHOT).zip $(SNAPFILES) @@ -57,12 +57,21 @@ dummies.dll: $(DUMMIES_OBJECTS) -del $(@:.dll=.exp) -del $(@:.dll=.lib) -cyclist.exe: $(SHAREDDIR)\common\binport.c - -del /S $(SHAREDDIR)\common\binport.obj - -cl $(CFLAGS) $(INCLUDES) /DBINPORT_STANDALONE /o $@ \ - $(VCLIBDIR)\libc.lib $(SHAREDDIR)\common\binport.c +maxmode.dll: $(MAXMODE_OBJECTS) + -link /dll /out:$@ $(LIBS) $** /export:$(@:.dll=_setup) + -@copy $@ $(EXTDIR) + -@move $@ $(BINDIR) + -del $(@:.dll=.exp) + -del $(@:.dll=.lib) + +cyclist.exe: $(SHAREDDIR)\common\binport.c \ + $(SHAREDDIR)\common\lex.c $(SHAREDDIR)\unstable\standalone.c + -del /S $(SHAREDDIR)\common\binport.obj $(SHAREDDIR)\common\lex.obj + -cl $(CFLAGS) $(INCLUDES) /DMIXED_STANDALONE /o $@ \ + $(VCLIBDIR)\libc.lib $(SHAREDDIR)\common\binport.c \ + $(SHAREDDIR)\common\lex.c $(SHAREDDIR)\unstable\standalone.c -@move $@ $(BINDIR) - -del /S binport.obj + -del /S binport.obj lex.obj .c.obj: ; cl /c $(CFLAGS) $(INCLUDES) /Fo$*.obj $*.c diff --git a/ViCious/cyclone/objects b/ViCious/cyclone/objects index 1790c79..c74d47b 100644 --- a/ViCious/cyclone/objects +++ b/ViCious/cyclone/objects @@ -3,6 +3,7 @@ CYCLONE_OBJECTS = \ $(SRCDIR)\shadow\nettles.obj \ $(SHAREDDIR)\common\loud.obj \ $(SHAREDDIR)\common\grow.obj \ + $(SHAREDDIR)\common\lex.obj \ $(SHAREDDIR)\common\binport.obj \ $(SHAREDDIR)\common\port.obj \ $(SHAREDDIR)\hammer\file.obj \ @@ -98,9 +99,8 @@ HAMMER_OBJECTS = $(ALL_HAMMERS) \ $(SHAREDDIR)\common\grow.obj \ $(SHAREDDIR)\common\rand.obj \ $(SHAREDDIR)\common\vefl.obj \ - $(SHAREDDIR)\common\sq.obj \ - $(SHAREDDIR)\common\bifi.obj \ $(SHAREDDIR)\common\mifi.obj \ + $(SHAREDDIR)\common\lex.obj \ $(SHAREDDIR)\common\binport.obj \ $(SHAREDDIR)\common\port.obj \ $(SHAREDDIR)\hammer\file.obj \ @@ -108,8 +108,7 @@ HAMMER_OBJECTS = $(ALL_HAMMERS) \ $(SHAREDDIR)\hammer\tree.obj \ $(SHAREDDIR)\unstable\forky.obj \ $(SHAREDDIR)\unstable\fragile.obj \ - $(SHAREDDIR)\unstable\fringe.obj \ - $(SHAREDDIR)\unstable\loader.obj + $(SHAREDDIR)\unstable\fringe.obj ALL_SICKLES = \ $(SRCDIR)\sickle\abs.obj \ @@ -195,6 +194,8 @@ SICKLE_OBJECTS = $(ALL_SICKLES) \ $(SHAREDDIR)\common\loud.obj \ $(SHAREDDIR)\common\grow.obj \ $(SHAREDDIR)\common\vefl.obj \ + $(SHAREDDIR)\common\clc.obj \ + $(SHAREDDIR)\common\lex.obj \ $(SHAREDDIR)\common\binport.obj \ $(SHAREDDIR)\common\port.obj \ $(SHAREDDIR)\hammer\file.obj \ @@ -202,19 +203,20 @@ SICKLE_OBJECTS = $(ALL_SICKLES) \ $(SHAREDDIR)\sickle\arsic.obj \ $(SHAREDDIR)\unstable\forky.obj \ $(SHAREDDIR)\unstable\fragile.obj \ - $(SHAREDDIR)\unstable\fringe.obj \ - $(SHAREDDIR)\unstable\loader.obj + $(SHAREDDIR)\unstable\fringe.obj DUMMIES_OBJECTS = \ $(SRCDIR)\shadow\dummies.obj \ $(SHAREDDIR)\common\loud.obj \ $(SHAREDDIR)\common\grow.obj \ + $(SHAREDDIR)\common\lex.obj \ $(SHAREDDIR)\common\binport.obj \ $(SHAREDDIR)\common\port.obj \ $(SHAREDDIR)\unstable\forky.obj \ $(SHAREDDIR)\unstable\fragile.obj \ $(SHAREDDIR)\unstable\fringe.obj -CYCLIST_OBJECTS = \ - $(SHAREDDIR)\common\binport.obj - +MAXMODE_OBJECTS = \ + $(SRCDIR)\shadow\maxmode.obj \ + $(SHAREDDIR)\common\loud.obj \ + $(SHAREDDIR)\unstable\loader.obj diff --git a/ViCious/cyclone/snapfiles b/ViCious/cyclone/snapfiles index d5ce6de..d2b2a2f 100644 --- a/ViCious/cyclone/snapfiles +++ b/ViCious/cyclone/snapfiles @@ -3,6 +3,7 @@ SNAPFILES = \ miXed\bin\hammer.dll \ miXed\bin\sickle.dll \ miXed\bin\dummies.dll \ + miXed\bin\maxmode.dll \ miXed\bin\cyclist.exe \ miXed\ViCious\cyclone\makefile \ miXed\ViCious\cyclone\objects \ diff --git a/ViCious/toxy/snapfiles b/ViCious/toxy/snapfiles index d83b373..301180a 100644 --- a/ViCious/toxy/snapfiles +++ b/ViCious/toxy/snapfiles @@ -6,4 +6,5 @@ SNAPFILES = \ miXed\ViCious\toxy\objects \ miXed\ViCious\toxy\snapfiles \ miXed\LICENSE.txt \ - miXed\test\toxy\*.* + miXed\test\toxy*.* \ + miXed\test\toxy\stress\*.* diff --git a/cyclone/Makefile.objects b/cyclone/Makefile.objects index db4f7f3..5199008 100644 --- a/cyclone/Makefile.objects +++ b/cyclone/Makefile.objects @@ -12,12 +12,13 @@ HTREE_OBJECTS = hammer/tree.o common/loud.o HTREEFILEVEFL_OBJECTS = hammer/tree.o hammer/file.o \ common/vefl.o common/loud.o unstable/forky.o unstable/fragile.o HGUI_OBJECTS = hammer/gui.o common/loud.o -HSEQ_OBJECTS = common/sq.o common/bifi.o common/mifi.o \ - hammer/file.o common/grow.o common/loud.o unstable/forky.o +HSEQ_OBJECTS = common/mifi.o hammer/file.o \ + common/grow.o common/loud.o unstable/forky.o SSIC_OBJECTS = sickle/sic.o common/loud.o SFORKY_OBJECTS = sickle/sic.o common/loud.o unstable/forky.o SFRAGILE_OBJECTS = sickle/sic.o common/loud.o unstable/fragile.o SGROW_OBJECTS = common/grow.o sickle/sic.o common/loud.o +SGROWCLC_OBJECTS = common/grow.o common/clc.o sickle/sic.o common/loud.o SGROWFORKY_OBJECTS = common/grow.o sickle/sic.o common/loud.o unstable/forky.o SVEFL_OBJECTS = common/vefl.o sickle/sic.o common/loud.o unstable/fragile.o SARSIC_OBJECTS = sickle/sic.o sickle/arsic.o common/vefl.o \ diff --git a/cyclone/Makefile.sources b/cyclone/Makefile.sources index e9aacd5..0c338fe 100644 --- a/cyclone/Makefile.sources +++ b/cyclone/Makefile.sources @@ -11,7 +11,6 @@ hammer/asin.c \ hammer/Bucket.c \ hammer/cartopol.c \ hammer/cosh.c \ -hammer/counter.c \ hammer/flush.c \ hammer/forward.c \ hammer/fromsymbol.c \ @@ -40,6 +39,7 @@ HLOUD_SOURCES = \ hammer/anal.c \ hammer/bangbang.c \ hammer/Borax.c \ +hammer/counter.c \ hammer/cycle.c \ hammer/decide.c \ hammer/Decode.c \ @@ -182,11 +182,14 @@ sickle/poltocar.c SGROW_TILDE = $(TILDE) SGROW_SOURCES = \ sickle/click.c \ -sickle/curve.c \ sickle/frameaccum.c \ sickle/framedelta.c \ sickle/Line.c +SGROWCLC_TILDE = $(TILDE) +SGROWCLC_SOURCES = \ +sickle/curve.c + SGROWFORKY_TILDE = $(TILDE) SGROWFORKY_SOURCES = \ sickle/Scope.c diff --git a/cyclone/build_counter b/cyclone/build_counter index fa90455..acc65a6 100644 --- a/cyclone/build_counter +++ b/cyclone/build_counter @@ -1,7 +1,7 @@ #define CYCLONE_VERSION "0.1" #define CYCLONE_RELEASE "alpha" -#define CYCLONE_BUILD 49 +#define CYCLONE_BUILD 50 #if 0 -CYCLONE_SNAPSHOT = 0.1-alpha49 +CYCLONE_SNAPSHOT = 0.1-alpha50 #endif diff --git a/cyclone/cyclone-shared.include b/cyclone/cyclone-shared.include index 6604d19..26662ac 100644 --- a/cyclone/cyclone-shared.include +++ b/cyclone/cyclone-shared.include @@ -21,10 +21,8 @@ shared/common/rand.c shared/common/rand.h shared/common/vefl.c shared/common/vefl.h -shared/common/sq.c -shared/common/sq.h -shared/common/bifi.c -shared/common/bifi.h +shared/common/clc.c +shared/common/clc.h shared/common/mifi.c shared/common/mifi.h shared/hammer/file.c diff --git a/cyclone/hammer/Append.c b/cyclone/hammer/Append.c index 0cdea60..ad615d5 100644 --- a/cyclone/hammer/Append.c +++ b/cyclone/hammer/Append.c @@ -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. */ @@ -21,9 +21,17 @@ typedef struct _append int x_entered; int x_auxsize; t_atom *x_auxbuf; + t_pd *x_proxy; } t_append; +typedef struct _appendxy +{ + t_pd xy_pd; + t_append *xy_owner; +} t_appendxy; + static t_class *append_class; +static t_class *appendxy_class; /* Usually a preallocation method is used, except in special cases of: 1) reentrant output request, or 2) an output request which would cause @@ -32,8 +40,7 @@ static t_class *append_class; A separately preallocated output buffer is not used, thus avoiding memcpying of the stored message (a small performance gain when the preallocation method is chosen). Instead, self-invoked 'set' - messages are postponed, using an auxiliary buffer. -*/ + messages are postponed, using an auxiliary buffer. */ /* Any Append's output goes through outlet_anything() -> typedmess() */ @@ -132,9 +139,11 @@ static void append_symbol(t_append *x, t_symbol *s) /* LATER gpointer */ -static void append_set(t_append *x, t_symbol *s, int ac, t_atom *av) +static void append_doset(t_append *x, t_symbol *s, int ac, t_atom *av) { int newsize = ac * 2; + if (s) + newsize += 2; if (newsize > 0) { if (x->x_entered) @@ -147,7 +156,15 @@ static void append_set(t_append *x, t_symbol *s, int ac, t_atom *av) } if (x->x_auxbuf = getbytes(newsize * sizeof(*x->x_auxbuf))) { - memcpy(x->x_auxbuf + ac, av, ac * sizeof(*x->x_auxbuf)); + t_atom *ap = x->x_auxbuf + ac; + if (s) + { + ap++; + SETSYMBOL(ap, s); + ap++; + } + if (ac) + memcpy(ap, av, ac * sizeof(*x->x_auxbuf)); x->x_auxsize = newsize; } } @@ -161,19 +178,86 @@ static void append_set(t_append *x, t_symbol *s, int ac, t_atom *av) APPEND_INISIZE, x->x_messini, sizeof(*x->x_messbuf)); if (sz != newsize) + { ac = sz / 2; /* LATER rethink */ + if (s) + ac--; + } + } + if (s) + { + append_setnatoms(x, ac + 1); + ap = x->x_message; + SETSYMBOL(ap, s); + ap++; + } + else + { + append_setnatoms(x, ac); + ap = x->x_message; } - append_setnatoms(x, ac); - ap = x->x_message; while (ac--) *ap++ = *av++; } } } +static void append_set(t_append *x, t_symbol *s, int ac, t_atom *av) +{ + if (shared_getmaxcompatibility()) + append_doset(x, 0, ac, av); + else + append_anything(x, s, ac, av); +} + +static void appendxy_bang(t_appendxy *xy) +{ + append_doset(xy->xy_owner, 0, 0, 0); /* LATER rethink */ +} + +static void appendxy_float(t_appendxy *xy, t_float f) +{ + t_atom at; + SETFLOAT(&at, f); + append_doset(xy->xy_owner, 0, 1, &at); +} + +static void appendxy_symbol(t_appendxy *xy, t_symbol *s) +{ + t_atom at; + if (!s || s == &s_) + s = &s_symbol; /* LATER rethink */ + SETSYMBOL(&at, s); + append_doset(xy->xy_owner, 0, 1, &at); +} + +static void appendxy_list(t_appendxy *xy, t_symbol *s, int ac, t_atom *av) +{ + if (ac) + append_doset(xy->xy_owner, 0, ac, av); + else + { /* LATER rethink */ + t_atom at; + SETSYMBOL(&at, &s_list); + append_doset(xy->xy_owner, 0, 1, &at); + } +} + +static void appendxy_anything(t_appendxy *xy, t_symbol *s, int ac, t_atom *av) +{ + append_doset(xy->xy_owner, s, ac, av); +} + static void append_free(t_append *x) { if (x->x_messbuf != x->x_messini) freebytes(x->x_messbuf, x->x_size * sizeof(*x->x_messbuf)); + if (x->x_auxbuf) + { + bug("append_free"); /* LATER rethink */ + freebytes(x->x_auxbuf, x->x_auxsize * sizeof(*x->x_auxbuf)); + } + if (x->x_proxy) + pd_free(x->x_proxy); } static void *append_new(t_symbol *s, int ac, t_atom *av) @@ -184,9 +268,20 @@ static void *append_new(t_symbol *s, int ac, t_atom *av) x->x_messbuf = x->x_messini; x->x_auxbuf = 0; x->x_entered = 0; - outlet_new((t_object *)x, &s_anything); append_setnatoms(x, 0); - append_set(x, 0, ac, av); + shared_usecompatibility(); + if (ac) + { + x->x_proxy = 0; + append_doset(x, 0, ac, av); + } + else + { + x->x_proxy = pd_new(appendxy_class); + ((t_appendxy *)x->x_proxy)->xy_owner = x; + inlet_new((t_object *)x, x->x_proxy, 0, 0); + } + outlet_new((t_object *)x, &s_anything); return (x); } @@ -204,4 +299,12 @@ void Append_setup(void) class_addanything(append_class, append_anything); class_addmethod(append_class, (t_method)append_set, gensym("set"), A_GIMME, 0); + + appendxy_class = class_new(gensym("append"), 0, 0, sizeof(t_appendxy), + CLASS_PD | CLASS_NOINLET, 0); + class_addbang(appendxy_class, appendxy_bang); + class_addfloat(appendxy_class, appendxy_float); + class_addsymbol(appendxy_class, appendxy_symbol); + class_addlist(appendxy_class, appendxy_list); + class_addanything(appendxy_class, appendxy_anything); } diff --git a/cyclone/hammer/Decode.c b/cyclone/hammer/Decode.c index 4dee7e0..77d8f38 100644 --- a/cyclone/hammer/Decode.c +++ b/cyclone/hammer/Decode.c @@ -78,6 +78,7 @@ static void *Decode_new(t_floatarg val) nouts = DECODE_DEFOUTS; if (nouts > DECODE_C74MAXOUTS) { + shared_usecompatibility(); loud_incompatible_max(Decode_class, DECODE_C74MAXOUTS, "outlets"); if (!(outs = (t_outlet **)getbytes(nouts * sizeof(*outs)))) return (0); diff --git a/cyclone/hammer/Makefile.objects b/cyclone/hammer/Makefile.objects index 76de16f..78bff7e 100644 --- a/cyclone/hammer/Makefile.objects +++ b/cyclone/hammer/Makefile.objects @@ -6,9 +6,8 @@ common/loud.o \ common/grow.o \ common/rand.o \ common/vefl.o \ -common/sq.o \ -common/bifi.o \ common/mifi.o \ +common/lex.o \ common/binport.o \ common/port.o \ hammer/file.o \ diff --git a/cyclone/hammer/Table.c b/cyclone/hammer/Table.c index e366cbb..e613293 100644 --- a/cyclone/hammer/Table.c +++ b/cyclone/hammer/Table.c @@ -233,6 +233,7 @@ static void tablecommon_doread(t_tablecommon *cc, t_symbol *fn, t_canvas *cv) char buf[MAXPDSTRING]; if (!fn) return; /* CHECKME complaint */ + /* FIXME use open_via_path() */ if (cv || (cv = cc->c_lastcanvas)) /* !cv: 'read' w/o arg */ canvas_makefilename(cv, fn->s_name, buf, MAXPDSTRING); else diff --git a/cyclone/hammer/bangbang.c b/cyclone/hammer/bangbang.c index 695a558..d1eb53a 100644 --- a/cyclone/hammer/bangbang.c +++ b/cyclone/hammer/bangbang.c @@ -47,7 +47,10 @@ static void *bangbang_new(t_floatarg val) if (nouts < BANGBANG_MINOUTS) nouts = BANGBANG_DEFOUTS; if (nouts > BANGBANG_C74MAXOUTS) + { + shared_usecompatibility(); loud_incompatible_max(bangbang_class, BANGBANG_C74MAXOUTS, "outlets"); + } if (nouts > BANGBANG_DEFOUTS) { if (!(outs = (t_outlet **)getbytes(nouts * sizeof(*outs)))) diff --git a/cyclone/hammer/coll.c b/cyclone/hammer/coll.c index 850c2c7..031f6c6 100644 --- a/cyclone/hammer/coll.c +++ b/cyclone/hammer/coll.c @@ -591,6 +591,7 @@ static void collcommon_doread(t_collcommon *cc, t_symbol *fn, t_canvas *cv) char buf[MAXPDSTRING]; if (!fn && !(fn = cc->c_filename)) /* !fn: 'readagain' */ return; + /* FIXME use open_via_path() */ if (cv || (cv = cc->c_lastcanvas)) /* !cv: 'read' w/o arg, 'readagain' */ canvas_makefilename(cv, fn->s_name, buf, MAXPDSTRING); else diff --git a/cyclone/hammer/comment.c b/cyclone/hammer/comment.c index 72e8a57..03d4d03 100644 --- a/cyclone/hammer/comment.c +++ b/cyclone/hammer/comment.c @@ -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. */ @@ -200,6 +200,9 @@ static void comment_validate(t_comment *x, t_glist *glist) static void comment_grabbedkey(void *z, t_floatarg f) { /* LATER think about replacing #key binding/comment_float() with grabbing */ +#ifdef COMMENT_DEBUG + post("comment_grabbedkey %g", f); +#endif } static void comment_dograb(t_comment *x) @@ -520,7 +523,7 @@ static void comment_float(t_comment *x, t_float f) /* LATER delete the box... this causes reentrancy problems now. */ /* glist_delete(x->x_glist, &x->x_text->te_g); */ - return; + goto donefloat; } else if (x->x_selstart && (x->x_selstart == x->x_selend)) x->x_selstart--; @@ -556,6 +559,10 @@ static void comment_float(t_comment *x, t_float f) } } else bug("comment_float"); + donefloat:; +#ifdef COMMENT_DEBUG + post("donefloat"); +#endif } static void comment_list(t_comment *x, t_symbol *s, int ac, t_atom *av) @@ -625,7 +632,7 @@ static void comment_list(t_comment *x, t_symbol *s, int ac, t_atom *av) canvas_unsetcurrent(x->x_glist); canvas_dirty(x->x_glist, 1); clock_delay(x->x_transclock, 0); /* LATER rethink */ - return; + goto donelist; } else if (!strcmp(keysym->s_name, "F5")) { @@ -640,11 +647,15 @@ static void comment_list(t_comment *x, t_symbol *s, int ac, t_atom *av) canvas_unsetcurrent(x->x_glist); canvas_dirty(x->x_glist, 1); binbuf_free(bb); - return; + goto donelist; } - else return; + else goto donelist; comment_update(x); } + donelist:; +#ifdef COMMENT_DEBUG + post("donelist"); +#endif } static void comment_free(t_comment *x) @@ -830,6 +841,8 @@ void comment_setup(void) pd $target _click $target [$cvname canvasx $x] [$cvname canvasy $y]\ [$cvname index $tag @$x,$y] [$cvname bbox $tag]\\;}\n"); + /* LATER think how to conditionally (FORKY_VERSION >= 38) + replace puts with pdtk_post */ sys_gui("proc comment_entext {enc tt} {\n\ set rr [catch {encoding convertfrom $enc $tt} tt1]\n\ if {$rr == 0} {concat $tt1} else {\n\ diff --git a/cyclone/hammer/counter.c b/cyclone/hammer/counter.c index 3117e6c..73cd841 100644 --- a/cyclone/hammer/counter.c +++ b/cyclone/hammer/counter.c @@ -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. */ @@ -11,6 +11,7 @@ of checking -- I will not bother, until there is some feedback. */ #include "m_pd.h" +#include "common/loud.h" #define COUNTER_UP 0 #define COUNTER_DOWN 1 @@ -315,12 +316,16 @@ static void *counter_new(t_floatarg f1, t_floatarg f2, t_floatarg f3) int i2 = (int)f2; int i3 = (int)f3; int i; - static int warned = 0; - if (!warned) + shared_usecompatibility(); + if (shared_getmaxcompatibility()) { - post("warning: counter is not fully compatible, \ -please report differences"); - warned = 1; + static int warned = 0; + if (!warned) + { + post("warning: counter is not fully compatible,\ + please report differences"); + warned = 1; + } } x->x_dir = COUNTER_UP; x->x_inc = 1; /* previous value required by counter_dir() */ diff --git a/cyclone/hammer/cycle.c b/cyclone/hammer/cycle.c index 2e6dbd0..244a64d 100644 --- a/cyclone/hammer/cycle.c +++ b/cyclone/hammer/cycle.c @@ -119,6 +119,7 @@ static void *cycle_new(t_floatarg f1, t_floatarg f2) nouts = CYCLE_DEFOUTS; if (nouts > CYCLE_C74MAXOUTS) { + shared_usecompatibility(); loud_incompatible_max(cycle_class, CYCLE_C74MAXOUTS, "outlets"); /* CHECKED: max clips with an error: ``perhaps you were trying to make an oscillator?'' */ diff --git a/cyclone/hammer/funbuff.c b/cyclone/hammer/funbuff.c index 3ce6080..ff4773b 100644 --- a/cyclone/hammer/funbuff.c +++ b/cyclone/hammer/funbuff.c @@ -1,9 +1,8 @@ -/* 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. */ #include "m_pd.h" -#include "unstable/fragile.h" #include "common/loud.h" #include "common/vefl.h" #include "hammer/tree.h" @@ -49,13 +48,16 @@ static void funbuff_bang(t_funbuff *x) { /* LATER consider using extra fields, updated on the fly */ count = 1; - xmin = np->n_index; - xmax = x->x_tree.t_last->n_index; - ymin = ymax = np->n_value; + xmin = np->n_key; + xmax = x->x_tree.t_last->n_key; + ymin = ymax = HAMMERNODE_GETFLOAT(np); while (np = np->n_next) { - if (np->n_value < ymin) ymin = np->n_value; - else if (np->n_value > ymax) ymax = np->n_value; + t_float f = HAMMERNODE_GETFLOAT(np); + if (f < ymin) + ymin = f; + else if (f > ymax) + ymax = f; count++; } } @@ -75,15 +77,14 @@ static void funbuff_float(t_funbuff *x, t_float f) t_hammernode *np; if (x->x_valueset) { - if (np = hammertree_insert(&x->x_tree, ndx)) - np->n_value = x->x_value; + np = hammertree_insertfloat(&x->x_tree, ndx, x->x_value, 1); x->x_valueset = 0; } else if (np = hammertree_closest(&x->x_tree, ndx, 0)) - funbuff_dooutput(x, np->n_value, x->x_lastdelta); + funbuff_dooutput(x, HAMMERNODE_GETFLOAT(np), x->x_lastdelta); /* CHECKED pointer is updated -- 'next' outputs np also in a !valueset case (it is sent twice) */ - x->x_pointer = np; + x->x_pointer = np; /* FIXME */ x->x_pointerset = 0; } @@ -120,9 +121,10 @@ static void funbuff_min(t_funbuff *x) t_hammernode *np; if (np = x->x_tree.t_first) /* CHECKED nop if empty */ { - t_float result = np->n_value; + t_float result = HAMMERNODE_GETFLOAT(np); while (np = np->n_next) - if (np->n_value < result) result = np->n_value; + if (HAMMERNODE_GETFLOAT(np) < result) + result = HAMMERNODE_GETFLOAT(np); funbuff_dooutput(x, result, x->x_lastdelta); /* CHECKED pointer not updated */ } @@ -134,9 +136,10 @@ static void funbuff_max(t_funbuff *x) t_hammernode *np; if (np = x->x_tree.t_first) /* CHECKED nop if empty */ { - t_float result = np->n_value; + t_float result = HAMMERNODE_GETFLOAT(np); while (np = np->n_next) - if (np->n_value > result) result = np->n_value; + if (HAMMERNODE_GETFLOAT(np) > result) + result = HAMMERNODE_GETFLOAT(np); funbuff_dooutput(x, result, x->x_lastdelta); /* CHECKED pointer not updated */ } @@ -156,10 +159,10 @@ static void funbuff_next(t_funbuff *x) if (x->x_pointerset) x->x_lastdelta = 0; else if (np->n_prev) - x->x_lastdelta = np->n_index - np->n_prev->n_index; + x->x_lastdelta = np->n_key - np->n_prev->n_key; else x->x_lastdelta = 0; /* CHECKED corrupt delta sent here... */ - funbuff_dooutput(x, np->n_value, x->x_lastdelta); + funbuff_dooutput(x, HAMMERNODE_GETFLOAT(np), x->x_lastdelta); x->x_pointer = np->n_next; x->x_pointerset = 0; } @@ -185,10 +188,9 @@ static void funbuff_set(t_funbuff *x, t_symbol *s, int ac, t_atom *av) funbuff_clear(x); /* CHECKED the contents is replaced */ while (ac--) { - t_hammernode *np; - if (np = hammertree_insert(&x->x_tree, (int)av++->a_w.w_float)) - np->n_value = av++->a_w.w_float; - else return; + int ndx = (int)av++->a_w.w_float; + if (!hammertree_insertfloat(&x->x_tree, ndx, av++->a_w.w_float, 1)) + return; ac--; } } @@ -199,6 +201,7 @@ static void funbuff_doread(t_funbuff *x, t_symbol *fn) int ac; t_atom *av; char buf[MAXPDSTRING]; + /* FIXME use open_via_path() */ canvas_makefilename(x->x_canvas, fn->s_name, buf, MAXPDSTRING); binbuf_read(bb, buf, "", 0); if ((ac = binbuf_getnatom(bb)) && @@ -226,7 +229,7 @@ static void funbuff_dowrite(t_funbuff *x, t_symbol *fn) t_hammernode *np; binbuf_addv(bb, "s", gensym("funbuff")); for (np = x->x_tree.t_first; np; np = np->n_next) - binbuf_addv(bb, "if", np->n_index, np->n_value); + binbuf_addv(bb, "if", np->n_key, HAMMERNODE_GETFLOAT(np)); canvas_makefilename(x->x_canvas, fn->s_name, buf, MAXPDSTRING); binbuf_write(bb, buf, "", 0); binbuf_free(bb); @@ -248,7 +251,7 @@ static void funbuff_embedhook(t_pd *z, t_binbuf *bb, t_symbol *bindsym) { binbuf_addv(bb, "ss", bindsym, gensym("set")); for (; np; np = np->n_next) - binbuf_addv(bb, "if", np->n_index, np->n_value); + binbuf_addv(bb, "if", np->n_key, HAMMERNODE_GETFLOAT(np)); binbuf_addsemi(bb); } } @@ -283,11 +286,11 @@ static void funbuff_delete(t_funbuff *x, t_symbol *s, int ac, t_atom *av) int ndx = (int)av->a_w.w_float; t_hammernode *np; if ((np = hammertree_search(&x->x_tree, ndx)) && - (ac == 1 || np->n_value == av[1].a_w.w_float)) + (ac == 1 || HAMMERNODE_GETFLOAT(np) == av[1].a_w.w_float)) { if (np == x->x_pointer) x->x_pointer = 0; /* CHECKED corrupt pointer left here... */ - hammertree_delete(&x->x_tree, np); + hammertree_delete(&x->x_tree, np); /* FIXME */ } /* CHECKED mismatch silently ignored */ } @@ -302,8 +305,8 @@ static void funbuff_find(t_funbuff *x, t_floatarg f) do { /* CHECKED lastdelta preserved */ - if (np->n_value == f) - funbuff_dooutput(x, np->n_index, x->x_lastdelta); + if (HAMMERNODE_GETFLOAT(np) == f) + funbuff_dooutput(x, np->n_key, x->x_lastdelta); } while (np = np->n_next); /* CHECKED no bangout, no complaint if nothing found */ @@ -318,9 +321,9 @@ static void funbuff_dump(t_funbuff *x) { do { - x->x_lastdelta = np->n_value; /* CHECKED */ + x->x_lastdelta = HAMMERNODE_GETFLOAT(np); /* CHECKED */ /* float value preserved (this is incompatible) */ - funbuff_dooutput(x, np->n_index, np->n_value); + funbuff_dooutput(x, np->n_key, x->x_lastdelta); } while (np = np->n_next); /* CHECKED no bangout */ @@ -336,14 +339,14 @@ static void funbuff_dointerp(t_funbuff *x, t_floatarg f, int vsz, t_float *vec) if (trunc > f) trunc--; /* CHECKME negative floats */ if (np1 = hammertree_closest(&x->x_tree, trunc, 0)) { - float value = np1->n_value; + float value = HAMMERNODE_GETFLOAT(np1); t_hammernode *np2 = np1->n_next; if (np2) { - float delta = (float)(np2->n_index - np1->n_index); + float delta = (float)(np2->n_key - np1->n_key); /* this is incompatible -- CHECKED float argument is silently truncated (which does not make much sense here), CHECKME again */ - float frac = f - np1->n_index; + float frac = f - np1->n_key; if (frac < 0 || frac >= delta) { bug("funbuff_dointerp"); @@ -364,12 +367,14 @@ static void funbuff_dointerp(t_funbuff *x, t_floatarg f, int vsz, t_float *vec) vec += vndx; frac = *vec + (vec[1] - *vec) * vfrac; } - value += (np2->n_value - np1->n_value) * frac; + value += + (HAMMERNODE_GETFLOAT(np2) - HAMMERNODE_GETFLOAT(np1)) * frac; } funbuff_dooutput(x, value, x->x_lastdelta); /* CHECKME !np2 */ } else if (np1 = hammertree_closest(&x->x_tree, trunc, 1)) - funbuff_dooutput(x, np1->n_value, x->x_lastdelta); /* CHECKME */ + /* CHECKME */ + funbuff_dooutput(x, HAMMERNODE_GETFLOAT(np1), x->x_lastdelta); } static void funbuff_interp(t_funbuff *x, t_floatarg f) @@ -428,7 +433,7 @@ static void funbuff_undo(t_funbuff *x) #ifdef HAMMERTREE_DEBUG static void funbuff_debug(t_funbuff *x, t_floatarg f) { - hammertree_debug(&x->x_tree, (int)f); + hammertree_debug(&x->x_tree, (int)f, 0); } #endif @@ -447,7 +452,7 @@ static void *funbuff_new(t_symbol *s) x->x_pointerset = 0; /* CHECKME, rename to intraversal? */ x->x_lastdelta = 0; x->x_embedflag = 0; - hammertree_init(&x->x_tree, 0); + hammertree_inittyped(&x->x_tree, HAMMERTYPE_FLOAT, 0); inlet_new((t_object *)x, (t_pd *)x, &s_float, gensym("ft1")); outlet_new((t_object *)x, &s_float); x->x_deltaout = outlet_new((t_object *)x, &s_float); @@ -466,9 +471,9 @@ static void *funbuff_new(t_symbol *s) void funbuff_setup(void) { funbuff_class = class_new(gensym("funbuff"), - (t_newmethod)funbuff_new, - (t_method)funbuff_free, - sizeof(t_funbuff), 0, A_DEFSYM, 0); + (t_newmethod)funbuff_new, + (t_method)funbuff_free, + sizeof(t_funbuff), 0, A_DEFSYM, 0); class_addbang(funbuff_class, funbuff_bang); class_addfloat(funbuff_class, funbuff_float); class_addmethod(funbuff_class, (t_method)funbuff_ft1, diff --git a/cyclone/hammer/gate.c b/cyclone/hammer/gate.c index 19035a5..80210cb 100644 --- a/cyclone/hammer/gate.c +++ b/cyclone/hammer/gate.c @@ -100,7 +100,10 @@ static void *gate_new(t_floatarg f1, t_floatarg f2) if (nouts < GATE_MINOUTS) nouts = GATE_DEFOUTS; if (nouts > GATE_C74MAXOUTS) + { + shared_usecompatibility(); loud_incompatible_max(gate_class, GATE_C74MAXOUTS, "outlets"); + } nouts++; /* for convenience (the cost is one pointer) */ if (!(outs = (t_outlet **)getbytes(nouts * sizeof(*outs)))) return (0); diff --git a/cyclone/hammer/hammer.c b/cyclone/hammer/hammer.c index 3c4f3c2..ea4642c 100644 --- a/cyclone/hammer/hammer.c +++ b/cyclone/hammer/hammer.c @@ -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. */ @@ -76,6 +76,11 @@ void hammer_setup(void) loud_errand(0, "without having hammer library preloaded"); return; } + if (zgetfn(&pd_objectmaker, gensym("hammer"))) + { + loud_error(0, "hammer is already loaded"); + return; + } if (!zgetfn(&pd_objectmaker, gensym("cyclone"))) post("this is hammer %s, %s %s build", CYCLONE_VERSION, loud_ordinal(CYCLONE_BUILD), CYCLONE_RELEASE); diff --git a/cyclone/hammer/maximum.c b/cyclone/hammer/maximum.c index f7f01f1..b5f6069 100644 --- a/cyclone/hammer/maximum.c +++ b/cyclone/hammer/maximum.c @@ -75,6 +75,7 @@ static void *maximum_new(t_floatarg f) t_maximum *x = (t_maximum *)pd_new(maximum_class); x->x_last = 0; /* CHECKME */ x->x_test = f; + shared_usecompatibility(); floatinlet_new((t_object *)x, &x->x_test); outlet_new((t_object *)x, &s_float); return (x); diff --git a/cyclone/hammer/minimum.c b/cyclone/hammer/minimum.c index 415235d..71875fc 100644 --- a/cyclone/hammer/minimum.c +++ b/cyclone/hammer/minimum.c @@ -75,6 +75,7 @@ static void *minimum_new(t_floatarg f) t_minimum *x = (t_minimum *)pd_new(minimum_class); x->x_last = 0; /* CHECKME */ x->x_test = f; + shared_usecompatibility(); floatinlet_new((t_object *)x, &x->x_test); outlet_new((t_object *)x, &s_float); return (x); diff --git a/cyclone/hammer/mtr.c b/cyclone/hammer/mtr.c index ecc80d3..51ba856 100644 --- a/cyclone/hammer/mtr.c +++ b/cyclone/hammer/mtr.c @@ -336,11 +336,14 @@ static void mtrack_write(t_mtrack *tp, t_symbol *s) static void mtrack_tempo(t_mtrack *tp, t_floatarg f) { float newtempo; - static int warned = 0; - if (!warned) + if (shared_getmaxcompatibility()) { - loud_incompatible(mtr_class, "no 'tempo' control in Max"); - warned = 1; + static int warned = 0; + if (!warned) + { + loud_incompatible(mtr_class, "no 'tempo' control in Max"); + warned = 1; + } } if (f < 1e-20) f = 1e-20; @@ -476,6 +479,7 @@ static void mtr_doread(t_mtr *x, t_mtrack *target, t_symbol *fname) { char path[MAXPDSTRING]; FILE *fp; + /* FIXME use open_via_path() */ if (x->x_glist) canvas_makefilename(x->x_glist, fname->s_name, path, MAXPDSTRING); else @@ -756,6 +760,7 @@ static void *mtr_new(t_floatarg f) x->x_glist = canvas_getcurrent(); x->x_filehandle = hammerfile_new((t_pd *)x, 0, mtr_readhook, mtr_writehook, 0); + shared_usecompatibility(); if (ntracks > MTR_C74MAXTRACKS) loud_incompatible_max(mtr_class, MTR_C74MAXTRACKS, "tracks"); x->x_ntracks = ntracks; diff --git a/cyclone/hammer/offer.c b/cyclone/hammer/offer.c index 3823c85..1e247e7 100644 --- a/cyclone/hammer/offer.c +++ b/cyclone/hammer/offer.c @@ -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. */ @@ -7,9 +7,9 @@ #include "hammer/tree.h" /* As a class `derived' from the common hammertree code (also in funbuff), - offer uses the auxiliary list, generally not needed here. - As a side-effect, it gets a bonus of a small speedup of deletion, - and a penalty of a small slowdown of insertion. */ + offer maintains the auxiliary list, the main purpose of which is faster + traversal (not needed here). As a side-effect, there is a bonus of a small + speedup of deletion, and a penalty of a small slowdown of insertion. */ typedef struct _offer { @@ -29,13 +29,12 @@ static void offer_float(t_offer *x, t_float f) t_hammernode *np; if (x->x_valueset) { - if (np = hammertree_insert(&x->x_tree, ndx)) - np->n_value = x->x_value; + hammertree_insertfloat(&x->x_tree, ndx, x->x_value, 1); x->x_valueset = 0; } else if (np = hammertree_search(&x->x_tree, ndx)) { - outlet_float(((t_object *)x)->ob_outlet, np->n_value); + outlet_float(((t_object *)x)->ob_outlet, HAMMERNODE_GETFLOAT(np)); hammertree_delete(&x->x_tree, np); } } @@ -57,7 +56,7 @@ static void offer_clear(t_offer *x) #ifdef HAMMERTREE_DEBUG static void offer_debug(t_offer *x, t_floatarg f) { - hammertree_debug(&x->x_tree, (int)f); + hammertree_debug(&x->x_tree, (int)f, 0); } #endif @@ -70,7 +69,7 @@ static void *offer_new(void) { t_offer *x = (t_offer *)pd_new(offer_class); x->x_valueset = 0; - hammertree_init(&x->x_tree, 0); + hammertree_inittyped(&x->x_tree, HAMMERTYPE_FLOAT, 0); inlet_new((t_object *)x, (t_pd *)x, &s_float, gensym("ft1")); outlet_new((t_object *)x, &s_float); return (x); diff --git a/cyclone/hammer/past.c b/cyclone/hammer/past.c index cb0b4ef..bf4300c 100644 --- a/cyclone/hammer/past.c +++ b/cyclone/hammer/past.c @@ -112,6 +112,7 @@ static void past_set(t_past *x, t_symbol *s, int ac, t_atom *av) t_atom *vp = x->x_thresh; if (ac > x->x_size) { + shared_usecompatibility(); loud_incompatible_max(past_class, PAST_C74MAXSIZE, "guard points"); x->x_thresh = grow_nodata(&ac, &x->x_size, x->x_thresh, PAST_C74MAXSIZE, x->x_thrini, diff --git a/cyclone/hammer/prepend.c b/cyclone/hammer/prepend.c index 052c8d6..1b3bd73 100644 --- a/cyclone/hammer/prepend.c +++ b/cyclone/hammer/prepend.c @@ -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. */ @@ -21,9 +21,17 @@ typedef struct _prepend int x_entered; int x_auxsize; t_atom *x_auxbuf; + t_pd *x_proxy; } t_prepend; +typedef struct _prependxy +{ + t_pd xy_pd; + t_prepend *xy_owner; +} t_prependxy; + static t_class *prepend_class; +static t_class *prependxy_class; /* Usually a preallocation method is used, except in special cases of: 1) reentrant output request, or 2) an output request which would cause @@ -32,83 +40,86 @@ static t_class *prepend_class; A separately preallocated output buffer is not used, thus avoiding memcpying of the stored message (a small performance gain when the preallocation method is chosen). Instead, self-invoked 'set' - messages are postponed, using an auxiliary buffer. -*/ + messages are postponed, using an auxiliary buffer. */ -static void prepend_dooutput(t_prepend *x, t_symbol *s, int ac, t_atom *av) +/* called only from prepend_doanything() */ +static void prepend_dooutput(t_prepend *x, int ac, t_atom *av) { - if (s == &s_float) + if (x->x_selector == &s_float) { if (ac > 1) outlet_list(((t_object *)x)->ob_outlet, &s_list, ac, av); else outlet_float(((t_object *)x)->ob_outlet, av->a_w.w_float); } - else if (s == &s_list) + else if (x->x_selector == &s_list) outlet_list(((t_object *)x)->ob_outlet, &s_list, ac, av); - else if (s) + else /* x->x_selector guaranteed non-empty */ /* CHECKED: 'bang' is prepended -- we cannot do so... ('symbol' cannot be compatible too) */ - { - outlet_anything(((t_object *)x)->ob_outlet, s, ac, av); - } + outlet_anything(((t_object *)x)->ob_outlet, x->x_selector, ac, av); } -static void prepend_anything(t_prepend *x, t_symbol *s, int ac, t_atom *av) +static void prepend_doanything(t_prepend *x, t_symbol *s, int ac, t_atom *av) { int reentered = x->x_entered; - int prealloc = !reentered; - int ntotal = x->x_natoms + ac; - t_atom *buf; x->x_entered = 1; - if (s == &s_) s = 0; - if (s) - ntotal++; - if (prealloc && ntotal > x->x_size) - { - if (ntotal > PREPEND_MAXSIZE) - prealloc = 0; - else - { - int nrequested = ntotal; - x->x_message = grow_withdata(&nrequested, &x->x_natoms, - &x->x_size, x->x_message, - PREPEND_INISIZE, x->x_messini, - sizeof(*x->x_message)); - prealloc = (nrequested == ntotal); - } - } - if (prealloc) + if (s == &s_) + s = 0; + if (s || x->x_natoms) { - buf = x->x_message + x->x_natoms; + int prealloc = !reentered; + int ntotal = x->x_natoms + ac; + t_atom *buf; if (s) + ntotal++; + if (prealloc && ntotal > x->x_size) { - SETSYMBOL(buf, s); - buf++; + if (ntotal > PREPEND_MAXSIZE) + prealloc = 0; + else + { + int nrequested = ntotal; + x->x_message = grow_withdata(&nrequested, &x->x_natoms, + &x->x_size, x->x_message, + PREPEND_INISIZE, x->x_messini, + sizeof(*x->x_message)); + prealloc = (nrequested == ntotal); + } } - if (ac) - memcpy(buf, av, ac * sizeof(*buf)); - prepend_dooutput(x, x->x_selector, ntotal, x->x_message); - } - else - { - /* LATER consider using the stack if ntotal <= MAXSTACK */ - if (buf = getbytes(ntotal * sizeof(*buf))) + if (prealloc) { - t_atom *bp = buf + x->x_natoms; - if (x->x_natoms) - memcpy(buf, x->x_message, x->x_natoms * sizeof(*buf)); + buf = x->x_message + x->x_natoms; if (s) { - SETSYMBOL(bp, s); - bp++; + SETSYMBOL(buf, s); + buf++; } if (ac) - memcpy(bp, av, ac * sizeof(*bp)); - prepend_dooutput(x, x->x_selector, ntotal, buf); - freebytes(buf, ntotal * sizeof(*buf)); + memcpy(buf, av, ac * sizeof(*buf)); + prepend_dooutput(x, ntotal, x->x_message); + } + else + { + /* LATER consider using the stack if ntotal <= MAXSTACK */ + if (buf = getbytes(ntotal * sizeof(*buf))) + { + t_atom *bp = buf + x->x_natoms; + if (x->x_natoms) + memcpy(buf, x->x_message, x->x_natoms * sizeof(*buf)); + if (s) + { + SETSYMBOL(bp, s); + bp++; + } + if (ac) + memcpy(bp, av, ac * sizeof(*bp)); + prepend_dooutput(x, ntotal, buf); + freebytes(buf, ntotal * sizeof(*buf)); + } } } + else prepend_dooutput(x, ac, av); if (!reentered) { x->x_entered = 0; @@ -136,51 +147,75 @@ static void prepend_anything(t_prepend *x, t_symbol *s, int ac, t_atom *av) static void prepend_bang(t_prepend *x) { - t_atom at; - SETSYMBOL(&at, &s_bang); /* CHECKED */ - prepend_anything(x, 0, 1, &at); + if (x->x_selector) + { + t_atom at; + SETSYMBOL(&at, &s_bang); /* CHECKED */ + prepend_doanything(x, 0, 1, &at); + } + else outlet_bang(((t_object *)x)->ob_outlet); } static void prepend_float(t_prepend *x, t_float f) { - t_atom at; - SETFLOAT(&at, f); - prepend_anything(x, 0, 1, &at); + if (x->x_selector) + { + t_atom at; + SETFLOAT(&at, f); + prepend_doanything(x, 0, 1, &at); + } + else outlet_float(((t_object *)x)->ob_outlet, f); } static void prepend_symbol(t_prepend *x, t_symbol *s) { - t_atom at; - SETSYMBOL(&at, s); - prepend_anything(x, 0, 1, &at); + if (x->x_selector) + { + t_atom at; + SETSYMBOL(&at, s); + prepend_doanything(x, 0, 1, &at); + } + else outlet_symbol(((t_object *)x)->ob_outlet, s); } /* LATER gpointer */ static void prepend_list(t_prepend *x, t_symbol *s, int ac, t_atom *av) { - prepend_anything(x, 0, ac, av); + if (x->x_selector) + prepend_doanything(x, 0, ac, av); + else + outlet_list(((t_object *)x)->ob_outlet, s, ac, av); } -static void prepend_set(t_prepend *x, t_symbol *s, int ac, t_atom *av) +static void prepend_anything(t_prepend *x, t_symbol *s, int ac, t_atom *av) { - if (ac) + if (x->x_selector) + prepend_doanything(x, s, ac, av); + else + outlet_anything(((t_object *)x)->ob_outlet, s, ac, av); +} + +static void prepend_doset(t_prepend *x, t_symbol *s, int ac, t_atom *av) +{ + if (s) + x->x_selector = s; + else if (ac) { - int newsize; - if (av->a_type == A_FLOAT) - { - if (ac > 1) x->x_selector = &s_list; - else x->x_selector = &s_float; - } - else if (av->a_type == A_SYMBOL) + if (av->a_type == A_SYMBOL) { x->x_selector = av->a_w.w_symbol; - ac--; - av++; + ac--; av++; } + else if (av->a_type == A_FLOAT) + x->x_selector = (ac > 1 ? &s_list : &s_float); else return; /* LATER rethink */ - newsize = ac * 2; + } + else x->x_selector = 0; + if (ac) + { + int newsize = ac * 2; if (x->x_entered) { if (x->x_auxbuf) @@ -212,12 +247,56 @@ static void prepend_set(t_prepend *x, t_symbol *s, int ac, t_atom *av) while (ac--) *ap++ = *av++; } } + else x->x_natoms = 0; +} + +static void prepend_set(t_prepend *x, t_symbol *s, int ac, t_atom *av) +{ + if (shared_getmaxcompatibility()) + prepend_doset(x, 0, ac, av); + else + prepend_anything(x, s, ac, av); +} + +static void prependxy_bang(t_prependxy *xy) +{ + prepend_doset(xy->xy_owner, 0, 0, 0); /* LATER rethink */ +} + +static void prependxy_float(t_prependxy *xy, t_float f) +{ + t_atom at; + SETFLOAT(&at, f); + prepend_doset(xy->xy_owner, &s_float, 1, &at); +} + +static void prependxy_symbol(t_prependxy *xy, t_symbol *s) +{ + prepend_doset(xy->xy_owner, + (s && s != &s_ ? s : &s_symbol), 0, 0); /* LATER rethink */ +} + +static void prependxy_list(t_prependxy *xy, t_symbol *s, int ac, t_atom *av) +{ + prepend_doset(xy->xy_owner, &s_list, ac, av); /* LATER rethink */ +} + +static void prependxy_anything(t_prependxy *xy, t_symbol *s, int ac, t_atom *av) +{ + prepend_doset(xy->xy_owner, s, ac, av); } static void prepend_free(t_prepend *x) { if (x->x_message != x->x_messini) freebytes(x->x_message, x->x_size * sizeof(*x->x_message)); + if (x->x_auxbuf) + { + bug("prepend_free"); /* LATER rethink */ + freebytes(x->x_auxbuf, x->x_auxsize * sizeof(*x->x_auxbuf)); + } + if (x->x_proxy) + pd_free(x->x_proxy); } static void *prepend_new(t_symbol *s, int ac, t_atom *av) @@ -229,15 +308,25 @@ static void *prepend_new(t_symbol *s, int ac, t_atom *av) x->x_message = x->x_messini; x->x_auxbuf = 0; x->x_entered = 0; - if (!ac) + shared_usecompatibility(); + if (ac) { - loud_incompatible(prepend_class, - "creating an object without an argument"); - /* CHECKED: this is not compatible -- in max an object without an outlet - is created, and there is no warning if loading from a file. */ + x->x_proxy = 0; + prepend_doset(x, 0, ac, av); + } + else + { + if (shared_getmaxcompatibility()) + /* CHECKED: this is still not compatible -- in max an object + without an outlet is created, and there is no warning when + loading from a file. */ + loud_incompatible(prepend_class, + "creating an object without an argument"); + x->x_proxy = pd_new(prependxy_class); + ((t_prependxy *)x->x_proxy)->xy_owner = x; + inlet_new((t_object *)x, x->x_proxy, 0, 0); } outlet_new((t_object *)x, &s_anything); - prepend_set(x, 0, ac, av); return (x); } @@ -255,4 +344,12 @@ void prepend_setup(void) class_addanything(prepend_class, prepend_anything); class_addmethod(prepend_class, (t_method)prepend_set, gensym("set"), A_GIMME, 0); + + prependxy_class = class_new(gensym("prepend"), 0, 0, sizeof(t_prependxy), + CLASS_PD | CLASS_NOINLET, 0); + class_addbang(prependxy_class, prependxy_bang); + class_addfloat(prependxy_class, prependxy_float); + class_addsymbol(prependxy_class, prependxy_symbol); + class_addlist(prependxy_class, prependxy_list); + class_addanything(prependxy_class, prependxy_anything); } diff --git a/cyclone/hammer/prob.c b/cyclone/hammer/prob.c index e20e420..114a41d 100644 --- a/cyclone/hammer/prob.c +++ b/cyclone/hammer/prob.c @@ -274,6 +274,7 @@ static void *prob_new(void) x->x_embedmode = 0; /* CHECKED */ x->x_silent = 0; rand_seed(&x->x_seed, 0); + shared_usecompatibility(); outlet_new((t_object *)x, &s_float); x->x_bangout = outlet_new((t_object *)x, &s_bang); x->x_filehandle = hammerfile_new((t_pd *)x, prob_embedhook, 0, 0, 0); diff --git a/cyclone/hammer/seq.c b/cyclone/hammer/seq.c index edeff88..f29c803 100644 --- a/cyclone/hammer/seq.c +++ b/cyclone/hammer/seq.c @@ -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. */ @@ -11,26 +11,33 @@ #include "shared.h" #include "common/loud.h" #include "common/grow.h" -#include "common/sq.h" -#include "common/bifi.h" #include "common/mifi.h" +#include "unstable/forky.h" #include "hammer/file.h" -//#define SEQ_DEBUG +#define SEQ_DEBUG -#define SEQ_INISIZE 256 /* LATER rethink */ -#define SEQ_EOM 255 /* end of message marker, LATER rethink */ -#define SEQ_TICKSPERSEC 48 -#define SEQ_MINTICKDELAY 1. /* LATER rethink */ +#define SEQ_INISEQSIZE 256 /* LATER rethink */ +#define SEQ_INITEMPOMAPSIZE 128 /* LATER rethink */ +#define SEQ_EOM 255 /* end of message marker, LATER rethink */ +#define SEQ_TICKSPERSEC 48 +#define SEQ_MINTICKDELAY 1. /* LATER rethink */ +#define SEQ_TICKEPSILON ((double).0001) enum { SEQ_IDLEMODE, SEQ_RECMODE, SEQ_PLAYMODE, SEQ_SLAVEMODE }; typedef struct _seqevent { - int e_delta; + double e_delta; unsigned char e_bytes[4]; } t_seqevent; +typedef struct _seqtempo +{ + double t_scoretime; /* score ticks from start */ + double t_sr; /* score ticks per second */ +} t_seqtempo; + typedef struct _seq { t_object x_ob; @@ -39,18 +46,24 @@ typedef struct _seq t_hammerfile *x_filehandle; int x_mode; int x_playhead; - float x_tempo; - float x_newtempo; + float x_timescale; + float x_newtimescale; double x_prevtime; double x_slaveprevtime; double x_clockdelay; unsigned char x_status; - int x_evesize; - int x_expectedsize; - int x_size; /* as allocated */ + int x_evelength; + int x_expectedlength; + int x_eventreadhead; + int x_seqsize; /* as allocated */ int x_nevents; /* as used */ t_seqevent *x_sequence; - t_seqevent x_seqini[SEQ_INISIZE]; + t_seqevent x_seqini[SEQ_INISEQSIZE]; + int x_temporeadhead; + int x_tempomapsize; /* as allocated */ + int x_ntempi; /* as used */ + t_seqtempo *x_tempomap; + t_seqtempo x_tempomapini[SEQ_INITEMPOMAPSIZE]; t_clock *x_clock; t_clock *x_slaveclock; t_outlet *x_bangout; @@ -60,35 +73,85 @@ static t_class *seq_class; static void seq_doclear(t_seq *x, int dofree) { - if (dofree && x->x_sequence != x->x_seqini) + if (dofree) { - freebytes(x->x_sequence, x->x_size * sizeof(*x->x_sequence)); - x->x_sequence = x->x_seqini; - x->x_size = SEQ_INISIZE; + if (x->x_sequence != x->x_seqini) + { + freebytes(x->x_sequence, x->x_seqsize * sizeof(*x->x_sequence)); + x->x_sequence = x->x_seqini; + x->x_seqsize = SEQ_INISEQSIZE; + } + if (x->x_tempomap != x->x_tempomapini) + { + freebytes(x->x_tempomap, + x->x_tempomapsize * sizeof(*x->x_tempomap)); + x->x_tempomap = x->x_tempomapini; + x->x_tempomapsize = SEQ_INITEMPOMAPSIZE; + } } x->x_nevents = 0; + x->x_ntempi = 0; +} + +static int seq_dogrowing(t_seq *x, int nevents, int ntempi) +{ + if (nevents > x->x_seqsize) + { + int nrequested = nevents; +#ifdef SEQ_DEBUG + post("growing for %d events...", nevents); +#endif + x->x_sequence = + grow_nodata(&nrequested, &x->x_seqsize, x->x_sequence, + SEQ_INISEQSIZE, x->x_seqini, sizeof(*x->x_sequence)); + if (nrequested < nevents) + { + x->x_nevents = 0; + x->x_ntempi = 0; + return (0); + } + } + if (ntempi > x->x_tempomapsize) + { + int nrequested = ntempi; +#ifdef SEQ_DEBUG + post("growing for %d tempi...", ntempi); +#endif + x->x_tempomap = + grow_nodata(&nrequested, &x->x_tempomapsize, x->x_tempomap, + SEQ_INITEMPOMAPSIZE, x->x_tempomapini, + sizeof(*x->x_tempomap)); + if (nrequested < ntempi) + { + x->x_ntempi = 0; + return (0); + } + } + x->x_nevents = nevents; + x->x_ntempi = ntempi; + return (1); } static void seq_complete(t_seq *x) { - if (x->x_evesize < x->x_expectedsize) + if (x->x_evelength < x->x_expectedlength) { /* CHECKED no warning if no data after status byte requiring data */ - if (x->x_evesize > 1) + if (x->x_evelength > 1) post("seq: truncated midi message"); /* CHECKED */ /* CHECKED nothing stored */ } else { t_seqevent *ep = &x->x_sequence[x->x_nevents]; - ep->e_delta = (int)clock_gettimesince(x->x_prevtime); + ep->e_delta = clock_gettimesince(x->x_prevtime); x->x_prevtime = clock_getlogicaltime(); - if (x->x_evesize < 4) - ep->e_bytes[x->x_evesize] = SEQ_EOM; + if (x->x_evelength < 4) + ep->e_bytes[x->x_evelength] = SEQ_EOM; x->x_nevents++; - if (x->x_nevents >= x->x_size) + if (x->x_nevents >= x->x_seqsize) { - int nexisting = x->x_size; + int nexisting = x->x_seqsize; /* store-ahead scheme, LATER consider using x_currevent */ int nrequested = x->x_nevents + 1; #ifdef SEQ_DEBUG @@ -96,54 +159,55 @@ static void seq_complete(t_seq *x) #endif x->x_sequence = grow_withdata(&nrequested, &nexisting, - &x->x_size, x->x_sequence, - SEQ_INISIZE, x->x_seqini, sizeof(*x->x_sequence)); + &x->x_seqsize, x->x_sequence, + SEQ_INISEQSIZE, x->x_seqini, + sizeof(*x->x_sequence)); if (nrequested <= x->x_nevents) x->x_nevents = 0; } } - x->x_evesize = 0; + x->x_evelength = 0; } static void seq_checkstatus(t_seq *x, unsigned char c) { - if (x->x_status && x->x_evesize > 1) /* LATER rethink */ + if (x->x_status && x->x_evelength > 1) /* LATER rethink */ seq_complete(x); if (c < 192) - x->x_expectedsize = 3; + x->x_expectedlength = 3; else if (c < 224) - x->x_expectedsize = 2; + x->x_expectedlength = 2; else if (c < 240) - x->x_expectedsize = 3; + x->x_expectedlength = 3; else if (c < 248) { /* FIXME */ - x->x_expectedsize = -1; + x->x_expectedlength = -1; } else { x->x_sequence[x->x_nevents].e_bytes[0] = c; - x->x_evesize = x->x_expectedsize = 1; + x->x_evelength = x->x_expectedlength = 1; seq_complete(x); return; } x->x_status = x->x_sequence[x->x_nevents].e_bytes[0] = c; - x->x_evesize = 1; + x->x_evelength = 1; } static void seq_addbyte(t_seq *x, unsigned char c, int docomplete) { - x->x_sequence[x->x_nevents].e_bytes[x->x_evesize++] = c; - if (x->x_evesize == x->x_expectedsize) + x->x_sequence[x->x_nevents].e_bytes[x->x_evelength++] = c; + if (x->x_evelength == x->x_expectedlength) { seq_complete(x); if (x->x_status) { x->x_sequence[x->x_nevents].e_bytes[0] = x->x_status; - x->x_evesize = 1; + x->x_evelength = 1; } } - else if (x->x_evesize == 4) + else if (x->x_evelength == 4) { if (x->x_status != 240) bug("seq_addbyte"); @@ -195,8 +259,8 @@ static void seq_startrecording(t_seq *x, int modechanged) { x->x_prevtime = clock_getlogicaltime(); x->x_status = 0; - x->x_evesize = 0; - x->x_expectedsize = -1; /* LATER rethink */ + x->x_evelength = 0; + x->x_expectedlength = -1; /* LATER rethink */ } /* CHECKED running status not used in playback */ @@ -210,19 +274,19 @@ static void seq_startplayback(t_seq *x, int modechanged) x->x_playhead = 0; /* playback data never sent within the scheduler event of a start message (even for the first delta <= 0), LATER rethink */ - x->x_clockdelay = x->x_sequence->e_delta * x->x_newtempo; + x->x_clockdelay = x->x_sequence->e_delta * x->x_newtimescale; } else { - /* CHECKED tempo change */ + /* CHECKED timescale change */ x->x_clockdelay -= clock_gettimesince(x->x_prevtime); - x->x_clockdelay *= x->x_newtempo / x->x_tempo; + x->x_clockdelay *= x->x_newtimescale / x->x_timescale; } if (x->x_clockdelay < 0.) x->x_clockdelay = 0.; clock_delay(x->x_clock, x->x_clockdelay); x->x_prevtime = clock_getlogicaltime(); - x->x_tempo = x->x_newtempo; + x->x_timescale = x->x_newtimescale; } else x->x_mode = SEQ_IDLEMODE; } @@ -280,14 +344,14 @@ static void seq_setmode(t_seq *x, int newmode) } } -static void seq_settempo(t_seq *x, float newtempo) +static void seq_settimescale(t_seq *x, float newtimescale) { - if (newtempo < 1e-20) - x->x_newtempo = 1e-20; - else if (newtempo > 1e20) - x->x_newtempo = 1e20; + if (newtimescale < 1e-20) + x->x_newtimescale = 1e-20; + else if (newtimescale > 1e20) + x->x_newtimescale = 1e20; else - x->x_newtempo = newtempo; + x->x_newtimescale = newtimescale; } static void seq_clocktick(t_seq *x) @@ -313,7 +377,7 @@ nextevent: if (x->x_playhead < x->x_nevents) { ep++; - if (ep->e_delta <= 0) + if (ep->e_delta < SEQ_TICKEPSILON) /* continue output in the same scheduler event, LATER rethink */ { x->x_playhead++; @@ -322,7 +386,7 @@ nextevent: } else { - x->x_clockdelay = ep->e_delta * x->x_tempo; + x->x_clockdelay = ep->e_delta * x->x_timescale; if (x->x_clockdelay < 0.) x->x_clockdelay = 0.; clock_delay(x->x_clock, x->x_clockdelay); @@ -355,36 +419,36 @@ static void seq_tick(t_seq *x) if (elapsed < SEQ_MINTICKDELAY) return; clock_delay(x->x_slaveclock, elapsed); - seq_settempo(x, (float)(elapsed * (SEQ_TICKSPERSEC / 1000.))); + seq_settimescale(x, (float)(elapsed * (SEQ_TICKSPERSEC / 1000.))); if (x->x_prevtime > 0) { x->x_clockdelay -= clock_gettimesince(x->x_prevtime); - x->x_clockdelay *= x->x_newtempo / x->x_tempo; + x->x_clockdelay *= x->x_newtimescale / x->x_timescale; } else x->x_clockdelay = - x->x_sequence[x->x_playhead].e_delta * x->x_newtempo; + x->x_sequence[x->x_playhead].e_delta * x->x_newtimescale; if (x->x_clockdelay < 0.) x->x_clockdelay = 0.; clock_delay(x->x_clock, x->x_clockdelay); x->x_prevtime = clock_getlogicaltime(); x->x_slaveprevtime = x->x_prevtime; - x->x_tempo = x->x_newtempo; + x->x_timescale = x->x_newtimescale; } else { x->x_clockdelay = 0.; /* redundant */ x->x_prevtime = 0.; /* redundant */ x->x_slaveprevtime = clock_getlogicaltime(); - x->x_tempo = 1.; /* redundant */ + x->x_timescale = 1.; /* redundant */ } } } -/* CHECKED bang does the same as 'start 1024', not 'start ' +/* CHECKED bang does the same as 'start 1024', not 'start ' (also if already in SEQ_PLAYMODE) */ static void seq_bang(t_seq *x) { - seq_settempo(x, 1.); + seq_settimescale(x, 1.); seq_setmode(x, SEQ_PLAYMODE); /* CHECKED 'bang' stops recording */ } @@ -450,7 +514,7 @@ static void seq_start(t_seq *x, t_floatarg f) } else { - seq_settempo(x, (f == 0 ? 1. : 1024. / f)); + seq_settimescale(x, (f == 0 ? 1. : 1024. / f)); seq_setmode(x, SEQ_PLAYMODE); /* CHECKED 'start' stops recording */ } } @@ -465,7 +529,7 @@ static void seq_delay(t_seq *x, t_floatarg f) { if (x->x_nevents) /* CHECKED signed/unsigned bug (not emulated) */ - x->x_sequence->e_delta = (f > 0 ? f : 0); + x->x_sequence->e_delta = (f > SEQ_TICKEPSILON ? f : 0.); } /* CHECKED all delta times are set permanently (they are stored in a file) */ @@ -481,223 +545,177 @@ static void seq_hook(t_seq *x, t_floatarg f) } } -static int seq_dogrowing(t_seq *x, int nevents) +static int seq_eventcomparehook(const void *e1, const void *e2) { - if (nevents > x->x_size) - { - int nrequested = nevents; -#ifdef SEQ_DEBUG - post("growing..."); -#endif - x->x_sequence = - grow_nodata(&nrequested, &x->x_size, x->x_sequence, - SEQ_INISIZE, x->x_seqini, sizeof(*x->x_sequence)); - if (nrequested < nevents) - { - x->x_nevents = 0; - return (0); - } - } - x->x_nevents = nevents; - return (1); -} - -static int seq_seekhook(t_squiter *it, int offset) -{ - t_seq *x = (t_seq *)it->i_owner; - post("seek in %d", x->x_nevents); - it->i_nelems = x->x_nevents; - it->i_sequence = x->x_sequence; - if (offset < 0) - offset += it->i_nelems; - if (offset >= 0 && offset < it->i_nelems) - { - it->i_element = (t_seqevent *)it->i_sequence + offset; - it->i_index = offset; - return (1); - } - else return (0); -} - -static void seq_incrhook(t_squiter *it) -{ - ((t_seqevent *)it->i_element)++; - it->i_index++; + return (((t_seqevent *)e1)->e_delta > ((t_seqevent *)e2)->e_delta ? 1 : -1); } -/* LATER put seq_mfwrite_doit() functionality here */ -static void seq_getevehook(t_squiter *it, t_mifi_event *mev, int *ret) +static int seq_tempocomparehook(const void *t1, const void *t2) { - *ret = 1; + return (((t_seqtempo *)t1)->t_scoretime > + ((t_seqtempo *)t2)->t_scoretime ? 1 : -1); } -static void seq_setevehook(t_squiter *it, t_mifi_event *mev, int *ret) +static int seq_mrhook(t_mifiread *mr, void *hookdata, int evtype) { - t_seqevent *sev = it->i_element; - sev->e_delta = mev->e_delay; - sev->e_bytes[0] = mev->e_status | mev->e_channel; - sev->e_bytes[1] = mev->e_data[0]; - if (MIFI_ONE_DATABYTE(mev->e_status)) - sev->e_bytes[2] = SEQ_EOM; - else + t_seq *x = (t_seq *)hookdata; + double scoretime = mifiread_getscoretime(mr); + if (evtype >= 0xf0) { - sev->e_bytes[2] = mev->e_data[1]; - sev->e_bytes[3] = SEQ_EOM; } - *ret = 1; -} - -static t_float seq_gettimhook(t_squiter *it, int *ret) -{ - t_seqevent *sev = it->i_element; - *ret = 1; - return (sev->e_delta); -} - -static void seq_settimhook(t_squiter *it, t_float f, int *ret) -{ - t_seqevent *sev = it->i_element; - sev->e_delta = f; - *ret = 1; -} - -static t_symbol *seq_gettarhook(t_squiter *it, int *ret) -{ - *ret = 1; - return (0); -} - -static void seq_settarhook(t_squiter *it, t_symbol *s, int *ret) -{ - *ret = 1; -} - -static int seq_make_iterator(t_seq *x, t_mifi_stream *stp) -{ - t_squiter *it = squiter_new(stp); - if (it) + else if (evtype >= 0x80) { - it->i_owner = x; - it->i_nelems = x->x_nevents; - it->i_sequence = it->i_element = x->x_sequence; - it->i_index = 0; - it->i_hooks[SQUITER_SEEKHOOK] = (t_squiterhook)seq_seekhook; - it->i_hooks[SQUITER_INCRHOOK] = (t_squiterhook)seq_incrhook; - it->i_hooks[SQUITER_GETEVEHOOK] = (t_squiterhook)seq_getevehook; - it->i_hooks[SQUITER_SETEVEHOOK] = (t_squiterhook)seq_setevehook; - it->i_hooks[SQUITER_GETTIMHOOK] = (t_squiterhook)seq_gettimhook; - it->i_hooks[SQUITER_SETTIMHOOK] = (t_squiterhook)seq_settimhook; - it->i_hooks[SQUITER_GETTARHOOK] = (t_squiterhook)seq_gettarhook; - it->i_hooks[SQUITER_SETTARHOOK] = (t_squiterhook)seq_settarhook; - return (1); + if (x->x_eventreadhead < x->x_nevents) + { + t_seqevent *sev = &x->x_sequence[x->x_eventreadhead++]; + int status = mifiread_getstatus(mr); + sev->e_delta = scoretime; + sev->e_bytes[0] = status | mifiread_getchannel(mr); + sev->e_bytes[1] = mifiread_getdata1(mr); + if (MIFI_ONEDATABYTE(status)) + sev->e_bytes[2] = SEQ_EOM; + else + { + sev->e_bytes[2] = mifiread_getdata2(mr); + sev->e_bytes[3] = SEQ_EOM; + } + } + else if (x->x_eventreadhead == x->x_nevents) + { + bug("seq_mrhook 1"); + x->x_eventreadhead++; + } } - else return (0); -} - -static t_mifi_stream *seq_makestream(t_seq *x) -{ - t_mifi_stream *stp = 0; - if (stp = mifi_stream_new()) + else if (evtype == MIFIMETA_TEMPO) { - if (seq_make_iterator(x, stp)) - return (stp); - else - mifi_stream_free(stp); + if (x->x_temporeadhead < x->x_ntempi) + { + t_seqtempo *stm = &x->x_tempomap[x->x_temporeadhead++]; + stm->t_scoretime = scoretime; + stm->t_sr = mifiread_gettempo(mr); +#ifdef SEQ_DEBUG + post("tempo %g at %g", stm->t_sr, scoretime); +#endif + } + else if (x->x_temporeadhead == x->x_ntempi) + { + bug("seq_mrhook 2"); + x->x_temporeadhead++; + } } - return (0); + return (1); } -static int seq_comparehook(const void *e1, const void *e2) +/* apply tempo and fold */ +static void seq_foldtime(t_seq *x, double deftempo) { - return (((t_seqevent *)e1)->e_delta > ((t_seqevent *)e2)->e_delta ? 1 : -1); + t_seqevent *sev; + t_seqtempo *stm = x->x_tempomap; + double coef = 1000. / deftempo; + int ex, tx = 0; + double prevscoretime = 0.; + while (tx < x->x_ntempi && stm->t_scoretime < SEQ_TICKEPSILON) + tx++, coef = 1000. / stm++->t_sr; + for (ex = 0, sev = x->x_sequence; ex < x->x_nevents; ex++, sev++) + { + double clockdelta = 0.; + while (tx < x->x_ntempi && stm->t_scoretime <= sev->e_delta) + { + clockdelta += (stm->t_scoretime - prevscoretime) * coef; + prevscoretime = stm->t_scoretime; + tx++; + coef = 1000. / stm++->t_sr; + } + clockdelta += (sev->e_delta - prevscoretime) * coef; + prevscoretime = sev->e_delta; + sev->e_delta = clockdelta; + } } -/* FIXME */ static int seq_mfread(t_seq *x, char *path) { int result = 0; - t_mifi_stream *stp = 0; - if (!(stp = seq_makestream(x)) || - !mifi_read_start(stp, path, "", 0)) - goto readfailed; + t_mifiread *mr = mifiread_new((t_pd *)x); + if (!mifiread_open(mr, path, "", 0)) + goto mfreadfailed; #ifdef SEQ_DEBUG - if (stp->s_nframes) - post("midifile (format %d): %d tracks, %d ticks (%d smpte frames)", - stp->s_format, stp->s_hdtracks, stp->s_nticks, stp->s_nframes); + startpost("midifile (format %d): %d tracks, %d ticks", + mifiread_getformat(mr), mifiread_gethdtracks(mr), + mifiread_getbeatticks(mr)); + if (mifiread_getnframes(mr)) + post(" (%d smpte frames)", mifiread_getnframes(mr)); else - post("midifile (format %d): %d tracks, %d ticks per beat", - stp->s_format, stp->s_hdtracks, stp->s_nticks); + post(" per beat"); #endif - if (mifi_read_analyse(stp) != MIFI_READ_EOF || - !seq_dogrowing(x, stp->s_nevents) || - !mifi_read_restart(stp) || - mifi_read_doit(stp) != MIFI_READ_EOF) - goto readfailed; - squmpi_sort(stp); - qsort(x->x_sequence, stp->s_nevents, sizeof(*x->x_sequence), - seq_comparehook); - sq_fold_time(stp); + if (!seq_dogrowing(x, mifiread_getnevents(mr), mifiread_getntempi(mr))) + goto mfreadfailed; + x->x_eventreadhead = 0; + x->x_temporeadhead = 0; + if (mifiread_doit(mr, seq_mrhook, x) != MIFIREAD_EOF) + goto mfreadfailed; + if (x->x_eventreadhead < x->x_nevents) + { + bug("seq_mfread 1"); + post("declared %d events, got %d", x->x_nevents, x->x_eventreadhead); + x->x_nevents = x->x_eventreadhead; + } + if (x->x_nevents) + qsort(x->x_sequence, x->x_nevents, sizeof(*x->x_sequence), + seq_eventcomparehook); + if (x->x_temporeadhead < x->x_ntempi) + { + bug("seq_mfread 2"); + post("declared %d tempi, got %d", x->x_ntempi, x->x_temporeadhead); + x->x_ntempi = x->x_temporeadhead; + } + if (x->x_ntempi) + qsort(x->x_tempomap, x->x_ntempi, sizeof(*x->x_tempomap), + seq_tempocomparehook); + seq_foldtime(x, mifiread_getdeftempo(mr)); #ifdef SEQ_DEBUG - post("finished reading %d events from midifile", stp->s_nevents); + post("seq: got %d events from midi file", x->x_nevents); #endif result = 1; -readfailed: - if (stp) - { - mifi_read_end(stp); - mifi_stream_free(stp); - } +mfreadfailed: + mifiread_free(mr); return (result); } -/* FIXME */ -static int seq_mfwrite_doit(t_seq *x, t_mifi_stream *stp) +static int seq_mfwrite(t_seq *x, char *path) { - t_mifi_event *mev = stp->s_auxeve; + int result = 0; t_seqevent *sev = x->x_sequence; int nevents = x->x_nevents; + t_mifiwrite *mw = mifiwrite_new((t_pd *)x); + if (!mifiwrite_open(mw, path, "", 1, 1)) + goto mfwritefailed; + if (!mifiwrite_opentrack(mw, "seq-track", 1)) + goto mfwritefailed; while (nevents--) { unsigned char *bp = sev->e_bytes; - int i; - mev->e_delay = (uint32)(sev->e_delta * stp->s_timecoef); - mev->e_status = *bp & 0xf0; - mev->e_channel = *bp & 0x0f; - /* FIXME sysex continuation */ - for (i = 0, bp++; i < 3 && *bp != SEQ_EOM; i++, bp++) - mev->e_data[i] = *bp; - if (!mifi_write_event(stp, mev)) - return (0); + unsigned status = *bp & 0xf0; + if (status > 127 && status < 240) + { + if (!mifiwrite_channelevent(mw, sev->e_delta, status, *bp & 0x0f, + bp[1], bp[2])) /* SEQ_EOM ignored */ + { + loud_error((t_pd *)x, "cannot write channel event %d", status); + goto mfwritefailed; + } + } + /* FIXME system, sysex (first, and continuation) */ sev++; } - return (1); -} - -/* FIXME */ -static int seq_mfwrite(t_seq *x, char *path) -{ - int result = 0; - t_mifi_stream *stp = 0; - if (!(stp = seq_makestream(x))) - goto writefailed; - stp->s_ntracks = 1; - stp->s_hdtracks = 1; - stp->s_format = 0; - if (!mifi_write_start(stp, path, "")) - goto writefailed; - mifi_event_settext(stp->s_auxeve, MIFI_META_TRACKNAME, "seq-track"); - if (!mifi_write_start_track(stp) || - !mifi_write_event(stp, stp->s_auxeve) || - !seq_mfwrite_doit(x, stp) || - !mifi_write_adjust_track(stp, 0)) - goto writefailed; + if (!mifiwrite_closetrack(mw, 0., 1)) + goto mfwritefailed; + mifiwrite_close(mw); result = 1; -writefailed: - if (stp) - { - mifi_write_end(stp); - mifi_stream_free(stp); - } +mfwritefailed: + if (!result) + loud_errand((t_pd *)x, + "while saving sequence into midi file \"%s\"", path); + mifiwrite_free(mw); return (result); } @@ -716,7 +734,7 @@ static int seq_frombinbuf(t_seq *x, t_binbuf *bb) t_seqevent *ep; float prevtime = 0; int i = -1; - if (!seq_dogrowing(x, nevents)) + if (!seq_dogrowing(x, nevents, 0)) return (0); nevents = 0; ac = binbuf_getnatom(bb); @@ -815,6 +833,7 @@ static void seq_textwrite(t_seq *x, char *path) static void seq_doread(t_seq *x, t_symbol *fn, int creation) { char buf[MAXPDSTRING]; + /* FIXME use open_via_path() */ if (x->x_canvas) canvas_makefilename(x->x_canvas, fn->s_name, buf, MAXPDSTRING); else @@ -853,7 +872,8 @@ static void seq_dowrite(t_seq *x, t_symbol *fn) /* save as text for any extension other then ".mid" */ if ((dotp = strrchr(fn->s_name, '.')) && strcmp(dotp + 1, "mid")) seq_textwrite(x, buf); - else /* save as mf for ".mid" or no extension at all, LATER rethink */ + else /* save as mf for ".mid" (FIXME ignore case?) or no extension at all, + LATER rethink */ seq_mfwrite(x, buf); } @@ -884,6 +904,23 @@ static void seq_write(t_seq *x, t_symbol *s) canvas_getdir(x->x_canvas), x->x_defname); } +static void seq_eventstring(t_seq *x, char *buf, t_seqevent *ep) +{ + unsigned char *bp = ep->e_bytes; + int i; + if (*bp < 128 || *bp == 247) + sprintf(buf, "(%g)->", ep->e_delta); + else + sprintf(buf, "(%g)", ep->e_delta); + buf += strlen(buf); + sprintf(buf, " %g", (float)*bp); + for (i = 0, bp++; i < 3 && *bp != SEQ_EOM; i++, bp++) + { + buf += strlen(buf); + sprintf(buf, " %g", (float)*bp); + } +} + static void seq_print(t_seq *x) { int nevents = x->x_nevents; @@ -891,39 +928,52 @@ static void seq_print(t_seq *x) if (nevents) { t_seqevent *ep = x->x_sequence; + char buf[MAXPDSTRING+2]; int truncated; if (nevents > 16) nevents = 16, truncated = 1; else truncated = 0; + endpost(); while (nevents--) { - unsigned char *bp = ep->e_bytes; - int i; - if (*bp < 128 || *bp == 247) - /* CHECKED (sysex continuation) */ - startpost("\n(%d)->", ep->e_delta); - else - startpost("\n(%d)", ep->e_delta); - /* CHECKED space-separated, no semi */ - postfloat((float)*bp); - for (i = 0, bp++; i < 3 && *bp != SEQ_EOM; i++, bp++) - postfloat((float)*bp); + /* CHECKED bytes are space-separated, no semi */ + seq_eventstring(x, buf, ep); + post(buf); ep++; } - endpost(); if (truncated) post("..."); /* CHECKED */ } else post(" no sequence"); /* CHECKED */ } +static void seq_properties(t_gobj *z, t_glist *glist) +{ + t_seq *x = (t_seq *)z; + t_seqevent *ep = x->x_sequence; + int nevents = x->x_nevents; + char buf[MAXPDSTRING+2]; + sprintf(buf, "seq: %s", (x->x_defname && x->x_defname != &s_ ? + x->x_defname->s_name : "")); + hammereditor_open(x->x_filehandle, buf); + while (nevents--) + { + seq_eventstring(x, buf, ep); + strcat(buf, "\n"); + hammereditor_append(x->x_filehandle, buf); + ep++; + } +} + static void seq_free(t_seq *x) { if (x->x_clock) clock_free(x->x_clock); if (x->x_slaveclock) clock_free(x->x_slaveclock); - hammerfile_free(x->x_filehandle); + if (x->x_filehandle) hammerfile_free(x->x_filehandle); if (x->x_sequence != x->x_seqini) - freebytes(x->x_sequence, x->x_size * sizeof(*x->x_sequence)); + freebytes(x->x_sequence, x->x_seqsize * sizeof(*x->x_sequence)); + if (x->x_tempomap != x->x_tempomapini) + freebytes(x->x_tempomap, x->x_tempomapsize * sizeof(*x->x_tempomap)); } static void *seq_new(t_symbol *s) @@ -938,13 +988,16 @@ static void *seq_new(t_symbol *s) x->x_canvas = canvas_getcurrent(); x->x_filehandle = hammerfile_new((t_pd *)x, 0, seq_readhook, seq_writehook, 0); - x->x_tempo = 1.; - x->x_newtempo = 1.; + x->x_timescale = 1.; + x->x_newtimescale = 1.; x->x_prevtime = 0.; x->x_slaveprevtime = 0.; - x->x_size = SEQ_INISIZE; + x->x_seqsize = SEQ_INISEQSIZE; x->x_nevents = 0; x->x_sequence = x->x_seqini; + x->x_tempomapsize = SEQ_INITEMPOMAPSIZE; + x->x_ntempi = 0; + x->x_tempomap = x->x_tempomapini; outlet_new((t_object *)x, &s_anything); x->x_bangout = outlet_new((t_object *)x, &s_bang); if (s && s != &s_) @@ -991,5 +1044,6 @@ void seq_setup(void) gensym("write"), A_DEFSYM, 0); class_addmethod(seq_class, (t_method)seq_print, gensym("print"), 0); + forky_setpropertiesfn(seq_class, seq_properties); hammerfile_setup(seq_class, 0); } diff --git a/cyclone/hammer/switch.c b/cyclone/hammer/switch.c index d69660d..03c9f7f 100644 --- a/cyclone/hammer/switch.c +++ b/cyclone/hammer/switch.c @@ -103,7 +103,10 @@ static void *switch_new(t_floatarg f1, t_floatarg f2) if (nproxies < SWITCH_MININLETS) nproxies = SWITCH_DEFINLETS; if (nproxies > SWITCH_C74MAXINLETS) + { + shared_usecompatibility(); loud_incompatible_max(switch_class, SWITCH_C74MAXINLETS, "inlets"); + } if (!(proxies = (t_pd **)getbytes(nproxies * sizeof(*proxies)))) return (0); for (ninlets = 0; ninlets < nproxies; ninlets++) diff --git a/cyclone/hammer/urn.c b/cyclone/hammer/urn.c index d6983a2..f496616 100644 --- a/cyclone/hammer/urn.c +++ b/cyclone/hammer/urn.c @@ -122,6 +122,7 @@ static void *urn_new(t_floatarg f1, t_floatarg f2) x->x_urn = x->x_urnini; urn_resize(x, f1, 1); urn_seed(x, f2); /* CHECKME */ + shared_usecompatibility(); inlet_new((t_object *)x, (t_pd *)x, &s_float, gensym("ft1")); outlet_new((t_object *)x, &s_float); x->x_bangout = outlet_new((t_object *)x, &s_bang); diff --git a/cyclone/shadow/Makefile b/cyclone/shadow/Makefile index b700e5e..dc39772 100644 --- a/cyclone/shadow/Makefile +++ b/cyclone/shadow/Makefile @@ -2,6 +2,6 @@ ROOT_DIR = ../.. redefault: default $(ROOT_DIR)/bin/cyclist include $(ROOT_DIR)/Makefile.common -$(ROOT_DIR)/bin/cyclist: $(SHARED_DIR)/common/binport.c - $(CC) -DBINPORT_STANDALONE -o $@ $< - +$(ROOT_DIR)/bin/cyclist: $(SHARED_DIR)/common/binport.c \ + $(SHARED_DIR)/common/lex.c $(SHARED_DIR)/unstable/standalone.c + $(CC) $(CFLAGS) -DMIXED_STANDALONE -o $@ $^ diff --git a/cyclone/shadow/Makefile.objects b/cyclone/shadow/Makefile.objects index f1449e0..dd8fb57 100644 --- a/cyclone/shadow/Makefile.objects +++ b/cyclone/shadow/Makefile.objects @@ -1,6 +1,7 @@ SHARED_OBJECTS = \ common/loud.o \ common/grow.o \ +common/lex.o \ common/binport.o \ common/port.o \ hammer/file.o \ diff --git a/cyclone/shadow/Makefile.sources b/cyclone/shadow/Makefile.sources index 061f37b..be9a840 100644 --- a/cyclone/shadow/Makefile.sources +++ b/cyclone/shadow/Makefile.sources @@ -1,5 +1,6 @@ CX_SOURCES = \ cyclone.c \ +maxmode.c \ dummies.c OTHER_SOURCES = \ diff --git a/cyclone/shadow/cyclone.c b/cyclone/shadow/cyclone.c index 20d1396..a702074 100644 --- a/cyclone/shadow/cyclone.c +++ b/cyclone/shadow/cyclone.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. */ @@ -122,6 +122,11 @@ void cyclone_setup(void) loud_errand(0, "without having cyclone library preloaded"); return; } + if (zgetfn(&pd_objectmaker, gensym("cyclone"))) + { + loud_error(0, "cyclone is already loaded"); + return; + } post("this is cyclone %s, %s %s build", CYCLONE_VERSION, loud_ordinal(CYCLONE_BUILD), CYCLONE_RELEASE); cyclone_class = class_new(gensym("cyclone"), diff --git a/cyclone/sickle/Makefile.objects b/cyclone/sickle/Makefile.objects index f9d4ae7..4528d83 100644 --- a/cyclone/sickle/Makefile.objects +++ b/cyclone/sickle/Makefile.objects @@ -5,6 +5,8 @@ unstable/fringe.o \ common/loud.o \ common/grow.o \ common/vefl.o \ +common/clc.o \ +common/lex.o \ common/binport.o \ common/port.o \ hammer/file.o \ diff --git a/cyclone/sickle/buffir.c b/cyclone/sickle/buffir.c index 0551501..ee42af9 100644 --- a/cyclone/sickle/buffir.c +++ b/cyclone/sickle/buffir.c @@ -38,11 +38,14 @@ static void buffir_setrange(t_buffir *x, t_floatarg f1, t_floatarg f2) { int newsize, pos = x->x_lohead - x->x_histlo; int oldbytes = x->x_histsize * sizeof(*x->x_histlo); - static int warned = 0; - if (!warned) + if (shared_getmaxcompatibility()) { - loud_incompatible(buffir_class, "stretching history buffer"); - warned = 1; + static int warned = 0; + if (!warned) + { + loud_incompatible(buffir_class, "stretching history buffer"); + warned = 1; + } } newsize = x->x_histsize * 2; while (newsize < siz) newsize *= 2; @@ -194,6 +197,7 @@ static void *buffir_new(t_symbol *s, t_floatarg f1, t_floatarg f2) x->x_histlo = x->x_histini; buffir_clear(x); buffir_setrange(x, f1, f2); + shared_usecompatibility(); } return (x); } diff --git a/cyclone/sickle/curve.c b/cyclone/sickle/curve.c index e6bade1..2bad587 100644 --- a/cyclone/sickle/curve.c +++ b/cyclone/sickle/curve.c @@ -1,4 +1,4 @@ -/* Copyright (c) 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. */ @@ -7,54 +7,14 @@ #include "shared.h" #include "common/grow.h" #include "common/loud.h" +#include "common/clc.h" #include "sickle/sic.h" //#define CURVE_DEBUG -/* CHECKED apparently c74's formula was not very carefully tuned. It has 5% - deviation from the straight line for ccinput=0 (ccinput is user's curve - control parameter, <0..1>) at half-domain, range=1. It generates nans for - ccinput > .995. - - The formula below generates curves with < .000004% deviation and no nans. - - Problem: find a function f : ccinput -> cc, such that the curves will bend - in a semi-linear way over the ccinput's range of 0..1. The curve function - is then g(x, p) = (exp(f(p) * x) - 1) / (exp(f(p)) - 1), where x is curve's - domain, and p is ccinput. If, for example, the points g(0.5, p) are to make - a semi-linear pattern, then the solution is a function f that minimizes - the integral of the error function e(p) = sqr(((1-p)/2)-g(.5, p)) over 0..1. - Until someone does this analytically, we are left with a lame formula, which - has been tweaked and tested in gnuplot: f(p) = h(p) / (1 - h(p)), where - h(p) = (((p + 1e-20) * 1.2) ** .41) * .91. The file curve.gp, in the - sickle's source directory, may come handy, in case there is anyone, who - fancy tweaking it even further. - - To implement this, start from these equations: - bb * mm ^ npoints = bb + 1 - (bb ^ 2) * (mm ^ npoints) = ((exp(ff/2) - 1) / (exp(ff) - 1)) ^ 2 - - and calculate: - hh = pow(((ccinput + c1) * c2), c3) * c4 - ff = hh / (1 - hh) - eff = exp(ff) - 1 - gh = (exp(ff * .5) - 1) / eff - bb = gh * (gh / (1 - (gh + gh))) - mm = ((exp(ff * (1/npoints)) - 1) / (eff * bb)) + 1 - - The loop is: - for (vv = bb, i = 0; i < n; vv *= mm, i++) - result = (vv - bb) * (y1 - y0) + y0 - where y0, y1 are start and destination values -*/ - -#define CURVE_C1 1e-20 -#define CURVE_C2 1.2 -#define CURVE_C3 0.41 -#define CURVE_C4 0.91 - -#define CURVE_MINCCINPUT -1. -#define CURVE_MAXCCINPUT 1. +/* CHECKED apparently c74's formula has not been carefully tuned (yet?). + It has 5% deviation from the straight line for ccinput = 0 at half-domain, + range 1, and generates nans for ccinput > .995 (cf comment in clc.h). */ #define CURVE_INISIZE 64 /* LATER rethink */ #define CURVE_MAXSIZE 64 @@ -63,7 +23,7 @@ typedef struct _curveseg { float s_target; float s_delta; - int s_npoints; + int s_nhops; float s_ccinput; double s_bb; double s_mm; @@ -95,7 +55,7 @@ typedef struct _curve #ifdef CURVE_DEBUG int dbg_nretargets; int dbg_exitpoint; - int dbg_npoints; + int dbg_nhops; #endif } t_curve; @@ -104,42 +64,10 @@ static double curve_coef; static void curve_cc(t_curve *x, t_curveseg *segp, float f) { - int npoints = segp->s_delta * x->x_ksr + 0.5; /* LATER rethink */ + int nhops = segp->s_delta * x->x_ksr + 0.5; /* LATER rethink */ segp->s_ccinput = f; - if (npoints > 0) - { - double hh, ff, eff, gh; - segp->s_npoints = npoints; - if (f < 0) - { - if (f < CURVE_MINCCINPUT) - f = CURVE_MINCCINPUT; - hh = pow(((CURVE_C1 - f) * CURVE_C2), CURVE_C3) * CURVE_C4; - ff = hh / (1. - hh); - eff = exp(ff) - 1.; - gh = (exp(ff * .5) - 1.) / eff; - segp->s_bb = gh * (gh / (1. - (gh + gh))); - segp->s_mm = 1. / (((exp(ff * (1. / (double)npoints)) - 1.) / - (eff * segp->s_bb)) + 1.); - } - else - { - if (f > CURVE_MAXCCINPUT) - f = CURVE_MAXCCINPUT; - hh = pow(((f + CURVE_C1) * CURVE_C2), CURVE_C3) * CURVE_C4; - ff = hh / (1. - hh); - eff = exp(ff) - 1.; - gh = (exp(ff * .5) - 1.) / eff; - segp->s_bb = gh * (gh / (1. - (gh + gh))); - segp->s_mm = ((exp(ff * (1. / (double)npoints)) - 1.) / - (eff * segp->s_bb)) + 1.; - } - } - else - { - segp->s_npoints = 0; - segp->s_bb = segp->s_mm = 1.; - } + segp->s_nhops = (nhops > 0 ? nhops : 0); + clccurve_coefs(segp->s_nhops, (double)f, &segp->s_bb, &segp->s_mm); #ifdef CURVE_DEBUG post("%g %g %g %g", segp->s_target, segp->s_delta, segp->s_bb, segp->s_mm); @@ -152,9 +80,9 @@ static void curve_tick(t_curve *x) #ifdef CURVE_DEBUG post("exit point %d, after %d retarget calls", x->dbg_exitpoint, x->dbg_nretargets); - post("at value %g, after last %d npoints, with bb %g, mm %g", - x->x_value, x->dbg_npoints, x->x_bb, x->x_mm); - x->dbg_nretargets = x->dbg_exitpoint = x->dbg_npoints = 0; + post("at value %g, after last %d nhops, with bb %g, mm %g", + x->x_value, x->dbg_nhops, x->x_bb, x->x_mm); + x->dbg_nretargets = x->dbg_exitpoint = x->dbg_nhops = 0; #endif } @@ -177,42 +105,32 @@ retarget: { float target = x->x_curseg->s_target; float delta = x->x_curseg->s_delta; - int npoints = x->x_curseg->s_npoints; + int nhops = x->x_curseg->s_nhops; + bb = x->x_curseg->s_bb; mm = x->x_curseg->s_mm; if (x->x_curseg->s_ccinput < 0) - { - bb = x->x_curseg->s_bb + 1.; dy = x->x_value - target; - } else - { - bb = x->x_curseg->s_bb; dy = target - x->x_value; - } #ifdef CURVE_DEBUG x->dbg_nretargets++; #endif x->x_nsegs--; x->x_curseg++; - while (npoints <= 0) + while (nhops <= 0) { curval = x->x_value = target; if (x->x_nsegs) { target = x->x_curseg->s_target; delta = x->x_curseg->s_delta; - npoints = x->x_curseg->s_npoints; + nhops = x->x_curseg->s_nhops; + bb = x->x_curseg->s_bb; mm = x->x_curseg->s_mm; if (x->x_curseg->s_ccinput < 0) - { - bb = x->x_curseg->s_bb + 1.; dy = x->x_value - target; - } else - { - bb = x->x_curseg->s_bb; dy = target - x->x_value; - } x->x_nsegs--; x->x_curseg++; } @@ -228,7 +146,7 @@ retarget: return (w + 4); } } - nxfer = x->x_nleft = npoints; + nxfer = x->x_nleft = nhops; x->x_vv = vv = bb; x->x_bb = bb; x->x_mm = mm; @@ -237,7 +155,7 @@ retarget: x->x_target = target; x->x_retarget = 0; #ifdef CURVE_DEBUG - x->dbg_npoints = npoints; + x->dbg_nhops = nhops; #endif } if (nxfer >= nblock) @@ -422,13 +340,7 @@ static void curve_free(t_curve *x) static void *curve_new(t_floatarg f1, t_floatarg f2) { - static int initialized = 0; t_curve *x = (t_curve *)pd_new(curve_class); - if (!initialized) - { - curve_coef = CURVE_C2 / exp(CURVE_C3); - initialized = 1; - } x->x_value = x->x_target = f1; x->x_ccinput = f2; x->x_deltaset = 0; diff --git a/cyclone/sickle/sickle.c b/cyclone/sickle/sickle.c index 8df7974..807c2ad 100644 --- a/cyclone/sickle/sickle.c +++ b/cyclone/sickle/sickle.c @@ -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. */ @@ -76,6 +76,11 @@ void sickle_setup(void) loud_errand(0, "without having sickle library preloaded"); return; } + if (zgetfn(&pd_objectmaker, gensym("sickle"))) + { + loud_error(0, "sickle is already loaded"); + return; + } if (!zgetfn(&pd_objectmaker, gensym("cyclone"))) post("this is sickle %s, %s %s build", CYCLONE_VERSION, loud_ordinal(CYCLONE_BUILD), CYCLONE_RELEASE); 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); diff --git a/test/cyclone/offer-test.pd b/test/cyclone/offer-test.pd index 537a3b9..94cdfa7 100644 --- a/test/cyclone/offer-test.pd +++ b/test/cyclone/offer-test.pd @@ -1,11 +1,11 @@ #N canvas 299 297 735 363 12; #X obj 49 244 offer; -#X floatatom 49 279 5 0 0; +#X floatatom 49 279 5 0 0 0 - - -; #X msg 50 114 clear; -#X floatatom 172 181 5 0 0; +#X floatatom 172 181 5 0 0 0 - - -; #X obj 212 244 * -1; #X obj 172 209 t 0 0; -#X floatatom 172 147 5 0 0; +#X floatatom 172 147 5 0 0 0 - - -; #X obj 279 110 Uzi; #X msg 49 181 debug \$1; #X msg 71 147 1; @@ -30,11 +30,14 @@ #X obj 531 234 t 0 b; #X msg 531 196 50; #X msg 582 196 100; -#X obj 429 234 Uzi; -#X msg 429 196 50; -#X msg 480 196 100; -#X obj 429 271 urn 100; +#X obj 409 218 Uzi; +#X msg 330 147 50; +#X msg 369 147 100; #X msg 117 209 1.5; +#X obj 409 181 t 0 0; +#X obj 409 255 urn; +#X msg 409 147 500; +#X msg 449 147 5000; #X connect 0 0 1 0; #X connect 2 0 0 0; #X connect 3 0 5 0; @@ -69,8 +72,12 @@ #X connect 28 1 27 0; #X connect 29 0 28 0; #X connect 30 0 28 0; -#X connect 31 0 34 0; -#X connect 32 0 31 0; -#X connect 33 0 31 0; +#X connect 31 0 36 0; +#X connect 32 0 35 0; +#X connect 33 0 35 0; #X connect 34 0 0 0; -#X connect 35 0 0 0; +#X connect 35 0 31 0; +#X connect 35 1 36 1; +#X connect 36 0 0 0; +#X connect 37 0 35 0; +#X connect 38 0 35 0; diff --git a/test/toxy/setup.wid b/test/toxy/setup.wid index 9da8775..3ae0a70 100644 --- a/test/toxy/setup.wid +++ b/test/toxy/setup.wid @@ -116,20 +116,20 @@ proc ::toxy::item_visconfig {path target name varname cvpath px py} { } } - if {[info exists ::toxy::masterinits]} { - set failed [catch {eval $::toxy::masterinits} res] - unset ::toxy::masterinits - if {$failed} { error [concat in ::toxy::masterinits: $res] } + if {[info exists ::toxy::masterinit]} { + set failed [catch {eval $::toxy::masterinit} res] + unset ::toxy::masterinit + if {$failed} { error [concat in ::toxy::masterinit: $res] } } - if {[info exists ::toxy::typeinits]} { - set failed [catch {eval $::toxy::typeinits} res] - unset ::toxy::typeinits - if {$failed} { error [concat in ::toxy::typeinits: $res] } + if {[info exists ::toxy::typeinit]} { + set failed [catch {eval $::toxy::typeinit} res] + unset ::toxy::typeinit + if {$failed} { error [concat in ::toxy::typeinit: $res] } } - if {[info exists ::toxy::iteminits]} { - set failed [catch {eval $::toxy::iteminits} res] - unset ::toxy::iteminits - if {$failed} { error [concat in ::toxy::iteminits: $res] } + if {[info exists ::toxy::iteminit]} { + set failed [catch {eval $::toxy::iteminit} res] + unset ::toxy::iteminit + if {$failed} { error [concat in ::toxy::iteminit: $res] } } ::toxy::item_getconfig $path $target @@ -204,7 +204,15 @@ proc ::toxy::master {path toppath cvpath target} { # FIXME proc ::toxy::scale_command {target sel v} { - pd [concat $target $sel $v \;] + if {$::toxy::scale_isactive} { + pd [concat $target $sel $v \;] + } + set ::toxy::scale_isactive 1 +} + +proc ::toxy::scale_doset {path v} { + set ::toxy::scale_isactive 0 + $path set $v } proc ::toxy::popup_command {path target remote i text} { @@ -239,6 +247,9 @@ proc ::toxy::popup {path target remote entries args} { ::toxy::master .- .- .^.c .| +# FIXME +set ::toxy::scale_isactive 1 + # standard widget types #> bang button @@ -250,6 +261,7 @@ proc ::toxy::popup {path target remote entries args} { #. -command [concat ::toxy::scale_command .| _cb] #. -bg pink -activebackground red -length 200 #. @float .- set .#1 +#. @vset ::toxy::scale_doset .- .#1 #> symbol entry #. -bg pink -font .(helvetica 24.) -width 16 diff --git a/toxy/Makefile b/toxy/Makefile index 3035cb4..cd9cd5a 100644 --- a/toxy/Makefile +++ b/toxy/Makefile @@ -8,6 +8,7 @@ checkwiq: rm -f $(WIQFILE) ; fi $(WIQFILE): $(WIDPATH) @echo transferring widget definitions from \"$<\" to \"$@\" +# LATER think how to replace puts with pdtk_post @echo -e '// Do not edit this file (edit "$<", and run "make").\ \n//\nputs stderr [concat loading built-in widget definitions]' \ | cat - $< | sed \ diff --git a/toxy/plustot.print.c b/toxy/plustot.print.c index 42ef385..0bd6357 100644 --- a/toxy/plustot.print.c +++ b/toxy/plustot.print.c @@ -16,6 +16,11 @@ typedef struct _plustot_print static t_class *plustot_print_class; +static char *plustot_print_symbolname(t_symbol *s) +{ + return (s && s != &s_ ? s->s_name : "???"); +} + static void plustot_print_symbol(t_plustot_print *x, t_symbol *s) { Tcl_Obj *ob = plustag_tobvalue(s, (t_pd *)x); @@ -36,10 +41,9 @@ static void plustot_print_symbol(t_plustot_print *x, t_symbol *s) t_atom *av = binbuf_getvec(x->x_bb); if (av->a_type == A_SYMBOL || av->a_type == A_FLOAT) { - char *lstring = - (x->x_label ? x->x_label->s_name : - loud_symbolname(plustag_typename(s, 1, (t_pd *)x), - "???")); + char *lstring = (x->x_label ? x->x_label->s_name : + plustot_print_symbolname( + plustag_typename(s, 1, (t_pd *)x))); if (glname) startpost("%s (%s):", lstring, glname->s_name); else diff --git a/toxy/widget.c b/toxy/widget.c index d544145..7bfbb74 100644 --- a/toxy/widget.c +++ b/toxy/widget.c @@ -2,8 +2,6 @@ * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ -/* LATER consider supporting a special @ini handler, also think about - differentiating 'ini' from 'vis' */ /* LATER think about reloading method for .wid files */ #include @@ -39,6 +37,18 @@ typedef struct _widgetentry struct _widgetentry *we_next; } t_widgetentry; +/* move to widgettype.c&h */ +typedef struct _widgethandlers +{ + t_scriptlet *wh_initializer; + t_scriptlet *wh_new; + t_scriptlet *wh_free; + t_scriptlet *wh_bang; + t_scriptlet *wh_float; + t_scriptlet *wh_symbol; + /* ... (varsized vector) */ +} t_widgethandlers; + typedef struct _widget { t_object x_ob; @@ -55,6 +65,7 @@ typedef struct _widget t_props *x_options; /* instance options */ t_props *x_handlers; /* instance handlers */ t_props *x_arguments; /* instance arguments */ + t_widgethandlers x_cache; /* actual handlers */ t_scriptlet *x_iniscript; /* instance initializer */ t_scriptlet *x_optscript; /* option scriptlet */ t_scriptlet *x_auxscript; /* auxiliary scriptlet */ @@ -145,18 +156,35 @@ static t_symbol *widget_getmypathname(t_widget *x, t_glist *glist) return (gensym(buf)); } -static void widget_postatoms(char *msg, int ac, t_atom *av) +/* pity cannot set sys_printtostderr... */ +static void widget_postatoms(FILE *fp, char *msg, int ac, t_atom *av) { - startpost(msg); - while (ac--) + if (fp) { - if (av->a_type == A_FLOAT) - postfloat(av->a_w.w_float); - else if (av->a_type == A_SYMBOL) - poststring(av->a_w.w_symbol->s_name); - av++; + fputs(msg, fp); + while (ac--) + { + char buf[80]; + atom_string(av, buf, 80); + fputc(' ', fp); + fputs(buf, fp); + av++; + } + fputc('\n', fp); + } + else + { + startpost(msg); + while (ac--) + { + if (av->a_type == A_FLOAT) + postfloat(av->a_w.w_float); + else if (av->a_type == A_SYMBOL) + poststring(av->a_w.w_symbol->s_name); + av++; + } + endpost(); } - endpost(); } /* If Tk widget creation fails, gui will send the '_failure' message @@ -292,26 +320,54 @@ static void widget_pushoptions(t_widget *x, int doit) static void widget_pushinits(t_widget *x) { - if (masterwidget_evaluate(x->x_transient, 0, 0, 0, x->x_arguments)) - scriptlet_vpush(x->x_transient, "masterinits"); + if (masterwidget_ievaluate(x->x_transient, 0, 0, 0, x->x_arguments)) + scriptlet_vpush(x->x_transient, "masterinit"); else bug("widget_pushinits (master)"); if (widgettype_isdefined(x->x_typedef)) { int sz; - if (widgettype_evaluate(x->x_typedef, x->x_transient, 0, - 0, 0, x->x_arguments)) - scriptlet_vpush(x->x_transient, "typeinits"); - else if (*widgettype_getcontents(x->x_typedef, &sz) && sz > 0) + if (widgettype_ievaluate(x->x_typedef, x->x_transient, 0, + 0, 0, x->x_arguments)) + scriptlet_vpush(x->x_transient, "typeinit"); + else if (*widgettype_getinitializer(x->x_typedef, &sz) && sz > 0) bug("widget_pushinits (type)"); } if (scriptlet_evaluate(x->x_iniscript, x->x_transient, 0, 0, 0, x->x_arguments)) - scriptlet_vpush(x->x_transient, "iteminits"); + scriptlet_vpush(x->x_transient, "iteminit"); else if (!scriptlet_isempty(x->x_iniscript)) bug("widget_pushinits (instance)"); } +static void widget_pushconstructors(t_widget *x) +{ + /* LATER master constructor */ + if (widgettype_isdefined(x->x_typedef)) + { + int sz; + if (widgettype_cevaluate(x->x_typedef, x->x_transient, 0, + 0, 0, x->x_arguments)) + scriptlet_push(x->x_transient); + else if (*widgettype_getconstructor(x->x_typedef, &sz) && sz > 0) + bug("widget_pushconstructors (type)"); + } +} + +static void widget_pushdestructors(t_widget *x) +{ + /* LATER master destructor */ + if (widgettype_isdefined(x->x_typedef)) + { + int sz; + if (widgettype_devaluate(x->x_typedef, x->x_transient, 0, + 0, 0, x->x_arguments)) + scriptlet_push(x->x_transient); + else if (*widgettype_getdestructor(x->x_typedef, &sz) && sz > 0) + bug("widget_pushdestructors (type)"); + } +} + static void widget_getconfig(t_widget *x) { sys_vgui("::toxy::item_getconfig %s %s\n", @@ -465,7 +521,9 @@ static void widget_update(t_widget *x, t_props *op) } else { - /* LATER cache handlers */ + /* LATER cache handlers. + We get here both during construction, and after any change + in our handlers -- the cache never stales. */ } } @@ -697,7 +755,7 @@ static void widget_refresh(t_widget *x) static void widget__failure(t_widget *x, t_symbol *s, int ac, t_atom *av) { #if 0 - /* moved to the gui side, in order to alow special chars in error message */ + /* moved to the gui side -- supporting special chars in error message */ startpost("tcl error:"); postatom(ac, av); endpost(); @@ -839,39 +897,56 @@ static void widget_debug(t_widget *x) t_symbol *mn = widget_getmypathname(x, 0); int sz, i, nopt; t_atom *ap; + static char bempty[] = ""; char *bp, *key; - post("containing glist: %x", x->x_glist); - post("cv pathname%s %s", (pn ? ":" : ""), (pn ? pn->s_name : "unknown")); - post("my pathname%s %s", (mn ? ":" : ""), (mn ? mn->s_name : "unknown")); + fprintf(stderr, "containing glist: %x\n", (int)x->x_glist); + fprintf(stderr, "cv pathname%s %s\n", + (pn ? ":" : ""), (pn ? pn->s_name : "unknown")); + fprintf(stderr, "my pathname%s %s\n", + (mn ? ":" : ""), (mn ? mn->s_name : "unknown")); if (ap = props_getall(widgettype_getoptions(x->x_typedef), &nopt)) - widget_postatoms("default options:", nopt, ap); + widget_postatoms(stderr, "default options:", nopt, ap); if (ap = props_getall(x->x_options, &nopt)) - widget_postatoms("instance options:", nopt, ap); + widget_postatoms(stderr, "instance options:", nopt, ap); if (ap = props_getall(widgettype_gethandlers(x->x_typedef), &nopt)) - widget_postatoms("default handlers:", nopt, ap); + widget_postatoms(stderr, "default handlers:", nopt, ap); if (ap = props_getall(x->x_handlers, &nopt)) - widget_postatoms("instance handlers:", nopt, ap); + widget_postatoms(stderr, "instance handlers:", nopt, ap); if (ap = props_getall(widgettype_getarguments(x->x_typedef), &nopt)) - widget_postatoms("default arguments:", nopt, ap); + widget_postatoms(stderr, "default arguments:", nopt, ap); if (ap = props_getall(x->x_arguments, &nopt)) - widget_postatoms("instance arguments:", nopt, ap); - post("dictionary:"); + widget_postatoms(stderr, "instance arguments:", nopt, ap); + fprintf(stderr, "dictionary:\n"); bp = props_firstvalue(x->x_arguments, &key); while (bp) { - post("\t%s: \"%s\"", key, bp); + fprintf(stderr, "\t%s: \"%s\"\n", key, bp); bp = props_nextvalue(x->x_arguments, &key); } bp = scriptlet_getcontents(x->x_transient, &sz); - post("transient buffer (size %d):\n\"%s\"", sz, bp); + fprintf(stderr, "transient buffer (size %d):\n\"%s\"\n", + sz, (bp ? bp : bempty)); bp = scriptlet_getcontents(x->x_optscript, &sz); - post("option buffer (size %d):\n\"%s\"", sz, bp); - bp = widgettype_getcontents(x->x_typedef, &sz); - post("type initializer (size %d):\n\"%s\"", sz, bp); + fprintf(stderr, "option buffer (size %d):\n\"%s\"\n", + sz, (bp ? bp : bempty)); + bp = widgettype_getconstructor(x->x_typedef, &sz); + fprintf(stderr, "type constructor (size %d):\n\"%s\"\n", + sz, (bp ? bp : bempty)); + bp = widgettype_getdestructor(x->x_typedef, &sz); + fprintf(stderr, "type destructor (size %d):\n\"%s\"\n", + sz, (bp ? bp : bempty)); + bp = masterwidget_getinitializer(&sz); + fprintf(stderr, "master initializer (size %d):\n\"%s\"\n", + sz, (bp ? bp : bempty)); + bp = widgettype_getinitializer(x->x_typedef, &sz); + fprintf(stderr, "type initializer (size %d):\n\"%s\"\n", + sz, (bp ? bp : bempty)); bp = scriptlet_getcontents(x->x_iniscript, &sz); - post("instance initializer (size %d):\n\"%s\"", sz, bp); + fprintf(stderr, "instance initializer (size %d):\n\"%s\"\n", + sz, (bp ? bp : bempty)); bp = masterwidget_getcontents(&sz); - post("setup definitions (size %d):\n\"%s\"", sz, bp); + fprintf(stderr, "setup definitions (size %d):\n\"%s\"\n", + sz, (bp ? bp : bempty)); } #endif @@ -889,6 +964,7 @@ static void gui_unbind(t_pd *x, t_symbol *s) static void widget_free(t_widget *x) { widget_novis(x); + widget_pushdestructors(x); gui_unbind((t_pd *)x, x->x_cbtarget); gui_unbind((t_pd *)x, x->x_rptarget); props_freeall(x->x_options); @@ -907,7 +983,7 @@ static void *widget_new(t_symbol *s, int ac, t_atom *av) char buf[MAXPDSTRING]; if (widget_transforming) return (0); - masterwidget_initialize(); + masterwidget_validate(); x = (t_widget *)pd_new(widget_class); x->x_type = 0; x->x_name = 0; @@ -978,6 +1054,7 @@ static void *widget_new(t_symbol *s, int ac, t_atom *av) x->x_disabled = 0; x->x_vised = 0; widget_attach(x); + widget_pushconstructors(x); return (x); } @@ -988,7 +1065,7 @@ static t_glist *tow_getglist(t_tow *x, int complain) (t_glist *)pd_findbyclass(x->x_cvremote, canvas_class) : x->x_glist); if (!glist && complain) loud_error((t_pd *)x, "bad canvas name '%s'", x->x_cvname->s_name); - return (glist); + return (glist_getcanvas(glist)); } static void tow_bang(t_tow *x) @@ -1160,20 +1237,22 @@ static void tow_detach(t_tow *x) static void tow_debug(t_tow *x) { t_widgetentry *we; - post("attached widgets:"); + fprintf(stderr, "attached widgets:\n"); for (we = x->x_widgetlist; we; we = we->we_next) { t_widget *w = we->we_widget; t_towentry *te; int other = 0, found = 0; - startpost("\t%s %s", w->x_type->s_name, w->x_cbtarget->s_name); + fprintf(stderr, "\t%s %s", w->x_type->s_name, w->x_cbtarget->s_name); for (te = w->x_towlist; te; te = te->te_next) if (te->te_tow == x) found++; else other++; - post(" (%d other tow%s)", other, (other == 1 ? "" : "s")); - if (found != 1) post("BUG: listed %d times in widget's towlist", found); + fprintf(stderr, " (%d other tow%s)\n", other, (other == 1 ? "" : "s")); + if (found != 1) + fprintf(stderr, "BUG: listed %d times in widget's towlist\n", + found); } } #endif @@ -1207,8 +1286,28 @@ static void *tow_new(t_symbol *s1, t_symbol *s2, t_symbol *s3) t_tow *x = (t_tow *)pd_new(tow_class); char buf[64]; x->x_glist = canvas_getcurrent(); - if (s1 && s1 != &s_ && strcmp(s1->s_name, ".")) - x->x_cvremote = canvas_makebindsym(x->x_cvname = s1); + if (s1 == &s_ || !strcmp(s1->s_name, ".")) + s1 = 0; + if (s1) + { + if (strcmp(s1->s_name, ".parent")) + x->x_cvremote = canvas_makebindsym(x->x_cvname = s1); + else + { + if (x->x_glist->gl_owner) + { + x->x_glist = x->x_glist->gl_owner; + x->x_cvremote = 0; + x->x_cvname = x->x_glist->gl_name; + } + else + { + /* FIXME */ + loud_error((t_pd *)x, "no parent patch"); + x->x_cvremote = canvas_makebindsym(x->x_cvname = s1); + } + } + } else { x->x_cvremote = 0; diff --git a/toxy/widgettype.c b/toxy/widgettype.c index 3cb7d6e..0b8163f 100644 --- a/toxy/widgettype.c +++ b/toxy/widgettype.c @@ -16,7 +16,7 @@ static char masterwidget_builtin[] = ; #define WIDGETTYPE_VERBOSE -//#define WIDGETTYPE_DEBUG +#define WIDGETTYPE_DEBUG struct _widgettype { @@ -28,6 +28,8 @@ struct _widgettype t_props *wt_handlers; t_props *wt_arguments; t_scriptlet *wt_iniscript; + t_scriptlet *wt_newscript; + t_scriptlet *wt_freescript; }; struct _masterwidget @@ -36,7 +38,7 @@ struct _masterwidget t_symbol *mw_target; t_scriptlet *mw_setupscript; t_dict *mw_typemap; - t_widgettype *mw_mastertype; /* contains master iniscript */ + t_widgettype *mw_mastertype; /* contains master initializer */ t_widgettype *mw_parsedtype; /* the type currently parsed, if loading */ t_binbuf *mw_bb; /* auxiliary, LATER remove */ }; @@ -68,6 +70,10 @@ static t_widgettype *widgettype_new(t_masterwidget *mw, wt->wt_arguments = props_new(0, "argument", "#", wt->wt_options, 0); wt->wt_iniscript = scriptlet_new((t_pd *)wt, mw->mw_target, mw->mw_target, 0, 0, widgettype_cvhook); + wt->wt_newscript = scriptlet_new((t_pd *)wt, mw->mw_target, mw->mw_target, + 0, 0, widgettype_cvhook); + wt->wt_freescript = scriptlet_new((t_pd *)wt, mw->mw_target, mw->mw_target, + 0, 0, widgettype_cvhook); dict_bind(mw->mw_typemap, (t_pd *)wt, wt->wt_typekey); return (wt); } @@ -93,7 +99,7 @@ static t_scriptlet *masterwidget_cmnthook(t_pd *caller, char *rc, if (!cls) cls = buf; typekey = dict_key(mw->mw_typemap, buf); - typeval = (t_widgettype *)dict_value(mw->mw_typemap, typekey); + typeval = (t_widgettype *)dict_firstvalue(mw->mw_typemap, typekey, 0); if (caller == (t_pd *)mw) { /* setup.wid or built-in defaults */ if (mw->mw_mastertype) @@ -162,6 +168,24 @@ static t_scriptlet *masterwidget_cmnthook(t_pd *caller, char *rc, } } } + else if (sel == '@') + { /* multiline definition of a handler */ + scriptlet_nextword(buf); + if (mw->mw_parsedtype) + { + if (!strcmp(buf, "vis") || !strcmp(buf, "ini")) + return (mw->mw_parsedtype->wt_iniscript); + else if (!strcmp(buf, "new")) + return (mw->mw_parsedtype->wt_newscript); + else if (!strcmp(buf, "free")) + return (mw->mw_parsedtype->wt_freescript); + else + { + /* LATER start parsing any method handler: search for it, + create if not found, return */ + } + } + } return (SCRIPTLET_UNLOCK); } @@ -170,9 +194,9 @@ t_widgettype *widgettype_get(t_symbol *s) t_widgettype *wt; /* Design decision: setup.wid defs are NOT overridden by .wid (sacrificing flexibility for feature stability). */ - if (wt = (t_widgettype *)dict_value(masterwidget->mw_typemap, - dict_key(masterwidget->mw_typemap, - s->s_name))) + if (wt = (t_widgettype *)dict_firstvalue(masterwidget->mw_typemap, + dict_key(masterwidget->mw_typemap, + s->s_name), 0)) masterwidget->mw_parsedtype = 0; else { @@ -193,7 +217,7 @@ t_widgettype *widgettype_get(t_symbol *s) == SCRIPTLET_OK) { #ifdef WIDGETTYPE_VERBOSE - post("using %s's initializer", s->s_name); + post("using a separate %s's definition file", s->s_name); #endif if (!scriptlet_isempty(mwsp)) { @@ -239,18 +263,42 @@ t_props *widgettype_getarguments(t_widgettype *wt) return (wt->wt_arguments); } -char *widgettype_getcontents(t_widgettype *wt, int *szp) +char *widgettype_getinitializer(t_widgettype *wt, int *szp) { return (scriptlet_getcontents(wt->wt_iniscript, szp)); } -int widgettype_evaluate(t_widgettype *wt, t_scriptlet *outsp, - int visedonly, int ac, t_atom *av, t_props *argprops) +char *widgettype_getconstructor(t_widgettype *wt, int *szp) +{ + return (scriptlet_getcontents(wt->wt_newscript, szp)); +} + +char *widgettype_getdestructor(t_widgettype *wt, int *szp) +{ + return (scriptlet_getcontents(wt->wt_freescript, szp)); +} + +int widgettype_ievaluate(t_widgettype *wt, t_scriptlet *outsp, + int visedonly, int ac, t_atom *av, t_props *argprops) { return (scriptlet_evaluate(wt->wt_iniscript, outsp, visedonly, ac, av, argprops)); } +int widgettype_cevaluate(t_widgettype *wt, t_scriptlet *outsp, + int visedonly, int ac, t_atom *av, t_props *argprops) +{ + return (scriptlet_evaluate(wt->wt_newscript, outsp, + visedonly, ac, av, argprops)); +} + +int widgettype_devaluate(t_widgettype *wt, t_scriptlet *outsp, + int visedonly, int ac, t_atom *av, t_props *argprops) +{ + return (scriptlet_evaluate(wt->wt_freescript, outsp, + visedonly, ac, av, argprops)); +} + void widgettype_setup(void) { static int done = 0; @@ -264,11 +312,10 @@ void widgettype_setup(void) } } -int masterwidget_evaluate(t_scriptlet *outsp, int visedonly, - int ac, t_atom *av, t_props *argprops) +char *masterwidget_getinitializer(int *szp) { - return (scriptlet_evaluate(masterwidget->mw_mastertype->wt_iniscript, - outsp, visedonly, ac, av, argprops)); + return (scriptlet_getcontents(masterwidget->mw_mastertype->wt_iniscript, + szp)); } char *masterwidget_getcontents(int *szp) @@ -276,7 +323,14 @@ char *masterwidget_getcontents(int *szp) return (scriptlet_getcontents(masterwidget->mw_setupscript, szp)); } -void masterwidget_initialize(void) +int masterwidget_ievaluate(t_scriptlet *outsp, int visedonly, + int ac, t_atom *av, t_props *argprops) +{ + return (scriptlet_evaluate(masterwidget->mw_mastertype->wt_iniscript, + outsp, visedonly, ac, av, argprops)); +} + +void masterwidget_validate(void) { int rcresult; t_symbol *typekey; @@ -315,7 +369,8 @@ void masterwidget_initialize(void) "no file 'setup.wid'... using built-in defaults"); } typekey = dict_key(masterwidget->mw_typemap, "master"); - if ((typeval = (t_widgettype *)dict_value(masterwidget->mw_typemap, typekey)) + if ((typeval = (t_widgettype *)dict_firstvalue(masterwidget->mw_typemap, + typekey, 0)) && !scriptlet_isempty(masterwidget->mw_setupscript)) { masterwidget->mw_mastertype = typeval; diff --git a/toxy/widgettype.h b/toxy/widgettype.h index d0df8c6..4894d00 100644 --- a/toxy/widgettype.h +++ b/toxy/widgettype.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. */ @@ -18,14 +18,21 @@ t_props *widgettype_getoptions(t_widgettype *wt); t_props *widgettype_gethandlers(t_widgettype *wt); t_props *widgettype_getarguments(t_widgettype *wt); char *widgettype_propname(t_symbol *s); -char *widgettype_getcontents(t_widgettype *wt, int *szp); -int widgettype_evaluate(t_widgettype *wt, t_scriptlet *outsp, - int visedonly, int ac, t_atom *av, t_props *argprops); +char *widgettype_getinitializer(t_widgettype *wt, int *szp); +char *widgettype_getconstructor(t_widgettype *wt, int *szp); +char *widgettype_getdestructor(t_widgettype *wt, int *szp); +int widgettype_ievaluate(t_widgettype *wt, t_scriptlet *outsp, + int visedonly, int ac, t_atom *av, t_props *argprops); +int widgettype_cevaluate(t_widgettype *wt, t_scriptlet *outsp, + int visedonly, int ac, t_atom *av, t_props *argprops); +int widgettype_devaluate(t_widgettype *wt, t_scriptlet *outsp, + int visedonly, int ac, t_atom *av, t_props *argprops); void widgettype_setup(void); +char *masterwidget_getinitializer(int *szp); char *masterwidget_getcontents(int *szp); -int masterwidget_evaluate(t_scriptlet *outsp, int visedonly, - int ac, t_atom *av, t_props *argprops); -void masterwidget_initialize(void); +int masterwidget_ievaluate(t_scriptlet *outsp, int visedonly, + int ac, t_atom *av, t_props *argprops); +void masterwidget_validate(void); #endif -- cgit v1.2.1