diff options
Diffstat (limited to 'shared/common')
-rw-r--r-- | shared/common/Makefile | 4 | ||||
-rw-r--r-- | shared/common/Makefile.objects | 0 | ||||
-rw-r--r-- | shared/common/Makefile.sources | 16 | ||||
-rw-r--r-- | shared/common/bifi.c | 217 | ||||
-rw-r--r-- | shared/common/bifi.h | 40 | ||||
-rw-r--r-- | shared/common/binport.c | 559 | ||||
-rw-r--r-- | shared/common/binport.h | 15 | ||||
-rw-r--r-- | shared/common/grow.c | 105 | ||||
-rw-r--r-- | shared/common/grow.h | 17 | ||||
-rw-r--r-- | shared/common/loud.c | 210 | ||||
-rw-r--r-- | shared/common/loud.h | 31 | ||||
-rw-r--r-- | shared/common/mifi.c | 867 | ||||
-rw-r--r-- | shared/common/mifi.h | 84 | ||||
-rw-r--r-- | shared/common/port.c | 612 | ||||
-rw-r--r-- | shared/common/port.h | 10 | ||||
-rw-r--r-- | shared/common/rand.c | 61 | ||||
-rw-r--r-- | shared/common/rand.h | 14 | ||||
-rw-r--r-- | shared/common/sq.c | 371 | ||||
-rw-r--r-- | shared/common/sq.h | 169 | ||||
-rw-r--r-- | shared/common/vefl.c | 233 | ||||
-rw-r--r-- | shared/common/vefl.h | 36 |
21 files changed, 3671 insertions, 0 deletions
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 |