aboutsummaryrefslogtreecommitdiff
path: root/shared
diff options
context:
space:
mode:
Diffstat (limited to 'shared')
-rw-r--r--shared/Makefile2
-rw-r--r--shared/Makefile.dirs1
-rw-r--r--shared/Makefile.objects0
-rw-r--r--shared/Makefile.sources2
-rw-r--r--shared/common/Makefile4
-rw-r--r--shared/common/Makefile.objects0
-rw-r--r--shared/common/Makefile.sources16
-rw-r--r--shared/common/bifi.c217
-rw-r--r--shared/common/bifi.h40
-rw-r--r--shared/common/binport.c559
-rw-r--r--shared/common/binport.h15
-rw-r--r--shared/common/grow.c105
-rw-r--r--shared/common/grow.h17
-rw-r--r--shared/common/loud.c210
-rw-r--r--shared/common/loud.h31
-rw-r--r--shared/common/mifi.c867
-rw-r--r--shared/common/mifi.h84
-rw-r--r--shared/common/port.c612
-rw-r--r--shared/common/port.h10
-rw-r--r--shared/common/rand.c61
-rw-r--r--shared/common/rand.h14
-rw-r--r--shared/common/sq.c371
-rw-r--r--shared/common/sq.h169
-rw-r--r--shared/common/vefl.c233
-rw-r--r--shared/common/vefl.h36
-rw-r--r--shared/hammer/Makefile4
-rw-r--r--shared/hammer/Makefile.objects0
-rw-r--r--shared/hammer/Makefile.sources4
-rw-r--r--shared/hammer/file.c402
-rw-r--r--shared/hammer/file.h43
-rw-r--r--shared/hammer/gui.c438
-rw-r--r--shared/hammer/gui.h30
-rw-r--r--shared/hammer/tree.c482
-rw-r--r--shared/hammer/tree.h37
-rw-r--r--shared/shared.c11
-rw-r--r--shared/shared.h166
-rw-r--r--shared/sickle/Makefile4
-rw-r--r--shared/sickle/Makefile.objects0
-rw-r--r--shared/sickle/Makefile.sources3
-rw-r--r--shared/sickle/arsic.c221
-rw-r--r--shared/sickle/arsic.h38
-rw-r--r--shared/sickle/sic.c119
-rw-r--r--shared/sickle/sic.h25
-rw-r--r--shared/unstable/Makefile4
-rw-r--r--shared/unstable/Makefile.objects0
-rw-r--r--shared/unstable/Makefile.sources4
-rw-r--r--shared/unstable/forky.c56
-rw-r--r--shared/unstable/forky.h11
-rw-r--r--shared/unstable/fragile.c67
-rw-r--r--shared/unstable/fragile.h13
-rw-r--r--shared/unstable/loader.c139
-rw-r--r--shared/unstable/loader.h12
-rw-r--r--shared/unstable/pd_imp.h60
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