diff options
Diffstat (limited to 'shared')
53 files changed, 6069 insertions, 0 deletions
diff --git a/shared/Makefile b/shared/Makefile new file mode 100644 index 0000000..fc022be --- /dev/null +++ b/shared/Makefile @@ -0,0 +1,2 @@ +ROOT_DIR = .. +include $(ROOT_DIR)/Makefile.common diff --git a/shared/Makefile.dirs b/shared/Makefile.dirs new file mode 100644 index 0000000..d9be5aa --- /dev/null +++ b/shared/Makefile.dirs @@ -0,0 +1 @@ +MIXED_DIRS = common hammer sickle toys unstable diff --git a/shared/Makefile.objects b/shared/Makefile.objects new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/shared/Makefile.objects diff --git a/shared/Makefile.sources b/shared/Makefile.sources new file mode 100644 index 0000000..6e792b8 --- /dev/null +++ b/shared/Makefile.sources @@ -0,0 +1,2 @@ +OTHER_SOURCES = \ +shared.c diff --git a/shared/common/Makefile b/shared/common/Makefile new file mode 100644 index 0000000..5dcb2c8 --- /dev/null +++ b/shared/common/Makefile @@ -0,0 +1,4 @@ +ROOT_DIR = ../.. +include $(ROOT_DIR)/Makefile.common + +all: $(OBJECTS) diff --git a/shared/common/Makefile.objects b/shared/common/Makefile.objects new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/shared/common/Makefile.objects diff --git a/shared/common/Makefile.sources b/shared/common/Makefile.sources new file mode 100644 index 0000000..64da6f8 --- /dev/null +++ b/shared/common/Makefile.sources @@ -0,0 +1,16 @@ +OTHER_SOURCES = \ +bifi.c \ +binport.c \ +dict.c \ +grow.c \ +hyphen.c \ +loud.c \ +mfbb.c \ +mifi.c \ +port.c \ +props.c \ +rand.c \ +sofi.c \ +sq.c \ +squeal.c \ +vefl.c diff --git a/shared/common/bifi.c b/shared/common/bifi.c new file mode 100644 index 0000000..22f3df3 --- /dev/null +++ b/shared/common/bifi.c @@ -0,0 +1,217 @@ +/* Copyright (c) 2002-2003 krzYszcz and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* generic helpers for binary file reading and writing */ + +#ifdef NT +#include <io.h> +#else +#include <unistd.h> +#endif +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include "m_pd.h" +#include "shared.h" +#include "common/bifi.h" + +#if 1 +#define BIFI_VERBOSE +#if 0 +#define BIFI_DEBUG +#endif +#endif + +static int bifi_swapping = 1; /* set in bifi_clear() */ + +/* one helper from g_array.c (the original is global, but since + garray_ambigendian() lacks EXTERN specifier, .dll externs cannot see it; + btw. it has a comment: ``this should be renamed and moved...'') +*/ +static int ambigendian(void) +{ + unsigned short s = 1; + unsigned char c = *(char *)(&s); + return (c==0); +} + +/* two helpers from d_soundfile.c */ +uint32 bifi_swap4(uint32 n) +{ + if (bifi_swapping) + return (((n & 0xff) << 24) | ((n & 0xff00) << 8) | + ((n & 0xff0000) >> 8) | ((n & 0xff000000) >> 24)); + else return (n); +} + +uint16 bifi_swap2(uint16 n) +{ + if (bifi_swapping) + return (((n & 0xff) << 8) | ((n & 0xff00) >> 8)); + else return (n); +} + +static void bifi_error_clear(t_bifi *x) +{ + x->b_err = BIFI_ERR_OK; + x->b_syserrno = 0; + errno = 0; +} + +static void bifi_error_set(t_bifi *x, int errcode) +{ + x->b_err = errcode; + x->b_syserrno = errno; + if (errcode != BIFI_ERR_OK && x->b_fp) + { + fclose(x->b_fp); + x->b_fp = 0; + } +#if 0 /* LATER use Pd's own error logging mechanism, maybe by calling this: */ + sys_unixerror((char *)x); /* sys_logerror((char *)x, "...")? */ +#endif +} + +void bifi_clear(t_bifi *x) +{ + bifi_swapping = !ambigendian(); + x->b_fp = 0; + x->b_filename[0] = '\0'; + bifi_error_clear(x); +} + +t_bifi *bifi_new(t_bifi *x, char *hdr, size_t hdrsz) +{ + t_bifi *result = x; + if (result) result->b_selfalloc = 0; + else { + if (!(result = getbytes(sizeof(*result)))) return (0); + result->b_selfalloc = 1; + } + if (hdr || !hdrsz) result->b_hdralloc = 0; + else { + if (!(hdr = getbytes(hdrsz))) + { + if (result->b_selfalloc) freebytes(result, sizeof(*result)); + return (0); + } + result->b_hdralloc = 1; + } + result->b_header = hdr; + result->b_headersize = hdrsz; + bifi_clear(result); + return (result); +} + +void bifi_free(t_bifi *x) +{ + if (x->b_fp) fclose(x->b_fp); + if (x->b_hdralloc) freebytes(x->b_header, x->b_headersize); + if (x->b_selfalloc) freebytes(x, sizeof(*x)); +} + +void bifi_error_report(t_bifi *x) +{ + char *errmess = 0; + switch (x->b_err) + { + case BIFI_ERR_OK: + break; + case BIFI_ERR_OPEN: + errmess = "cannot open"; + break; + case BIFI_ERR_READ: + errmess = "error reading"; + break; + case BIFI_ERR_WRITE: + errmess = "error writing"; + break; + case BIFI_ERR_BADHEADER: + errmess = "missing header of"; + break; + default: + post("binary file i/o unknown error"); + } + if (errmess) + post("%s binary file `%s\' (errno %d: %s)", errmess, + x->b_filename, x->b_syserrno, strerror(x->b_syserrno)); + bifi_error_clear(x); +} + +/* Open file and read in its header (x must be a valid t_bifi pointer, + no checks are being made...) +*/ +int bifi_read_start(t_bifi *x, const char *filename, const char *dirname) +{ + int fd; + char dirbuf[MAXPDSTRING], *nameptr; + + bifi_clear(x); + strcpy(x->b_filename, filename); + if ((fd = open_via_path(dirname, filename, + "", dirbuf, &nameptr, MAXPDSTRING, 1)) < 0) + { + bifi_error_set(x, BIFI_ERR_OPEN); + return (0); + } + + /* Closing/reopening dance. This is unnecessary under linux, and we + could have tried to convert fd to fp (since we prefer using streams), + but under windows open_via_path() returns what seems to be an invalid + fd. LATER try to understand what is going on here... */ + close(fd); + if (dirbuf != nameptr) + { + char *slashpos = dirbuf + strlen(dirbuf); + *slashpos++ = '/'; + /* try not to be dependent on current open_via_path() implementation */ + if (nameptr != slashpos) + strcpy(slashpos, nameptr); + } + sys_unbashfilename(dirbuf, dirbuf); + if (!(x->b_fp = fopen(dirbuf, "rb"))) + { + bifi_error_set(x, BIFI_ERR_OPEN); + return (0); + } + + if (x->b_headersize && + fread(x->b_header, 1, x->b_headersize, x->b_fp) < x->b_headersize) + { + bifi_error_set(x, BIFI_ERR_BADHEADER); + return (0); + } + return (1); +} + +/* Open file and write the supplied header (x must be a valid t_bifi pointer + with header data properly filled, no checks are being made...) +*/ +int bifi_write_start(t_bifi *x, const char *filename, const char *dirname) +{ + char fnamebuf[MAXPDSTRING]; + + bifi_clear(x); + strcpy(x->b_filename, filename); + + fnamebuf[0] = 0; + if (*dirname) + strcat(fnamebuf, dirname), strcat(fnamebuf, "/"); + strcat(fnamebuf, filename); + sys_bashfilename(fnamebuf, fnamebuf); + if (!(x->b_fp = fopen(fnamebuf, "wb"))) + { + bifi_error_set(x, BIFI_ERR_OPEN); + return (0); + } + + if (x->b_headersize && + fwrite(x->b_header, 1, x->b_headersize, x->b_fp) < x->b_headersize) + { + bifi_error_set(x, BIFI_ERR_WRITE); + return (0); + } + return (1); +} diff --git a/shared/common/bifi.h b/shared/common/bifi.h new file mode 100644 index 0000000..29fe5ae --- /dev/null +++ b/shared/common/bifi.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2002-2003 krzYszcz and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* generic helpers for binary file reading and writing */ + +#ifndef __BIFI_H__ +#define __BIFI_H__ + +#define BIFI_ERR_OK 0 +#define BIFI_ERR_OPEN -1 +#define BIFI_ERR_READ -2 /* generic read failure */ +#define BIFI_ERR_WRITE -3 /* generic write failure */ +#define BIFI_ERR_BADHEADER -4 /* header missing or short */ + +typedef struct _bifi +{ + int b_selfalloc:1; + int b_hdralloc:1; + char *b_header; + size_t b_headersize; + FILE *b_fp; + char b_filename[MAXPDSTRING]; + int b_err; /* BIFI_ERR code */ + int b_syserrno; /* system error code */ +} t_bifi; + +uint32 bifi_swap4(uint32 n); +uint16 bifi_swap2(uint16 n); + +t_bifi *bifi_new(t_bifi *x, char *hdr, size_t hdrsz); +void bifi_free(t_bifi *x); +void bifi_clear(t_bifi *x); + +int bifi_read_start(t_bifi *x, const char *filename, const char *dirname); +int bifi_write_start(t_bifi *x, const char *filename, const char *dirname); + +void bifi_error_report(t_bifi *x); + +#endif diff --git a/shared/common/binport.c b/shared/common/binport.c new file mode 100644 index 0000000..05cc5df --- /dev/null +++ b/shared/common/binport.c @@ -0,0 +1,559 @@ +/* Copyright (c) 1997-2003 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. */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> + +#define BINPORT_MAXSTRING 256 +#define BINPORT_SYMGROW 64 + +#ifndef BINPORT_STANDALONE +/* load max binary file to a binbuf */ + +#include "m_pd.h" + +#else +/* 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. */ + +#define BINPORT_VERBOSE +//#define BINPORT_DEBUG + +#endif + +#include "binport.h" + +static void binport_error(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "ERROR (binport): "); + vfprintf(stderr, fmt, ap); + putc('\n', stderr); + va_end(ap); +} + +static void binport_warning(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "warning (binport): "); + vfprintf(stderr, fmt, ap); + putc('\n', stderr); + va_end(ap); +} + +static void binport_bug(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "BUG (binport): "); + vfprintf(stderr, fmt, ap); + putc('\n', stderr); + va_end(ap); +} + +#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 */ + +/* clumsy... LATER find a better way */ +#ifdef BINPORT_STANDALONE +#define A_INT (A_CANT + 1) +#endif + +static int binport_getint(t_atom *ap) +{ +#ifdef A_INT + return (*(int *)&ap->a_w); +#else + return (ap->a_w.w_float); +#endif +} + +static void binport_setint(t_atom *ap, int i) +{ +#ifdef A_INT + ap->a_type = A_INT; + *(int *)&ap->a_w = i; +#else + SETFLOAT(ap, (float)i); +#endif +} + +static void binport_setfloat(t_atom *ap, float f) +{ + ap->a_type = A_FLOAT; + ap->a_w.w_float = f; +} + +typedef struct _binport +{ + FILE *b_fp; + int b_nsymbols; + int b_symsize; + t_symbol **b_symtable; +} t_binport; + +static int binport_getbuf(t_binport *bp, char *buf, size_t sz) +{ + return (fread(buf, 1, sz, bp->b_fp) == sz); +} + +static int binport_getbyte(t_binport *bp, unsigned char *buf) +{ + int c; + if ((c = fgetc(bp->b_fp)) == EOF) + return (0); + *buf = (unsigned char)c; + return (1); +} + +static int binport_getstring(t_binport *bp, char *buf) +{ + int c, i = 0; + while (c = fgetc(bp->b_fp)) + { + if (c == EOF) + return (0); + if (++i < BINPORT_MAXSTRING) + *buf++ = (unsigned char)c; + } + *buf = '\0'; + if (i >= BINPORT_MAXSTRING) + binport_warning("symbol string too long, skipped"); + return (1); +} + +static t_symbol *binport_makesymbol(t_binport *bp, int id) +{ + char s[BINPORT_MAXSTRING]; + if (id < bp->b_nsymbols) + binport_bug("symbol id mismatch"); + else if (id > bp->b_nsymbols) + binport_error("unexpected symbol id"); + else if (binport_getstring(bp, s)) + { + int reqsize = ++bp->b_nsymbols; + if (reqsize > bp->b_symsize) + { + reqsize += (BINPORT_SYMGROW - 1); +#ifdef BINPORT_DEBUG + binport_warning("resizing symbol table to %d elements", reqsize); +#endif + if (bp->b_symtable = + resizebytes(bp->b_symtable, + bp->b_symsize * sizeof(*bp->b_symtable), + reqsize * sizeof(*bp->b_symtable))) + bp->b_symsize = reqsize; + else + { + bp->b_nsymbols = bp->b_symsize = 0; + return (0); + } + } + return (bp->b_symtable[id] = gensym(s)); + } + return (0); +} + +static t_symbol *binport_getsymbol(t_binport *bp, int id) +{ + if (id < bp->b_nsymbols) + return (bp->b_symtable[id]); + else + return (binport_makesymbol(bp, id)); +} + +static int binport_setsymbol(t_binport *bp, t_atom *ap, int id) +{ + t_symbol *s = binport_getsymbol(bp, id); + if (s) + { + ap->a_type = A_SYMBOL; + ap->a_w.w_symbol = s; + } + return (s != 0); +} + +static int binport_nextatom(t_binport *bp, t_atom *ap) +{ + unsigned char opcode; + int opval; + char buf[64]; + if (!binport_getbyte(bp, &opcode)) + goto bad; + opval = opcode & 0x0f; + switch (opcode >> 4) + { + case 1: /* variable length int, + opval: length (number of bytes that follow) */ + if (!binport_getbuf(bp, buf, opval)) + goto bad; + else + { + unsigned char *p = (unsigned char *)buf + opval; + int i = 0; + while (opval--) i = (i << 8) | *--p; + if (opcode == 0x12) /* FIXME */ + i = (short)i; + binport_setint(ap, i); + } + break; + case 2: /* variable length float, + opval: length (number of bytes that follow) */ + if (!binport_getbuf(bp, buf, opval)) + goto bad; + else + { + unsigned char *p = (unsigned char *)buf + opval; + int i = 0; + while (opval--) i = (i << 8) | *--p; + binport_setfloat(ap, *(t_float *)&i); + } + break; + case 3: /* variable length symbol id, + opval: length (number of bytes that follow) */ + if (!binport_getbuf(bp, buf, opval)) + goto bad; + else + { + unsigned char *p = (unsigned char *)buf + opval; + int i = 0; + while (opval--) i = (i << 8) | *--p; + if (!binport_setsymbol(bp, ap, i)) + goto bad; + } + break; + case 5: /* half-byte int */ + binport_setint(ap, opval); + break; + case 7: /* half-byte symbol id */ + if (!binport_setsymbol(bp, ap, opval)) + goto bad; + break; + case 12: /* #number */ + sprintf(buf, "#%d", opval); + ap->a_type = A_SYMBOL; + ap->a_w.w_symbol = gensym(buf); + break; + case 13: /* #symbol id, + opval: length (number of bytes that follow) */ + if (!binport_getbuf(bp, buf, opval)) + goto bad; + else + { + unsigned char *p = (unsigned char *)buf + opval; + int i = 0; + while (opval--) i = (i << 8) | *--p; + if (!binport_setsymbol(bp, ap, i)) + goto bad; + } + sprintf(buf, "#%s", ap->a_w.w_symbol->s_name); +#ifdef BINPORT_DEBUG + binport_warning(buf); +#endif + ap->a_w.w_symbol = gensym(buf); + break; + default: + switch (opcode) + { + case 0xa0: + ap->a_type = A_SEMI; + break; + default: + goto unknown; + } + } + return (1); +unknown: + binport_error("unknown opcode %x", (int)opcode); +bad: + return (0); +} + +static void binport_free(t_binport *bp) +{ + fclose(bp->b_fp); + freebytes(bp->b_symtable, bp->b_symsize * sizeof(*bp->b_symtable)); + freebytes(bp, sizeof(*bp)); +} + +static t_binport *binport_new(FILE *fp, int *ftypep) +{ + static char binport_header[4] = { 2, 0, 0, 0 }; + char header[4]; + *ftypep = BINPORT_INVALID; + if (fread(header, 1, 4, fp) == 4) + { + if (memcmp(header, binport_header, 4)) + { + if (memcmp(header, "max", 3)) + { + if (header[0] == '#') /* LATER rethink */ + *ftypep = BINPORT_PDFILE; +#ifdef BINPORT_VERBOSE + else binport_warning("unknown header: %x %x %x %x", + (int)header[0], (int)header[1], + (int)header[2], (int)header[3]); +#endif + } + else *ftypep = BINPORT_MAXTEXT; + } + else + { + t_binport *bp = getbytes(sizeof(*bp)); + bp->b_fp = fp; + bp->b_nsymbols = 0; + bp->b_symsize = BINPORT_SYMGROW; + bp->b_symtable = getbytes(bp->b_symsize * sizeof(*bp->b_symtable)); + *ftypep = BINPORT_OK; + return (bp); + } + } +#ifdef BINPORT_VERBOSE + else binport_warning("file too short"); +#endif + fclose(fp); + return (0); +} + +#ifndef BINPORT_STANDALONE + +/* LATER deal with corrupt binary files? */ +int binport_read(t_binbuf *bb, char *filename, char *dirname) +{ + FILE *fp; + char namebuf[MAXPDSTRING]; + namebuf[0] = 0; + if (*dirname) + strcat(namebuf, dirname), strcat(namebuf, "/"); + strcat(namebuf, filename); + sys_bashfilename(namebuf, namebuf); + if (fp = fopen(namebuf, "rb")) + { + int ftype; + t_binport *bp = binport_new(fp, &ftype); + if (bp) + { + t_atom at; + while (binport_nextatom(bp, &at)) + binbuf_add(bb, 1, &at); + binport_free(bp); + return (BINPORT_OK); + } + else if (ftype == BINPORT_MAXTEXT || ftype == BINPORT_PDFILE) + return (ftype); + else + binport_error("\"%s\" doesn't look like a patch file", filename); + } + else binport_bug("cannot open file"); + return (BINPORT_INVALID); +} + +#else + +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", binport_getint(ap)); 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, "???"); + } +} + +int main(int ac, char **av) +{ + if (ac > 1) + { + FILE *fp = fopen(av[1], "rb"); + if (fp) + { + int ftype; + t_binport *bp = binport_new(fp, &ftype); + if (bp) + { + char buf[BINPORT_MAXSTRING]; + t_atom at; + int ac = 0; + while (binport_nextatom(bp, &at)) + { + if (at.a_type == A_SEMI) + { + fputs(";\n", stdout); + ac = 0; + } + else + { + if (ac++) fputc(' ', stdout); + binport_atomstring(&at, buf, BINPORT_MAXSTRING); + fputs(buf, stdout); + } + } + binport_free(bp); + } + else if (ftype == BINPORT_MAXTEXT) + binport_warning("\"%s\" looks like a Max text file", av[1]); + else if (ftype == BINPORT_PDFILE) + binport_warning("\"%s\" looks like a Pd patch file", av[1]); + else + binport_error("\"%s\" doesn't look like a patch file", av[1]); + } + else binport_error("cannot open file \"%s\"", av[1]); + } + else binport_error("what file?"); + return (0); +} + +#endif diff --git a/shared/common/binport.h b/shared/common/binport.h new file mode 100644 index 0000000..0b6b607 --- /dev/null +++ b/shared/common/binport.h @@ -0,0 +1,15 @@ +/* Copyright (c) 2003 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 __BINPORT_H__ +#define __BINPORT_H__ + +enum { BINPORT_OK, BINPORT_MAXTEXT, BINPORT_PDFILE, + BINPORT_INVALID, BINPORT_CORRUPT }; + +#ifndef BINPORT_STANDALONE +int binport_read(t_binbuf *bb, char *filename, char *dirname); +#endif + +#endif diff --git a/shared/common/grow.c b/shared/common/grow.c new file mode 100644 index 0000000..f02509a --- /dev/null +++ b/shared/common/grow.c @@ -0,0 +1,105 @@ +/* Copyright (c) 2002-2003 krzYszcz and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* LATER generic handling of reentrant output request and self-invoked set */ + +#include <string.h> +#include "m_pd.h" +#include "common/grow.h" + +/* Prior to this call a caller is supposed to check for *nrequested > *sizep. + Returns a reallocated buffer's pointer (success) or a given 'bufini' + default value (failure). + Upon return *nrequested contains the actual number of elements: + requested (success) or a given default value of 'inisize' (failure). */ +void *grow_nodata(int *nrequested, int *sizep, void *bufp, + int inisize, void *bufini, size_t typesize) +{ + int newsize = *sizep * 2; + while (newsize < *nrequested) newsize *= 2; + if (bufp == bufini) + bufp = getbytes(newsize * typesize); + else + bufp = resizebytes(bufp, *sizep * typesize, newsize * typesize); + if (bufp) + { + *sizep = newsize; + return (bufp); + } + else + { + *nrequested = *sizep = inisize; + return (bufini); + } +} + +/* Like grow_nodata(), but preserving first *nexisting elements. */ +void *grow_withdata(int *nrequested, int *nexisting, + int *sizep, void *bufp, + int inisize, void *bufini, size_t typesize) +{ + int newsize = *sizep * 2; + while (newsize < *nrequested) newsize *= 2; + if (bufp == bufini) + { + if (!(bufp = getbytes(newsize * typesize))) + { + *nrequested = *sizep = inisize; + return (bufini); + } + *sizep = newsize; + memcpy(bufp, bufini, *nexisting * typesize); + } + else + { + int oldsize = *sizep; + if (!(bufp = resizebytes(bufp, *sizep * typesize, newsize * typesize))) + { + *nrequested = *sizep = inisize; + *nexisting = 0; + return (bufini); + } + *sizep = newsize; + } + return (bufp); +} + +/* Like grow_nodata(), but preserving a 'tail' of *nexisting elements, + starting from *startp. */ +/* LATER rethink handling of a start pointer (clumsy now) */ +void *grow_withtail(int *nrequested, int *nexisting, char **startp, + int *sizep, void *bufp, + int inisize, void *bufini, size_t typesize) +{ + int newsize = *sizep * 2; + while (newsize < *nrequested) newsize *= 2; + if (bufp == bufini) + { + char *oldstart = *startp; + if (!(bufp = getbytes(newsize * typesize))) + { + *nrequested = *sizep = inisize; + return (bufini); + } + *startp = (char *)bufp + (newsize - *nexisting) * typesize; + *sizep = newsize; + memcpy(*startp, oldstart, *nexisting * typesize); + } + else + { + int oldsize = *sizep; + if (!(bufp = resizebytes(bufp, *sizep * typesize, newsize * typesize))) + { + *startp = (char *)bufini + inisize * typesize; + *nrequested = *sizep = inisize; + *nexisting = 0; + return (bufini); + } + *startp = (char *)bufp + (newsize - *nexisting) * typesize; + *sizep = newsize; + memmove(*startp, (char *)bufp + (oldsize - *nexisting) * typesize, + *nexisting * typesize); + } + return (bufp); +} diff --git a/shared/common/grow.h b/shared/common/grow.h new file mode 100644 index 0000000..1749cfc --- /dev/null +++ b/shared/common/grow.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2002-2003 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 __GROW_H__ +#define __GROW_H__ + +void *grow_nodata(int *nrequested, int *sizep, void *bufp, + int inisize, void *bufini, size_t typesize); +void *grow_withdata(int *nrequested, int *nexisting, + int *sizep, void *bufp, + int inisize, void *bufini, size_t typesize); +void *grow_withtail(int *nrequested, int *nexisting, char **startp, + int *sizep, void *bufp, + int inisize, void *bufini, size_t typesize); + +#endif diff --git a/shared/common/loud.c b/shared/common/loud.c new file mode 100644 index 0000000..638f431 --- /dev/null +++ b/shared/common/loud.c @@ -0,0 +1,210 @@ +/* Copyright (c) 2002-2003 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 <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include "m_pd.h" +#include "common/loud.h" + +#define LOUD_ERROR_DEFAULT "error (miXed): " + +/* LATER move it somewhere else */ +t_symbol *loud_floatsym(void) +{ + static t_symbol *s = 0; + return (s ? s : (s = gensym("noninteger float"))); +} + +/* LATER move it somewhere else */ +char *loud_symbolname(t_symbol *s, char *nullname) +{ + return (s && s != &s_ ? s->s_name : nullname); +} + +/* LATER move it somewhere else */ +char *loud_ordinal(int n) +{ + static char buf[16]; /* assuming 10-digit INT_MAX */ + sprintf(buf, "%dth", n); + if (n < 0) n = -n; + n %= 100; + if (n > 20) n %= 10; + if (n && n <= 3) + { + char *ptr = buf + strlen(buf) - 2; + switch (n) + { + case 1: strcpy(ptr, "st"); break; + case 2: strcpy(ptr, "nd"); break; + case 3: strcpy(ptr, "rd"); break; + } + } + return (buf); +} + +void loud_error(t_pd *x, char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (x) + { + char buf[MAXPDSTRING]; + fprintf(stderr, "%s's ", class_getname(*x)); + vsprintf(buf, fmt, ap); + pd_error(x, buf); + } + else + { + fputs(LOUD_ERROR_DEFAULT, stderr); + vfprintf(stderr, fmt, ap); + putc('\n', stderr); + } + va_end(ap); +} + +void loud_errand(t_pd *x, char *fmt, ...) +{ + 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); + va_end(ap); +} + +void loud_syserror(t_pd *x, char *msg) +{ + if (msg) + loud_error(x, "%s (%s)", msg, strerror(errno)); + else + loud_error(x, strerror(errno)); +} + +void loud_nomethod(t_pd *x, t_symbol *s) +{ + loud_error(x, "doesn't understand \"%s\"", s->s_name); +} + +void loud_messarg(t_pd *x, t_symbol *s) +{ + loud_error(x, "bad arguments for message \"%s\"", s->s_name); +} + +int loud_checkint(t_pd *x, t_float f, int *valuep, t_symbol *mess) +{ + if ((*valuep = (int)f) == f) + return (1); + else + { + if (mess == &s_float) + loud_nomethod(x, loud_floatsym()); + else if (mess) + loud_error(x, "\"%s\" argument invalid for message \"%s\"", + loud_floatsym()->s_name, mess->s_name); + return (0); + } +} + +void loud_classarg(t_class *c) +{ + loud_error(0, "missing or bad arguments in \"%s\"", class_getname(c)); +} + +void loud_warning(t_pd *x, char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "warning (%s): ", (x ? class_getname(*x) : "miXed")); + vfprintf(stderr, fmt, ap); + va_end(ap); + putc('\n', stderr); +} + +void loud_notimplemented(t_pd *x, char *name) +{ + if (name) + loud_warning(x, "\"%s\" method not implemented (yet)", name); + else + loud_warning(x, "not implemented (yet)"); +} + +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); +} + +void loud_incompatible_max(t_class *c, int maxmax, char *what) +{ + loud_incompatible(c, "more than %d %s requested", maxmax, what); +} + +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) +{ + int result = LOUD_ARGOK; + if (which < ac) + { + av += which; + if (av->a_type == A_FLOAT) + { + t_float f = av->a_w.w_float; + if (f < minval) + { + *vp = (underaction & LOUD_CLIP ? minval : f); + if (underaction) + result = LOUD_ARGUNDER; + } + else if (f > maxval) + { + *vp = (overaction & LOUD_CLIP ? maxval : f); + if (overaction) + result = LOUD_ARGOVER; + } + else *vp = f; + } + else result = LOUD_ARGTYPE; + } + else result = LOUD_ARGMISSING; + if (what) + { + switch (result) + { + case LOUD_ARGUNDER: + if (underaction & LOUD_WARN) + { + if (underaction & LOUD_CLIP) + loud_warning(&c, "%s rounded up to %g", what, minval); + else + loud_incompatible(c, "less than %g %s requested", + minval, what); + } + break; + case LOUD_ARGOVER: + if (overaction & LOUD_WARN) + { + if (overaction & LOUD_CLIP) + loud_warning(&c, "%s truncated to %g", what, maxval); + else + loud_incompatible(c, "more than %g %s requested", + maxval, what); + } + break; + case LOUD_ARGTYPE: + loud_error(0, "bad argument %d (%s)", which, class_getname(c)); + break; + default:; + } + } + return (result); +} diff --git a/shared/common/loud.h b/shared/common/loud.h new file mode 100644 index 0000000..17c02bf --- /dev/null +++ b/shared/common/loud.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2002-2003 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 __LOUD_H__ +#define __LOUD_H__ + +#define LOUD_CLIP 1 +#define LOUD_WARN 2 + +enum { LOUD_ARGOK, LOUD_ARGUNDER, LOUD_ARGOVER, LOUD_ARGTYPE, LOUD_ARGMISSING }; + +t_symbol *loud_floatsym(void); +char *loud_symbolname(t_symbol *s, char *nullname); +char *loud_ordinal(int n); +void loud_error(t_pd *x, char *fmt, ...); +void loud_errand(t_pd *x, char *fmt, ...); +void loud_syserror(t_pd *x, char *msg); +void loud_nomethod(t_pd *x, t_symbol *s); +void loud_messarg(t_pd *x, t_symbol *s); +int loud_checkint(t_pd *x, t_float f, int *valuep, t_symbol *mess); +void loud_classarg(t_class *c); +void loud_warning(t_pd *x, char *fmt, ...); +void loud_notimplemented(t_pd *x, char *name); +void loud_incompatible(t_class *c, char *fmt, ...); +void loud_incompatible_max(t_class *c, int maxmax, char *what); +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); + +#endif diff --git a/shared/common/mifi.c b/shared/common/mifi.c new file mode 100644 index 0000000..ad16b3b --- /dev/null +++ b/shared/common/mifi.c @@ -0,0 +1,867 @@ +/* Copyright (c) 2001-2003 krzYszcz and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* reading/writing midifiles, a prototype version */ + +#ifdef NT +#include <io.h> +#else +#include <unistd.h> +#endif +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include "m_pd.h" +#include "shared.h" +#include "common/sq.h" +#include "common/bifi.h" +#include "common/mifi.h" + +#if 1 +#define MIFI_VERBOSE +#if 0 +#define MIFI_DEBUG +#endif +#endif + +#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 + +/* header structures for midifile and track */ + +typedef struct _mifi_header +{ + char h_type[4]; + uint32 h_length; + uint16 h_format; + uint16 h_ntracks; + uint16 h_division; +} t_mifi_header; + +typedef struct _mifi_trackheader +{ + char h_type[4]; + uint32 h_length; +} t_mifi_trackheader; + +/* reading helpers */ + +static void mifi_earlyeof(t_mifi_stream *x) +{ + x->s_bytesleft = 0; + x->s_eof = 1; +} + +/* Get next byte from track data. + On error: return 0 (which is a valid result) and set x->s_eof. +*/ +static uchar mifi_getbyte(t_mifi_stream *x) +{ + if (x->s_bytesleft) + { + int c; + if ((c = fgetc(x->s_fp)) == EOF) + { + mifi_earlyeof(x); + return (0); + } + else { + x->s_bytesleft--; + return ((uchar)c); + } + } + else return (0); +} + +static uint32 mifi_readbytes(t_mifi_stream *x, uchar *buf, uint32 size) +{ + size_t res; + if (size > x->s_bytesleft) + size = x->s_bytesleft; + if ((res = fread(buf, 1, (size_t)size, x->s_fp)) == size) + x->s_bytesleft -= res; + else + mifi_earlyeof(x); + return (res); +} + +static int mifi_skipbytes(t_mifi_stream *x, uint32 size) +{ + if (size > x->s_bytesleft) + size = x->s_bytesleft; + if (size) + { + int res = fseek(x->s_fp, size, SEEK_CUR); + if (res < 0) + mifi_earlyeof(x); + else + x->s_bytesleft -= size; + return res; + } + else return (0); +} + +/* helpers handling variable-length quantities */ + +static size_t mifi_writevarlen(t_mifi_stream *x, uint32 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, x->s_fp) == length) ? length : 0); +} + +static uint32 mifi_readvarlen(t_mifi_stream *x) +{ + uint32 n = 0; + uchar c; + uint32 count = x->s_bytesleft; + if (count > 4) count = 4; + while (count--) + { + n = (n << 7) + ((c = mifi_getbyte(x)) & 0x7f); + if ((c & 0x80) == 0) + break; + } + return (n); +} + +/* other helpers */ + +static int mifi_read_start_track(t_mifi_stream *x) +{ + t_mifi_trackheader header; + long skip; + int notyet = 1; + do { + if (fread(&header, 1, + MIFI_TRACKHEADER_SIZE, x->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 (x->s_anapass) + post("unknown chunk %s in midifile -- skipped", buf); + } + else if (header.h_length < MIFI_SHORTEST_EVENT) + { + if (x->s_anapass) post("empty track in midifile -- skipped"); + } + else notyet = 0; + if (notyet && (skip = header.h_length) && + fseek(x->s_fp, skip, SEEK_CUR) < 0) + goto nomoretracks; + } while (notyet); + + x->s_track++; + x->s_newtrack = 1; + x->s_status = x->s_channel = 0; + x->s_bytesleft = header.h_length; + x->s_time = 0; + + return (1); +nomoretracks: + if (x->s_track == 0) + if (x->s_anapass) post("no valid miditracks"); + return (0); +} + +/* public interface */ + +t_mifi_event *mifi_event_new(void) +{ + t_mifi_event *e = getbytes(sizeof(*e)); + if (e && !(e->e_data = getbytes(e->e_bufsize = MIFI_EVENT_NALLOC))) + { + freebytes(e, sizeof(*e)); + return (0); + } + return (e); +} + +void mifi_event_free(t_mifi_event *e) +{ + freebytes(e->e_data, e->e_bufsize); + freebytes(e, sizeof(*e)); +} + +int mifi_event_settext(t_mifi_event *e, int type, char *text) +{ + e->e_delay = 0; + e->e_status = MIFI_EVENT_META; + e->e_meta = type; + e->e_length = strlen(text); + if (squb_checksize(e, e->e_length + 1, 1) <= e->e_length) + { + e->e_length = 0; + return (0); + } + strcpy(e->e_data, text); + return (1); +} + +void mifi_event_printmeta(t_mifi_event *e) +{ + static int isprintable[MIFI_META_MAXPRINTABLE+1] = + { +#ifdef MIFI_DEBUG + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +#elif defined MIFI_VERBOSE + 0, 0, 1, 1, 1, 1, 1, 1 +#endif + }; + static char *printformat[MIFI_META_MAXPRINTABLE+1] = + { + "", "text: %s", "copyright: %s", "track name: %s", + "instrument name: %s", "lyric: %s", "marker: %s", "cue point: %s" + }; + if (e->e_meta <= MIFI_META_MAXPRINTABLE) + { + if (isprintable[e->e_meta] && printformat[e->e_meta]) + post(printformat[e->e_meta], e->e_data); + } +#ifdef MIFI_DEBUG /* in verbose mode tempo printout done only after sorting */ + else if (e->e_meta == MIFI_META_TEMPO) + { + int tempo = bifi_swap4(*(uint32*)e->e_data); + post("tempo %d after %d", tempo, e->e_delay); + } +#endif +} + +void mifi_stream_reset(t_mifi_stream *x) +{ + sq_reset(x); + x->s_status = x->s_channel = 0; + x->s_timecoef = sq_msecs2ticks(x, 0); + x->s_bytesleft = 0; +} + +t_mifi_stream *mifi_stream_new(void) +{ + t_mifi_stream *x = sq_new(); + if (x) + { + if (x->s_auxeve = mifi_event_new()) + { + x->s_hdtracks = 1; + x->s_alltracks = 0; + mifi_stream_reset(x); /* LATER avoid calling sq_reset() twice */ + } + else + { + mifi_stream_free(x); + return (0); + } + } + return (x); +} + +void mifi_stream_free(t_mifi_stream *x) +{ + if (x->s_auxeve) + mifi_event_free(x->s_auxeve); + sq_free(x); +} + +/* Open midifile for reading, parse the header. May be used as t_mifi_stream + allocator (if x is a null pointer), to be freed by mifi_read_end() or + explicitly. + + Return value: null on error, else x if passed a valid pointer, else pointer + to an allocated structure. +*/ +t_mifi_stream *mifi_read_start(t_mifi_stream *x, + const char *filename, const char *dirname, + int complain) +{ + t_mifi_stream *result = x; + t_bifi bifi; + t_bifi *bp = &bifi; + t_mifi_header header; + long skip; + + bifi_new(bp, (char *)&header, MIFI_HEADER_SIZE); + if (!bifi_read_start(bp, filename, dirname)) + { + bifi_error_report(bp); + bifi_free(bp); + return (0); + } + 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) + { + post("%ld extra bytes of midifile header -- skipped", skip); + if (fseek(bp->b_fp, skip, SEEK_CUR) < 0) + goto badstart; + } + + /* since we will tolerate other incompatibilities, now we can allocate */ + if (x) mifi_stream_reset(x); + else + { + if (!(result = mifi_stream_new())) + goto badstart; + result->s_autoalloc = 1; + } + 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) + { + result->s_nframes = (result->s_nticks >> 8); + result->s_nticks &= 0xff; + } + else result->s_nframes = 0; + if (result->s_nticks == 0) + goto badheader; + + return (result); +badheader: + if (complain) + post("`%s\' is not a valid midifile", filename); +badstart: + if (result && !x) mifi_stream_free(result); + bifi_free(bp); + return (0); +} + +int mifi_read_restart(t_mifi_stream *x) +{ + FILE *fp = x->s_fp; + mifi_stream_reset(x); + x->s_anapass = 0; + x->s_fp = fp; + return (fseek(fp, 0, SEEK_SET) ? 0 : 1); +} + +/* Close midifile and free t_mifi_stream if it was allocated + by mifi_read_start() */ +void mifi_read_end(t_mifi_stream *x) +{ + if (x->s_fp) fclose(x->s_fp); + if (x->s_autoalloc) mifi_stream_free(x); +} + +/* Read next event from midifile. + Return value: see #defines in mifi.h. +*/ +int mifi_read_event(t_mifi_stream *x, t_mifi_event *e) +{ + uchar status, channel; + uint32 length; + + x->s_newtrack = 0; +nextattempt: + if (x->s_bytesleft < MIFI_SHORTEST_EVENT && !mifi_read_start_track(x)) + return (MIFI_READ_EOF); + + x->s_time += (e->e_delay = mifi_readvarlen(x)); + + if ((status = mifi_getbyte(x)) < 0x80) + { + if (MIFI_IS_CHANNEL(x->s_status)) + { + e->e_data[0] = status; + e->e_length = 1; + status = x->s_status; + e->e_channel = x->s_channel; + } + else { + if (x->s_anapass) + post("missing running status in midifile -- skip to end of track"); + goto endoftrack; + } + } + else e->e_length = 0; + + /* channel message */ + if (status < 0xf0) + { + if (e->e_length == 0) + { + e->e_data[0] = mifi_getbyte(x); + e->e_length = 1; + x->s_status = status & 0xf0; + x->s_channel = e->e_channel = status & 0x0f; + status = x->s_status; + } + if (!MIFI_ONE_DATABYTE(status)) + { + e->e_data[1] = mifi_getbyte(x); + e->e_length = 2; + } + } + + /* system exclusive */ + else if (status == MIFI_SYSEX_FIRST || status == MIFI_SYSEX_NEXT) + { + /* LATER choose the right way -- + do we really need all those huge bulk dumps? */ + length = mifi_readvarlen(x); + if (squb_checksize(e, length, 1) < length) + { + if (mifi_skipbytes(x, length) < 0) + return (MIFI_READ_FATAL); + goto nextattempt; + } + if (mifi_readbytes(x, e->e_data, length) != length) + return (MIFI_READ_FATAL); + e->e_length = length; +#ifdef MIFI_VERBOSE + if (x->s_anapass) post("got %d bytes of sysex", length); +#endif + } + + /* meta-event */ + else if (status == MIFI_EVENT_META) + { + e->e_meta = mifi_getbyte(x); + length = mifi_readvarlen(x); + if (e->e_meta > 127) + { + /* try to skip corrupted meta-event (quietly) */ +#ifdef MIFI_VERBOSE + if (x->s_anapass) post("bad meta: %d > 127", e->e_meta); +#endif + if (mifi_skipbytes(x, length) < 0) + return (MIFI_READ_FATAL); + goto nextattempt; + } + switch (e->e_meta) + { + case MIFI_META_EOT: + if (length) + { + /* corrupted eot: ignore and skip to the real end of track */ +#ifdef MIFI_VERBOSE + if (x->s_anapass) post("corrupted eot, length %d", length); +#endif + goto endoftrack; + } + break; + case MIFI_META_TEMPO: + if (length != 3) + { + if (x->s_anapass) + post("corrupted event in midifile -- skip to end of track"); + goto endoftrack; + } + if (mifi_readbytes(x, e->e_data+1, 3) != 3) + return (MIFI_READ_FATAL); + e->e_data[0] = 0; + x->s_tempo = bifi_swap4(*(uint32*)e->e_data); + break; + default: + if (squb_checksize(e, length + 1, 1) <= length) + { + if (mifi_skipbytes(x, length) < 0) + return (MIFI_READ_FATAL); + goto nextattempt; + } + if (mifi_readbytes(x, e->e_data, length) != length) + return (MIFI_READ_FATAL); + e->e_length = length; + if (e->e_meta && e->e_meta <= MIFI_META_MAXPRINTABLE) + e->e_data[length] = '\0'; /* text meta-event nultermination */ + } + } + else { + if (x->s_anapass) + post("unknown event type in midifile -- skip to end of track"); + goto endoftrack; + } + + return ((e->e_status = status) == MIFI_EVENT_META ? e->e_meta : status); + +endoftrack: + if (mifi_skipbytes(x, x->s_bytesleft) < 0) + return (MIFI_READ_FATAL); + return (MIFI_READ_SKIP); +} + +/* 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 *x) +{ + t_mifi_event *evp = x->s_auxeve; + int evtype, result = MIFI_READ_FATAL; + int isnewtrack = 0; + int i; + char tnamebuf[MAXPDSTRING]; + t_symbol *tnamesym = 0; + t_squack *trp = 0; + + *tnamebuf = '\0'; + x->s_alltracks = x->s_ntracks = 0; + x->s_nevents = 0; + x->s_ntempi = 0; + + while ((evtype = mifi_read_event(x, evp)) >= MIFI_READ_SKIP) + { + if (evtype == MIFI_READ_SKIP) + continue; + if (x->s_newtrack) + { +#ifdef MIFI_VERBOSE + post("track %d", x->s_track); +#endif + isnewtrack = 1; + *tnamebuf = '\0'; + tnamesym = 0; /* set to nonzero for nonempty tracks only */ + } + if (MIFI_IS_CHANNEL(evtype)) + { + if (isnewtrack) + { + isnewtrack = 0; + x->s_alltracks++; + if (!(trp = squax_add(x))) + goto anafail; + if (*tnamebuf) + { + tnamesym = trp->tr_name = gensym(tnamebuf); +#ifdef MIFI_DEBUG + post("nonempty track name %s", tnamesym->s_name); +#endif + } + else tnamesym = trp->tr_name = &s_; + } + x->s_nevents++; + } + else if (evtype < 0x80) + { + mifi_event_printmeta(evp); + if (evtype == MIFI_META_TEMPO) + x->s_ntempi++; + else if (evtype == MIFI_META_TRACKNAME) + { + char *p1 = evp->e_data; + if (*p1 && + !*tnamebuf) /* take the first one */ + { + while (*p1 == ' ') p1++; + if (*p1) + { + char *p2 = evp->e_data + evp->e_length - 1; + while (p2 > p1 && *p2 == ' ') *p2-- = '\0'; + p2 = p1; + 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); + } + else strcpy(tnamebuf, p1); + } + } + } + } + } + if (evtype != MIFI_READ_EOF) + goto anafail; + + i = x->s_ntracks; + while (--i >= 0) + { + if (!x->s_track_name(i) || x->s_track_name(i) == &s_) + { + sprintf(tnamebuf, "%d-track", i); + x->s_track_name(i) = gensym(tnamebuf); + } + } + + /* now (re)allocate the buffers */ + if (squb_checksize(x->s_mytempi, + x->s_ntempi, sizeof(t_squmpo)) < x->s_ntempi) + goto anafail; + x->s_track_nevents(0) = 0; + x->s_track_nevents(x->s_ntracks) = x->s_nevents; /* guard point */ + + result = 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 *x) +{ + t_mifi_event *evp = x->s_auxeve; + t_squiter *it = x->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 = x->s_nevents; /* three proxies... */ + int ntracks = x->s_ntracks; + int ntempi = x->s_ntempi; + int trackno = 0; + t_symbol *trackname = 0; + int isnewtrack = 0; + t_squmpo *tp = x->s_tempomap; + + if (!it || !seekhook(it, 0)) + goto readfailed; + + while ((evtype = mifi_read_event(x, evp)) >= MIFI_READ_SKIP) + { + if (evtype == MIFI_READ_SKIP) + continue; + if (x->s_newtrack) + isnewtrack = 1; + if (MIFI_IS_CHANNEL(evtype)) + { + int ret; + if (isnewtrack) + { + isnewtrack = 0; + trackname = x->s_track_name(trackno); + trackno++; + if (!trackname || trackname == &s_) + { + bug("mifi_read_doit: empty track name"); + trackname = gensym("bug-track"); + } + } + x->s_track_nevents(trackno)++; + if (ret = squiter_inrange(it)) + { + evehook(it, (t_squeve *)evp, &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)x->s_time, &ret); + if (ret) tarhook(it, trackname, &ret); + } + if (ret) incrhook(it); + else + goto readfailed; + } + else if (evtype < 0x80) + { + if (evtype == MIFI_META_TEMPO) + { + tp->te_onset = x->s_time; + tp->te_value = x->s_tempo; + tp++; + } + } + } + if (evtype != MIFI_READ_EOF) + goto readfailed; + + result = evtype; +readfailed: + return (result); +} + +/* Open midifile for saving, write the header. May be used as t_mifi_stream + allocator (if x is a null pointer), to be freed by mifi_write_end() or + explicitly. + + Return value: null on error, else x if passed a valid pointer, else pointer + to allocated structure. +*/ +t_mifi_stream *mifi_write_start(t_mifi_stream *x, + const char *filename, const char *dirname) +{ + t_mifi_stream *result = x; + t_bifi bifi; + t_bifi *bp = &bifi; + t_mifi_header header; + + /* this must precede bifi_swap() calls */ + bifi_new(bp, (char *)&header, MIFI_HEADER_SIZE); + + if (x->s_format == 0) + { + if (x->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, x->s_ntracks); +#endif + + strncpy(header.h_type, "MThd", 4); + header.h_length = bifi_swap4(MIFI_HEADERDATA_SIZE); + if (x) + { + if (!x->s_hdtracks || !x->s_nticks) + goto startfailure; + header.h_format = bifi_swap2(x->s_format); + header.h_ntracks = bifi_swap2(x->s_hdtracks); + if (x->s_nframes) + header.h_division = ((x->s_nframes << 8) | x->s_nticks) | 0x8000; + else + header.h_division = x->s_nticks & 0x7fff; + header.h_division = bifi_swap2(header.h_division); + } + else { + header.h_format = 0; + header.h_ntracks = bifi_swap2(1); + header.h_division = bifi_swap2(192); /* LATER parametrize this somehow */ + } + + if (!bifi_write_start(bp, filename, dirname)) + { + bifi_error_report(bp); + bifi_free(bp); + return (0); + } + + if (x) mifi_stream_reset(x); + else + { + if (!(result = mifi_stream_new())) + goto startfailure; + result->s_autoalloc = 1; + } + result->s_fp = bp->b_fp; + result->s_track = 0; + + return (result); +startfailure: + if (result && !x) mifi_stream_free(result); + bifi_free(bp); + return (0); +} + +/* Close midifile, free t_mifi_stream if it was allocated + by mifi_write_start(). */ +void mifi_write_end(t_mifi_stream *x) +{ + if (x->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 (x->s_fp) fclose(x->s_fp); + if (x->s_autoalloc) mifi_stream_free(x); +} + +int mifi_write_start_track(t_mifi_stream *x) +{ + t_mifi_trackheader header; + /* LATER check if (x->s_track < x->s_hdtracks)... after some thinking */ + strncpy(header.h_type, "MTrk", 4); + header.h_length = 0; + x->s_trackid = x->s_track_id(x->s_track); + x->s_track++; + x->s_newtrack = 1; + x->s_status = x->s_channel = 0; + x->s_bytesleft = 0; + x->s_time = 0; + if (fwrite(&header, 1, + MIFI_TRACKHEADER_SIZE, x->s_fp) != MIFI_TRACKHEADER_SIZE) + { + post("unable to write midifile header"); + return (0); + } + return (1); +} + +/* append eot meta and update length field in a track header */ +int mifi_write_adjust_track(t_mifi_stream *x, uint32 eotdelay) +{ + t_mifi_event *evp = x->s_auxeve; + long skip; + uint32 length; + evp->e_delay = eotdelay; + evp->e_status = MIFI_EVENT_META; + evp->e_meta = MIFI_META_EOT; + evp->e_length = 0; + if (!mifi_write_event(x, evp)) + return (0); + skip = x->s_bytesleft + 4; + length = bifi_swap4(x->s_bytesleft); +#ifdef MIFI_DEBUG + post("adjusting track size to %d", x->s_bytesleft); +#endif + /* LATER add sanity check (compare to saved filepos) */ + if (skip > 4 && + fseek(x->s_fp, -skip, SEEK_CUR) < 0 || + fwrite(&length, 1, 4, x->s_fp) != 4 || + fseek(x->s_fp, 0, SEEK_END) < 0) + { + post("unable to adjust length field in midifile track header (length %d)", + x->s_bytesleft); + return (0); + } + return (1); +} + +/* LATER analyse shrinking effect caused by truncation */ +int mifi_write_event(t_mifi_stream *x, t_mifi_event *e) +{ + uchar buf[3], *ptr = buf; + size_t size = mifi_writevarlen(x, e->e_delay); + if (!size) + return (0); + x->s_bytesleft += size; + if (MIFI_IS_CHANNEL(e->e_status)) + { + if ((*ptr = e->e_status | e->e_channel) == x->s_status) + size = 1; + else { + x->s_status = *ptr++; + size = 2; + } + *ptr++ = e->e_data[0]; + if (!MIFI_ONE_DATABYTE(e->e_status)) + { + *ptr = e->e_data[1]; + size++; + } + ptr = buf; + } + else if (e->e_status == MIFI_EVENT_META) + { + x->s_status = 0; /* sysex and meta-events cancel any running status */ + buf[0] = e->e_status; + buf[1] = e->e_meta; + if (fwrite(buf, 1, 2, x->s_fp) != 2) + return (0); + x->s_bytesleft += 2; + size = mifi_writevarlen(x, (uint32)(e->e_length)); + if (!size) + return (0); + x->s_bytesleft += size; + size = e->e_length; + ptr = e->e_data; + } + else return (0); + if (fwrite(ptr, 1, size, x->s_fp) != size) + return (0); + x->s_bytesleft += size; + return (1); +} diff --git a/shared/common/mifi.h b/shared/common/mifi.h new file mode 100644 index 0000000..3893616 --- /dev/null +++ b/shared/common/mifi.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2001-2003 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 */ +/* ...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 + +/* true if one of channel messages */ +#define MIFI_IS_CHANNEL(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 *e); +int mifi_event_settext(t_mifi_event *e, int type, char *text); +void mifi_event_printmeta(t_mifi_event *e); + +t_mifi_stream *mifi_stream_new(void); +void mifi_stream_reset(t_mifi_stream *x); +void mifi_stream_free(t_mifi_stream *x); + +t_mifi_stream *mifi_read_start(t_mifi_stream *x, + const char *filename, const char *dirname, + int complain); +int mifi_read_restart(t_mifi_stream *x); +void mifi_read_end(t_mifi_stream *x); +int mifi_read_event(t_mifi_stream *x, t_mifi_event *e); +int mifi_read_analyse(t_mifi_stream *stp); +int mifi_read_doit(t_mifi_stream *stp); + +t_mifi_stream *mifi_write_start(t_mifi_stream *x, + const char *filename, const char *dirname); +void mifi_write_end(t_mifi_stream *x); +int mifi_write_start_track(t_mifi_stream *x); +int mifi_write_adjust_track(t_mifi_stream *x, uint32 eotdelay); +int mifi_write_event(t_mifi_stream *x, t_mifi_event *e); + +#endif diff --git a/shared/common/port.c b/shared/common/port.c new file mode 100644 index 0000000..f1471bf --- /dev/null +++ b/shared/common/port.c @@ -0,0 +1,612 @@ +/* Copyright (c) 1997-2003 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. */ + +/* CHECKME inlet/outlet vs inlet~/outlet~ */ +/* LATER think about abstractions */ +/* LATER sort out escaping rules (also revisit binport.c) */ +/* LATER quoting */ +/* LATER resolve object names (preserve connections of unknown dummies?) */ + +#ifdef UNIX +#include <unistd.h> +#endif +#ifdef NT +#include <io.h> +#endif +#include <stdio.h> +#include <string.h> +#include "m_pd.h" +#include "common/loud.h" +#include "common/grow.h" +#include "common/binport.h" +#include "common/port.h" + +#define PORT_INISTACK 256 /* LATER rethink */ +#define PORT_INISIZE 512 /* LATER rethink */ + +enum { PORT_OK, + PORT_NEXT, /* next line, please */ + PORT_UNKNOWN, PORT_CORRUPT, PORT_FATAL }; + +#define PORT_DEFFONTSIZE 10. +#define PORT_XSTRETCH 1.25 +#define PORT_YSTRETCH 1.1 +#define PORT_WSTRETCH 1.25 + +typedef struct _port +{ + t_binbuf *x_oldbb; + t_binbuf *x_newbb; + int x_nobj; + int x_inatoms; + t_atom *x_inmess; + int x_outsize; + int x_outatoms; + t_atom *x_outmess; + t_atom x_outini[PORT_INISIZE]; + int x_stacksize; + int x_stackdepth; + int *x_stack; + int x_stackini[PORT_INISTACK]; +} t_port; + +static t_float port_floatarg(t_port *x, int ndx) +{ + if (ndx < x->x_inatoms) + { + t_atom *av = &x->x_inmess[ndx]; + return (av->a_type == A_FLOAT ? av->a_w.w_float : 0); + } + else return (0); +} + +static t_symbol *port_symbolarg(t_port *x, int ndx) +{ + if (ndx < x->x_inatoms) + { + t_atom *av = &x->x_inmess[ndx]; + return (av->a_type == A_SYMBOL ? av->a_w.w_symbol : &s_); + } + else return (&s_); +} + +static int port_xstretch(float f) +{ + return ((int)(f * PORT_XSTRETCH + 0.5)); +} + +static int port_ystretch(float f) +{ + return ((int)(f * PORT_YSTRETCH + 0.5)); +} + +static int port_wstretch(float f) +{ + return ((int)(f * PORT_WSTRETCH + 0.5)); +} + +static t_float port_xarg(t_port *x, int ndx) +{ + return ((t_float)port_xstretch(port_floatarg(x, ndx))); +} + +static t_float port_yarg(t_port *x, int ndx) +{ + return ((t_float)port_ystretch(port_floatarg(x, ndx))); +} + +static t_float port_widtharg(t_port *x, int ndx) +{ + return ((t_float)port_wstretch(port_floatarg(x, ndx))); +} + +static void port_setxy(t_port *x, int ndx, t_atom *ap) +{ + float f = port_xarg(x, ndx); + SETFLOAT(ap, f); + ndx++; ap++; + f = port_yarg(x, ndx); + SETFLOAT(ap, f); +} + +static int import_obj(t_port *x, char *name) +{ + int ndx = (x->x_inmess[1].a_w.w_symbol == gensym("user") ? 3 : 2); + binbuf_addv(x->x_newbb, "ssffs;", + gensym("#X"), gensym("obj"), + port_xarg(x, ndx), port_yarg(x, ndx + 1), + (name ? gensym(name) : + x->x_inmess[ndx == 2 ? 6 : 2].a_w.w_symbol)); + x->x_nobj++; + return (PORT_NEXT); +} + +static int import_objarg(t_port *x, char *name) +{ + int ndx = (x->x_inmess[1].a_w.w_symbol == gensym("user") ? 3 : 2); + if (x->x_inatoms > 6) + { + t_atom *in = x->x_inmess + ndx + 4; + t_atom *out = x->x_outmess; + SETSYMBOL(out, gensym("#X")); out++; + SETSYMBOL(out, gensym("obj")); out++; + port_setxy(x, ndx, out); out++; out++; + if (name) + { + SETSYMBOL(out, gensym(name)); out++; + if (ndx == 2) in++; + } + else *out++ = (ndx == 2 ? *in++ : x->x_inmess[2]); + for (ndx = 7; ndx < x->x_inatoms; ndx++) + *out++ = *in++; + SETSEMI(out); + binbuf_add(x->x_newbb, x->x_inatoms - 1, x->x_outmess); + x->x_nobj++; + return (PORT_NEXT); + } + else return (PORT_CORRUPT); +} + +static int imaction_vpatcher(t_port *x, char *arg) +{ + if (x->x_stackdepth >= x->x_stacksize) + { + int rqsz = x->x_stackdepth + 1; + int sz = rqsz; + x->x_stack = grow_withdata(&rqsz, &x->x_stackdepth, + &x->x_stacksize, x->x_stack, + PORT_INISTACK, x->x_stackini, + sizeof(*x->x_stack)); + if (rqsz != sz) + { + post("too many embedded patches"); + return (PORT_FATAL); + } + } + x->x_stack[x->x_stackdepth++] = x->x_nobj; + x->x_nobj = 0; + binbuf_addv(x->x_newbb, "ssfffff;", + gensym("#N"), gensym("canvas"), + port_xarg(x, 2), port_yarg(x, 3), + (float)port_xstretch(port_floatarg(x, 4) - port_floatarg(x, 2)), + (float)port_ystretch(port_floatarg(x, 5) - port_floatarg(x, 3)), + PORT_DEFFONTSIZE); + return (PORT_NEXT); +} + +static int imaction_patcher(t_port *x, char *arg) +{ + binbuf_addv(x->x_newbb, "ssffss;", + gensym("#X"), gensym("restore"), + port_xarg(x, 2), port_yarg(x, 3), + gensym("pd"), port_symbolarg(x, 7)); + if (x->x_stackdepth) /* LATER consider returning PORT_FATAL otherwise */ + x->x_stackdepth--; + x->x_nobj = x->x_stack[x->x_stackdepth]; + x->x_nobj++; + return (PORT_NEXT); +} + +static int imaction_trigger(t_port *x, char *arg) +{ + int i; + for (i = 7; i < x->x_inatoms; i++) + if (x->x_inmess[i].a_type == A_SYMBOL && + x->x_inmess[i].a_w.w_symbol == gensym("i")) + x->x_inmess[i].a_w.w_symbol = gensym("f"); + return (PORT_OK); +} + +static int imaction_scope(t_port *x, char *name) +{ + if (x->x_inatoms > 6) + { + t_atom *in = x->x_inmess + 7; + t_atom *out = x->x_outmess; + int i, xpix, ypix; + SETSYMBOL(out, gensym("#X")); out++; + SETSYMBOL(out, gensym("obj")); out++; + port_setxy(x, 3, out); + xpix = (int)out++->a_w.w_float; + ypix = (int)out++->a_w.w_float; + if (name) + { + SETSYMBOL(out, gensym(name)); out++; + } + else *out++ = x->x_inmess[2]; + port_setxy(x, 5, out); + out++->a_w.w_float -= xpix; + out++->a_w.w_float -= ypix; + for (i = 7; i < x->x_inatoms; i++) + *out++ = *in++; + SETSEMI(out); + binbuf_add(x->x_newbb, x->x_inatoms + 1, x->x_outmess); + x->x_nobj++; + return (PORT_NEXT); + } + else return (PORT_CORRUPT); +} + +/* width fontsize fontfamily encoding fontprops red green blue text... */ +static int imaction_comment(t_port *x, char *arg) +{ + int outatoms; + SETSYMBOL(x->x_outmess, gensym("#X")); + SETSYMBOL(x->x_outmess + 1, gensym("obj")); + port_setxy(x, 2, x->x_outmess + 2); + SETSYMBOL(x->x_outmess + 4, gensym("comment")); + if (x->x_inatoms > 5) + { + int i, fontsize, fontprops; + float width = port_widtharg(x, 4); + t_atom *ap = x->x_inmess + 5; + SETFLOAT(x->x_outmess + 5, width); + if (ap->a_type == A_FLOAT) + { + fontsize = ((int)ap->a_w.w_float) & 0x0ff; + fontprops = ((int)ap->a_w.w_float) >> 8; + } + else fontsize = 10, fontprops = 0; + SETFLOAT(x->x_outmess + 6, fontsize); + SETSYMBOL(x->x_outmess + 7, gensym("helvetica")); + SETSYMBOL(x->x_outmess + 8, gensym("?")); + SETFLOAT(x->x_outmess + 9, fontprops); + SETFLOAT(x->x_outmess + 10, 0); + SETFLOAT(x->x_outmess + 11, 0); + SETFLOAT(x->x_outmess + 12, 0); + outatoms = x->x_inatoms + 7; + for (i = 13; i < outatoms ; i++) + x->x_outmess[i] = x->x_inmess[i - 7]; + } + else outatoms = 5; + SETSEMI(x->x_outmess + outatoms); + binbuf_add(x->x_newbb, outatoms + 1, x->x_outmess); + x->x_nobj++; + return (PORT_NEXT); +} + +static int imaction_message(t_port *x, char *arg) +{ + int i; + SETSYMBOL(x->x_outmess, gensym("#X")); + SETSYMBOL(x->x_outmess + 1, gensym("msg")); + port_setxy(x, 2, x->x_outmess + 2); + for (i = 6; i < x->x_inatoms; i++) + x->x_outmess[i-2] = x->x_inmess[i]; + SETSEMI(x->x_outmess + x->x_inatoms - 2); + binbuf_add(x->x_newbb, x->x_inatoms - 1, x->x_outmess); + x->x_nobj++; + return (PORT_NEXT); +} + +/* FIXME this is no longer true */ +static int imaction_inlet(t_port *x, char *arg) +{ + return (import_obj(x, (port_floatarg(x, 5) ? "inlet~" : "inlet"))); +} + +/* FIXME this is no longer true */ +static int imaction_outlet(t_port *x, char *arg) +{ + return (import_obj(x, (port_floatarg(x, 5) ? "outlet~" : "outlet"))); +} + +static int imaction_number(t_port *x, char *arg) +{ + binbuf_addv(x->x_newbb, "ssff;", + gensym("#X"), gensym("floatatom"), + port_xarg(x, 2), port_yarg(x, 3)); + x->x_nobj++; + return (PORT_NEXT); +} + +static int imaction_connect(t_port *x, char *arg) +{ + binbuf_addv(x->x_newbb, "ssffff;", + gensym("#X"), gensym("connect"), + x->x_nobj - port_floatarg(x, 2) - 1, + port_floatarg(x, 3), + x->x_nobj - port_floatarg(x, 4) - 1, + port_floatarg(x, 5)); + return (PORT_NEXT); +} + +typedef int (*t_portaction)(t_port *, char *arg); + +typedef struct _portslot +{ + char *s_name; + int s_index; + t_portaction s_action; + char *s_actionarg; + struct _portnode *s_subtree; + t_symbol *s_symbol; +} t_portslot; + +typedef struct _portnode /* a parser's symbol definition, sort of... */ +{ + t_portslot *n_table; + int n_nslots; +} t_portnode; + +#define PORT_NSLOTS(slots) (sizeof(slots)/sizeof(*(slots))) + +static t_portslot imslots__N[] = +{ + { "vpatcher", 1, imaction_vpatcher, 0, 0, 0 } +}; +static t_portnode imnode__N = { imslots__N, PORT_NSLOTS(imslots__N) }; + +static t_portslot imslots_newobj[] = +{ + { "patcher", 6, imaction_patcher, 0, 0, 0 }, + { "p", 6, imaction_patcher, 0, 0, 0 }, + /* state is embedded in #N vtable...; #T set...; */ + { "table", 6, import_obj, "Table", 0, 0 } +}; +static t_portnode imnode_newobj = { imslots_newobj, + PORT_NSLOTS(imslots_newobj) }; + +/* LATER consider merging newobj and newex */ +static t_portslot imslots_newex[] = +{ + { "append", 6, import_objarg, "Append", 0, 0 }, + { "biquad~", 6, import_objarg, "Biquad~", 0, 0 }, + { "change", 6, import_objarg, "Change", 0, 0 }, + { "clip", 6, import_objarg, "Clip", 0, 0 }, + { "clip~", 6, import_objarg, "Clip~", 0, 0 }, + { "key", 6, import_obj, "Key", 0, 0 }, + { "keyup", 6, import_obj, "Keyup", 0, 0 }, + { "line", 6, import_objarg, "Line", 0, 0 }, + { "line~", 6, import_objarg, "Line~", 0, 0 }, + { "poly", 6, import_objarg, "Poly", 0, 0 }, + { "snapshot~", 6, import_objarg, "Snapshot~", 0, 0 }, + { "trigger", 6, imaction_trigger, 0, 0, 0 }, + { "t", 6, imaction_trigger, 0, 0, 0 } +}; +static t_portnode imnode_newex = { imslots_newex, + PORT_NSLOTS(imslots_newex) }; + +static t_portslot imslots_user[] = +{ + { "GSwitch", 2, import_objarg, "Gswitch", 0, 0 }, + { "GSwitch2", 2, import_objarg, "Ggate", 0, 0 }, + { "number~", 2, import_obj, 0, 0, 0 }, + { "scope~", 2, imaction_scope, "Scope~", 0, 0 }, + { "uslider", 2, import_obj, "vsl", 0, 0 } /* LATER range and offset */ +}; +static t_portnode imnode_user = { imslots_user, + PORT_NSLOTS(imslots_user) }; + +static t_portslot imslots__P[] = +{ + { "comment", 1, imaction_comment, 0, 0, 0 }, + { "message", 1, imaction_message, 0, 0, 0 }, + { "newobj", 1, import_objarg, 0, &imnode_newobj, 0 }, + { "newex", 1, import_objarg, 0, &imnode_newex, 0 }, + { "inlet", 1, imaction_inlet, 0, 0, 0 }, + { "inlet~", 1, imaction_inlet, 0, 0, 0 }, + { "outlet", 1, imaction_outlet, 0, 0, 0 }, + { "outlet~", 1, imaction_outlet, 0, 0, 0 }, + { "number", 1, imaction_number, 0, 0, 0 }, + { "flonum", 1, imaction_number, 0, 0, 0 }, + { "button", 1, import_obj, "bng", 0, 0 }, + { "slider" , 1, import_obj, "vsl", 0, 0 }, /* LATER range and offset */ + { "hslider", 1, import_obj, "hsl", 0, 0 }, /* LATER range and offset */ + { "toggle", 1, import_obj, "tgl", 0, 0 }, + { "user", 1, import_objarg, 0, &imnode_user, 0 }, + /* state is embedded in #N vpreset <nslots>; #X append... */ + { "preset", 1, import_obj, "preset", 0, 0 }, + /* an object created from the "Paste Picture" menu, + state is embedded in #N picture; #K...; */ + { "vpicture", 1, import_obj, "vpicture", 0, 0 }, + { "connect", 1, imaction_connect, 0, 0, 0 }, + { "fasten", 1, imaction_connect, 0, 0, 0 } +}; +static t_portnode imnode__P = { imslots__P, PORT_NSLOTS(imslots__P) }; + +static t_portslot imslots_[] = +{ + { "#N", 0, 0, 0, &imnode__N, 0 }, + { "#P", 0, 0, 0, &imnode__P, 0 } +}; +static t_portnode imnode_ = { imslots_, PORT_NSLOTS(imslots_) }; + +static int port_doit(t_port *x, t_portnode *node) +{ + int nslots = node->n_nslots; + if (nslots > 0) + { + t_portslot *slot = node->n_table; + t_symbol *s = port_symbolarg(x, slot->s_index); + while (nslots--) + { + if (slot->s_symbol == s) + { + if (slot->s_subtree) + { + int nobj = x->x_nobj; + int result = port_doit(x, slot->s_subtree); + if (result == PORT_FATAL || result == PORT_CORRUPT || + result == PORT_NEXT) + return (result); + } + if (slot->s_action) + return (slot->s_action(x, slot->s_actionarg)); + else + return (PORT_OK); /* LATER rethink */ + } + slot++; + } + } + else bug("port_doit"); + return (PORT_UNKNOWN); +} + +static void port_dochecksetup(t_portnode *node) +{ + t_portslot *slots = node->n_table; + int i, nslots = node->n_nslots; + for (i = 0; i < nslots; i++) + { + t_portnode *subtree = slots[i].s_subtree; + slots[i].s_symbol = gensym(slots[i].s_name); + if (subtree) + port_dochecksetup(subtree); + } +} + +static void port_checksetup(void) +{ + static int done = 0; + if (!done) + { + port_dochecksetup(&imnode_); + done = 1; + } +} + +static t_port *port_new(void) +{ + t_port *x = (t_port *)getbytes(sizeof(*x)); + x->x_oldbb = binbuf_new(); + x->x_outsize = PORT_INISIZE; + x->x_outatoms = 0; + x->x_outmess = x->x_outini; + x->x_stacksize = PORT_INISTACK; + x->x_stackdepth = 0; + x->x_stack = x->x_stackini; + return (x); +} + +static void port_free(t_port *x) +{ + if (x->x_outmess != x->x_outini) + freebytes(x->x_outmess, x->x_outsize * sizeof(*x->x_outmess)); + if (x->x_stack != x->x_stackini) + freebytes(x->x_stack, x->x_stacksize * sizeof(*x->x_stack)); + freebytes(x, sizeof(*x)); +} + +static int import_binbuf(t_port *x) +{ + t_atom *av = binbuf_getvec(x->x_oldbb); + int ac = binbuf_getnatom(x->x_oldbb); + int startmess, endmess; + x->x_newbb = binbuf_new(); + for (startmess = 0; startmess < ac; startmess = endmess + 1) + { + t_atom *mess = av + startmess, *ap; + int i; + for (endmess = startmess, ap = mess; + ap->a_type != A_SEMI; endmess++, ap++) + if (endmess == ac) + return (PORT_CORRUPT); /* no final semi */ + if (endmess == startmess || endmess == startmess + 1 + || mess->a_type != A_SYMBOL || mess[1].a_type != A_SYMBOL) + { + startmess = endmess + 1; + continue; + } + if (mess[1].a_w.w_symbol == gensym("hidden")) + { + t_symbol *sel = mess[1].a_w.w_symbol; + mess[1].a_w.w_symbol = mess->a_w.w_symbol; + startmess++; + mess++; + if (endmess == startmess + 1 || mess[1].a_type != A_SYMBOL) + { + startmess = endmess + 1; + continue; + } + } + x->x_inatoms = endmess - startmess; + x->x_inmess = mess; + if ((i = x->x_inatoms + 16) > x->x_outsize) /* LATER rethink */ + { + int sz = i; + x->x_outmess = grow_nodata(&sz, &x->x_outsize, x->x_outmess, + PORT_INISIZE, x->x_outini, + sizeof(*x->x_outmess)); + if (sz != i) + { + startmess = endmess + 1; + continue; /* LATER rethink */ + } + } + + /* dollar signs in file translate to symbols, + LATER rethink, also #-signs */ + for (i = 0, ap = x->x_inmess; i < x->x_inatoms; i++, ap++) + { + if (ap->a_type == A_DOLLAR) + { + char buf[100]; + sprintf(buf, "$%d", ap->a_w.w_index); + SETSYMBOL(ap, gensym(buf)); + } + else if (ap->a_type == A_DOLLSYM) + { + char buf[100]; + sprintf(buf, "$%s", ap->a_w.w_symbol->s_name); + SETSYMBOL(ap, gensym(buf)); + } + } + if (port_doit(x, &imnode_) == PORT_FATAL) + return (PORT_FATAL); + } + return (PORT_OK); +} + +void import_max(char *fn, char *dir) +{ + t_port *x; + int failure, fd, ftype; + char buf[MAXPDSTRING], *bufp; + t_pd *stackp = 0; + int dspstate = canvas_suspend_dsp(); + port_checksetup(); + if ((fd = open_via_path(dir, fn, "", buf, &bufp, MAXPDSTRING, 0)) < 0) + { + loud_error(0, "%s: can't open", fn); + return; + } + else close (fd); + + x = port_new(); + glob_setfilename(0, gensym(bufp), gensym(buf)); + ftype = binport_read(x->x_oldbb, bufp, buf); + if (ftype == BINPORT_MAXTEXT || ftype == BINPORT_PDFILE) + failure = binbuf_read(x->x_oldbb, bufp, buf, 0); + else + failure = (ftype != BINPORT_OK); /* LATER rethink */ + if (failure) + { + perror(fn); /* FIXME */ + binbuf_free(x->x_oldbb); + } + else + { + if (ftype == BINPORT_PDFILE) x->x_newbb = x->x_oldbb; + else + { + import_binbuf(x); + binbuf_free(x->x_oldbb); +#if 1 + binbuf_write(x->x_newbb, "import-result.pd", "", 0); +#endif + } + binbuf_eval(x->x_newbb, 0, 0, 0); + binbuf_free(x->x_newbb); + } + port_free(x); + + glob_setfilename(0, &s_, &s_); + canvas_resume_dsp(dspstate); + while ((stackp != s__X.s_thing) && (stackp = s__X.s_thing)) + vmess(stackp, gensym("pop"), "i", 1); +#if 0 /* LATER */ + pd_doloadbang(); +#endif +} diff --git a/shared/common/port.h b/shared/common/port.h new file mode 100644 index 0000000..5d2c118 --- /dev/null +++ b/shared/common/port.h @@ -0,0 +1,10 @@ +/* Copyright (c) 2003 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 __PORT_H__ +#define __PORT_H__ + +void import_max(char *fn, char *dir); + +#endif diff --git a/shared/common/rand.c b/shared/common/rand.c new file mode 100644 index 0000000..37dcf62 --- /dev/null +++ b/shared/common/rand.c @@ -0,0 +1,61 @@ +/* Copyright (c) 1997-2003 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. */ + +#include <time.h> +#include "m_pd.h" +EXTERN double sys_getrealtime(void); /* used to be in m_imp.h */ +#include "common/rand.h" + +/* borrowed from x_misc.c, LATER rethink */ +void rand_seed(unsigned int *statep, unsigned int seed) +{ + if (seed) *statep = (seed & 0x7fffffff); + else + { + /* LATER consider using time elapsed from system startup, + (or login -- in linux we might call getutent) */ + static unsigned int failsafe = 1489853723; + static int shift = 0; + static unsigned int lastticks = 0; + /* LATER rethink -- it might fail on faster machine than mine + (but does it matter?) */ + unsigned int newticks = (unsigned int)(sys_getrealtime() * 1000000.); + if (newticks == lastticks) + { + failsafe = failsafe * 435898247 + 938284287; + *statep = (failsafe & 0x7fffffff); +#ifdef RAND_DEBUG + post("rand_seed failed (newticks %d)", newticks); +#endif + } + else + { + if (!shift) + shift = (int)time(0); /* LATER deal with error return (-1) */ + *statep = ((newticks + shift) & 0x7fffffff); +#if 0 + post("rand_seed: newticks %d, shift %d", newticks, shift); +#endif + } + lastticks = newticks; + } +} + +/* borrowed from x_misc.c, LATER rethink */ +int rand_int(unsigned int *statep, int range) +{ + int result; + *statep = *statep * 472940017 + 832416023; + result = ((double)range) * ((double)*statep) * (1./4294967296.); + return (result < range ? result : range - 1); +} + +/* borrowed from d_osc.c, LATER rethink */ +float rand_float(unsigned int *statep) +{ + float result = ((float)((*statep & 0x7fffffff) - 0x40000000)) + * (float)(1.0 / 0x40000000); + *statep = *statep * 435898247 + 382842987; + return (result); +} diff --git a/shared/common/rand.h b/shared/common/rand.h new file mode 100644 index 0000000..75350d2 --- /dev/null +++ b/shared/common/rand.h @@ -0,0 +1,14 @@ +/* Copyright (c) 2002-2003 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 __RAND_H__ +#define __RAND_H__ + +#define RAND_DEBUG + +void rand_seed(unsigned int *statep, unsigned int seed); +int rand_int(unsigned int *statep, int range); +float rand_float(unsigned int *statep); + +#endif diff --git a/shared/common/sq.c b/shared/common/sq.c new file mode 100644 index 0000000..d7b1ea5 --- /dev/null +++ b/shared/common/sq.c @@ -0,0 +1,371 @@ +/* Copyright (c) 2001-2003 krzYszcz and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* squeak and squeal: sequencing utilities, a prototype version + (the main, 'sq' part of the library) */ + +#include <stdlib.h> +#include <stdio.h> +#include "m_pd.h" +#include "shared.h" +#include "common/sq.h" + +#if 1 +#define SQ_VERBOSE +#if 0 +#define SQ_DEBUG +#endif +#endif + +/* #define SQUMPI_IGNORE */ + +#define SQUB_NALLOC 32 +#define SQUMPI_NALLOC (32 * sizeof(t_squmpo)) +#define SQUAX_NALLOC (32 * sizeof(t_squack)) + +#define SQUMPI_DEFAULT 500000 /* 120 bpm in microseconds per beat */ + +/* Arguments reqcount and elsize as in calloc, returns count */ +size_t squb_checksize(void *buf, size_t reqcount, size_t elsize) +{ + t_squb *x = buf; + size_t reqsize = reqcount * elsize; + size_t newsize = x->b_bufsize; + while (newsize < reqsize) newsize *= 2; + if (newsize == x->b_bufsize) + return (newsize); +#ifdef SQ_DEBUG + post("need to resize buffer %x from %d to %d (requested size %d)", + (int)x, x->b_bufsize, newsize, reqsize); +#endif + if (!(x->b_data = resizebytes(x->b_data, x->b_bufsize, newsize)) && + /* rather hopeless... */ + !(x->b_data = getbytes(newsize = SQUB_NALLOC * elsize))) + newsize = 0; + return ((x->b_bufsize = newsize) / elsize); /* LATER do it right */ +} + +/* generic event */ + +/* tempo map */ + +/* comparison function used by qsort */ +static int squmpi_compare(const void *tp1, const void *tp2) +{ + return (((t_squmpo *)tp1)->te_onset > ((t_squmpo *)tp2)->te_onset ? 1 : -1); +} + +void squmpi_sort(t_sq *x) +{ + int i; + t_squmpo *tp; + qsort(x->s_tempomap, x->s_ntempi, sizeof(t_squmpo), squmpi_compare); +#if defined SQ_VERBOSE && ! defined SQ_DEBUG + for (i = x->s_ntempi, tp = x->s_tempomap; i > 0 ; i--, tp++) + post("tempo %d at %d", tp->te_value, (int)tp->te_onset); +#endif +} + +t_squmpo *squmpi_add(t_sq *x) +{ + size_t count = x->s_ntempi + 1; + t_squmpo *tep; + if (squb_checksize(x->s_mytempi, count, sizeof(t_squmpo)) < count) + return (0); + tep = x->s_tempomap + x->s_ntempi++; + squmpo_reset(tep); + return (tep); +} + +void squmpo_reset(t_squmpo *x) +{ + x->te_onset = 0; + x->te_value = SQUMPI_DEFAULT; +} + +/* track map */ + +t_squack *squax_add(t_sq *x) +{ + size_t count = x->s_ntracks + 2; /* guard point */ + t_squack *trp; + if (squb_checksize(x->s_mytracks, count, sizeof(t_squack)) < count) + return (0); + trp = x->s_trackmap + x->s_ntracks++; + squack_reset(trp); + return (trp); +} + +void squack_reset(t_squack *x) +{ + x->tr_id = 0; /* this is no-id */ + x->tr_nevents = 0; + x->tr_name = 0; + x->tr_head = 0; +} + +/* generic iterator */ + +void *squiter_new(t_sq *x) +{ + if (x->s_myiter = getbytes(sizeof(*x->s_myiter))) + { + } + return (x->s_myiter); +} + +/* routines to access iterator hooks (setting hooks is explicit only) */ +t_squiter_seekhook squiter_seekhook(t_squiter *x) +{ + return (x ? (t_squiter_seekhook)x->i_hooks[SQUITER_SEEKHOOK] : 0); +} + +t_squiter_incrhook squiter_incrhook(t_squiter *x) +{ + return (x ? (t_squiter_incrhook)x->i_hooks[SQUITER_INCRHOOK] : 0); +} + +t_squiter_getevehook squiter_getevehook(t_squiter *x) +{ + return (x ? (t_squiter_getevehook)x->i_hooks[SQUITER_GETEVEHOOK] : 0); +} + +t_squiter_setevehook squiter_setevehook(t_squiter *x) +{ + return (x ? (t_squiter_setevehook)x->i_hooks[SQUITER_SETEVEHOOK] : 0); +} + +t_squiter_gettimhook squiter_gettimhook(t_squiter *x) +{ + return (x ? (t_squiter_gettimhook)x->i_hooks[SQUITER_GETTIMHOOK] : 0); +} + +t_squiter_settimhook squiter_settimhook(t_squiter *x) +{ + return (x ? (t_squiter_settimhook)x->i_hooks[SQUITER_SETTIMHOOK] : 0); +} + +t_squiter_gettarhook squiter_gettarhook(t_squiter *x) +{ + return (x ? (t_squiter_gettarhook)x->i_hooks[SQUITER_GETTARHOOK] : 0); +} + +t_squiter_settarhook squiter_settarhook(t_squiter *x) +{ + return (x ? (t_squiter_settarhook)x->i_hooks[SQUITER_SETTARHOOK] : 0); +} + +/* time conversion */ + +/* Compute reusable coefficient, rather then repeatedly apply the formula. + For smpte time: + d msecs == (d / 1000.) secs == ((d * nframes * nticks) / 1000.) ticks + or for metrical time: + d msecs == (d * 1000.) usecs == ((d * 1000.) / tempo) beats + == ((d * nticks * 1000.) / tempo) ticks +*/ +/* LATER ntsc */ +float sq_ticks2msecs(t_sq *x, uint32 tempo) +{ + if (x->s_nframes) + return (1000. / (x->s_nframes * x->s_nticks)); + if (tempo <= 0) + tempo = x->s_tempo; + if (tempo <= 0) + tempo = SQUMPI_DEFAULT; + return (tempo / (x->s_nticks * 1000.)); +} + +float sq_msecs2ticks(t_sq *x, uint32 tempo) +{ + if (x->s_nframes) + return (((x->s_nframes * x->s_nticks) / 1000.)); + if (!tempo) + tempo = x->s_tempo; + if (!tempo) + tempo = SQUMPI_DEFAULT; + return ((x->s_nticks * 1000.) / tempo); +} + +/* transform onset ticks into delta msecs */ +void sq_fold_time(t_sq *x) +{ + t_squiter *it = x->s_myiter; + t_squiter_seekhook seekhook = squiter_seekhook(it); + t_squiter_incrhook incrhook = squiter_incrhook(it); + t_squiter_gettimhook gethook = squiter_gettimhook(it); + t_squiter_settimhook sethook = squiter_settimhook(it); + int i, ret, nevents = x->s_nevents; + + if (!it || !seekhook(it, 0)) + return; + if (x->s_nframes) + { + float coef = sq_ticks2msecs(x, 0); + t_float lasttime = 0; + for (i = 0; i < nevents; i++) + { + if (ret = squiter_inrange(it)) + { + t_float thistime = gethook(it, &ret) * coef; + /* back to delta time */ + if (ret) sethook(it, thistime - lasttime, &ret); + lasttime = thistime; + } + if (ret) incrhook(it); + else + { + post("sequence folding error: bad iterator"); + break; + } + } + } + else /* apply tempomap */ + { + float coef = sq_ticks2msecs(x, SQUMPI_DEFAULT); + int ntempi = x->s_ntempi; + t_float lasttime = 0, thistime = 0; + t_float temposince = 0; + t_float tempoonset = 0; + int tempondx = 0; + for (i = 0; i < nevents; i++) + { + if (ret = squiter_inrange(it)) + { + t_float thisonset = gethook(it, &ret); + t_float nexttempoonset; +#ifdef SQUMPI_IGNORE + thistime = thisonset * coef; +#else + while (tempondx < ntempi /* LATER consider using guard point */ + && (nexttempoonset = x->s_tempo_onset(tempondx)) + < thisonset) + { + temposince += (nexttempoonset - tempoonset) * coef; + tempoonset = nexttempoonset; + coef = sq_ticks2msecs(x, x->s_tempo_value(tempondx)); + tempondx++; + } + thistime = temposince + (thisonset - tempoonset) * coef; +#endif + if (thistime < lasttime) + { +#ifdef SQ_DEBUG + /* FIXME under msvc -- horror! */ + if (thistime != lasttime) + post("ndx %d, this-last (%x-%x) %.15f, \ +tix %.9f, tsince %.9f, ttix %.9f, coef %.9f", + tempondx, (int)thistime, (int)lasttime, + thistime - lasttime, + thisonset, temposince, tempoonset, coef); +#endif + thistime = lasttime; + } + /* back to delta time */ + if (ret) sethook(it, thistime - lasttime, &ret); + lasttime = thistime; + } + if (ret) incrhook(it); + else + { + post("sequence folding error: bad iterator"); + break; + } + } + } +} + +/* transform delta msecs into onset msecs */ +/* LATER add an option (or a separate function) for obtaining ticks + (according to tempomap) */ +void sq_unfold_time(t_sq *x) +{ + t_squiter *it = x->s_myiter; + t_squiter_seekhook seekhook = squiter_seekhook(it); + t_squiter_incrhook incrhook = squiter_incrhook(it); + t_squiter_gettimhook gethook = squiter_gettimhook(it); + t_squiter_settimhook sethook = squiter_settimhook(it); + int i, ret, nevents = x->s_nevents; + t_float thisonset = 0; + + if (!it || !seekhook(it, 0)) + return; + for (i = 0; i < nevents; i++) + { + if (ret = squiter_inrange(it)) + { + thisonset += gethook(it, &ret); + if (ret) sethook(it, thisonset, &ret); + } + if (ret) incrhook(it); + else + { + post("sequence unfolding error: bad iterator"); + break; + } + } +} + +void sq_reset(t_sq *x) +{ + x->s_eof = 0; + x->s_newtrack = 0; + x->s_anapass = 1; + x->s_fp = 0; + x->s_time = 0; + x->s_tempo = SQUMPI_DEFAULT; + x->s_track = 0; +} + +t_sq *sq_new(void) +{ + t_sq *x = (t_sq *)getbytes(sizeof(*x)); + if (!x) + goto constructorfailure; + + /* these two are allocated in derived structure constructor */ + x->s_myiter = 0; + x->s_auxeve = 0; + + if (!(x->s_mytempi = getbytes(sizeof(t_squmpi)))) + goto constructorfailure; + if (!(x->s_tempomap = getbytes(x->s_mytempi->m_bufsize = SQUMPI_NALLOC))) + goto constructorfailure; + x->s_ntempi = 0; + if (!(x->s_mytracks = getbytes(sizeof(t_squax)))) + goto constructorfailure; + if (!(x->s_trackmap = getbytes(x->s_mytracks->m_bufsize = SQUAX_NALLOC))) + goto constructorfailure; + x->s_ntracks = 0; + + x->s_autoalloc = 0; + x->s_format = 0; + x->s_nticks = 192; /* LATER parametrize this somehow */ + x->s_nframes = 0; + + sq_reset(x); + return (x); +constructorfailure: + if (x) sq_free(x); + return (0); +} + +void sq_free(t_sq *x) +{ + if (x->s_mytempi) + { + if (x->s_tempomap) + freebytes(x->s_tempomap, x->s_mytempi->m_bufsize); + freebytes(x->s_mytempi, sizeof(t_squmpi)); + } + if (x->s_mytracks) + { + if (x->s_trackmap) + freebytes(x->s_trackmap, x->s_mytracks->m_bufsize); + freebytes(x->s_mytracks, sizeof(t_squax)); + } + if (x->s_myiter) + freebytes(x->s_myiter, sizeof(*x->s_myiter)); + freebytes(x, sizeof(*x)); +} diff --git a/shared/common/sq.h b/shared/common/sq.h new file mode 100644 index 0000000..6b7586e --- /dev/null +++ b/shared/common/sq.h @@ -0,0 +1,169 @@ +/* Copyright (c) 2001-2003 krzYszcz and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* squeak and squeal: sequencing utilities, a prototype version + (the main, 'sq' part of the library) */ +/* LATER move everything not needed in mifi to squeal */ + +#ifndef __SQ_H__ +#define __SQ_H__ + +/* Generic buffer structure, the `base' for t_squeve, t_squmpi and t_squax */ +typedef struct _squb +{ + uint32 b_length; /* length of data currently used (in items) */ + uchar *b_data; /* data buffer */ + size_t b_bufsize; /* allocated size of data buffer (in bytes) */ +} t_squb; + +/* Generic event structure. Designed as an interface to squeak routines, + and not to be kept in arrays or lists (use sequence containers instead). + Data buffer is automatically allocated and resized by those routines. */ +typedef struct _squeve +{ + uint32 e_length; /* set for any event type! */ + uchar *e_data; + size_t e_bufsize; + uint32 e_delay; +} t_squeve; + +/* tempo map element */ +typedef struct _squmpo +{ + t_float te_onset; /* ticks or microseconds from start of sequence */ + uint32 te_value; /* microseconds per beat */ +} t_squmpo; + +typedef struct _squmpi +{ + uint32 m_ntempi; + t_squmpo *m_map; + size_t m_bufsize; /* allocated size of m_map array in bytes */ +} t_squmpi; + +/* track/subtrack map element */ +typedef struct _squack +{ + int tr_id; /* according to target template */ + uint32 tr_nevents; /* number of events (in this track or pre-total) */ + t_symbol *tr_name; /* track name */ + void *tr_head; /* pointer to first event */ +} t_squack; + +typedef struct _squax +{ + uint32 m_ntracks; + t_squack *m_map; + size_t m_bufsize; /* allocated size of m_map array in bytes */ +} t_squax; + +/* generic type of callback routines used to read/write + sequence containers through t_squiter */ +typedef int (*t_squiterhook)(void *it); + +#define SQUITER_SEEKHOOK 0 +#define SQUITER_INCRHOOK 1 +#define SQUITER_GETEVEHOOK 2 +#define SQUITER_SETEVEHOOK 3 +#define SQUITER_GETTIMHOOK 4 +#define SQUITER_SETTIMHOOK 5 +#define SQUITER_GETTARHOOK 6 +#define SQUITER_SETTARHOOK 7 +#define SQUITER_NHOOKS 8 +/* LATER move these typedefs to sq.c, if still not used globally */ +typedef int (*t_squiter_seekhook)(void *it, int offset); +typedef void (*t_squiter_incrhook)(void *it); +typedef void (*t_squiter_getevehook)(void *it, t_squeve *evp, int *ret); +typedef void (*t_squiter_setevehook)(void *it, t_squeve *evp, int *ret); +typedef t_float (*t_squiter_gettimhook)(void *it, int *ret); +typedef void (*t_squiter_settimhook)(void *it, t_float v, int *ret); +typedef t_symbol (*t_squiter_gettarhook)(void *it, int *ret); +typedef void (*t_squiter_settarhook)(void *it, t_symbol *s, int *ret); + +/* elements might be 'atoms' or whole events, whatever suits better */ +typedef struct _squiter +{ + void *i_owner; + int i_nelems; + void *i_sequence; /* first element pointer */ + void *i_element; /* current element pointer */ + int i_index; /* current element index */ + t_squiterhook i_hooks[SQUITER_NHOOKS]; +} t_squiter; + +/* This is a good candidate for a derivation hierarchy. */ +typedef struct _sq +{ + t_squiter *s_myiter; + t_squax *s_mytracks; + t_squmpi *s_mytempi; /* use shortcuts #defined below */ + void *s_auxeve; /* auxiliary event */ + uint32 s_nevents; /* total number of events */ + FILE *s_fp; /* hmm... */ + int s_autoalloc:1; /* set if auto-allocated */ + int s_eof:1; /* reading: set in case of early eof (error) */ + int s_newtrack:1; /* reading: set if first event in a track */ + int s_anapass:1; /* read/write: set during analysis (pass #1) */ + uchar s_nframes; /* fps if nonzero, else use metrical time */ + uint16 s_nticks; /* number of ticks per beat or per frame */ + uint16 s_format; /* `ismultitrack' flag, LATER add other formats */ + uint32 s_time; /* current time in ticks */ + uint32 s_tempo; /* current tempo, or last one encountered in a file */ + int s_trackid; /* LATER remove? */ + uint16 s_track; /* current track number */ + + /* fields below are specific to midifile streams */ + uchar s_status; /* current running status, | channel in writing */ + uchar s_channel; /* current channel, not used in writing */ + uint16 s_hdtracks; /* number of tracks declared in a midifile header */ + uint32 s_alltracks; /* total number of nonempty tracks */ + /* (s_ntracks counts `in range' nonempty tracks) */ + float s_timecoef; /* msecs->ticks, used in writing only */ + uint32 s_bytesleft; /* nbytes remaining to be read from current track, + or number of bytes written to a track so far */ +} t_sq; + +#define s_ntempi s_mytempi->m_ntempi +#define s_tempomap s_mytempi->m_map +#define s_tempo_onset(ndx) s_mytempi->m_map[ndx].te_onset +#define s_tempo_value(ndx) s_mytempi->m_map[ndx].te_value +#define s_ntracks s_mytracks->m_ntracks +#define s_trackmap s_mytracks->m_map +#define s_track_id(ndx) s_mytracks->m_map[ndx].tr_id +#define s_track_nevents(ndx) s_mytracks->m_map[ndx].tr_nevents +#define s_track_name(ndx) s_mytracks->m_map[ndx].tr_name +#define s_track_head(ndx) s_mytracks->m_map[ndx].tr_head + +/* prototypes of public interface routines */ + +size_t squb_checksize(void *buf, size_t reqcount, size_t elsize); + +#define squiter_inrange(it) ((it)->i_index < (it)->i_nelems) +void *squiter_new(t_sq *x); +t_squiter_seekhook squiter_seekhook(t_squiter *x); +t_squiter_incrhook squiter_incrhook(t_squiter *x); +t_squiter_getevehook squiter_getevehook(t_squiter *x); +t_squiter_setevehook squiter_setevehook(t_squiter *x); +t_squiter_gettimhook squiter_gettimhook(t_squiter *x); +t_squiter_settimhook squiter_settimhook(t_squiter *x); +t_squiter_gettarhook squiter_gettarhook(t_squiter *x); +t_squiter_settarhook squiter_settarhook(t_squiter *x); + +void squmpi_sort(t_sq *x); +t_squmpo *squmpi_add(t_sq *x); +void squmpo_reset(t_squmpo *x); +t_squack *squax_add(t_sq *x); +void squack_reset(t_squack *x); + +float sq_ticks2msecs(t_sq *x, uint32 tempo); +float sq_msecs2ticks(t_sq *x, uint32 tempo); + +void sq_fold_time(t_sq *x); +void sq_unfold_time(t_sq *x); + +t_sq *sq_new(void); +void sq_reset(t_sq *x); +void sq_free(t_sq *x); + +#endif diff --git a/shared/common/vefl.c b/shared/common/vefl.c new file mode 100644 index 0000000..19f37a2 --- /dev/null +++ b/shared/common/vefl.c @@ -0,0 +1,233 @@ +/* Copyright (c) 1997-2003 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. */ + +/* The simplest of garrays: vector of floats */ + +/* Array checking is done in three points: + 1. vefl_new(): never complains + 2. vefl_renew(): this should be called once per every message + 3. vefl_tick(): no template checking (only redraw is involved) +*/ + +/* LATER rethink indsp flag */ + +#include "m_pd.h" +#include "g_canvas.h" +#include "shared.h" +#include "unstable/fragile.h" +#include "common/loud.h" +#include "common/vefl.h" + +#if 1 +#define VEFL_VERBOSE +#if 0 +#define VEFL_DEBUG +#endif +#endif + +/* on failure *vszp is not modified */ +t_float *vefl_get(t_symbol *name, int *vszp, int indsp, t_pd *complain) +{ + if (name && name != &s_) + { + t_garray *ap = (t_garray *)pd_findbyclass(name, garray_class); + if (ap) + { + int vsz; + t_float *vec; + if (garray_getfloatarray(ap, &vsz, &vec)) + { + if (indsp) garray_usedindsp(ap); + if (vszp) *vszp = vsz; + return (vec); + } + else loud_error(complain, /* always complain */ + "bad template of array '%s'", name->s_name); + } + else if (complain) + loud_error(complain, "no such array '%s'", name->s_name); + } + return (0); +} + +static void vefl_tick(t_vefl *vp) +{ + if (vp->v_name && vp->v_name != &s_ + /* Check if an array has not been deleted + (e.g. during patch closing sequence). */ + && (vp->v_garray = + (t_garray *)pd_findbyclass(vp->v_name, garray_class))) + { + vp->v_glist = fragile_garray_glist(vp->v_garray); + garray_redraw(vp->v_garray); + } + vp->v_clockset = 0; + vp->v_updtime = clock_getsystime(); +} + +t_vefl *vefl_placement_new(t_vefl *vp, t_symbol *name, + int writable, t_glist *gl, t_garray *arr) +{ + if (sizeof(t_word) != sizeof(t_float)) + { + bug("vefl_new: sizeof(t_word) != sizeof(t_float)"); + return (0); + } + if (!vp) + { + if (!(vp = getbytes(sizeof(*vp)))) + return (0); + vp->v_autoalloc = 1; + } + else vp->v_autoalloc = 0; + vp->v_name = name; + if (writable) + { + vp->v_updtime = clock_getsystime(); + vp->v_clock = clock_new(vp, (t_method)vefl_tick); + vp->v_clockset = 0; + } + else vp->v_clock = 0; + vp->v_glist = gl; + vp->v_garray = arr; + vp->v_size = 0; + vp->v_data = 0; + vp->v_type = &s_float; + if (!arr && name && name != &s_) + { + vp->v_garray = (t_garray *)pd_findbyclass(name, garray_class); + vp->v_glist = vp->v_garray ? fragile_garray_glist(vp->v_garray) : 0; + } + if (vp->v_garray + && !garray_getfloatarray(vp->v_garray, &vp->v_size, &vp->v_data)) + { + vp->v_glist = 0; + vp->v_garray = 0; + vp->v_type = 0; /* template mismatch */ + } + return (vp); +} + +t_vefl *vefl_new(t_symbol *name, int writable, t_glist *gl, t_garray *arr) +{ + return (vefl_placement_new(0, name, writable, gl, arr)); +} + +void vefl_free(t_vefl *vp) +{ + if (vp->v_clock) clock_free(vp->v_clock); + if (vp->v_autoalloc) freebytes(vp, sizeof(*vp)); +} + +/* LATER handle yonset */ +int vefl_renew(t_vefl *vp, t_symbol *name, t_pd *complain) +{ + if (!name || name == &s_) name = vp->v_name; + if (name && name != &s_) + { + vp->v_glist = 0; + /* There are three possible ways: */ +#if 0 + vp->v_name = 0; +#elif 1 /* , do nothing, and */ + vp->v_name = name; +#endif /* LATER check all the cases and decide... */ + if (!(vp->v_garray = (t_garray *)pd_findbyclass(name, garray_class))) + { + if (complain) + loud_error(complain, "no such array '%s'", name->s_name); + } + else if (!garray_getfloatarray(vp->v_garray, &vp->v_size, &vp->v_data)) + { + vp->v_garray = 0; + loud_error(complain, /* always complain */ + "bad template of array '%s'", name->s_name); + } + else + { + vp->v_glist = fragile_garray_glist(vp->v_garray); + vp->v_name = name; + return (1); + } + } + return (0); +} + +void vefl_redraw(t_vefl *vp, float suppresstime) +{ + if (vp->v_clock) /* requests from readers are ignored */ + { + if (suppresstime > 0) + { + double timesince = clock_gettimesince(vp->v_updtime); + if (timesince > suppresstime) + { + clock_unset(vp->v_clock); + vefl_tick(vp); + } + else if (!vp->v_clockset) + { + clock_delay(vp->v_clock, suppresstime - timesince); + vp->v_clockset = 1; + } + } + else { + clock_unset(vp->v_clock); + vefl_tick(vp); + } + } +} + +void vefl_redraw_stop(t_vefl *vp) +{ + if (vp->v_clock) /* requests from readers are ignored */ + { + clock_unset(vp->v_clock); + vp->v_clockset = 0; + } +} + +/* Y-bounds flipped here */ +void vefl_getbounds(t_vefl *vp, t_float *xminp, t_float *yminp, + t_float *xmaxp, t_float *ymaxp) +{ + t_glist *gl = vp->v_glist; + if (gl) + { + *xminp = gl->gl_x1; + *xmaxp = gl->gl_x2; + *yminp = gl->gl_y2; + *ymaxp = gl->gl_y1; + } +} + +/* Y-bounds flipped here */ +void vefl_setbounds(t_vefl *vp, t_float xmin, t_float ymin, + t_float xmax, t_float ymax) +{ + vmess((t_pd *)vp->v_glist, gensym("bounds"), "ffff", + xmin, ymax, xmax, ymin); +} + +void vefl_getrange(t_vefl *vp, t_float *yminp, t_float *ymaxp) +{ + int vsz = vp->v_size; + t_float *vec = vp->v_data; + if (vec && vsz) + { + t_float ymin = SHARED_FLT_MAX, ymax = -SHARED_FLT_MAX; + while (vsz--) + { + if (*vec > ymax) + { + ymax = *vec; + if (ymax < ymin) ymin = ymax; + } + else if (*vec < ymin) ymin = *vec; + vec++; + } + *yminp = ymin; + *ymaxp = ymax; + } +} diff --git a/shared/common/vefl.h b/shared/common/vefl.h new file mode 100644 index 0000000..e470a80 --- /dev/null +++ b/shared/common/vefl.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2001-2003 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 __VEFL_H__ +#define __VEFL_H__ + +typedef struct _vefl +{ + int v_autoalloc; + t_symbol *v_name; + t_glist *v_glist; + t_garray *v_garray; + int v_size; + t_float *v_data; + t_symbol *v_type; + t_clock *v_clock; + int v_clockset; + double v_updtime; +} t_vefl; + +t_float *vefl_get(t_symbol *name, int *vszp, int indsp, t_pd *complain); +t_vefl *vefl_new(t_symbol *name, int writable, t_glist *gl, t_garray *arr); +t_vefl *vefl_placement_new(t_vefl *vp, t_symbol *name, + int writable, t_glist *gl, t_garray *arr); +void vefl_free(t_vefl *vp); +int vefl_renew(t_vefl *vp, t_symbol *name, t_pd *complain); +void vefl_redraw(t_vefl *vp, float suppresstime); +void vefl_redraw_stop(t_vefl *vp); +void vefl_getbounds(t_vefl *vp, t_float *xminp, t_float *yminp, + t_float *xmaxp, t_float *ymaxp); +void vefl_setbounds(t_vefl *vp, t_float xmin, t_float ymin, + t_float xmax, t_float ymax); +void vefl_getrange(t_vefl *vp, t_float *yminp, t_float *ymaxp); + +#endif diff --git a/shared/hammer/Makefile b/shared/hammer/Makefile new file mode 100644 index 0000000..5dcb2c8 --- /dev/null +++ b/shared/hammer/Makefile @@ -0,0 +1,4 @@ +ROOT_DIR = ../.. +include $(ROOT_DIR)/Makefile.common + +all: $(OBJECTS) diff --git a/shared/hammer/Makefile.objects b/shared/hammer/Makefile.objects new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/shared/hammer/Makefile.objects diff --git a/shared/hammer/Makefile.sources b/shared/hammer/Makefile.sources new file mode 100644 index 0000000..5a6d99a --- /dev/null +++ b/shared/hammer/Makefile.sources @@ -0,0 +1,4 @@ +OTHER_SOURCES = \ +file.c \ +gui.c \ +tree.c diff --git a/shared/hammer/file.c b/shared/hammer/file.c new file mode 100644 index 0000000..6a94c92 --- /dev/null +++ b/shared/hammer/file.c @@ -0,0 +1,402 @@ +/* Copyright (c) 2002-2003 krzYszcz and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* The three uses of the 'hammerfile' proxy class are: + 1. providing `embedding' facility -- storing master object's state + in a .pd file, + 2. encapsulating openpanel/savepanel management, + 3. extending the gui of Pd with a simple text editor window. + + A master class which needs embedding feature (like coll), passes + a nonzero flag to the hammerfile setup routine, and a nonzero embedfn + function pointer to the hammerfile constructor. If a master needs + access to the panels (like collcommon), then it passes nonzero readfn + and/or writefn callback pointers to the constructor. A master which has + an associated text editor, AND wants to update object's state after + edits, passes a nonzero updatefn callback in a call to the constructor. */ + +#include <stdio.h> +#include <string.h> +#include "m_pd.h" +#include "g_canvas.h" +/* need this for t_class::c_wb field access, LATER find a better way... */ +#include "unstable/pd_imp.h" +#include "hammer/file.h" + +static t_class *hammerfile_class = 0; +static t_hammerfile *hammerfile_proxies; +static t_symbol *ps__C; + +static t_hammerfile *hammerfile_getproxy(t_pd *master) +{ + t_hammerfile *f; + for (f = hammerfile_proxies; f; f = f->f_next) + if (f->f_master == master) + return (f); + return (0); +} + +/* FIXME somehow plug the "save changes" dialog into close-by-wm */ +/* FIXME dirty condition */ +static void hammereditor_guidefs(void) +{ + sys_gui("proc hammereditor_open {name geometry title} {\n"); + sys_gui(" if {[winfo exists $name]} {\n"); + sys_gui(" $name.text delete 1.0 end\n"); + sys_gui(" } else {\n"); + sys_gui(" toplevel $name\n"); + sys_gui(" wm title $name $title\n"); + sys_gui(" wm geometry $name $geometry\n"); + sys_gui(" text $name.text -relief raised -bd 2 \\\n"); + sys_gui(" -font -*-courier-medium--normal--12-* \\\n"); + sys_gui(" -yscrollcommand \"$name.scroll set\" -background lightgrey\n"); + sys_gui(" scrollbar $name.scroll -command \"$name.text yview\"\n"); + sys_gui(" pack $name.scroll -side right -fill y\n"); + sys_gui(" pack $name.text -side left -fill both -expand 1\n"); + sys_gui(" }\n"); + sys_gui("}\n"); + + sys_gui("proc hammereditor_doclose {name} {\n"); + sys_gui(" destroy $name\n"); + sys_gui("}\n"); + + sys_gui("proc hammereditor_append {name contents} {\n"); + sys_gui(" if {[winfo exists $name]} {\n"); + sys_gui(" $name.text insert end $contents\n"); + sys_gui(" }\n"); + sys_gui("}\n"); + + /* FIXME make it more reliable */ + sys_gui("proc hammereditor_send {name} {\n"); + sys_gui(" if {[winfo exists $name]} {\n"); + sys_gui(" set ii [$name.text index [concat end - 1 lines]]\n"); + sys_gui(" pd [concat miXed$name clear \\;]\n"); + sys_gui(" for {set i 1} \\\n"); + sys_gui(" {[$name.text compare $i.end < $ii]} \\\n"); + sys_gui(" {incr i 1} {\n"); + sys_gui(" set lin [$name.text get $i.0 $i.end]\n"); + sys_gui(" if {$lin != \"\"} {\n"); + /* LATER rethink semi/comma mapping */ + sys_gui(" regsub -all \\; $lin \" _semi_ \" tmplin\n"); + sys_gui(" regsub -all \\, $tmplin \" _comma_ \" lin\n"); + sys_gui(" pd [concat miXed$name addline $lin \\;]\n"); + sys_gui(" }\n"); + sys_gui(" }\n"); + sys_gui(" pd [concat miXed$name end \\;]\n"); + sys_gui(" }\n"); + sys_gui("}\n"); + + sys_gui("proc hammereditor_close {name ask} {\n"); + sys_gui(" if {[winfo exists $name]} {\n"); + sys_gui(" set dirty $ask\n"); /* FIXME */ + sys_gui(" if {$dirty == 0} {hammereditor_doclose $name} else {\n"); + sys_gui(" set title [wm title $name]\n"); + sys_gui(" set answer [tk_messageBox \\-type yesnocancel \\\n"); + sys_gui(" \\-icon question \\\n"); + sys_gui(" \\-message [concat Save changes to $title?]]\n"); + sys_gui(" if {$answer == \"yes\"} {hammereditor_send $name}\n"); + sys_gui(" if {$answer != \"cancel\"} {hammereditor_doclose $name}\n"); + sys_gui(" }\n"); + sys_gui(" }\n"); + sys_gui("}\n"); +} + +void hammereditor_open(t_hammerfile *f, char *title) +{ + if (!title) title = class_getname(*f->f_master); + sys_vgui("hammereditor_open .%x %dx%d {%s}\n", (int)f, 600, 340, title); +} + +static void hammereditor_tick(t_hammerfile *f) +{ + sys_vgui("hammereditor_close .%x %d\n", (int)f, 1); +} + +void hammereditor_close(t_hammerfile *f, int ask) +{ + if (ask) + /* hack: deferring modal dialog creation in order to allow for + a message box redraw to happen -- LATER investigate */ + clock_delay(f->f_editorclock, 0); + else + sys_vgui("hammereditor_close .%x %d\n", (int)f, 0); +} + +void hammereditor_append(t_hammerfile *f, char *contents) +{ + if (!contents) contents = ""; + sys_vgui("hammereditor_append .%x {%s}\n", (int)f, contents); +} + +static void hammereditor_clear(t_hammerfile *f) +{ + if (f->f_editorfn) + { + if (f->f_binbuf) + binbuf_clear(f->f_binbuf); + else + f->f_binbuf = binbuf_new(); + } +} + +static void hammereditor_addline(t_hammerfile *f, + t_symbol *s, int ac, t_atom *av) +{ + if (f->f_editorfn) + { + int i; + t_atom *ap; + for (i = 0, ap = av; i < ac; i++, ap++) + { + if (ap->a_type == A_SYMBOL) + { + /* LATER rethink semi/comma mapping */ + if (!strcmp(ap->a_w.w_symbol->s_name, "_semi_")) + SETSEMI(ap); + else if (!strcmp(ap->a_w.w_symbol->s_name, "_comma_")) + SETCOMMA(ap); + } + } + binbuf_add(f->f_binbuf, ac, av); + } +} + +static void hammereditor_end(t_hammerfile *f) +{ + if (f->f_editorfn) + { + (*f->f_editorfn)(f->f_master, 0, binbuf_getnatom(f->f_binbuf), + binbuf_getvec(f->f_binbuf)); + binbuf_clear(f->f_binbuf); + } +} + +static void hammerpanel_guidefs(void) +{ + sys_gui("proc hammerpanel_save {target inidir inifile} {\n"); + sys_gui(" if {$inifile != \"\"} {\n"); + sys_gui(" set filename [tk_getSaveFile \\\n"); + sys_gui(" -initialdir $inidir -initialfile $inifile]\n"); + sys_gui(" } else {\n"); + sys_gui(" set filename [tk_getSaveFile]\n"); + sys_gui(" }\n"); + sys_gui(" if {$filename != \"\"} {\n"); + sys_gui(" pd [concat $target symbol [pdtk_enquote $filename] \\;]\n"); + sys_gui(" }\n"); + sys_gui("}\n"); +} + +static void hammerpanel_symbol(t_hammerfile *f, t_symbol *s) +{ + if (s && s != &s_ && f->f_panelfn) + (*f->f_panelfn)(f->f_master, s, 0, 0); +} + +static void hammerpanel_tick(t_hammerfile *f) +{ + if (f->f_savepanel) + sys_vgui("pdtk_openpanel %s\n", f->f_bindname->s_name); + else + sys_vgui("hammerpanel_save %s {%s} {%s}\n", f->f_bindname->s_name, + f->f_inidir->s_name, f->f_inifile->s_name); +} + +/* these are hacks: deferring modal dialog creation in order to allow for + a message box redraw to happen -- LATER investigate */ +void hammerpanel_open(t_hammerfile *f) +{ + clock_delay(f->f_panelclock, 0); +} + +void hammerpanel_save(t_hammerfile *f, t_symbol *inidir, t_symbol *inifile) +{ + /* LATER ask if we can rely on s_ pointing to "" */ + f->f_savepanel->f_inidir = (inidir ? inidir : &s_); + f->f_savepanel->f_inifile = (inifile ? inifile : &s_); + clock_delay(f->f_savepanel->f_panelclock, 0); +} + +/* Currently embeddable hammer classes do not use the 'saveto' method. + In order to use it, any embeddable class would have to add a creation + method to pd_canvasmaker -- then saving could be done with a 'proper' + sequence: #N <master> <args>; #X <whatever>; ...; #X restore <x> <y>; + However, this works only for -lib externals. So, we choose a sequence: + #X obj <x> <y> <master> <args>; #C <whatever>; ...; #C restore; + Since the first message in this sequence is a valid creation message + on its own, we have to distinguish loading from a .pd file, and other + cases (editing). */ + +static void hammerembed_gc(t_pd *x, t_symbol *s, int expected) +{ + t_pd *garbage; + int count = 0; + while (garbage = pd_findbyclass(s, *x)) pd_unbind(garbage, s), count++; + if (count != expected) + bug("hammerembed_gc (%d garbage bindings)", count); +} + +static void hammerembed_restore(t_pd *master) +{ + hammerembed_gc(master, ps__C, 1); +} + +void hammerembed_save(t_gobj *master, t_binbuf *bb) +{ + t_hammerfile *f = hammerfile_getproxy((t_pd *)master); + t_text *t = (t_text *)master; + binbuf_addv(bb, "ssii", &s__X, gensym("obj"), + (int)t->te_xpix, (int)t->te_ypix); + binbuf_addbinbuf(bb, t->te_binbuf); + binbuf_addsemi(bb); + if (f && f->f_embedfn) + (*f->f_embedfn)(f->f_master, bb, ps__C); + binbuf_addv(bb, "ss;", ps__C, gensym("restore")); +} + +int hammerfile_ismapped(t_hammerfile *f) +{ + return (f->f_canvas->gl_mapped); +} + +int hammerfile_isloading(t_hammerfile *f) +{ + return (f->f_canvas->gl_loading); +} + +/* LATER find a better way */ +int hammerfile_ispasting(t_hammerfile *f) +{ + int result = 0; + t_canvas *cv = f->f_canvas; + if (!cv->gl_loading) + { + t_pd *z = s__X.s_thing; + if (z == (t_pd *)cv) + { + pd_popsym(z); + if (s__X.s_thing == (t_pd *)cv) result = 1; + pd_pushsym(z); + } + else if (z) result = 1; + } +#if 0 + if (result) post("pasting"); +#endif + return (result); +} + +void hammerfile_free(t_hammerfile *f) +{ + t_hammerfile *prev, *next; + hammereditor_close(f, 0); + if (f->f_embedfn) + /* just in case of missing 'restore' */ + hammerembed_gc(f->f_master, ps__C, 0); + if (f->f_savepanel) + { + pd_unbind((t_pd *)f->f_savepanel, f->f_savepanel->f_bindname); + pd_free((t_pd *)f->f_savepanel); + } + if (f->f_bindname) pd_unbind((t_pd *)f, f->f_bindname); + if (f->f_panelclock) clock_free(f->f_panelclock); + if (f->f_editorclock) clock_free(f->f_editorclock); + for (prev = 0, next = hammerfile_proxies; + next; prev = next, next = next->f_next) + if (next == f) + break; + if (prev) + prev->f_next = f->f_next; + else if (f == hammerfile_proxies) + hammerfile_proxies = f->f_next; + pd_free((t_pd *)f); +} + +t_hammerfile *hammerfile_new(t_pd *master, t_hammerembedfn embedfn, + t_hammerfilefn readfn, t_hammerfilefn writefn, + t_hammerfilefn updatefn) +{ + t_hammerfile *result = (t_hammerfile *)pd_new(hammerfile_class); + result->f_master = master; + result->f_next = hammerfile_proxies; + hammerfile_proxies = result; + if (!(result->f_canvas = canvas_getcurrent())) + { + bug("hammerfile_new: out of context"); + return (result); + } + + /* 1. embedding */ + if (result->f_embedfn = embedfn) + { + /* just in case of missing 'restore' */ + hammerembed_gc(master, ps__C, 0); + if (hammerfile_isloading(result) || hammerfile_ispasting(result)) + pd_bind(master, ps__C); + } + + /* 2. the panels */ + if (readfn || writefn) + { + t_hammerfile *f; + char buf[64]; + sprintf(buf, "miXed.%x", (int)result); + result->f_bindname = gensym(buf); + pd_bind((t_pd *)result, result->f_bindname); + result->f_panelfn = readfn; + result->f_panelclock = clock_new(result, (t_method)hammerpanel_tick); + f = (t_hammerfile *)pd_new(hammerfile_class); + f->f_master = master; + sprintf(buf, "miXed.%x", (int)f); + f->f_bindname = gensym(buf); + pd_bind((t_pd *)f, f->f_bindname); + f->f_panelfn = writefn; + f->f_panelclock = clock_new(f, (t_method)hammerpanel_tick); + result->f_savepanel = f; + } + else result->f_savepanel = 0; + + /* 3. editor */ + if (result->f_editorfn = updatefn) + { + result->f_editorclock = clock_new(result, (t_method)hammereditor_tick); + if (!result->f_bindname) + { + char buf[64]; + sprintf(buf, "miXed.%x", (int)result); + result->f_bindname = gensym(buf); + pd_bind((t_pd *)result, result->f_bindname); + } + } + return (result); +} + +void hammerfile_setup(t_class *c, int embeddable) +{ + if (embeddable) + { + t_widgetbehavior *newwb = getbytes(sizeof(*newwb)); /* never freed */ + *newwb = *c->c_wb; + newwb->w_savefn = hammerembed_save; + class_setwidget(c, newwb); + class_addmethod(c, (t_method)hammerembed_restore, + gensym("restore"), 0); + } + if (!hammerfile_class) + { + ps__C = gensym("#C"); + hammerfile_class = class_new(gensym("_hammerfile"), 0, 0, + sizeof(t_hammerfile), + CLASS_PD | CLASS_NOINLET, 0); + class_addsymbol(hammerfile_class, hammerpanel_symbol); + class_addmethod(hammerfile_class, (t_method)hammereditor_clear, + gensym("clear"), 0); + class_addmethod(hammerfile_class, (t_method)hammereditor_addline, + gensym("addline"), A_GIMME, 0); + class_addmethod(hammerfile_class, (t_method)hammereditor_end, + gensym("end"), 0); + /* LATER find a way of ensuring that these are not defined yet... */ + hammereditor_guidefs(); + hammerpanel_guidefs(); + } +} diff --git a/shared/hammer/file.h b/shared/hammer/file.h new file mode 100644 index 0000000..d0f6526 --- /dev/null +++ b/shared/hammer/file.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2002-2003 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 __HAMMERFILE_H__ +#define __HAMMERFILE_H__ + +typedef void (*t_hammerfilefn)(t_pd *, t_symbol *, int, t_atom *); +typedef void (*t_hammerembedfn)(t_pd *, t_binbuf *, t_symbol *); + +typedef struct _hammerfile +{ + t_pd f_pd; + t_pd *f_master; + t_canvas *f_canvas; + t_symbol *f_bindname; + t_symbol *f_inidir; + t_symbol *f_inifile; + t_hammerfilefn f_panelfn; + t_hammerfilefn f_editorfn; + t_hammerembedfn f_embedfn; + t_binbuf *f_binbuf; + t_clock *f_panelclock; + t_clock *f_editorclock; + struct _hammerfile *f_savepanel; + struct _hammerfile *f_next; +} t_hammerfile; + +void hammereditor_open(t_hammerfile *f, char *title); +void hammereditor_close(t_hammerfile *f, int ask); +void hammereditor_append(t_hammerfile *f, char *contents); +void hammerpanel_open(t_hammerfile *f); +void hammerpanel_save(t_hammerfile *f, t_symbol *inidir, t_symbol *inifile); +int hammerfile_ismapped(t_hammerfile *f); +int hammerfile_isloading(t_hammerfile *f); +int hammerfile_ispasting(t_hammerfile *f); +void hammerfile_free(t_hammerfile *f); +t_hammerfile *hammerfile_new(t_pd *master, t_hammerembedfn embedfn, + t_hammerfilefn readfn, t_hammerfilefn writefn, + t_hammerfilefn updatefn); +void hammerfile_setup(t_class *c, int embeddable); + +#endif diff --git a/shared/hammer/gui.c b/shared/hammer/gui.c new file mode 100644 index 0000000..5e98ff8 --- /dev/null +++ b/shared/hammer/gui.c @@ -0,0 +1,438 @@ +/* Copyright (c) 2003 krzYszcz and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* FIXME use guiconnect */ + +#include <stdio.h> +#include "m_pd.h" +#include "g_canvas.h" +#include "hammer/gui.h" + +//#define HAMMERGUI_DEBUG + +static t_class *hammergui_class = 0; +static t_hammergui *sink = 0; +static t_symbol *ps__up; +static t_symbol *ps__focus; +static t_symbol *ps__vised; + +static void hammergui_anything(t_hammergui *snk, + t_symbol *s, int ac, t_atom *av) +{ + /* Dummy method, filtering out messages from gui to the masters. This is + needed in order to keep Pd's message system happy in a ``gray period'' + -- after last master is unbound, and before gui bindings are cleared. */ +#ifdef HAMMERGUI_DEBUG + startpost("%s", s->s_name); + postatom(ac, av); + endpost(); +#endif +} + +/* filtering out redundant "_up" messages */ +static void hammergui__up(t_hammergui *snk, t_floatarg f) +{ +#ifdef HAMMERGUI_DEBUG + post("_up %g", f); +#endif + if ((int)f) + { + if (!snk->g_up) + { + snk->g_up = 1; + if (snk->g_mouse->s_thing) + { + t_atom at; + SETFLOAT(&at, 1); + pd_typedmess(snk->g_mouse->s_thing, ps__up, 1, &at); + } + } + } + else + { + if (snk->g_up) + { + snk->g_up = 0; + if (snk->g_mouse->s_thing) + { + t_atom at; + SETFLOAT(&at, 0); + pd_typedmess(snk->g_mouse->s_thing, ps__up, 1, &at); + } + } + } +} + +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); +#endif + if (snk->g_focus->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); + } +} + +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); +#endif + if (snk->g_vised->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); + } +#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("bindtags %s {hammertag %s Canvas . all}\n", + s->s_name, s->s_name); +#endif +} + + +static void hammergui_dobindmouse(t_hammergui *snk) +{ +#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 <<hammerdown>> {pd [concat %s _up 0 \\;]}\n", + snk->g_gui->s_name); + sys_vgui("bind hammertag <<hammerup>> {pd [concat %s _up 1 \\;]}\n", + snk->g_gui->s_name); +#endif + sys_vgui("bind all <<hammerdown>> {pd [concat %s _up 0 \\;]}\n", + snk->g_gui->s_name); + sys_vgui("bind all <<hammerup>> {pd [concat %s _up 1 \\;]}\n", + snk->g_gui->s_name); +} + +static void hammergui__remouse(t_hammergui *snk) +{ + if (snk->g_mouse->s_thing) + { + /* if a new master was bound in a gray period, we need to + restore gui bindings */ +#if 1 + post("rebinding mouse..."); +#endif + hammergui_dobindmouse(snk); + } +} + +static void hammergui_dobindfocus(t_hammergui *snk) +{ + sys_vgui("bind Canvas <<hammerfocusin>> \ + {pd [concat %s _focus %%W 1 \\;]}\n", snk->g_gui->s_name); + sys_vgui("bind Canvas <<hammerfocusout>> \ + {pd [concat %s _focus %%W 0 \\;]}\n", snk->g_gui->s_name); +} + +static void hammergui__refocus(t_hammergui *snk) +{ + if (snk->g_focus->s_thing) + { + /* if a new master was bound in a gray period, we need to + restore gui bindings */ +#if 1 + post("rebinding focus..."); +#endif + hammergui_dobindfocus(snk); + } +} + +static void hammergui_dobindvised(t_hammergui *snk) +{ + sys_vgui("bind Canvas <<hammervised>> \ + {pd [concat %s _vised %%W 1 \\;]}\n", snk->g_gui->s_name); + sys_vgui("bind Canvas <<hammerunvised>> \ + {pd [concat %s _vised %%W 0 \\;]}\n", snk->g_gui->s_name); +} + +static void hammergui__revised(t_hammergui *snk) +{ + if (snk->g_vised->s_thing) + { + /* if a new master was bound in a gray period, we need to + restore gui bindings */ +#if 1 + post("rebinding vised events..."); +#endif + hammergui_dobindvised(snk); + } +} + +static void hammergui_setup(void) +{ + hammergui_class = class_new(gensym("_hammergui"), 0, 0, + sizeof(t_hammergui), + CLASS_PD | CLASS_NOINLET, 0); + class_addanything(hammergui_class, hammergui_anything); + class_addmethod(hammergui_class, (t_method)hammergui__remouse, + gensym("_remouse"), 0); + class_addmethod(hammergui_class, (t_method)hammergui__refocus, + 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); + + sys_gui("proc hammergui_remouse {} {\n"); + sys_gui(" bind all <<hammerdown>> {}\n"); + sys_gui(" bind all <<hammerup>> {}\n"); + sys_gui(" pd [concat #hammergui _remouse \\;]\n"); + sys_gui("}\n"); + + sys_gui("proc hammergui_mousexy {target} {\n"); + sys_gui(" set x [winfo pointerx .]\n"); + sys_gui(" set y [winfo pointery .]\n"); + sys_gui(" pd [concat #hammermouse $target $x $y \\;]\n"); + sys_gui("}\n"); + + /* visibility hack for msw, LATER rethink */ + sys_gui("global hammergui_ispolling\n"); + sys_gui("global hammergui_x\n"); + sys_gui("global hammergui_y\n"); + sys_gui("set hammergui_ispolling 0\n"); + sys_gui("set hammergui_x 0\n"); + sys_gui("set hammergui_y 0\n"); + + sys_gui("proc hammergui_poll {} {\n"); + sys_gui(" global hammergui_ispolling\n"); + sys_gui(" global hammergui_x\n"); + sys_gui(" global hammergui_y\n"); + sys_gui(" if {$hammergui_ispolling == 1} {\n"); + sys_gui(" set x [winfo pointerx .]\n"); + sys_gui(" set y [winfo pointery .]\n"); + sys_gui(" if {$hammergui_x != $x || $hammergui_y != $y} {\n"); + sys_gui(" pd [concat #hammermouse _poll $x $y \\;]\n"); + sys_gui(" set hammergui_x $x\n"); + sys_gui(" set hammergui_y $y\n"); + sys_gui(" }\n"); + sys_gui(" after 50 hammergui_poll\n"); + sys_gui(" }\n"); + sys_gui("}\n"); + + sys_gui("proc hammergui_refocus {} {\n"); + sys_gui(" bind Canvas <<hammerfocusin>> {}\n"); + sys_gui(" bind Canvas <<hammerfocusout>> {}\n"); + sys_gui(" pd [concat #hammergui _refocus \\;]\n"); + sys_gui("}\n"); + + sys_gui("proc hammergui_revised {} {\n"); + sys_gui(" bind Canvas <<hammervised>> {}\n"); + sys_gui(" bind Canvas <<hammerunvised>> {}\n"); + sys_gui(" pd [concat #hammergui _revised \\;]\n"); + sys_gui("}\n"); +} + +static int hammergui_validate(int dosetup) +{ + if (dosetup) + { + if (!hammergui_class) hammergui_setup(); + if (!sink) + { + sink = (t_hammergui *)pd_new(hammergui_class); + sink->g_gui = gensym("#hammergui"); + pd_bind((t_pd *)sink, sink->g_gui); + } + } + if (hammergui_class && sink) + return (1); + else + { + bug("hammergui_validate"); + return (0); + } +} + +static int hammergui_mousevalidate(int dosetup) +{ + if (dosetup && !sink->g_mouse) + { + sink->g_mouse = gensym("#hammermouse"); + sys_gui("event add <<hammerdown>> <ButtonPress>\n"); + sys_gui("event add <<hammerup>> <ButtonRelease>\n"); + } + if (sink->g_mouse) + return (1); + else + { + bug("hammergui_mousevalidate"); + return (0); + } +} + +static int hammergui_pollvalidate(int dosetup) +{ + if (dosetup && !sink->g_poll) + { + sink->g_poll = gensym("#hammerpoll"); + pd_bind((t_pd *)sink, sink->g_poll); /* never unbound */ + } + if (sink->g_poll) + return (1); + else + { + bug("hammergui_pollvalidate"); + return (0); + } +} + +static int hammergui_focusvalidate(int dosetup) +{ + if (dosetup && !sink->g_focus) + { + sink->g_focus = gensym("#hammerfocus"); + sys_gui("event add <<hammerfocusin>> <FocusIn>\n"); + sys_gui("event add <<hammerfocusout>> <FocusOut>\n"); + } + if (sink->g_focus) + return (1); + else + { + bug("hammergui_focusvalidate"); + return (0); + } +} + +static int hammergui_visedvalidate(int dosetup) +{ + if (dosetup && !sink->g_vised) + { + sink->g_vised = gensym("#hammervised"); + /* subsequent map events have to be filtered out at the caller's side, + LATER investigate */ + sys_gui("event add <<hammervised>> <Map>\n"); + sys_gui("event add <<hammerunvised>> <Destroy>\n"); + } + if (sink->g_vised) + return (1); + else + { + bug("hammergui_visedvalidate"); + return (0); + } +} + +void hammergui_bindmouse(t_pd *master) +{ + hammergui_validate(1); + hammergui_mousevalidate(1); + if (!sink->g_mouse->s_thing) + hammergui_dobindmouse(sink); + pd_bind(master, sink->g_mouse); +} + +void hammergui_unbindmouse(t_pd *master) +{ + if (hammergui_validate(0) && hammergui_mousevalidate(0) + && sink->g_mouse->s_thing) + { + pd_unbind(master, sink->g_mouse); + if (!sink->g_mouse->s_thing) + sys_gui("hammergui_remouse\n"); + } + else bug("hammergui_unbindmouse"); +} + +void hammergui_mousexy(t_symbol *s) +{ + if (hammergui_validate(0)) + sys_vgui("hammergui_mousexy %s\n", s->s_name); +} + +void hammergui_willpoll(void) +{ + hammergui_validate(1); + hammergui_pollvalidate(1); +} + +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); + if (doinit) + { + /* visibility hack for msw, LATER rethink */ + sys_gui("global hammergui_ispolling\n"); + sys_gui("set hammergui_ispolling 1\n"); + sys_gui("hammergui_poll\n"); + } + } +} + +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) + { + sys_gui("after cancel hammergui_poll\n"); + /* visibility hack for msw, LATER rethink */ + sys_gui("global hammergui_ispolling\n"); + sys_gui("set hammergui_ispolling 0\n"); + } + } +} + +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); +} + +void hammergui_unbindfocus(t_pd *master) +{ + if (hammergui_validate(0) && hammergui_focusvalidate(0) + && sink->g_focus->s_thing) + { + pd_unbind(master, sink->g_focus); + if (!sink->g_focus->s_thing) + sys_gui("hammergui_refocus\n"); + } + else bug("hammergui_unbindfocus"); +} + +void hammergui_bindvised(t_pd *master) +{ + hammergui_validate(1); + hammergui_visedvalidate(1); + if (!sink->g_vised->s_thing) + hammergui_dobindvised(sink); + pd_bind(master, sink->g_vised); +} + +void hammergui_unbindvised(t_pd *master) +{ + if (hammergui_validate(0) && hammergui_visedvalidate(0) + && sink->g_vised->s_thing) + { + pd_unbind(master, sink->g_vised); + if (!sink->g_vised->s_thing) + sys_gui("hammergui_revised\n"); + } + else bug("hammergui_unbindvised"); +} diff --git a/shared/hammer/gui.h b/shared/hammer/gui.h new file mode 100644 index 0000000..13afd0a --- /dev/null +++ b/shared/hammer/gui.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2003 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 __HAMMERGUI_H__ +#define __HAMMERGUI_H__ + +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_hammergui; + +void hammergui_bindmouse(t_pd *master); +void hammergui_unbindmouse(t_pd *master); +void hammergui_mousexy(t_symbol *s); +void hammergui_willpoll(void); +void hammergui_startpolling(t_pd *master); +void hammergui_stoppolling(t_pd *master); +void hammergui_bindfocus(t_pd *master); +void hammergui_unbindfocus(t_pd *master); +void hammergui_bindvised(t_pd *master); +void hammergui_unbindvised(t_pd *master); + +#endif diff --git a/shared/hammer/tree.c b/shared/hammer/tree.c new file mode 100644 index 0000000..549dd09 --- /dev/null +++ b/shared/hammer/tree.c @@ -0,0 +1,482 @@ +/* Copyright (c) 2003 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 "hammer/tree.h" + +/* Since there is no sentinel node, the deletion routine has to have + a few extra checks. LATER rethink. */ + +/* LATER freelist */ + +#ifdef HAMMERTREE_DEBUG +/* returns bh or 0 if failed */ +static int hammernode_verify(t_hammernode *np) +{ + if (np) + { + int bhl, bhr; + if (((bhl = hammernode_verify(np->n_left)) == 0) || + ((bhr = hammernode_verify(np->n_right)) == 0)) + return (0); + if (bhl != bhr) + { + /* failure: two paths rooted in the same node + contain different number of black nodes */ + bug("hammernode_verify: not balanced"); + return (0); + } + if (np->n_black) + return (bhl + 1); + else + { + if ((np->n_left && !np->n_left->n_black) || + (np->n_right && !np->n_right->n_black)) + { + bug("hammernode_verify: adjacent red nodes"); + return (0); + } + return (bhl); + } + } + else return (1); +} + +/* returns bh or 0 if failed */ +static int hammertree_verify(t_hammertree *tree) +{ + return (hammernode_verify(tree->t_root)); +} + +static void hammernode_post(t_hammernode *np) +{ + startpost("%d %g %d (", np->n_index, np->n_value, np->n_black); + if (np->n_left) + startpost("%d, ", np->n_left->n_index); + else + startpost("nul, "); + if (np->n_right) + post("%d)", np->n_right->n_index); + else + post("nul)"); +} + +/* this is a standard stackless traversal, not the best one, obviously... + (used only for debugging) */ +static int hammertree_traverse(t_hammertree *tree, int postit) +{ + t_hammernode *np = tree->t_root; + int count = 0; + while (np) + { + t_hammernode *prev = np->n_left; + if (prev) + { + while (prev->n_right && prev->n_right != np) prev = prev->n_right; + if (prev->n_right) + { + prev->n_right = 0; + if (postit) hammernode_post(np); + count++; + np = np->n_right; + } + else + { + prev->n_right = np; + np = np->n_left; + } + } + else + { + if (postit) hammernode_post(np); + count++; + np = np->n_right; + } + } + return (count); +} + +static int hammernode_height(t_hammernode *np) +{ + if (np) + { + int lh = hammernode_height(np->n_left); + int rh = hammernode_height(np->n_right); + return (lh > rh ? lh + 1 : rh + 1); + } + else return (0); +} + +void hammertree_debug(t_hammertree *tree, int level) +{ + t_hammernode *np; + int count; + post("------------------------"); + count = hammertree_traverse(tree, level); + if (level > 1) + { + post("***"); + for (np = tree->t_last; np; np = np->n_prev) + startpost("%d ", np->n_index); + endpost(); + } + post("count %d, height %d, root %d:", + count, hammernode_height(tree->t_root), + (tree->t_root ? tree->t_root->n_index : 0)); + post("...verified (black-height is %d)", hammertree_verify(tree)); + post("------------------------"); +} +#endif + +/* assuming that target node (np->n_right) exists */ +static void hammertree_lrotate(t_hammertree *tree, t_hammernode *np) +{ + t_hammernode *target = np->n_right; + if (np->n_right = target->n_left) + np->n_right->n_parent = np; + if (!(target->n_parent = np->n_parent)) + tree->t_root = target; + else if (np == np->n_parent->n_left) + np->n_parent->n_left = target; + else + np->n_parent->n_right = target; + target->n_left = np; + np->n_parent = target; +} + +/* assuming that target node (np->n_left) exists */ +static void hammertree_rrotate(t_hammertree *tree, t_hammernode *np) +{ + t_hammernode *target = np->n_left; + if (np->n_left = target->n_right) + np->n_left->n_parent = np; + if (!(target->n_parent = np->n_parent)) + tree->t_root = target; + else if (np == np->n_parent->n_left) + np->n_parent->n_left = target; + else + np->n_parent->n_right = target; + target->n_right = 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) +{ + t_hammernode *np, *parent, *result; + if (!(np = tree->t_root)) + { + if (!(np = getbytes(sizeof(*np)))) + return (0); + np->n_index = ndx; + 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)))) + return (0); + np->n_index = ndx; + np->n_parent = parent; + if (ndx < parent->n_index) + { + parent->n_left = np; + /* update the auxiliary linked list structure */ + np->n_next = parent; + if (np->n_prev = parent->n_prev) + np->n_prev->n_next = np; + else + tree->t_first = np; + parent->n_prev = np; + } + else + { + parent->n_right = np; + /* update the auxiliary linked list structure */ + np->n_prev = parent; + if (np->n_next = parent->n_next) + np->n_next->n_prev = np; + else + tree->t_last = np; + parent->n_next = np; + } + result = np; + + /* balance the tree -- LATER clean this if possible... */ + np->n_black = 0; + while (np != tree->t_root && !np->n_parent->n_black) + { + t_hammernode *uncle; + /* np->n_parent->n_parent exists (we always paint root node in black) */ + if (np->n_parent == np->n_parent->n_parent->n_left) + { + uncle = np->n_parent->n_parent->n_right; + if (!uncle /* (sentinel not used) */ + || uncle->n_black) + { + if (np == np->n_parent->n_right) + { + np = np->n_parent; + hammertree_lrotate(tree, np); + } + np->n_parent->n_black = 1; + np->n_parent->n_parent->n_black = 0; + hammertree_rrotate(tree, np->n_parent->n_parent); + } + else + { + np->n_parent->n_black = 1; + uncle->n_black = 1; + np = np->n_parent->n_parent; + np->n_black = 0; + } + } + else + { + uncle = np->n_parent->n_parent->n_left; + if (!uncle /* (sentinel not used) */ + || uncle->n_black) + { + if (np == np->n_parent->n_left) + { + np = np->n_parent; + hammertree_rrotate(tree, np); + } + np->n_parent->n_black = 1; + np->n_parent->n_parent->n_black = 0; + hammertree_lrotate(tree, np->n_parent->n_parent); + } + else + { + np->n_parent->n_black = 1; + uncle->n_black = 1; + np = np->n_parent->n_parent; + np->n_black = 0; + } + } + } + tree->t_root->n_black = 1; + return (result); +} + +/* assuming that requested node exists */ +void hammertree_delete(t_hammertree *tree, t_hammernode *np) +{ + 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) + { + 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; + /* 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; + } + 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; + else + tree->t_first = gone->n_next; + if (gone->n_next) + 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) + { + child->n_parent = 0; + child->n_black = 1; /* LATER rethink */ + } + 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 (gone->n_black) + { + /* balance the tree -- LATER clean this if possible... */ + /* on entry: tree is not empty, parent always exists, child + not necessarily... */ + while (child != tree->t_root && + (!child || /* (sentinel not used) */ + child->n_black)) + { + t_hammernode *other; /* another child of the same parent */ + if (child == parent->n_left) + { + other = parent->n_right; + if (other && /* (sentinel not used) */ + !other->n_black) + { + other->n_black = 1; + parent->n_black = 0; + hammertree_lrotate(tree, parent); + other = parent->n_right; + } + if (!other || /* (sentinel not used) */ + (!other->n_left || other->n_left->n_black) && + (!other->n_right || other->n_right->n_black)) + { + if (other) /* (sentinel not used) */ + other->n_black = 0; + child = parent; + parent = parent->n_parent; + } + else + { + if (!other || /* (sentinel not used) */ + !other->n_right || other->n_right->n_black) + { + if (other) /* (sentinel not used) */ + { + if (other->n_left) other->n_left->n_black = 1; + other->n_black = 0; + hammertree_rrotate(tree, other); + other = parent->n_right; + } + } + if (other) /* (sentinel not used) */ + { + if (other->n_right) other->n_right->n_black = 1; + other->n_black = parent->n_black; + } + parent->n_black = 1; + hammertree_lrotate(tree, parent); + tree->t_root->n_black = 1; /* LATER rethink */ + goto done; + } + } + else /* right child */ + { + other = parent->n_left; + if (other && /* (sentinel not used) */ + !other->n_black) + { + other->n_black = 1; + parent->n_black = 0; + hammertree_rrotate(tree, parent); + other = parent->n_left; + } + if (!other || /* (sentinel not used) */ + (!other->n_left || other->n_left->n_black) && + (!other->n_right || other->n_right->n_black)) + { + if (other) /* (sentinel not used) */ + other->n_black = 0; + child = parent; + parent = parent->n_parent; + } + else + { + if (!other || /* (sentinel not used) */ + !other->n_left || other->n_left->n_black) + { + if (other) /* (sentinel not used) */ + { + if (other->n_right) other->n_right->n_black = 1; + other->n_black = 0; + hammertree_lrotate(tree, other); + other = parent->n_left; + } + } + if (other) /* (sentinel not used) */ + { + if (other->n_left) other->n_left->n_black = 1; + other->n_black = parent->n_black; + } + parent->n_black = 1; + hammertree_rrotate(tree, parent); + tree->t_root->n_black = 1; /* LATER rethink */ + goto done; + } + } + } + if (child) /* (sentinel not used) */ + child->n_black = 1; + } +done: + freebytes(gone, sizeof(*gone)); +#ifdef HAMMERTREE_DEBUG + hammertree_verify(tree); +#endif +} + +t_hammernode *hammertree_search(t_hammertree *tree, int ndx) +{ + t_hammernode *np = tree->t_root; + while (np && np->n_index != ndx) + np = (ndx < np->n_index ? np->n_left : np->n_right); + return (np); +} + +t_hammernode *hammertree_closest(t_hammertree *tree, int ndx, int geqflag) +{ + t_hammernode *np, *parent; + if (!(np = tree->t_root)) + return (0); + do + if (np->n_index == ndx) + return (np); + else + parent = np; + while (np = (ndx < np->n_index ? np->n_left : np->n_right)); + if (geqflag) + return (ndx > parent->n_index ? parent->n_next : parent); + else + return (ndx < parent->n_index ? parent->n_prev : parent); +} + +/* LATER preallocate 'freecount' nodes */ +void hammertree_init(t_hammertree *tree, int freecount) +{ + tree->t_root = tree->t_first = tree->t_last = 0; +} + +/* LATER keep and/or preallocate 'freecount' nodes (if negative, keep all) */ +void hammertree_clear(t_hammertree *tree, int freecount) +{ + t_hammernode *np, *next = tree->t_first; + while (next) + { + np = next; + next = next->n_next; + freebytes(np, sizeof(*np)); + } + hammertree_init(tree, 0); +} diff --git a/shared/hammer/tree.h b/shared/hammer/tree.h new file mode 100644 index 0000000..fcbc036 --- /dev/null +++ b/shared/hammer/tree.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2003 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__ + +#define HAMMERTREE_DEBUG + +typedef struct _hammernode +{ + int n_index; + float n_value; + int n_black; + struct _hammernode *n_left; + struct _hammernode *n_right; + struct _hammernode *n_parent; + struct _hammernode *n_prev; + struct _hammernode *n_next; +} t_hammernode; + +typedef struct _hammertree +{ + t_hammernode *t_root; + t_hammernode *t_first; + t_hammernode *t_last; +} t_hammertree; + +t_hammernode *hammertree_insert(t_hammertree *tree, int ndx); +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_clear(t_hammertree *tree, int freecount); +void hammertree_debug(t_hammertree *tree, int level); + +#endif diff --git a/shared/shared.c b/shared/shared.c new file mode 100644 index 0000000..d6d2c98 --- /dev/null +++ b/shared/shared.c @@ -0,0 +1,11 @@ +/* Copyright (c) 2003 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 "shared.h" + +void shared_debug(void) +{ + /* LATER */ +} diff --git a/shared/shared.h b/shared/shared.h new file mode 100644 index 0000000..a0bd0e6 --- /dev/null +++ b/shared/shared.h @@ -0,0 +1,166 @@ +/* Copyright (c) 2003 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 __SHARED_H__ +#define __SHARED_H__ + +/* LATER find a proper place for #include <limits.h> */ +#ifdef INT_MAX +#define SHARED_INT_MAX INT_MAX +#else +#define SHARED_INT_MAX 0x7FFFFFFF +#endif +#ifdef INT_MIN +#define SHARED_INT_MIN INT_MIN +#else +#define SHARED_INT_MIN ((int)0x80000000) +#endif +/* LATER find a proper place for #include <float.h> */ +#ifdef FLT_MAX +#define SHARED_FLT_MAX FLT_MAX +#else +#define SHARED_FLT_MAX 1E+36 +#endif + +typedef unsigned long shared_t_bitmask; + +#ifdef __linux__ +#include <sys/types.h> +#ifndef int32 +typedef int32_t int32; +#endif +#ifndef uint32 +typedef u_int32_t uint32; +#endif +#ifndef int16 +typedef int16_t int16; +#endif +#ifndef uint16 +typedef u_int16_t uint16; +#endif +#ifndef uchar +typedef u_int8_t uchar; +#endif +#include <endian.h> +#if !defined(__BYTE_ORDER) || !defined(__LITTLE_ENDIAN) +#error No byte order defined +#endif +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define SHARED_HIOFFSET 1 +#define SHARED_LOWOFFSET 0 +#else +#define SHARED_HIOFFSET 0 +#define SHARED_LOWOFFSET 1 +#endif +#endif + +#ifdef NT +#ifndef int32 +typedef long int32; +#endif +#ifndef uint32 +typedef unsigned long uint32; +#endif +#ifndef int16 +typedef short int16; +#endif +#ifndef uint16 +typedef unsigned short uint16; +#endif +#ifndef uchar +typedef unsigned char uchar; +#endif +#define SHARED_HIOFFSET 1 +#define SHARED_LOWOFFSET 0 +#endif + +#ifdef MACOSX +#ifndef int32 +typedef int int32; +#endif +#ifndef uint32 +typedef unsigned int uint32; +#endif +#ifndef int16 +typedef short int16; +#endif +#ifndef uint16 +typedef unsigned short uint16; +#endif +#ifndef uchar +typedef unsigned char uchar; +#endif +#define SHARED_HIOFFSET 0 +#define SHARED_LOWOFFSET 1 +#endif + +#ifdef IRIX +#ifndef int32 +typedef long int32; +#endif +#ifndef uint32 +typedef unsigned long uint32; +#endif +#ifndef int16 +typedef short int16; +#endif +#ifndef uint16 +typedef unsigned short uint16; +#endif +#ifndef uchar +typedef unsigned char uchar; +#endif +#define SHARED_HIOFFSET 0 +#define SHARED_LOWOFFSET 1 +#endif + +#ifdef __FreeBSD__ +#include <sys/types.h> +#ifndef int32 +typedef int32_t int32; +#endif +#ifndef uint32 +typedef u_int32_t uint32; +#endif +#ifndef int16 +typedef int16_t int16; +#endif +#ifndef uint16 +typedef u_int16_t uint16; +#endif +#ifndef uchar +typedef u_int8_t uchar; +#endif +#include <machine/endian.h> +#if BYTE_ORDER == LITTLE_ENDIAN +#define SHARED_HIOFFSET 1 +#define SHARED_LOWOFFSET 0 +#else +#define SHARED_HIOFFSET 0 +#define SHARED_LOWOFFSET 1 +#endif +#endif + +#define SHARED_UNITBIT32 1572864. /* 3*(2^19) gives 32 fractional bits */ +#define SHARED_UNITBIT0 6755399441055744. /* 3*(2^51), no fractional bits */ +#define SHARED_UNITBIT0_HIPART 0x43380000 + +typedef union _shared_wrappy +{ + double w_d; + int32 w_i[2]; +} t_shared_wrappy; + +typedef union _shared_floatint +{ + t_float fi_f; + int32 fi_i; +} t_shared_floatint; + +#define SHARED_TRUEBITS 0x3f800000 /* t_float f = 1; *(int32 *)&f */ + +#define SHARED_PI 3.14159265359 +#define SHARED_2PI 6.28318530718 + +#endif diff --git a/shared/sickle/Makefile b/shared/sickle/Makefile new file mode 100644 index 0000000..5dcb2c8 --- /dev/null +++ b/shared/sickle/Makefile @@ -0,0 +1,4 @@ +ROOT_DIR = ../.. +include $(ROOT_DIR)/Makefile.common + +all: $(OBJECTS) diff --git a/shared/sickle/Makefile.objects b/shared/sickle/Makefile.objects new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/shared/sickle/Makefile.objects diff --git a/shared/sickle/Makefile.sources b/shared/sickle/Makefile.sources new file mode 100644 index 0000000..5575605 --- /dev/null +++ b/shared/sickle/Makefile.sources @@ -0,0 +1,3 @@ +OTHER_SOURCES = \ +sic.c \ +arsic.c diff --git a/shared/sickle/arsic.c b/shared/sickle/arsic.c new file mode 100644 index 0000000..8f0e309 --- /dev/null +++ b/shared/sickle/arsic.c @@ -0,0 +1,221 @@ +/* Copyright (c) 2003 krzYszcz and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* generic array-based signal class */ + +#include <stdio.h> +#include <string.h> +#include "m_pd.h" +#include "shared.h" +#include "common/vefl.h" +#include "sickle/sic.h" +#include "sickle/arsic.h" + +void arsic_clear(t_arsic *x) +{ + x->s_vecsize = 0; + memset(x->s_vectors, 0, x->s_nchannels * sizeof(*x->s_vectors)); +} + +void arsic_redraw(t_arsic *x) +{ + if (x->s_mononame) + { + t_garray *ap = + (t_garray *)pd_findbyclass(x->s_mononame, garray_class); + if (ap) garray_redraw(ap); + else if (x->s_vectors[0]) bug("arsic_redraw 1"); + } + else if (*x->s_stub) + { + int ch = x->s_nchannels; + while (ch--) + { + t_garray *ap = + (t_garray *)pd_findbyclass(x->s_channames[ch], garray_class); + if (ap) garray_redraw(ap); + else if (x->s_vectors[ch]) bug("arsic_redraw 2"); + } + } +} + +void arsic_validate(t_arsic *x, int complain) +{ + arsic_clear(x); + x->s_vecsize = SHARED_INT_MAX; + if (x->s_mononame) + { + x->s_vectors[0] = + vefl_get(x->s_mononame, &x->s_vecsize, 1, + (complain ? (t_pd *)x : 0)); + } + else if (*x->s_stub) + { + int ch; + for (ch = 0; ch < x->s_nchannels ; ch++) + { + int vsz = x->s_vecsize; /* ignore missing arrays */ + x->s_vectors[ch] = + vefl_get(x->s_channames[ch], &vsz, 1, + (complain ? (t_pd *)x : 0)); + if (vsz < x->s_vecsize) x->s_vecsize = vsz; + } + } + if (x->s_vecsize == SHARED_INT_MAX) x->s_vecsize = 0; +} + +void arsic_check(t_arsic *x) +{ + x->s_playable = (!((t_sic *)x)->s_disabled && x->s_vecsize >= x->s_minsize); +} + +int arsic_getnchannels(t_arsic *x) +{ + return (x->s_nchannels); +} + +void arsic_setarray(t_arsic *x, t_symbol *s, int complain) +{ + if (s) + { + if (x->s_mononame) x->s_mononame = s; + else + { + x->s_stub = s->s_name; + if (*x->s_stub) + { + char buf[MAXPDSTRING]; + int ch; + for (ch = 0; ch < x->s_nchannels; ch++) + { + sprintf(buf, "%d-%s", ch, x->s_stub); + x->s_channames[ch] = gensym(buf); + } + } + } + arsic_validate(x, complain); + } + arsic_check(x); +} + +void arsic_setminsize(t_arsic *x, int i) +{ + x->s_minsize = i; +} + +void arsic_dsp(t_arsic *x, t_signal **sp, t_perfroutine perf, int complain) +{ + t_int *ap = x->s_perfargs; + if (ap) + { + int i, nsigs = x->s_nperfargs - 2; + x->s_ksr = sp[0]->s_sr * 0.001; + arsic_validate(x, complain); + arsic_check(x); + + /* LATER consider glist traversing, and, if we have no feeders, + choosing an optimized version of perform routine */ + + *ap++ = (t_int)x; + *ap++ = (t_int)sp[0]->s_n; + for (i = 0; i < nsigs; i++) *ap++ = (t_int)sp[i]->s_vec; + dsp_addv(perf, x->s_nperfargs, x->s_perfargs); + } + else bug("arsic_dsp"); +} + +void arsic_free(t_arsic *x) +{ + if (x->s_vectors) + freebytes(x->s_vectors, x->s_nchannels * sizeof(*x->s_vectors)); + if (x->s_channames) + freebytes(x->s_channames, + x->s_nchannels * sizeof(*x->s_channames)); + if (x->s_perfargs) + freebytes(x->s_perfargs, x->s_nperfargs * sizeof(*x->s_perfargs)); +} + +/* If nauxsigs is positive, then the number of signals is nchannels + nauxsigs; + otherwise the channels are not used as signals, and the number of signals is + nsigs -- provided that nsigs is positive -- or, if it is not, then an arsic + is not used in dsp (peek~). */ +void *arsic_new(t_class *c, t_symbol *s, + int nchannels, int nsigs, int nauxsigs) +{ + t_arsic *x; + t_symbol *mononame; + char *stub; + t_float **vectors; + int nperfargs = 0; + t_int *perfargs = 0; + t_symbol **channames = 0; + if (!s) s = &s_; + if (nchannels < 1) + { + nchannels = 1; + mononame = s; + stub = 0; + } + else + { + mononame = 0; + stub = s->s_name; + } + if (!(vectors = (t_float **)getbytes(nchannels * sizeof(*vectors)))) + return (0); + if (nauxsigs > 0) + nperfargs = nchannels + nauxsigs + 2; + else if (nsigs > 0) + nperfargs = nsigs + 2; + if (nperfargs + && !(perfargs = (t_int *)getbytes(nperfargs * sizeof(*perfargs)))) + { + freebytes(vectors, nchannels * sizeof(*vectors)); + return (0); + } + if (stub && + !(channames = (t_symbol **)getbytes(nchannels * sizeof(*channames)))) + { + freebytes(vectors, nchannels * sizeof(*vectors)); + if (perfargs) freebytes(perfargs, nperfargs * sizeof(*perfargs)); + return (0); + } + x = (t_arsic *)pd_new(c); + x->s_vecsize = 0; + x->s_nchannels = nchannels; + x->s_vectors = vectors; + x->s_channames = channames; + x->s_nperfargs = nperfargs; + x->s_perfargs = perfargs; + x->s_mononame = mononame; + x->s_stub = stub; + x->s_ksr = sys_getsr() * 0.001; + ((t_sic *)x)->s_disabled = 0; + x->s_playable = 0; + x->s_minsize = 1; + arsic_setarray(x, s, 0); + return (x); +} + +static void arsic_enable(t_arsic *x, t_floatarg f) +{ + ((t_sic *)x)->s_disabled = (f == 0); + arsic_check(x); +} + +/* LATER somehow link this to sic_setup() */ +void arsic_setup(t_class *c, void *dspfn, void *floatfn) +{ + if (floatfn != SIC_NOMAINSIGNALIN) + { + if (floatfn) + { + class_domainsignalin(c, -1); + class_addfloat(c, floatfn); + } + else CLASS_MAINSIGNALIN(c, t_sic, s_f); + } + class_addmethod(c, (t_method)dspfn, gensym("dsp"), 0); + class_addmethod(c, (t_method)arsic_enable, gensym("enable"), 0); +} diff --git a/shared/sickle/arsic.h b/shared/sickle/arsic.h new file mode 100644 index 0000000..a941279 --- /dev/null +++ b/shared/sickle/arsic.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2003 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 __ARSIC_H__ +#define __ARSIC_H__ + +typedef struct _arsic +{ + t_sic s_sic; + int s_vecsize; /* used also as a validation flag */ + int s_nchannels; + t_float **s_vectors; + t_symbol **s_channames; + int s_nperfargs; + t_int *s_perfargs; + t_symbol *s_mononame; /* used also as an 'ismono' flag */ + char *s_stub; + float s_ksr; + int s_playable; + int s_minsize; +} t_arsic; + +void arsic_clear(t_arsic *x); +void arsic_redraw(t_arsic *x); +void arsic_validate(t_arsic *x, int complain); +void arsic_check(t_arsic *x); +int arsic_getnchannels(t_arsic *x); +void arsic_setarray(t_arsic *x, t_symbol *s, int complain); +void arsic_setminsize(t_arsic *x, int i); + +void arsic_dsp(t_arsic *x, t_signal **sp, t_perfroutine perf, int complain); +void *arsic_new(t_class *c, t_symbol *s, + int nchannels, int nsigs, int nauxsigs); +void arsic_free(t_arsic *x); +void arsic_setup(t_class *c, void *dspfn, void *floatfn); + +#endif diff --git a/shared/sickle/sic.c b/shared/sickle/sic.c new file mode 100644 index 0000000..003120e --- /dev/null +++ b/shared/sickle/sic.c @@ -0,0 +1,119 @@ +/* Copyright (c) 2003 krzYszcz and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* generic signal class */ + +#include <math.h> +#include "m_pd.h" +#include "shared.h" +#include "common/loud.h" +#include "sickle/sic.h" + +//#define SIC_DEBUG + +#if defined(NT) || defined(MACOSX) +/* cf pd/src/x_arithmetic.c */ +#define cosf cos +#endif + +t_inlet *sic_inlet(t_sic *x, int ix, t_float df, int ax, int ac, t_atom *av) +{ + t_inlet *in = 0; + if (ax < ac) + { + if (av[ax].a_type == A_FLOAT) + df = av[ax].a_w.w_float; + else + loud_error((t_pd *)x, "bad argument %d (float expected)", ax + 1); + } + if (ix) + { + in = inlet_new((t_object *)x, (t_pd *)x, &s_signal, &s_signal); + /* this is persistent (in->i_un.iu_floatsignalvalue = df) */ + pd_float((t_pd *)in, df); + } + else + { + in = ((t_object *)x)->ob_inlet; + pd_float((t_pd *)x, df); + } + return (in); +} + +t_inlet *sic_newinlet(t_sic *x, t_float f) +{ + return (sic_inlet(x, 1, f, 0, 0, 0)); +} + +t_float *sic_makecostable(int *sizep) +{ + /* permanent cache (tables are never freed); LATER rethink */ + static t_float *sic_costables[SIC_NCOSTABLES]; + int ndx, sz; + /* round upwards -- always return at least requested number of elements, + unless the maximum of 2^SIC_NCOSTABLES is exceeded; LATER rethink */ + /* (the minimum, at ndx 0, is 2^1) */ + for (ndx = 0, sz = 2; ndx < (SIC_NCOSTABLES - 1); ndx++, sz <<= 1) + if (sz >= *sizep) + break; +#ifdef SIC_DEBUG + post("request for a costable of %d points (effective %d, ndx %d)", + *sizep, sz, ndx); +#endif + *sizep = sz; + if (sic_costables[ndx]) + return (sic_costables[ndx]); + else if (sz == COSTABSIZE && cos_table) + return (sic_costables[ndx] = cos_table); + else + { + int cnt = sz + 1; + float phase = 0, phsinc = SHARED_2PI / sz; + t_float *table = (t_float *)getbytes(cnt * sizeof(*table)), *tp = table; + if (table) + { +#ifdef SIC_DEBUG + post("got %d points of a costable", cnt); +#endif + while (cnt--) + { + *tp++ = cosf(phase); + phase += phsinc; + } + } + return (sic_costables[ndx] = table); + } +} + +static void sic_enable(t_sic *x, t_floatarg f) +{ + x->s_disabled = (f == 0); +} + +void sic_setup(t_class *c, void *dspfn, void *floatfn) +{ + static int checked = 0; + if (!checked) + { + /* MSP: here we check at startup whether the byte alignment + is as we declared it. If not, the code has to be + recompiled the other way. */ + t_shared_wrappy wrappy; + wrappy.w_d = SHARED_UNITBIT32 + 0.5; + if ((unsigned)wrappy.w_i[SHARED_LOWOFFSET] != 0x80000000) + bug("sic_setup: unexpected machine alignment"); + checked = 1; + } + if (floatfn != SIC_NOMAINSIGNALIN) + { + if (floatfn) + { + class_domainsignalin(c, -1); + class_addfloat(c, floatfn); + } + else CLASS_MAINSIGNALIN(c, t_sic, s_f); + } + class_addmethod(c, (t_method)dspfn, gensym("dsp"), 0); + class_addmethod(c, (t_method)sic_enable, gensym("enable"), 0); +} diff --git a/shared/sickle/sic.h b/shared/sickle/sic.h new file mode 100644 index 0000000..9dce95f --- /dev/null +++ b/shared/sickle/sic.h @@ -0,0 +1,25 @@ +/* Copyright (c) 2003 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 __SIC_H__ +#define __SIC_H__ + +typedef struct _sic +{ + t_object s_ob; + t_float s_f; + int s_disabled; +} t_sic; + +#define SIC_FLOATTOSIGNAL ((void *)0) +#define SIC_NOMAINSIGNALIN ((void *)-1) + +#define SIC_NCOSTABLES 16 /* this is oscbank~'s max, LATER rethink */ + +t_inlet *sic_inlet(t_sic *x, int ix, t_float df, int ax, int ac, t_atom *av); +t_inlet *sic_newinlet(t_sic *x, t_float f); +t_float *sic_makecostable(int *sizep); +void sic_setup(t_class *c, void *dspfn, void *floatfn); + +#endif diff --git a/shared/unstable/Makefile b/shared/unstable/Makefile new file mode 100644 index 0000000..5dcb2c8 --- /dev/null +++ b/shared/unstable/Makefile @@ -0,0 +1,4 @@ +ROOT_DIR = ../.. +include $(ROOT_DIR)/Makefile.common + +all: $(OBJECTS) diff --git a/shared/unstable/Makefile.objects b/shared/unstable/Makefile.objects new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/shared/unstable/Makefile.objects diff --git a/shared/unstable/Makefile.sources b/shared/unstable/Makefile.sources new file mode 100644 index 0000000..4636701 --- /dev/null +++ b/shared/unstable/Makefile.sources @@ -0,0 +1,4 @@ +OTHER_SOURCES = \ +fragile.c \ +forky.c \ +loader.c diff --git a/shared/unstable/forky.c b/shared/unstable/forky.c new file mode 100644 index 0000000..7fb6b08 --- /dev/null +++ b/shared/unstable/forky.c @@ -0,0 +1,56 @@ +/* Copyright (c) 2003 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 "g_canvas.h" +#include "shared.h" +#include "unstable/forky.h" + +//#define FORKY_DEBUG + +/* To be called in a 'dsp' method -- e.g. if there are no feeders, the caller + might use an optimized version of a 'perform' routine. + LATER think about replacing 'linetraverser' calls with something faster. */ +int forky_hasfeeders(t_object *x, t_glist *glist, int inno, t_symbol *outsym) +{ + t_linetraverser t; + linetraverser_start(&t, glist); + while (linetraverser_next(&t)) + if (t.tr_ob2 == x && t.tr_inno == inno +#ifdef PD_VERSION /* FIXME ask Miller */ + && (!outsym || outsym == outlet_getsymbol(t.tr_outlet)) +#endif + ) + return (1); + return (0); +} + +/* Not really a forky, just found no better place to put it in. + Used in bitwise signal binops (sickle). Checked against msp2. */ +t_int forky_getbitmask(int ac, t_atom *av) +{ + t_int result = 0; + if (sizeof(shared_t_bitmask) >= sizeof(t_int)) + { + int nbits = sizeof(t_int) * 8; + shared_t_bitmask bitmask = 1 << (nbits - 1); + if (ac > nbits) + ac = nbits; + while (ac--) + { + if (av->a_type == A_FLOAT && + (int)av->a_w.w_float) /* CHECKED */ + result |= bitmask; + /* CHECKED symbols are zero */ + bitmask >>= 1; + av++; + } + /* CHECKED missing are zero */ +#ifdef FORKY_DEBUG + post("mask set to %.8x", result); +#endif + } + else bug("sizeof(shared_t_bitmask)"); + return (result); +} diff --git a/shared/unstable/forky.h b/shared/unstable/forky.h new file mode 100644 index 0000000..0d27080 --- /dev/null +++ b/shared/unstable/forky.h @@ -0,0 +1,11 @@ +/* Copyright (c) 2003 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 __FORKY_H__ +#define __FORKY_H__ + +int forky_hasfeeders(t_object *x, t_glist *glist, int inno, t_symbol *outsym); +t_int forky_getbitmask(int ac, t_atom *av); + +#endif diff --git a/shared/unstable/fragile.c b/shared/unstable/fragile.c new file mode 100644 index 0000000..3267721 --- /dev/null +++ b/shared/unstable/fragile.c @@ -0,0 +1,67 @@ +/* Copyright (c) 1997-2003 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. */ + +#include <string.h> +#include "m_pd.h" +#include "unstable/pd_imp.h" +#include "unstable/fragile.h" + +int fragile_class_count(void) +{ + return (pd_objectmaker->c_nmethod); +} + +void fragile_class_printnames(char *msg, int firstndx, int lastndx) +{ + t_methodentry *mp = pd_objectmaker->c_methods; + int ndx, len = strlen(msg); + startpost(msg); + for (ndx = firstndx, mp += ndx; ndx <= lastndx; ndx++, mp++) + { + t_symbol *s = mp->me_name; + if (s && s->s_name[0] != '_') + { + int l = 1 + strlen(s->s_name); + if ((len += l) > 66) + { + endpost(); + startpost(" "); + len = 3 + l; + } + poststring(s->s_name); + } + } + endpost(); +} + +/* This structure is local to g_array.c. We need it, + because there is no other way to get into array's graph. */ +struct _garray +{ + t_gobj x_gobj; + t_glist *x_glist; + /* ... */ +}; + +t_glist *fragile_garray_glist(void *arr) +{ + return (((struct _garray *)arr)->x_glist); +} + +/* This is local to m_obj.c. + LATER export write access to o_connections field ('grab' class). + LATER encapsulate 'traverseoutlet' routines (not in the stable API yet). */ +struct _outlet +{ + t_object *o_owner; + struct _outlet *o_next; + t_outconnect *o_connections; + t_symbol *o_sym; +}; + +/* obj_starttraverseoutlet() replacement */ +t_outconnect *fragile_outlet_connections(t_outlet *o) +{ + return (o ? o->o_connections : 0); +} diff --git a/shared/unstable/fragile.h b/shared/unstable/fragile.h new file mode 100644 index 0000000..c1ba8e3 --- /dev/null +++ b/shared/unstable/fragile.h @@ -0,0 +1,13 @@ +/* Copyright (c) 2003 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 __FRAGILE_H__ +#define __FRAGILE_H__ + +int fragile_class_count(void); +void fragile_class_printnames(char *msg, int firstndx, int lastndx); +t_glist *fragile_garray_glist(void *arr); +t_outconnect *fragile_outlet_connections(t_outlet *o); + +#endif diff --git a/shared/unstable/loader.c b/shared/unstable/loader.c new file mode 100644 index 0000000..91b7ff3 --- /dev/null +++ b/shared/unstable/loader.c @@ -0,0 +1,139 @@ +/* Copyright (c) 1997-2003 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. */ + +/* This is just a not-yet-in-the-API-sys_load_lib() duplication + (modulo differentiating the error return codes). LATER use the original. */ + +#include "loader.h" + +#ifdef __linux__ +#include <dlfcn.h> +#endif +#ifdef UNIX +#include <stdlib.h> +#include <unistd.h> +#endif +#ifdef NT +#include <io.h> +#include <windows.h> +#endif +#ifdef MACOSX +#include <mach-o/dyld.h> +#endif +#include <string.h> +#include "m_pd.h" +#include <stdio.h> + +typedef void (*t_xxx)(void); + +static char sys_dllextent[] = +#ifdef __FreeBSD__ + ".pd_freebsd"; +#endif +#ifdef IRIX +#ifdef N32 + ".pd_irix6"; +#else + ".pd_irix5"; +#endif +#endif +#ifdef __linux__ + ".pd_linux"; +#endif +#ifdef MACOSX + ".pd_darwin"; +#endif +#ifdef NT + ".dll"; +#endif + +int unstable_load_lib(char *dirname, char *classname) +{ + char symname[MAXPDSTRING], filename[MAXPDSTRING], dirbuf[MAXPDSTRING], + *nameptr, *lastdot; + void *dlobj; + t_xxx makeout; + int fd; +#ifdef NT + HINSTANCE ntdll; +#endif +#if 0 + fprintf(stderr, "lib %s %s\n", dirname, classname); +#endif + if ((fd = open_via_path(dirname, classname, sys_dllextent, + dirbuf, &nameptr, MAXPDSTRING, 1)) < 0) + { + return (LOADER_NOFILE); + } + else + { + close(fd); + /* refabricate the pathname */ + strcpy(filename, dirbuf); + strcat(filename, "/"); + strcat(filename, nameptr); + /* extract the setup function name */ + if (lastdot = strrchr(nameptr, '.')) + *lastdot = 0; + +#ifdef MACOSX + strcpy(symname, "_"); + strcat(symname, nameptr); +#else + strcpy(symname, nameptr); +#endif + /* if the last character is a tilde, replace with "_tilde" */ + if (symname[strlen(symname) - 1] == '~') + strcpy(symname + (strlen(symname) - 1), "_tilde"); + /* and append _setup to form the C setup function name */ + strcat(symname, "_setup"); +#ifdef __linux__ + dlobj = dlopen(filename, RTLD_NOW | RTLD_GLOBAL); + if (!dlobj) + { + post("%s: %s", filename, dlerror()); + return (LOADER_BADFILE); + } + makeout = (t_xxx)dlsym(dlobj, symname); +#endif +#ifdef NT + sys_bashfilename(filename, filename); + ntdll = LoadLibrary(filename); + if (!ntdll) + { + post("%s: couldn't load", filename); + return (LOADER_BADFILE); + } + makeout = (t_xxx)GetProcAddress(ntdll, symname); +#endif +#ifdef MACOSX + { + NSObjectFileImage image; + void *ret; + NSSymbol s; + if ( NSCreateObjectFileImageFromFile( filename, &image) != NSObjectFileImageSuccess ) + { + post("%s: couldn't load", filename); + return (LOADER_BADFILE); + } + ret = NSLinkModule( image, filename, + NSLINKMODULE_OPTION_BINDNOW + + NSLINKMODULE_OPTION_PRIVATE); + + s = NSLookupSymbolInModule(ret, symname); + + if (s) + makeout = (t_xxx)NSAddressOfSymbol( s); + else makeout = 0; + } +#endif + } + if (!makeout) + { + post("load_object: Symbol \"%s\" not found", symname); + return (LOADER_NOENTRY); + } + (*makeout)(); + return (LOADER_OK); +} diff --git a/shared/unstable/loader.h b/shared/unstable/loader.h new file mode 100644 index 0000000..e9766ac --- /dev/null +++ b/shared/unstable/loader.h @@ -0,0 +1,12 @@ +/* Copyright (c) 2003 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 __LOADER_H__ +#define __LOADER_H__ + +enum { LOADER_OK, LOADER_NOFILE, LOADER_BADFILE, LOADER_NOENTRY }; + +int unstable_load_lib(char *dirname, char *classname); + +#endif diff --git a/shared/unstable/pd_imp.h b/shared/unstable/pd_imp.h new file mode 100644 index 0000000..bf659ed --- /dev/null +++ b/shared/unstable/pd_imp.h @@ -0,0 +1,60 @@ +/* Copyright (c) 1997-2003 Miller Puckette and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#ifndef __PD_IMP_H__ +#define __PD_IMP_H__ + +#ifdef PD_MINOR_VERSION +#include "m_imp.h" +#else + +typedef struct _methodentry +{ + t_symbol *me_name; + t_gotfn me_fun; + t_atomtype me_arg[MAXPDARG+1]; +} t_methodentry; + +EXTERN_STRUCT _widgetbehavior; + +typedef void (*t_bangmethod)(t_pd *x); +typedef void (*t_pointermethod)(t_pd *x, t_gpointer *gp); +typedef void (*t_floatmethod)(t_pd *x, t_float f); +typedef void (*t_symbolmethod)(t_pd *x, t_symbol *s); +typedef void (*t_listmethod)(t_pd *x, t_symbol *s, int argc, t_atom *argv); +typedef void (*t_anymethod)(t_pd *x, t_symbol *s, int argc, t_atom *argv); + +struct _class +{ + t_symbol *c_name; /* name (mostly for error reporting) */ + t_symbol *c_helpname; /* name of help file */ + size_t c_size; /* size of an instance */ + t_methodentry *c_methods; /* methods other than bang, etc below */ + int c_nmethod; /* number of methods */ + t_method c_freemethod; /* function to call before freeing */ + t_bangmethod c_bangmethod; /* common methods */ + t_pointermethod c_pointermethod; + t_floatmethod c_floatmethod; + t_symbolmethod c_symbolmethod; + t_listmethod c_listmethod; + t_anymethod c_anymethod; + struct _widgetbehavior *c_wb; /* "gobjs" only */ + struct _parentwidgetbehavior *c_pwb;/* widget behavior in parent */ + int c_floatsignalin; /* onset to float for signal input */ + char c_gobj; /* true if is a gobj */ + char c_patchable; /* true if we have a t_object header */ + char c_firstin; /* if patchable, true if draw first inlet */ + char c_drawcommand; /* a drawing command for a template */ +}; + +EXTERN int obj_noutlets(t_object *x); +EXTERN int obj_ninlets(t_object *x); +EXTERN t_outconnect *obj_starttraverseoutlet(t_object *x, t_outlet **op, + int nout); +EXTERN t_outconnect *obj_nexttraverseoutlet(t_outconnect *lastconnect, + t_object **destp, t_inlet **inletp, int *whichp); + +#endif + +#endif |