aboutsummaryrefslogtreecommitdiff
path: root/threadlib/src/threadedsf.c
diff options
context:
space:
mode:
Diffstat (limited to 'threadlib/src/threadedsf.c')
-rwxr-xr-xthreadlib/src/threadedsf.c1919
1 files changed, 1919 insertions, 0 deletions
diff --git a/threadlib/src/threadedsf.c b/threadlib/src/threadedsf.c
new file mode 100755
index 0000000..c9d0b9f
--- /dev/null
+++ b/threadlib/src/threadedsf.c
@@ -0,0 +1,1919 @@
+/*
+*
+* threadedsf
+*
+* this is a little bit hacked version of the
+* threaded soundfiler of pd_devel_0.38 by Tim Blechmann
+*
+* (c) 2005, Georg Holzmann, <grh@mur.at>
+*/
+
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* this file contains, first, a collection of soundfile access routines, a
+sort of soundfile library. Second, the "soundfiler" object is defined which
+uses the routines to read or write soundfiles, synchronously, from garrays.
+These operations are not to be done in "real time" as they may have to wait
+for disk accesses (even the write routine.) Finally, the realtime objects
+readsf~ and writesf~ are defined which confine disk operations to a separate
+thread so that they can be used in real time. The readsf~ and writesf~
+objects use Posix-like threads. */
+
+/* threaded soundfiler by Tim Blechmann */
+
+
+#include "threadlib.h"
+
+#ifndef MSW
+#include <unistd.h>
+#include <fcntl.h>
+#endif
+#ifdef MSW
+#include <io.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "g_canvas.h"
+
+#define MAXSFCHANS 64
+
+
+// forward declarations
+
+struct _garray
+{
+ t_gobj x_gobj;
+ t_scalar *x_scalar; /* scalar "containing" the array */
+ t_glist *x_glist; /* containing glist */
+ t_symbol *x_name; /* unexpanded name (possibly with leading '$') */
+ t_symbol *x_realname; /* expanded name (symbol we're bound to) */
+ char x_usedindsp; /* true if some DSP routine is using this */
+ char x_saveit; /* true if we should save this with parent */
+ char x_listviewing; /* true if list view window is open */
+};
+
+t_array *garray_getarray(t_garray *x);
+int garray_ambigendian(void);
+
+
+
+/***************** soundfile header structures ************************/
+
+typedef unsigned short uint16;
+typedef unsigned int uint32; /* long isn't 32-bit on amd64 */
+
+#define FORMAT_WAVE 0
+#define FORMAT_AIFF 1
+#define FORMAT_NEXT 2
+
+/* the NeXTStep sound header structure; can be big or little endian */
+
+typedef struct _nextstep
+{
+ char ns_fileid[4]; /* magic number '.snd' if file is big-endian */
+ uint32 ns_onset; /* byte offset of first sample */
+ uint32 ns_length; /* length of sound in bytes */
+ uint32 ns_format; /* format; see below */
+ uint32 ns_sr; /* sample rate */
+ uint32 ns_nchans; /* number of channels */
+ char ns_info[4]; /* comment */
+} t_nextstep;
+
+#define NS_FORMAT_LINEAR_16 3
+#define NS_FORMAT_LINEAR_24 4
+#define NS_FORMAT_FLOAT 6
+#define SCALE (1./(1024. * 1024. * 1024. * 2.))
+
+/* the WAVE header. All Wave files are little endian. We assume
+ the "fmt" chunk comes first which is usually the case but perhaps not
+ always; same for AIFF and the "COMM" chunk. */
+
+typedef struct _wave
+{
+ char w_fileid[4]; /* chunk id 'RIFF' */
+ uint32 w_chunksize; /* chunk size */
+ char w_waveid[4]; /* wave chunk id 'WAVE' */
+ char w_fmtid[4]; /* format chunk id 'fmt ' */
+ uint32 w_fmtchunksize; /* format chunk size */
+ uint16 w_fmttag; /* format tag (WAV_INT etc) */
+ uint16 w_nchannels; /* number of channels */
+ uint32 w_samplespersec; /* sample rate in hz */
+ uint32 w_navgbytespersec; /* average bytes per second */
+ uint16 w_nblockalign; /* number of bytes per frame */
+ uint16 w_nbitspersample; /* number of bits in a sample */
+ char w_datachunkid[4]; /* data chunk id 'data' */
+ uint32 w_datachunksize; /* length of data chunk */
+} t_wave;
+
+typedef struct _fmt /* format chunk */
+{
+ uint16 f_fmttag; /* format tag, 1 for PCM */
+ uint16 f_nchannels; /* number of channels */
+ uint32 f_samplespersec; /* sample rate in hz */
+ uint32 f_navgbytespersec; /* average bytes per second */
+ uint16 f_nblockalign; /* number of bytes per frame */
+ uint16 f_nbitspersample; /* number of bits in a sample */
+} t_fmt;
+
+typedef struct _wavechunk /* ... and the last two items */
+{
+ char wc_id[4]; /* data chunk id, e.g., 'data' or 'fmt ' */
+ uint32 wc_size; /* length of data chunk */
+} t_wavechunk;
+
+#define WAV_INT 1
+#define WAV_FLOAT 3
+
+/* the AIFF header. I'm assuming AIFC is compatible but don't really know
+ that. */
+
+typedef struct _datachunk
+{
+ char dc_id[4]; /* data chunk id 'SSND' */
+ uint32 dc_size; /* length of data chunk */
+} t_datachunk;
+
+typedef struct _comm
+{
+ uint16 c_nchannels; /* number of channels */
+ uint16 c_nframeshi; /* # of sample frames (hi) */
+ uint16 c_nframeslo; /* # of sample frames (lo) */
+ uint16 c_bitspersamp; /* bits per sample */
+ unsigned char c_samprate[10]; /* sample rate, 80-bit float! */
+} t_comm;
+
+ /* this version is more convenient for writing them out: */
+typedef struct _aiff
+{
+ char a_fileid[4]; /* chunk id 'FORM' */
+ uint32 a_chunksize; /* chunk size */
+ char a_aiffid[4]; /* aiff chunk id 'AIFF' */
+ char a_fmtid[4]; /* format chunk id 'COMM' */
+ uint32 a_fmtchunksize; /* format chunk size, 18 */
+ uint16 a_nchannels; /* number of channels */
+ uint16 a_nframeshi; /* # of sample frames (hi) */
+ uint16 a_nframeslo; /* # of sample frames (lo) */
+ uint16 a_bitspersamp; /* bits per sample */
+ unsigned char a_samprate[10]; /* sample rate, 80-bit float! */
+} t_aiff;
+
+#define AIFFHDRSIZE 38 /* probably not what sizeof() gives */
+
+
+#define AIFFPLUS (AIFFHDRSIZE + 8) /* header size including first chunk hdr */
+
+#define WHDR1 sizeof(t_nextstep)
+#define WHDR2 (sizeof(t_wave) > WHDR1 ? sizeof (t_wave) : WHDR1)
+#define WRITEHDRSIZE (AIFFPLUS > WHDR2 ? AIFFPLUS : WHDR2)
+
+#define READHDRSIZE (16 > WHDR2 + 2 ? 16 : WHDR2 + 2)
+
+#define OBUFSIZE MAXPDSTRING /* assume MAXPDSTRING is bigger than headers */
+
+#ifdef MSW
+#include <fcntl.h>
+#define BINCREATE _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY
+#else
+#define BINCREATE O_WRONLY | O_CREAT | O_TRUNC
+#endif
+
+/* this routine returns 1 if the high order byte comes at the lower
+address on our architecture (big-endianness.). It's 1 for Motorola,
+0 for Intel: */
+
+extern int garray_ambigendian(void);
+
+/* byte swappers */
+
+static uint32 swap4(uint32 n, int doit)
+{
+ if (doit)
+ return (((n & 0xff) << 24) | ((n & 0xff00) << 8) |
+ ((n & 0xff0000) >> 8) | ((n & 0xff000000) >> 24));
+ else return (n);
+}
+
+static uint16 swap2(uint32 n, int doit)
+{
+ if (doit)
+ return (((n & 0xff) << 8) | ((n & 0xff00) >> 8));
+ else return (n);
+}
+
+static void swapstring(char *foo, int doit)
+{
+ if (doit)
+ {
+ char a = foo[0], b = foo[1], c = foo[2], d = foo[3];
+ foo[0] = d; foo[1] = c; foo[2] = b; foo[3] = a;
+ }
+}
+
+/******************** soundfile access routines **********************/
+
+/* This routine opens a file, looks for either a nextstep or "wave" header,
+* seeks to end of it, and fills in bytes per sample and number of channels.
+* Only 2- and 3-byte fixed-point samples and 4-byte floating point samples
+* are supported. If "headersize" is nonzero, the
+* caller should supply the number of channels, endinanness, and bytes per
+* sample; the header is ignored. Otherwise, the routine tries to read the
+* header and fill in the properties.
+*/
+
+int open_soundfile(const char *dirname, const char *filename, int headersize,
+ int *p_bytespersamp, int *p_bigendian, int *p_nchannels, long *p_bytelimit,
+ long skipframes)
+{
+ char buf[OBUFSIZE], *bufptr;
+ int fd, format, nchannels, bigendian, bytespersamp, swap, sysrtn;
+ long bytelimit = 0x7fffffff;
+ errno = 0;
+ fd = open_via_path(dirname, filename,
+ "", buf, &bufptr, MAXPDSTRING, 1);
+ if (fd < 0)
+ return (-1);
+ if (headersize >= 0) /* header detection overridden */
+ {
+ bigendian = *p_bigendian;
+ nchannels = *p_nchannels;
+ bytespersamp = *p_bytespersamp;
+ bytelimit = *p_bytelimit;
+ }
+ else
+ {
+ int bytesread = read(fd, buf, READHDRSIZE);
+ int format;
+ if (bytesread < 4)
+ goto badheader;
+ if (!strncmp(buf, ".snd", 4))
+ format = FORMAT_NEXT, bigendian = 1;
+ else if (!strncmp(buf, "dns.", 4))
+ format = FORMAT_NEXT, bigendian = 0;
+ else if (!strncmp(buf, "RIFF", 4))
+ {
+ if (bytesread < 12 || strncmp(buf + 8, "WAVE", 4))
+ goto badheader;
+ format = FORMAT_WAVE, bigendian = 0;
+ }
+ else if (!strncmp(buf, "FORM", 4))
+ {
+ if (bytesread < 12 || strncmp(buf + 8, "AIFF", 4))
+ goto badheader;
+ format = FORMAT_AIFF, bigendian = 1;
+ }
+ else
+ goto badheader;
+ swap = (bigendian != garray_ambigendian());
+ if (format == FORMAT_NEXT) /* nextstep header */
+ {
+ uint32 param;
+ if (bytesread < (int)sizeof(t_nextstep))
+ goto badheader;
+ nchannels = swap4(((t_nextstep *)buf)->ns_nchans, swap);
+ format = swap4(((t_nextstep *)buf)->ns_format, swap);
+ headersize = swap4(((t_nextstep *)buf)->ns_onset, swap);
+ if (format == NS_FORMAT_LINEAR_16)
+ bytespersamp = 2;
+ else if (format == NS_FORMAT_LINEAR_24)
+ bytespersamp = 3;
+ else if (format == NS_FORMAT_FLOAT)
+ bytespersamp = 4;
+ else goto badheader;
+ bytelimit = 0x7fffffff;
+ }
+ else if (format == FORMAT_WAVE) /* wave header */
+ {
+ /* This is awful. You have to skip over chunks,
+ except that if one happens to be a "fmt" chunk, you want to
+ find out the format from that one. The case where the
+ "fmt" chunk comes after the audio isn't handled. */
+ headersize = 12;
+ if (bytesread < 20)
+ goto badheader;
+ /* First we guess a number of channels, etc., in case there's
+ no "fmt" chunk to follow. */
+ nchannels = 1;
+ bytespersamp = 2;
+ /* copy the first chunk header to beginnning of buffer. */
+ memcpy(buf, buf + headersize, sizeof(t_wavechunk));
+ /* post("chunk %c %c %c %c",
+ ((t_wavechunk *)buf)->wc_id[0],
+ ((t_wavechunk *)buf)->wc_id[1],
+ ((t_wavechunk *)buf)->wc_id[2],
+ ((t_wavechunk *)buf)->wc_id[3]); */
+ /* read chunks in loop until we get to the data chunk */
+ while (strncmp(((t_wavechunk *)buf)->wc_id, "data", 4))
+ {
+ long chunksize = swap4(((t_wavechunk *)buf)->wc_size,
+ swap), seekto = headersize + chunksize + 8, seekout;
+
+ if (!strncmp(((t_wavechunk *)buf)->wc_id, "fmt ", 4))
+ {
+ long commblockonset = headersize + 8;
+ seekout = lseek(fd, commblockonset, SEEK_SET);
+ if (seekout != commblockonset)
+ goto badheader;
+ if (read(fd, buf, sizeof(t_fmt)) < (int) sizeof(t_fmt))
+ goto badheader;
+ nchannels = swap2(((t_fmt *)buf)->f_nchannels, swap);
+ format = swap2(((t_fmt *)buf)->f_nbitspersample, swap);
+ if (format == 16)
+ bytespersamp = 2;
+ else if (format == 24)
+ bytespersamp = 3;
+ else if (format == 32)
+ bytespersamp = 4;
+ else goto badheader;
+ }
+ seekout = lseek(fd, seekto, SEEK_SET);
+ if (seekout != seekto)
+ goto badheader;
+ if (read(fd, buf, sizeof(t_wavechunk)) <
+ (int) sizeof(t_wavechunk))
+ goto badheader;
+ /* post("new chunk %c %c %c %c at %d",
+ ((t_wavechunk *)buf)->wc_id[0],
+ ((t_wavechunk *)buf)->wc_id[1],
+ ((t_wavechunk *)buf)->wc_id[2],
+ ((t_wavechunk *)buf)->wc_id[3], seekto); */
+ headersize = seekto;
+ }
+ bytelimit = swap4(((t_wavechunk *)buf)->wc_size, swap);
+ headersize += 8;
+ }
+ else
+ {
+ /* AIFF. same as WAVE; actually predates it. Disgusting. */
+ headersize = 12;
+ if (bytesread < 20)
+ goto badheader;
+ /* First we guess a number of channels, etc., in case there's
+ no COMM block to follow. */
+ nchannels = 1;
+ bytespersamp = 2;
+ /* copy the first chunk header to beginnning of buffer. */
+ memcpy(buf, buf + headersize, sizeof(t_datachunk));
+ /* read chunks in loop until we get to the data chunk */
+ while (strncmp(((t_datachunk *)buf)->dc_id, "SSND", 4))
+ {
+ long chunksize = swap4(((t_datachunk *)buf)->dc_size,
+ swap), seekto = headersize + chunksize + 8, seekout;
+ /* post("chunk %c %c %c %c seek %d",
+ ((t_datachunk *)buf)->dc_id[0],
+ ((t_datachunk *)buf)->dc_id[1],
+ ((t_datachunk *)buf)->dc_id[2],
+ ((t_datachunk *)buf)->dc_id[3], seekto); */
+ if (!strncmp(((t_datachunk *)buf)->dc_id, "COMM", 4))
+ {
+ long commblockonset = headersize + 8;
+ seekout = lseek(fd, commblockonset, SEEK_SET);
+ if (seekout != commblockonset)
+ goto badheader;
+ if (read(fd, buf, sizeof(t_comm)) <
+ (int) sizeof(t_comm))
+ goto badheader;
+ nchannels = swap2(((t_comm *)buf)->c_nchannels, swap);
+ format = swap2(((t_comm *)buf)->c_bitspersamp, swap);
+ if (format == 16)
+ bytespersamp = 2;
+ else if (format == 24)
+ bytespersamp = 3;
+ else goto badheader;
+ }
+ seekout = lseek(fd, seekto, SEEK_SET);
+ if (seekout != seekto)
+ goto badheader;
+ if (read(fd, buf, sizeof(t_datachunk)) <
+ (int) sizeof(t_datachunk))
+ goto badheader;
+ headersize = seekto;
+ }
+ bytelimit = swap4(((t_datachunk *)buf)->dc_size, swap);
+ headersize += 8;
+ }
+ }
+ /* seek past header and any sample frames to skip */
+ sysrtn = lseek(fd, nchannels * bytespersamp * skipframes + headersize, 0);
+ if (sysrtn != nchannels * bytespersamp * skipframes + headersize)
+ return (-1);
+ bytelimit -= nchannels * bytespersamp * skipframes;
+ if (bytelimit < 0)
+ bytelimit = 0;
+ /* copy sample format back to caller */
+ *p_bigendian = bigendian;
+ *p_nchannels = nchannels;
+ *p_bytespersamp = bytespersamp;
+ *p_bytelimit = bytelimit;
+ return (fd);
+badheader:
+ /* the header wasn't recognized. We're threadable here so let's not
+ print out the error... */
+ errno = EIO;
+ return (-1);
+}
+
+static void soundfile_xferin(int sfchannels, int nvecs, float **vecs,
+ long itemsread, unsigned char *buf, int nitems, int bytespersamp,
+ int bigendian)
+{
+ int i, j;
+ unsigned char *sp, *sp2;
+ float *fp;
+ int nchannels = (sfchannels < nvecs ? sfchannels : nvecs);
+ int bytesperframe = bytespersamp * sfchannels;
+ for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp)
+ {
+ if (bytespersamp == 2)
+ {
+ if (bigendian)
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16));
+ }
+ else
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ *fp = SCALE * ((sp2[1] << 24) | (sp2[0] << 16));
+ }
+ }
+ else if (bytespersamp == 3)
+ {
+ if (bigendian)
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16)
+ | (sp2[2] << 8));
+ }
+ else
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ *fp = SCALE * ((sp2[2] << 24) | (sp2[1] << 16)
+ | (sp2[0] << 8));
+ }
+ }
+ else if (bytespersamp == 4)
+ {
+ if (bigendian)
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ *(long *)fp = ((sp2[0] << 24) | (sp2[1] << 16)
+ | (sp2[2] << 8) | sp2[3]);
+ }
+ else
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ *(long *)fp = ((sp2[3] << 24) | (sp2[2] << 16)
+ | (sp2[1] << 8) | sp2[0]);
+ }
+ }
+ }
+ /* zero out other outputs */
+ for (i = sfchannels; i < nvecs; i++)
+ for (j = nitems, fp = vecs[i]; j--; )
+ *fp++ = 0;
+
+}
+
+ /* soundfiler_write ...
+
+ usage: write [flags] filename table ...
+ flags:
+ -nframes <frames>
+ -skip <frames>
+ -bytes <bytes per sample>
+ -normalize
+ -nextstep
+ -wave
+ -big
+ -little
+ */
+
+ /* the routine which actually does the work should LATER also be called
+ from garray_write16. */
+
+
+ /* Parse arguments for writing. The "obj" argument is only for flagging
+ errors. For streaming to a file the "normalize", "onset" and "nframes"
+ arguments shouldn't be set but the calling routine flags this. */
+
+static int soundfiler_writeargparse(void *obj, int *p_argc, t_atom **p_argv,
+ t_symbol **p_filesym,
+ int *p_filetype, int *p_bytespersamp, int *p_swap, int *p_bigendian,
+ int *p_normalize, long *p_onset, long *p_nframes, float *p_rate)
+{
+ int argc = *p_argc;
+ t_atom *argv = *p_argv;
+ int bytespersamp = 2, bigendian = 0,
+ endianness = -1, swap, filetype = -1, normalize = 0;
+ long onset = 0, nframes = 0x7fffffff;
+ t_symbol *filesym;
+ float rate = -1;
+
+ while (argc > 0 && argv->a_type == A_SYMBOL &&
+ *argv->a_w.w_symbol->s_name == '-')
+ {
+ char *flag = argv->a_w.w_symbol->s_name + 1;
+ if (!strcmp(flag, "skip"))
+ {
+ if (argc < 2 || argv[1].a_type != A_FLOAT ||
+ ((onset = argv[1].a_w.w_float) < 0))
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(flag, "nframes"))
+ {
+ if (argc < 2 || argv[1].a_type != A_FLOAT ||
+ ((nframes = argv[1].a_w.w_float) < 0))
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(flag, "bytes"))
+ {
+ if (argc < 2 || argv[1].a_type != A_FLOAT ||
+ ((bytespersamp = argv[1].a_w.w_float) < 2) ||
+ bytespersamp > 4)
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(flag, "normalize"))
+ {
+ normalize = 1;
+ argc -= 1; argv += 1;
+ }
+ else if (!strcmp(flag, "wave"))
+ {
+ filetype = FORMAT_WAVE;
+ argc -= 1; argv += 1;
+ }
+ else if (!strcmp(flag, "nextstep"))
+ {
+ filetype = FORMAT_NEXT;
+ argc -= 1; argv += 1;
+ }
+ else if (!strcmp(flag, "aiff"))
+ {
+ filetype = FORMAT_AIFF;
+ argc -= 1; argv += 1;
+ }
+ else if (!strcmp(flag, "big"))
+ {
+ endianness = 1;
+ argc -= 1; argv += 1;
+ }
+ else if (!strcmp(flag, "little"))
+ {
+ endianness = 0;
+ argc -= 1; argv += 1;
+ }
+ else if (!strcmp(flag, "r") || !strcmp(flag, "rate"))
+ {
+ if (argc < 2 || argv[1].a_type != A_FLOAT ||
+ ((rate = argv[1].a_w.w_float) <= 0))
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else goto usage;
+ }
+ if (!argc || argv->a_type != A_SYMBOL)
+ goto usage;
+ filesym = argv->a_w.w_symbol;
+
+ /* check if format not specified and fill in */
+ if (filetype < 0)
+ {
+ if (strlen(filesym->s_name) >= 5 &&
+ (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".aif") ||
+ !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".AIF")))
+ filetype = FORMAT_AIFF;
+ if (strlen(filesym->s_name) >= 6 &&
+ (!strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".aiff") ||
+ !strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".AIFF")))
+ filetype = FORMAT_AIFF;
+ if (strlen(filesym->s_name) >= 5 &&
+ (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".snd") ||
+ !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".SND")))
+ filetype = FORMAT_NEXT;
+ if (strlen(filesym->s_name) >= 4 &&
+ (!strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".au") ||
+ !strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".AU")))
+ filetype = FORMAT_NEXT;
+ if (filetype < 0)
+ filetype = FORMAT_WAVE;
+ }
+ /* don't handle AIFF floating point samples */
+ if (bytespersamp == 4)
+ {
+ if (filetype == FORMAT_AIFF)
+ {
+ pd_error(obj, "AIFF floating-point file format unavailable");
+ goto usage;
+ }
+ }
+ /* for WAVE force little endian; for nextstep use machine native */
+ if (filetype == FORMAT_WAVE)
+ {
+ bigendian = 0;
+ if (endianness == 1)
+ pd_error(obj, "WAVE file forced to little endian");
+ }
+ else if (filetype == FORMAT_AIFF)
+ {
+ bigendian = 1;
+ if (endianness == 0)
+ pd_error(obj, "AIFF file forced to big endian");
+ }
+ else if (endianness == -1)
+ {
+ bigendian = garray_ambigendian();
+ }
+ else bigendian = endianness;
+ swap = (bigendian != garray_ambigendian());
+
+ argc--; argv++;
+
+ *p_argc = argc;
+ *p_argv = argv;
+ *p_filesym = filesym;
+ *p_filetype = filetype;
+ *p_bytespersamp = bytespersamp;
+ *p_swap = swap;
+ *p_normalize = normalize;
+ *p_onset = onset;
+ *p_nframes = nframes;
+ *p_bigendian = bigendian;
+ *p_rate = rate;
+ return (0);
+usage:
+ return (-1);
+}
+
+static int create_soundfile(t_canvas *canvas, const char *filename,
+ int filetype, int nframes, int bytespersamp,
+ int bigendian, int nchannels, int swap, float samplerate)
+{
+ char filenamebuf[MAXPDSTRING], buf2[MAXPDSTRING];
+ char headerbuf[WRITEHDRSIZE];
+ t_wave *wavehdr = (t_wave *)headerbuf;
+ t_nextstep *nexthdr = (t_nextstep *)headerbuf;
+ t_aiff *aiffhdr = (t_aiff *)headerbuf;
+ int fd, headersize = 0;
+
+ strncpy(filenamebuf, filename, MAXPDSTRING-10);
+ filenamebuf[MAXPDSTRING-10] = 0;
+
+ if (filetype == FORMAT_NEXT)
+ {
+ if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".snd"))
+ strcat(filenamebuf, ".snd");
+ if (bigendian)
+ strncpy(nexthdr->ns_fileid, ".snd", 4);
+ else strncpy(nexthdr->ns_fileid, "dns.", 4);
+ nexthdr->ns_onset = swap4(sizeof(*nexthdr), swap);
+ nexthdr->ns_length = 0;
+ nexthdr->ns_format = swap4((bytespersamp == 3 ? NS_FORMAT_LINEAR_24 :
+ (bytespersamp == 4 ? NS_FORMAT_FLOAT : NS_FORMAT_LINEAR_16)), swap);
+ nexthdr->ns_sr = swap4(samplerate, swap);
+ nexthdr->ns_nchans = swap4(nchannels, swap);
+ strcpy(nexthdr->ns_info, "Pd ");
+ swapstring(nexthdr->ns_info, swap);
+ headersize = sizeof(t_nextstep);
+ }
+ else if (filetype == FORMAT_AIFF)
+ {
+ long datasize = nframes * nchannels * bytespersamp;
+ long longtmp;
+ static unsigned char dogdoo[] =
+ {0x40, 0x0e, 0xac, 0x44, 0, 0, 0, 0, 0, 0, 'S', 'S', 'N', 'D'};
+ if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".aif") &&
+ strcmp(filenamebuf + strlen(filenamebuf)-5, ".aiff"))
+ strcat(filenamebuf, ".aif");
+ strncpy(aiffhdr->a_fileid, "FORM", 4);
+ aiffhdr->a_chunksize = swap4(datasize + sizeof(*aiffhdr) + 4, swap);
+ strncpy(aiffhdr->a_aiffid, "AIFF", 4);
+ strncpy(aiffhdr->a_fmtid, "COMM", 4);
+ aiffhdr->a_fmtchunksize = swap4(18, swap);
+ aiffhdr->a_nchannels = swap2(nchannels, swap);
+ longtmp = swap4(nframes, swap);
+ memcpy(&aiffhdr->a_nframeshi, &longtmp, 4);
+ aiffhdr->a_bitspersamp = swap2(8 * bytespersamp, swap);
+ memcpy(aiffhdr->a_samprate, dogdoo, sizeof(dogdoo));
+ longtmp = swap4(datasize, swap);
+ memcpy(aiffhdr->a_samprate + sizeof(dogdoo), &longtmp, 4);
+ headersize = AIFFPLUS;
+ }
+ else /* WAVE format */
+ {
+ long datasize = nframes * nchannels * bytespersamp;
+ if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".wav"))
+ strcat(filenamebuf, ".wav");
+ strncpy(wavehdr->w_fileid, "RIFF", 4);
+ wavehdr->w_chunksize = swap4(datasize + sizeof(*wavehdr) - 8, swap);
+ strncpy(wavehdr->w_waveid, "WAVE", 4);
+ strncpy(wavehdr->w_fmtid, "fmt ", 4);
+ wavehdr->w_fmtchunksize = swap4(16, swap);
+ wavehdr->w_fmttag =
+ swap2((bytespersamp == 4 ? WAV_FLOAT : WAV_INT), swap);
+ wavehdr->w_nchannels = swap2(nchannels, swap);
+ wavehdr->w_samplespersec = swap4(samplerate, swap);
+ wavehdr->w_navgbytespersec =
+ swap4((int)(samplerate * nchannels * bytespersamp), swap);
+ wavehdr->w_nblockalign = swap2(nchannels * bytespersamp, swap);
+ wavehdr->w_nbitspersample = swap2(8 * bytespersamp, swap);
+ strncpy(wavehdr->w_datachunkid, "data", 4);
+ wavehdr->w_datachunksize = swap4(datasize, swap);
+ headersize = sizeof(t_wave);
+ }
+
+ canvas_makefilename(canvas, filenamebuf, buf2, MAXPDSTRING);
+ sys_bashfilename(buf2, buf2);
+ if ((fd = open(buf2, BINCREATE, 0666)) < 0)
+ return (-1);
+
+ if (write(fd, headerbuf, headersize) < headersize)
+ {
+ close (fd);
+ return (-1);
+ }
+ return (fd);
+}
+
+static void soundfile_finishwrite(void *obj, char *filename, int fd,
+ int filetype, long nframes, long itemswritten, int bytesperframe, int swap)
+{
+ if (itemswritten < nframes)
+ {
+ if (nframes < 0x7fffffff)
+ pd_error(obj, "soundfiler_write: %d out of %d bytes written",
+ itemswritten, nframes);
+ /* try to fix size fields in header */
+ if (filetype == FORMAT_WAVE)
+ {
+ long datasize = itemswritten * bytesperframe, mofo;
+
+ if (lseek(fd,
+ ((char *)(&((t_wave *)0)->w_chunksize)) - (char *)0,
+ SEEK_SET) == 0)
+ goto baddonewrite;
+ mofo = swap4(datasize + sizeof(t_wave) - 8, swap);
+ if (write(fd, (char *)(&mofo), 4) < 4)
+ goto baddonewrite;
+ if (lseek(fd,
+ ((char *)(&((t_wave *)0)->w_datachunksize)) - (char *)0,
+ SEEK_SET) == 0)
+ goto baddonewrite;
+ mofo = swap4(datasize, swap);
+ if (write(fd, (char *)(&mofo), 4) < 4)
+ goto baddonewrite;
+ }
+ if (filetype == FORMAT_AIFF)
+ {
+ long mofo;
+ if (lseek(fd,
+ ((char *)(&((t_aiff *)0)->a_nframeshi)) - (char *)0,
+ SEEK_SET) == 0)
+ goto baddonewrite;
+ mofo = swap4(nframes, swap);
+ if (write(fd, (char *)(&mofo), 4) < 4)
+ goto baddonewrite;
+ }
+ if (filetype == FORMAT_NEXT)
+ {
+ /* do it the lazy way: just set the size field to 'unknown size'*/
+ uint32 nextsize = 0xffffffff;
+ if (lseek(fd, 8, SEEK_SET) == 0)
+ {
+ goto baddonewrite;
+ }
+ if (write(fd, &nextsize, 4) < 4)
+ {
+ goto baddonewrite;
+ }
+ }
+ }
+ return;
+baddonewrite:
+ post("%s: %s", filename, strerror(errno));
+}
+
+static void soundfile_xferout(int nchannels, float **vecs,
+ unsigned char *buf, int nitems, long onset, int bytespersamp,
+ int bigendian, float normalfactor)
+{
+ int i, j;
+ unsigned char *sp, *sp2;
+ float *fp;
+ int bytesperframe = bytespersamp * nchannels;
+ long xx;
+ for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp)
+ {
+ if (bytespersamp == 2)
+ {
+ float ff = normalfactor * 32768.;
+ if (bigendian)
+ {
+ for (j = 0, sp2 = sp, fp = vecs[i] + onset;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ {
+ int xx = 32768. + (*fp * ff);
+ xx -= 32768;
+ if (xx < -32767)
+ xx = -32767;
+ if (xx > 32767)
+ xx = 32767;
+ sp2[0] = (xx >> 8);
+ sp2[1] = xx;
+ }
+ }
+ else
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + onset;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ {
+ int xx = 32768. + (*fp * ff);
+ xx -= 32768;
+ if (xx < -32767)
+ xx = -32767;
+ if (xx > 32767)
+ xx = 32767;
+ sp2[1] = (xx >> 8);
+ sp2[0] = xx;
+ }
+ }
+ }
+ else if (bytespersamp == 3)
+ {
+ float ff = normalfactor * 8388608.;
+ if (bigendian)
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + onset;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ {
+ int xx = 8388608. + (*fp * ff);
+ xx -= 8388608;
+ if (xx < -8388607)
+ xx = -8388607;
+ if (xx > 8388607)
+ xx = 8388607;
+ sp2[0] = (xx >> 16);
+ sp2[1] = (xx >> 8);
+ sp2[2] = xx;
+ }
+ }
+ else
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + onset;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ {
+ int xx = 8388608. + (*fp * ff);
+ xx -= 8388608;
+ if (xx < -8388607)
+ xx = -8388607;
+ if (xx > 8388607)
+ xx = 8388607;
+ sp2[2] = (xx >> 16);
+ sp2[1] = (xx >> 8);
+ sp2[0] = xx;
+ }
+ }
+ }
+ else if (bytespersamp == 4)
+ {
+ if (bigendian)
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + onset;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ {
+ float f2 = *fp * normalfactor;
+ xx = *(long *)&f2;
+ sp2[0] = (xx >> 24); sp2[1] = (xx >> 16);
+ sp2[2] = (xx >> 8); sp2[3] = xx;
+ }
+ }
+ else
+ {
+ for (j = 0, sp2 = sp, fp=vecs[i] + onset;
+ j < nitems; j++, sp2 += bytesperframe, fp++)
+ {
+ float f2 = *fp * normalfactor;
+ xx = *(long *)&f2;
+ sp2[3] = (xx >> 24); sp2[2] = (xx >> 16);
+ sp2[1] = (xx >> 8); sp2[0] = xx;
+ }
+ }
+ }
+ }
+}
+
+
+
+/* ------- threadedsf - reads and writes soundfiles to/from "garrays" ---- */
+
+// in pd's soundfiler maxsize is 4000000
+// threaded we should be able to hande a bit more
+#define DEFMAXSIZE 40000000
+#define SAMPBUFSIZE 1024
+
+
+static t_class *threadedsf_class;
+
+typedef struct _threadedsf
+{
+ t_object x_obj;
+ t_canvas *x_canvas;
+} t_threadedsf;
+
+#include <sched.h>
+
+#ifdef _POSIX_MEMLOCK
+#include <sys/mman.h>
+#endif /* _POSIX_MEMLOCK */
+
+#ifdef DEBUG
+#define SFDEBUG
+#endif
+
+static pthread_t sf_thread_id; /* id of soundfiler thread */
+//static pthread_attr_t sf_attr;
+
+typedef struct _sfprocess
+{
+ void (* process) (t_threadedsf *,t_symbol *,
+ int, t_atom *); /* function to call */
+ t_threadedsf * x; /* soundfiler */
+ int argc;
+ t_atom * argv;
+ struct _sfprocess * next; /* next object in queue */
+ pthread_mutex_t mutex;
+} t_sfprocess;
+
+/* this is the queue for all threadedsf objects */
+typedef struct _sfqueue
+{
+ t_sfprocess * begin;
+ t_sfprocess * end;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+} t_sfqueue;
+
+static t_sfqueue * threadedsf_queue;
+
+
+/* we fill the queue */
+void threadedsf_queue_add(void (* process) (t_threadedsf *,t_symbol *,
+ int,t_atom *), void * x,
+ int argc, t_atom * argv)
+{
+ /* preparing entry */
+ t_sfprocess * last_entry = (t_sfprocess*)getbytes(sizeof(t_sfprocess));
+
+#ifdef SFDEBUG
+ post("adding process to queue");
+#endif
+
+ pthread_mutex_init(&(last_entry->mutex), NULL);
+ pthread_mutex_lock(&(last_entry->mutex));
+ last_entry->process = process;
+ last_entry->x = x;
+ last_entry->argc = argc;
+ last_entry->argv = copybytes (argv, argc * sizeof(t_atom));
+ last_entry->next = NULL;
+ pthread_mutex_unlock(&(last_entry->mutex));
+
+ /* add new entry to queue */
+ pthread_mutex_lock(&(threadedsf_queue->mutex));
+
+ if (threadedsf_queue->begin==NULL)
+ {
+ threadedsf_queue->begin=last_entry;
+ threadedsf_queue->end=last_entry;
+ }
+ else
+ {
+ pthread_mutex_lock(&(threadedsf_queue->end->mutex));
+ threadedsf_queue->end->next=last_entry;
+ pthread_mutex_unlock(&(threadedsf_queue->end->mutex));
+ threadedsf_queue->end=last_entry;
+ }
+
+
+ if ( threadedsf_queue->begin == threadedsf_queue->end )
+ {
+#ifdef DEBUG
+ post("signaling");
+#endif
+ pthread_mutex_unlock(&(threadedsf_queue->mutex));
+
+ /* and signal the helper thread */
+ pthread_cond_signal(&(threadedsf_queue->cond));
+ }
+ else
+ {
+#ifdef DEBUG
+ post("not signaling");
+#endif
+ pthread_mutex_unlock(&(threadedsf_queue->mutex));
+ }
+ return;
+}
+
+/* global threadedsf thread ... sleeping until signaled */
+void threadedsf_thread(void)
+{
+ t_sfprocess * me;
+ t_sfprocess * next;
+
+#ifdef DEBUG
+ post("threadedsf_thread ID: %d", pthread_self());
+#endif
+ while (1)
+ {
+#ifdef DEBUG
+ post("Soundfiler sleeping");
+#endif
+ pthread_cond_wait(&threadedsf_queue->cond, &threadedsf_queue->mutex);
+#ifdef DEBUG
+ post("Soundfiler awake");
+#endif
+
+ /* work on the queue */
+ while (threadedsf_queue->begin!=NULL)
+ {
+ post("threadedsf: locked queue");
+ /* locking process */
+ pthread_mutex_lock(&(threadedsf_queue->begin->mutex));
+
+ me = threadedsf_queue->begin;
+
+ pthread_mutex_unlock(&(me->mutex));
+ pthread_mutex_unlock(&(threadedsf_queue->mutex));
+#ifdef DEBUG
+ post("threadedsf: mutex unlocked, running process");
+#endif
+
+ /* running the specific function */
+ me->process(me->x, NULL, me->argc, me->argv);
+#ifdef DEBUG
+ post("threadedsf: process done, locking mutex");
+#endif
+
+ pthread_mutex_lock(&(threadedsf_queue->mutex));
+ pthread_mutex_lock(&(me->mutex));
+
+ /* freeing the argument vector */
+ freebytes(me->argv, sizeof(t_atom) * me->argc);
+
+ /* the process struct */
+ next=me->next;
+ threadedsf_queue->begin=next;
+ freebytes(me, sizeof(t_sfprocess));
+ };
+ threadedsf_queue->end=NULL;
+ }
+}
+
+extern int sys_hipriority; /* real-time flag, true if priority boosted */
+
+/* create soundfiler thread */
+void sys_start_sfthread(void)
+{
+ pthread_attr_t sf_attr;
+ struct sched_param sf_param;
+
+ int status;
+
+ //initialize queue
+ threadedsf_queue = getbytes (sizeof(t_sfqueue));
+
+ pthread_mutex_init (&threadedsf_queue->mutex,NULL);
+ pthread_cond_init (&threadedsf_queue->cond,NULL);
+
+ threadedsf_queue->begin=threadedsf_queue->end=NULL;
+/* pthread_mutex_unlock(&(threadedsf_queue->mutex)); */
+
+ // initialize thread
+ pthread_attr_init(&sf_attr);
+
+//#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
+ sf_param.sched_priority=sched_get_priority_min(SCHED_OTHER);
+ pthread_attr_setschedparam(&sf_attr,&sf_param);
+/* pthread_attr_setinheritsched(&sf_attr,PTHREAD_EXPLICIT_SCHED); */
+
+#ifdef UNIX
+ if (sys_hipriority == 1 && getuid() == 0)
+ {
+ sf_param.sched_priority=sched_get_priority_min(SCHED_RR);
+ pthread_attr_setschedpolicy(&sf_attr,SCHED_RR);
+ }
+ else
+ {
+/* pthread_attr_setschedpolicy(&sf_attr,SCHED_OTHER); */
+/* sf_param.sched_priority=sched_get_priority_min(SCHED_OTHER); */
+ }
+#endif /* UNIX */
+
+//#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */
+
+ //start thread
+ status = pthread_create(&sf_thread_id, &sf_attr,
+ (void *) threadedsf_thread,NULL);
+ if (status != 0)
+ error("Couldn't create threadedsf thread: %d",status);
+#ifdef DEBUG
+ else
+ post("global threadedsf thread launched, priority: %d",
+ sf_param.sched_priority);
+#endif
+}
+
+static void threadedsf_t_write(t_threadedsf *x, t_symbol *s,
+ int argc, t_atom *argv);
+
+static void threadedsf_t_write_addq(t_threadedsf *x, t_symbol *s,
+ int argc, t_atom *argv)
+{
+ threadedsf_queue_add(threadedsf_t_write,(void *)x,argc, argv);
+}
+
+static void threadedsf_t_read(t_threadedsf *x, t_symbol *s,
+ int argc, t_atom *argv);
+
+static void threadedsf_t_read_addq(t_threadedsf *x, t_symbol *s,
+ int argc, t_atom *argv)
+{
+ threadedsf_queue_add(threadedsf_t_read,(void *)x,argc, argv);
+}
+
+
+ /* threadedsf_read ...
+
+ usage: read [flags] filename table ...
+ flags:
+ -skip <frames> ... frames to skip in file
+ -nframes <frames>
+ -onset <frames> ... onset in table to read into (NOT DONE YET)
+ -raw <headersize channels bytes endian>
+ -resize
+ -maxsize <max-size>
+
+ TB: adapted for threaded use
+ */
+
+/* callback prototypes */
+static t_int threadedsf_read_update_garray(t_int * w);
+static t_int threadedsf_read_update_graphics(t_int * w);
+static t_int threadedsf_read_output(t_int * w);
+
+
+static void threadedsf_t_read(t_threadedsf *x, t_symbol *s,
+ int argc, t_atom *argv)
+{
+ int headersize = -1, channels = 0, bytespersamp = 0, bigendian = 0,
+ resize = 0, i, j;
+ long skipframes = 0,
+ nframes = 0,
+ finalsize = 0, /* new size */
+ maxsize = DEFMAXSIZE,
+ itemsread = 0,
+ bytelimit = 0x7fffffff;
+ int fd = -1;
+ char endianness, *filename;
+ t_garray *garrays[MAXSFCHANS];
+ t_float *vecs[MAXSFCHANS]; /* the old array */
+ t_float *nvecs[MAXSFCHANS]; /* the new array */
+ int vecsize[MAXSFCHANS]; /* the old array size */
+ char sampbuf[SAMPBUFSIZE];
+ int bufframes, nitems;
+ FILE *fp;
+ pthread_cond_t resume_after_callback = PTHREAD_COND_INITIALIZER;
+ pthread_mutex_t resume_after_callback_mutex = PTHREAD_MUTEX_INITIALIZER; /* dummy */
+ t_int* outargs;
+
+ while (argc > 0 && argv->a_type == A_SYMBOL &&
+ *argv->a_w.w_symbol->s_name == '-')
+ {
+ char *flag = argv->a_w.w_symbol->s_name + 1;
+ if (!strcmp(flag, "skip"))
+ {
+ if (argc < 2 || argv[1].a_type != A_FLOAT ||
+ ((skipframes = argv[1].a_w.w_float) < 0))
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(flag, "nframes"))
+ {
+ if (argc < 2 || argv[1].a_type != A_FLOAT ||
+ ((nframes = argv[1].a_w.w_float) < 0))
+ goto usage;
+ argc -= 2; argv += 2;
+ }
+ else if (!strcmp(flag, "raw"))
+ {
+ if (argc < 5 ||
+ argv[1].a_type != A_FLOAT ||
+ ((headersize = argv[1].a_w.w_float) < 0) ||
+ argv[2].a_type != A_FLOAT ||
+ ((channels = argv[2].a_w.w_float) < 1) ||
+ (channels > MAXSFCHANS) ||
+ argv[3].a_type != A_FLOAT ||
+ ((bytespersamp = argv[3].a_w.w_float) < 2) ||
+ (bytespersamp > 4) ||
+ argv[4].a_type != A_SYMBOL ||
+ ((endianness = argv[4].a_w.w_symbol->s_name[0]) != 'b'
+ && endianness != 'l' && endianness != 'n'))
+ goto usage;
+ if (endianness == 'b')
+ bigendian = 1;
+ else if (endianness == 'l')
+ bigendian = 0;
+ else
+ bigendian = garray_ambigendian();
+ argc -= 5; argv += 5;
+ }
+ else if (!strcmp(flag, "resize"))
+ {
+ resize = 1;
+ argc -= 1; argv += 1;
+ }
+ else if (!strcmp(flag, "maxsize"))
+ {
+ if (argc < 2 || argv[1].a_type != A_FLOAT ||
+ ((maxsize = argv[1].a_w.w_float) < 0))
+ goto usage;
+ resize = 1; /* maxsize implies resize. */
+ argc -= 2; argv += 2;
+ }
+ else goto usage;
+ }
+ if (argc < 2 || argc > MAXSFCHANS + 1 || argv[0].a_type != A_SYMBOL)
+ goto usage;
+ filename = argv[0].a_w.w_symbol->s_name;
+ argc--; argv++;
+
+ for (i = 0; i < argc; i++)
+ {
+ if (argv[i].a_type != A_SYMBOL)
+ goto usage;
+ if (!(garrays[i] =
+ (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class)))
+ {
+ pd_error(x, "%s: no such table", argv[i].a_w.w_symbol->s_name);
+ goto done;
+ }
+ else if (!garray_getfloatarray(garrays[i], &vecsize[i], &vecs[i]))
+ error("%s: bad template for tabwrite",
+ argv[i].a_w.w_symbol->s_name);
+ if (finalsize && finalsize != vecsize[i] && !resize)
+ {
+ post("threadedsf_read: arrays have different lengths; resizing...");
+ resize = 1;
+ }
+ finalsize = vecsize[i];
+ }
+ fd = open_soundfile(canvas_getdir(x->x_canvas)->s_name, filename,
+ headersize, &bytespersamp, &bigendian, &channels, &bytelimit,
+ skipframes);
+
+ if (fd < 0)
+ {
+ pd_error(x, "threadedsf_read: %s: %s", filename, (errno == EIO ?
+ "unknown or bad header format" : strerror(errno)));
+ goto done;
+ }
+
+ if (resize)
+ {
+ /* figure out what to resize to */
+ long poswas, eofis, framesinfile;
+
+ poswas = lseek(fd, 0, SEEK_CUR);
+ eofis = lseek(fd, 0, SEEK_END);
+ if (poswas < 0 || eofis < 0)
+ {
+ pd_error(x, "lseek failed");
+ goto done;
+ }
+ lseek(fd, poswas, SEEK_SET);
+ framesinfile = (eofis - poswas) / (channels * bytespersamp);
+ if (framesinfile > maxsize)
+ {
+ pd_error(x, "threadedsf_read: truncated to %d elements", maxsize);
+ framesinfile = maxsize;
+ }
+ if (framesinfile > bytelimit / (channels * bytespersamp))
+ framesinfile = bytelimit / (channels * bytespersamp);
+ finalsize = framesinfile;
+
+ }
+ if (!finalsize) finalsize = 0x7fffffff;
+ if (finalsize > bytelimit / (channels * bytespersamp))
+ finalsize = bytelimit / (channels * bytespersamp);
+ fp = fdopen(fd, "rb");
+ bufframes = SAMPBUFSIZE / (channels * bytespersamp);
+
+#ifdef SFDEBUG
+ post("buffers: %d", argc);
+ post("channels: %d", channels);
+#endif
+
+#ifdef _POSIX_MEMLOCK
+ munlockall();
+#endif
+
+ /* allocate memory for new array */
+ if (resize)
+ for (i = 0; i < argc; i++)
+ {
+#ifdef SFDEBUG
+ post("allocating buffer %d",i);
+#endif
+ nvecs[i]=getbytes (finalsize * sizeof(t_float));
+ /* if we are out of memory, free it again and quit */
+ if (nvecs[i]==0)
+ {
+ pd_error(x, "resize failed");
+ for (j=0; j!=i;++j)
+ /* if the resizing fails, we'll have to free all arrays again */
+ freebytes (nvecs[i],finalsize * sizeof(t_float));
+ goto done;
+ }
+ /* zero samples */
+ if(i > channels)
+ {
+ memset(nvecs[i],0,vecsize[i] * sizeof(t_float));
+ }
+ }
+ else
+ for (i = 0; i < argc; i++)
+ {
+ nvecs[i] = getbytes (vecsize[i] * sizeof(t_float));
+ /* if we are out of memory, free it again and quit */
+ if (nvecs[i]==0)
+ {
+ pd_error(x, "resize failed");
+ for (j=0; j!=i;++j)
+ /* if the resizing fails, we'll have to free all arrays again */
+ freebytes (nvecs[i],vecsize[i] * sizeof(t_float));
+ goto done;
+ }
+ /* zero samples */
+ if(i > channels)
+ memset(nvecs[i],0,vecsize[i] * sizeof(t_float));
+ }
+
+#ifdef SFDEBUG
+ post("transfer soundfile");
+#endif
+
+ for (itemsread = 0; itemsread < finalsize; )
+ {
+ int thisread = finalsize - itemsread;
+ thisread = (thisread > bufframes ? bufframes : thisread);
+ nitems = fread(sampbuf, channels * bytespersamp, thisread, fp);
+ if (nitems <= 0) break;
+ soundfile_xferin(channels, argc, nvecs, itemsread,
+ (unsigned char *)sampbuf, nitems, bytespersamp, bigendian);
+ itemsread += nitems;
+ }
+
+#ifdef SFDEBUG
+ post("zeroing remaining elements");
+#endif
+ /* zero out remaining elements of vectors */
+ for (i = 0; i < argc; i++)
+ {
+ for (j = itemsread; j < finalsize; j++)
+ nvecs[i][j] = 0;
+ }
+
+ /* set idle callback to switch pointers */
+#ifdef SFDEBUG
+ post("locked");
+#endif
+ for (i = 0; i < argc; i++)
+ {
+ t_int* w = (t_int*)getbytes(4*sizeof(t_int));
+
+ w[0] = (t_int)(garrays[i]);
+ w[1] = (t_int)nvecs[i];
+ w[2] = (t_int)finalsize;
+ w[3] = (t_int)(&resume_after_callback);
+
+ h_set_callback(&threadedsf_read_update_garray, w, 4);
+
+ pthread_cond_wait(&resume_after_callback, &resume_after_callback_mutex);
+ }
+
+#ifdef SFDEBUG
+ post("unlocked, doing graphics updates");
+#endif
+
+ /* do all graphics updates
+ * run this in the main thread via callback */
+ for (i = 0; i < argc; i++)
+ {
+ t_int* w = (t_int*)getbytes(2*sizeof(t_int));
+
+ w[0] = (t_int)(garrays[i]);
+ w[1] = (t_int)finalsize;
+
+ h_set_callback(&threadedsf_read_update_graphics, w, 2);
+ }
+
+ /* free the old arrays */
+ for (i = 0; i < argc; i++)
+ freebytes(vecs[i], vecsize[i] * sizeof(t_float));
+
+ fclose(fp);
+ fd = -1;
+ goto done;
+
+ usage:
+ pd_error(x, "usage: read [flags] filename tablename...");
+ post("flags: -skip <n> -nframes <n> -resize -maxsize <n> ...");
+ post("-raw <headerbytes> <channels> <bytespersamp> <endian (b, l, or n)>.");
+
+ done:
+ if (fd >= 0)
+ close (fd);
+
+#ifdef _POSIX_MEMLOCK
+ mlockall(MCL_FUTURE);
+#endif
+
+ outargs = (t_int*)getbytes(2*sizeof(t_int));
+ outargs[0] = (t_int)x->x_obj.ob_outlet;
+ outargs[1] = (t_int)itemsread;
+ h_set_callback(&threadedsf_read_output, outargs, 2);
+}
+
+/* idle callback for threadsafe synchronisation */
+static t_int threadedsf_read_update_garray(t_int * w)
+{
+ t_garray* garray = (t_garray*)w[0];
+ t_array *data = garray_getarray(garray);
+ t_int nvec = w[1];
+ t_int finalsize = w[2];
+ pthread_cond_t * conditional = (pthread_cond_t*) w[3];
+
+#ifdef SFDEBUG
+ post("lock array %p", garray);
+#endif
+
+ // no garray_locks in current pd
+ //garray_lock(garray);
+
+ data->a_vec = (char *) nvec;
+ data->a_n = finalsize;
+ if (garray->x_usedindsp) canvas_update_dsp();
+
+#ifdef SFDEBUG
+ post("unlock array %p", garray);
+#endif
+
+ // no garray_locks in current pd
+ //garray_unlock(garray);
+
+ /* signal helper thread */
+ pthread_cond_broadcast(conditional);
+
+ return 0;
+}
+
+static t_int threadedsf_read_update_graphics(t_int * w)
+{
+ t_garray* garray = (t_garray*) w[0];
+ t_glist *gl;
+ int n = w[1];
+ /* if this is the only array in the graph,
+ reset the graph's coordinates */
+
+#ifdef SFDEBUG
+ post("redraw array %p", garray);
+#endif
+
+ gl = garray->x_glist;
+ if (gl->gl_list == &garray->x_gobj && !garray->x_gobj.g_next)
+ {
+ vmess(&gl->gl_pd, gensym("bounds"), "ffff",
+ 0., gl->gl_y1, (double)(n > 1 ? n-1 : 1), gl->gl_y2);
+ /* close any dialogs that might have the wrong info now... */
+ gfxstub_deleteforkey(gl);
+ }
+ else
+ garray_redraw(garray);
+
+ return 0;
+}
+
+
+static t_int threadedsf_read_output(t_int * w)
+{
+ t_outlet* outlet = (t_outlet*) w[0];
+ float itemsread = (float) w[1];
+
+#ifdef SFDEBUG
+ post("bang %p", outlet);
+#endif
+
+ outlet_float (outlet, itemsread);
+
+ return 0;
+}
+
+
+
+
+ /* this is broken out from threadedsf_write below so garray_write can
+ call it too... not done yet though. */
+
+long threadedsf_t_dowrite(void *obj, t_canvas *canvas,
+ int argc, t_atom *argv)
+{
+ int bytespersamp, bigendian,
+ swap, filetype, normalize, i, j, nchannels;
+ long onset, nframes, itemswritten = 0;
+ t_garray *garrays[MAXSFCHANS];
+ t_float *vecs[MAXSFCHANS];
+ char sampbuf[SAMPBUFSIZE];
+ int bufframes;
+ int fd = -1;
+ float normfactor, biggest = 0, samplerate;
+ t_symbol *filesym;
+
+ if (soundfiler_writeargparse(obj, &argc, &argv, &filesym, &filetype,
+ &bytespersamp, &swap, &bigendian, &normalize, &onset, &nframes,
+ &samplerate))
+ goto usage;
+ nchannels = argc;
+ if (nchannels < 1 || nchannels > MAXSFCHANS)
+ goto usage;
+ if (samplerate < 0)
+ samplerate = sys_getsr();
+ for (i = 0; i < nchannels; i++)
+ {
+ int vecsize;
+ if (argv[i].a_type != A_SYMBOL)
+ goto usage;
+ if (!(garrays[i] =
+ (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class)))
+ {
+ pd_error(obj, "%s: no such table", argv[i].a_w.w_symbol->s_name);
+ goto fail;
+ }
+ else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i]))
+ error("%s: bad template for tabwrite",
+ argv[i].a_w.w_symbol->s_name);
+ if (nframes > vecsize - onset)
+ nframes = vecsize - onset;
+
+ for (j = 0; j < vecsize; j++)
+ {
+ if (vecs[i][j] > biggest)
+ biggest = vecs[i][j];
+ else if (-vecs[i][j] > biggest)
+ biggest = -vecs[i][j];
+ }
+ }
+ if (nframes <= 0)
+ {
+ pd_error(obj, "threadedsf_write: no samples at onset %ld", onset);
+ goto fail;
+ }
+
+ if ((fd = create_soundfile(canvas, filesym->s_name, filetype,
+ nframes, bytespersamp, bigendian, nchannels,
+ swap, samplerate)) < 0)
+ {
+ post("%s: %s\n", filesym->s_name, strerror(errno));
+ goto fail;
+ }
+ if (!normalize)
+ {
+ if ((bytespersamp != 4) && (biggest > 1))
+ {
+ post("%s: normalizing max amplitude %f to 1", filesym->s_name, biggest);
+ normalize = 1;
+ }
+ else post("%s: biggest amplitude = %f", filesym->s_name, biggest);
+ }
+ if (normalize)
+ normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1);
+ else normfactor = 1;
+
+ bufframes = SAMPBUFSIZE / (nchannels * bytespersamp);
+
+ for (itemswritten = 0; itemswritten < nframes; )
+ {
+ int thiswrite = nframes - itemswritten, nbytes;
+ thiswrite = (thiswrite > bufframes ? bufframes : thiswrite);
+ soundfile_xferout(argc, vecs, (unsigned char *)sampbuf, thiswrite,
+ onset, bytespersamp, bigendian, normfactor);
+ nbytes = write(fd, sampbuf, nchannels * bytespersamp * thiswrite);
+ if (nbytes < nchannels * bytespersamp * thiswrite)
+ {
+ post("%s: %s", filesym->s_name, strerror(errno));
+ if (nbytes > 0)
+ itemswritten += nbytes / (nchannels * bytespersamp);
+ break;
+ }
+ itemswritten += thiswrite;
+ onset += thiswrite;
+ }
+ if (fd >= 0)
+ {
+ soundfile_finishwrite(obj, filesym->s_name, fd,
+ filetype, nframes, itemswritten, nchannels * bytespersamp, swap);
+ close (fd);
+ }
+ return ((float)itemswritten);
+usage:
+ pd_error(obj, "usage: write [flags] filename tablename...");
+ post("flags: -skip <n> -nframes <n> -bytes <n> -wave -aiff -nextstep ...");
+ post("-big -little -normalize");
+ post("(defaults to a 16-bit wave file).");
+fail:
+ if (fd >= 0)
+ close (fd);
+ return (0);
+}
+
+static void threadedsf_t_write(t_threadedsf *x, t_symbol *s,
+ int argc, t_atom *argv)
+{
+ long bozo = threadedsf_t_dowrite(x, x->x_canvas,
+ argc, argv);
+ sys_lock();
+ outlet_float(x->x_obj.ob_outlet, (float)bozo);
+ sys_lock();
+}
+
+static void threadedsf_t_resize(t_threadedsf *x, t_symbol *s,
+ int argc, t_atom *argv);
+
+static void threadedsf_t_resize_addq(t_threadedsf *x, t_symbol *s,
+ int argc, t_atom *argv)
+{
+ threadedsf_queue_add(threadedsf_t_resize,(void *)x,argc, argv);
+}
+
+
+ /* TB: threadedsf_t_resize ...
+ usage: resize table size
+ adapted from garray_resize
+ */
+static void threadedsf_t_resize(t_threadedsf *y, t_symbol *s,
+ int argc, t_atom *argv)
+{
+ int was, elemsize; /* array contains was elements of size elemsize */
+ t_float * vec; /* old array */
+ t_glist *gl;
+ int n; /* resize of n elements */
+ char *nvec; /* new array */
+
+ t_garray * x = (t_garray *)pd_findbyclass(argv[0].a_w.w_symbol, garray_class);
+ t_array *data = garray_getarray(x); // TODO muss der im lock sein ?
+
+ if (!(x))
+ {
+ pd_error(y, "%s: no such table", argv[0].a_w.w_symbol->s_name);
+ goto usage;
+ }
+
+ vec = (t_float*) data->a_vec;
+ was = data->a_n;
+
+ if ((argv+1)->a_type == A_FLOAT)
+ {
+ n = (int) (argv+1)->a_w.w_float;
+ }
+ else
+ {
+ goto usage;
+ }
+
+ if (n == was)
+ {
+ return;
+ }
+
+ if (n < 1) n = 1;
+ elemsize = template_findbyname(data->a_templatesym)->t_n * sizeof(t_word);
+
+#ifdef _POSIX_MEMLOCK
+ munlockall();
+#endif
+ if (was > n)
+ {
+ nvec = (char*)copybytes(data->a_vec, was * elemsize);
+ }
+ else
+ {
+ nvec = getbytes(n * elemsize);
+ memcpy (nvec, data->a_vec, was * elemsize);
+
+ /* LATER should check t_resizebytes result */
+ memset(nvec + was*elemsize,
+ 0, (n - was) * elemsize);
+ }
+ if (!nvec)
+ {
+ pd_error(x, "array resize failed: out of memory");
+#ifdef _POSIX_MEMLOCK
+ mlockall(MCL_FUTURE);
+#endif
+ return;
+ }
+
+
+ /* TB: we'll have to be sure that no one is accessing the array */
+ sys_lock();
+
+ // no garray_locks in current pd
+//#ifdef GARRAY_THREAD_LOCK
+ //garray_lock(x);
+//#endif
+
+ data->a_vec = nvec;
+ data->a_n = n;
+
+ // no garray_locks in current pd
+//#ifdef GARRAY_THREAD_LOCK
+ //garray_unlock(x);
+//#endif
+
+ if (x->x_usedindsp) canvas_update_dsp();
+ sys_unlock();
+
+
+ /* if this is the only array in the graph,
+ reset the graph's coordinates */
+ gl = x->x_glist;
+ if (gl->gl_list == &x->x_gobj && !x->x_gobj.g_next)
+ {
+ vmess(&gl->gl_pd, gensym("bounds"), "ffff",
+ 0., gl->gl_y1, (double)(n > 1 ? n-1 : 1), gl->gl_y2);
+ /* close any dialogs that might have the wrong info now... */
+ gfxstub_deleteforkey(gl);
+ }
+ else garray_redraw(x);
+
+ freebytes (vec, was * elemsize);
+#ifdef _POSIX_MEMLOCK
+ mlockall(MCL_FUTURE);
+#endif
+ sys_lock();
+ outlet_float(y->x_obj.ob_outlet, (float)atom_getintarg(1,argc,argv));
+ sys_unlock();
+ return;
+
+ usage:
+ pd_error(x, "usage: resize tablename size");
+}
+
+
+//static void threadedsf_t_const(t_threadedsf *x, t_symbol *s,
+// int argc, t_atom *argv);
+//
+//static void threadedsf_t_const_addq(t_threadedsf *x, t_symbol *s,
+// int argc, t_atom *argv)
+//{
+// threadedsf_queue_add(threadedsf_t_const,(void *)x,argc, argv);
+//}
+
+
+/* TB: threadedsf_t_const ...
+ usage: const table value
+*/
+//static void threadedsf_t_const(t_threadedsf *y, t_symbol *s,
+// int argc, t_atom *argv)
+//{
+// int size, elemsize; /* array contains was elements of size elemsize */
+// t_float * vec; /* old array */
+// t_glist *gl;
+// int val; /* value */
+// char *nvec; /* new array */
+// int i;
+//
+// t_garray * x = (t_garray *)pd_findbyclass(argv[0].a_w.w_symbol, garray_class);
+// t_array *data = garray_getarray(x); // TODO muss der im lock sein ?
+//
+// if (!(x))
+// {
+// pd_error(y, "%s: no such table", argv[0].a_w.w_symbol->s_name);
+// goto usage;
+// }
+//
+//
+//
+// vec = (t_float*) data->a_vec;
+// size = data->a_n;
+//
+// if ((argv+1)->a_type == A_FLOAT)
+// {
+// val = (int) (argv+1)->a_w.w_float;
+// }
+// else
+// {
+// goto usage;
+// }
+//
+//#ifdef SFDEBUG
+// post("array size: %d; new value: %d",size,val);
+//#endif
+//
+// elemsize = template_findbyname(data->a_templatesym)->t_n * sizeof(t_word);
+//
+//
+// /* allocating memory */
+//#ifdef _POSIX_MEMLOCK
+// munlockall();
+//#endif
+// nvec = getbytes(size * elemsize);
+//
+// if (!nvec)
+// {
+// pd_error(x, "array resize failed: out of memory");
+//#ifdef _POSIX_MEMLOCK
+// mlockall(MCL_FUTURE);
+//#endif
+// return;
+// }
+//
+// /* setting array */
+// for (i=0; i!=size; ++i)
+// {
+// nvec[i]=val;
+// }
+//
+// /* TB: we'll have to be sure that no one is accessing the array */
+// sys_lock();
+//#ifdef GARRAY_THREAD_LOCK
+// garray_lock(x);
+//#endif
+// data->a_vec = nvec;
+//#ifdef GARRAY_THREAD_LOCK
+// garray_unlock(x);
+//#endif
+// if (x->x_usedindsp) canvas_update_dsp();
+// sys_unlock();
+//
+//
+// /* if this is the only array in the graph,
+// reset the graph's coordinates */
+// gl = x->x_glist;
+// if (gl->gl_list == &x->x_gobj && !x->x_gobj.g_next)
+// {
+// vmess(&gl->gl_pd, gensym("bounds"), "ffff",
+// 0., gl->gl_y1, (double)(size > 1 ? size-1 : 1), gl->gl_y2);
+// /* close any dialogs that might have the wrong info now... */
+// gfxstub_deleteforkey(gl);
+// }
+// else garray_redraw(x);
+//
+// freebytes (vec, size * elemsize);
+//#ifdef _POSIX_MEMLOCK
+// mlockall(MCL_FUTURE);
+//#endif
+// sys_lock();
+// outlet_float(y->x_obj.ob_outlet, size);
+// sys_unlock();
+// return;
+//
+// usage:
+// pd_error(x, "usage: const tablename value");
+//}
+
+
+static t_threadedsf *threadedsf_new(void)
+{
+ t_threadedsf *x = (t_threadedsf *)pd_new(threadedsf_class);
+ x->x_canvas = canvas_getcurrent();
+ outlet_new(&x->x_obj, &s_float);
+ return (x);
+}
+
+
+void threadedsf_setup(void)
+{
+ threadedsf_class = class_new(gensym("threadedsf"), (t_newmethod)threadedsf_new,
+ 0, sizeof(t_threadedsf), 0, 0);
+
+ class_addmethod(threadedsf_class, (t_method)threadedsf_t_read_addq,
+ gensym("read"), A_GIMME, 0);
+ class_addmethod(threadedsf_class, (t_method)threadedsf_t_write_addq,
+ gensym("write"), A_GIMME, 0);
+ class_addmethod(threadedsf_class, (t_method)threadedsf_t_resize_addq,
+ gensym("resize"), A_GIMME, 0);
+// class_addmethod(threadedsf_class, (t_method)threadedsf_t_const_addq,
+// gensym("const"), A_GIMME, 0);
+}