aboutsummaryrefslogtreecommitdiff
path: root/desiredata/src/d_soundfile.c
diff options
context:
space:
mode:
authorIOhannes m zmölnig <zmoelnig@users.sourceforge.net>2008-02-08 13:00:32 +0000
committerIOhannes m zmölnig <zmoelnig@users.sourceforge.net>2008-02-08 13:00:32 +0000
commit4d84d14ac1aa13958eaa2971b03f7f929a519105 (patch)
tree6579d3f2cea5410a10c4baac8d0f372fb0dff372 /desiredata/src/d_soundfile.c
parentb334d38aefbd8e0e159d7af6c20d63c5d2b64859 (diff)
reorganized
svn path=/trunk/; revision=9400
Diffstat (limited to 'desiredata/src/d_soundfile.c')
-rw-r--r--desiredata/src/d_soundfile.c2059
1 files changed, 2059 insertions, 0 deletions
diff --git a/desiredata/src/d_soundfile.c b/desiredata/src/d_soundfile.c
new file mode 100644
index 00000000..2a281869
--- /dev/null
+++ b/desiredata/src/d_soundfile.c
@@ -0,0 +1,2059 @@
+/* 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 */
+// #define THREADED_SF
+
+#ifndef MSW
+#include <unistd.h>
+#include <fcntl.h>
+#endif
+#include <pthread.h>
+#ifdef MSW
+#include <io.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#define PD_PLUSPLUS_FACE
+#include "desire.h"
+#define a_symbol a_w.w_symbol
+#define a_float a_w.w_float
+
+#define MAXSFCHANS 64
+
+#ifdef _LARGEFILE64_SOURCE
+# define open open64
+# define lseek lseek64
+#endif
+
+static bool debug=0;
+
+#define EAT_ARG(ATYPE,VAR) if (argc<1 || argv->a_type != ATYPE) goto usage; else {VAR = *argv++; argc--;}
+
+/***************** 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 */
+
+struct t_nextstep {
+ char fileid[4]; /* magic number '.snd' if file is big-endian */
+ uint32 onset; /* byte offset of first sample */
+ uint32 length; /* length of sound in bytes */
+ uint32 format; /* format; see below */
+ uint32 sr; /* sample rate */
+ uint32 nchans; /* number of channels */
+ char info[4]; /* comment */
+};
+
+#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. */
+
+struct t_wave {
+ char fileid[4]; /* chunk id 'RIFF' */
+ uint32 chunksize; /* chunk size */
+ char waveid[4]; /* wave chunk id 'WAVE' */
+ char fmtid[4]; /* format chunk id 'fmt ' */
+ uint32 fmtchunksize; /* format chunk size */
+ uint16 fmttag; /* format tag (WAV_INT etc) */
+ uint16 nchannels; /* number of channels */
+ uint32 samplespersec; /* sample rate in hz */
+ uint32 navgbytespersec; /* average bytes per second */
+ uint16 nblockalign; /* number of bytes per frame */
+ uint16 nbitspersample; /* number of bits in a sample */
+ char datachunkid[4]; /* data chunk id 'data' */
+ uint32 datachunksize; /* length of data chunk */
+};
+
+struct t_fmt { /* format chunk */
+ uint16 fmttag; /* format tag, 1 for PCM */
+ uint16 nchannels; /* number of channels */
+ uint32 samplespersec; /* sample rate in hz */
+ uint32 navgbytespersec; /* average bytes per second */
+ uint16 nblockalign; /* number of bytes per frame */
+ uint16 nbitspersample; /* number of bits in a sample */
+};
+
+struct t_wavechunk { /* ... and the last two items */
+ char id[4]; /* data chunk id, e.g., 'data' or 'fmt ' */
+ uint32 size; /* length of data chunk */
+};
+
+#define WAV_INT 1
+#define WAV_FLOAT 3
+
+typedef unsigned char byte;
+
+/* the AIFF header. I'm assuming AIFC is compatible but don't really know that. */
+
+struct t_datachunk {
+ char id[4]; // data chunk id 'SSND'
+ uint32 size; // length of data chunk
+ uint32 offset; // additional offset in bytes
+ uint32 block; // block size
+};
+
+struct t_comm {
+ uint16 nchannels; // number of channels
+ uint16 nframeshi; // # of sample frames (hi)
+ uint16 nframeslo; // # of sample frames (lo)
+ uint16 bitspersamp; // bits per sample
+ byte samprate[10];// sample rate, 80-bit float!
+};
+
+/* this version is more convenient for writing them out: */
+struct t_aiff {
+ char fileid[4]; // chunk id 'FORM'
+ uint32 chunksize; // chunk size
+ char aiffid[4]; // aiff chunk id 'AIFF'
+ char fmtid[4]; // format chunk id 'COMM'
+ uint32 fmtchunksize; // format chunk size, 18
+ uint16 nchannels; // number of channels
+ uint16 nframeshi; // # of sample frames (hi)
+ uint16 nframeslo; // # of sample frames (lo)
+ uint16 bitspersamp; // bits per sample
+ byte samprate[10]; // sample rate, 80-bit float!
+};
+
+struct t_param {
+ int bytespersample;
+ int bigendian;
+ int nchannels;
+ long bytelimit;
+ int bytesperchannel() {return bytespersample * nchannels;}
+};
+
+#define AIFFHDRSIZE 38 /* probably not what sizeof() gives */
+#define AIFFPLUS (AIFFHDRSIZE + 16) /* header size including SSND 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)
+
+#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();
+
+/* 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_via_fd(int fd, int headersize, t_param *p, long skipframes) {
+ int swap, sysrtn;
+ errno = 0;
+ t_param q;
+ q.bytelimit = 0x7fffffff;
+ if (headersize >= 0) { /* header detection overridden */
+ q = *p;
+ } else {
+ char buf[MAXPDSTRING];
+ int bytesread = read(fd, buf, READHDRSIZE);
+ int format;
+ if (bytesread < 4) goto badheader;
+ if (!strncmp(buf, ".snd", 4)) {format = FORMAT_NEXT; q.bigendian = 1;}
+ else if (!strncmp(buf, "dns.", 4)) {format = FORMAT_NEXT; q.bigendian = 0;}
+ else if (!strncmp(buf, "RIFF", 4)) {
+ if (bytesread < 12 || strncmp(buf + 8, "WAVE", 4)) goto badheader;
+ format = FORMAT_WAVE; q.bigendian = 0;
+ }
+ else if (!strncmp(buf, "FORM", 4)) {
+ if (bytesread < 12 || strncmp(buf + 8, "AIFF", 4)) goto badheader;
+ format = FORMAT_AIFF; q.bigendian = 1;
+ } else goto badheader;
+ swap = (q.bigendian != garray_ambigendian());
+ if (format == FORMAT_NEXT) { /* nextstep header */
+ if (bytesread < (int)sizeof(t_nextstep)) goto badheader;
+ q.nchannels = swap4(((t_nextstep *)buf)->nchans, swap);
+ format = swap4(((t_nextstep *)buf)->format, swap);
+ headersize = swap4(((t_nextstep *)buf)->onset, swap);
+ if (format == NS_FORMAT_LINEAR_16) q.bytespersample = 2;
+ else if (format == NS_FORMAT_LINEAR_24) q.bytespersample = 3;
+ else if (format == NS_FORMAT_FLOAT) q.bytespersample = 4;
+ else goto badheader;
+ q.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. */
+ q.nchannels = 1;
+ q.bytespersample = 2;
+ /* copy the first chunk header to beginnning of buffer. */
+ memcpy(buf, buf + headersize, sizeof(t_wavechunk));
+ /* read chunks in loop until we get to the data chunk */
+ while (strncmp(((t_wavechunk *)buf)->id, "data", 4)) {
+ long chunksize = swap4(((t_wavechunk *)buf)->size, swap), seekto = headersize + chunksize + 8, seekout;
+ if (!strncmp(((t_wavechunk *)buf)->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;
+ q.nchannels = swap2(((t_fmt *)buf)->nchannels, swap);
+ format = swap2(((t_fmt *)buf)->nbitspersample, swap);
+ if (format == 16) q.bytespersample = 2;
+ else if (format == 24) q.bytespersample = 3;
+ else if (format == 32) q.bytespersample = 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;
+ headersize = seekto;
+ }
+ q.bytelimit = swap4(((t_wavechunk *)buf)->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. */
+ q.nchannels = 1;
+ q.bytespersample = 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)->id, "SSND", 4)) {
+ long chunksize = swap4(((t_datachunk *)buf)->size, swap), seekto = headersize + chunksize + 8, seekout;
+ if (!strncmp(((t_datachunk *)buf)->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;
+ q.nchannels = swap2(((t_comm *)buf)->nchannels, swap);
+ format = swap2(((t_comm *)buf)->bitspersamp, swap);
+ if (format == 16) q.bytespersample = 2;
+ else if (format == 24) q.bytespersample = 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;
+ }
+ q.bytelimit = swap4(((t_datachunk *)buf)->size, swap);
+ headersize += 8;
+ }
+ }
+ /* seek past header and any sample frames to skip */
+ sysrtn = lseek(fd, q.bytesperchannel() * skipframes + headersize, 0);
+ if (sysrtn != q.bytesperchannel() * skipframes + headersize) return -1;
+ q.bytelimit -= q.bytesperchannel() * skipframes;
+ if (q.bytelimit < 0) q.bytelimit = 0;
+ *p = q;
+ return fd;
+badheader:
+ /* the header wasn't recognized. We're threadable here so let's not print out the error... */
+ errno = EIO;
+ return -1;
+}
+
+/* open a soundfile, using open_via_path(). This is used by readsf~ in
+ a not-perfectly-threadsafe way. LATER replace with a thread-hardened version of open_soundfile_via_canvas() */
+static int open_soundfile(const char *dirname, const char *filename, int headersize, t_param *p, long skipframes) {
+ char *buf, *bufptr;
+ int fd = open_via_path2(dirname, filename, "", &buf, &bufptr, 1);
+ if (fd < 0) return -1;
+ free(buf);
+ return open_soundfile_via_fd(fd, headersize, p, skipframes);
+}
+
+/* open a soundfile, using open_via_canvas(). This is used by readsf~ in
+ a not-perfectly-threadsafe way. LATER replace with a thread-hardened version of open_soundfile_via_canvas() */
+static int open_soundfile_via_canvas(t_canvas *canvas, const char *filename, int headersize, t_param *p, long skipframes) {
+ char *buf, *bufptr;
+ int fd = canvas_open2(canvas, filename, "", &buf, &bufptr, 1);
+ if (fd < 0) return -1;
+ free(buf);
+ return open_soundfile_via_fd(fd, headersize, p, skipframes);
+}
+
+static void soundfile_xferin(int sfchannels, int nvecs, float **vecs,
+ long itemsread, unsigned char *buf, int nitems, int bytespersample, int bigendian) {
+ unsigned char *sp, *sp2;
+ int nchannels = (sfchannels < nvecs ? sfchannels : nvecs);
+ int bytesperframe = bytespersample * sfchannels;
+ sp = buf;
+ for (int i=0; i < nchannels; i++, sp += bytespersample) {
+ int j;
+ sp2=sp;
+ float *fp=vecs[i] + itemsread;
+ #define LOOP for (j=0; j<nitems; j++, sp2 += bytesperframe, fp++)
+ if (bytespersample == 2) {
+ if (bigendian) LOOP {*fp = SCALE * ((sp2[0]<<24) | (sp2[1]<<16));}
+ else LOOP {*fp = SCALE * ((sp2[1]<<24) | (sp2[0]<<16));}
+ } else if (bytespersample == 3) {
+ if (bigendian) LOOP {*fp = SCALE * ((sp2[0]<<24) | (sp2[1]<<16) | (sp2[2]<<8));}
+ else LOOP {*fp = SCALE * ((sp2[2]<<24) | (sp2[1]<<16) | (sp2[0]<<8));}
+ } else if (bytespersample == 4) {
+ if (bigendian) LOOP {*(long *)fp = (sp2[0]<<24) | (sp2[1]<<16) | (sp2[2]<<8) | sp2[3];}
+ else LOOP {*(long *)fp = (sp2[3]<<24) | (sp2[2]<<16) | (sp2[1]<<8) | sp2[0];}
+ }
+ #undef LOOP
+ }
+ /* zero out other outputs */
+ for (int i=sfchannels; i < nvecs; i++) {
+ float *fp=vecs[i];
+ for (int j=nitems; 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 bytespersample = 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_symbol->name == '-') {
+ char *flag = argv->a_symbol->name + 1;
+ argc--; argv++;
+ if (!strcmp(flag, "skip")) {
+ EAT_ARG(A_FLOAT,onset); if (onset<0) goto usage;
+ } else if (!strcmp(flag, "nframes")) {
+ EAT_ARG(A_FLOAT,nframes); if (nframes<0) goto usage;
+ } else if (!strcmp(flag, "bytes")) {
+ EAT_ARG(A_FLOAT,bytespersample); if (bytespersample<2 || bytespersample>4) goto usage;
+ } else if (!strcmp(flag, "normalize")) {normalize = 1;
+ } else if (!strcmp(flag, "wave")) {filetype = FORMAT_WAVE;
+ } else if (!strcmp(flag, "nextstep")) {filetype = FORMAT_NEXT;
+ } else if (!strcmp(flag, "aiff")) {filetype = FORMAT_AIFF;
+ } else if (!strcmp(flag, "big")) {endianness = 1;
+ } else if (!strcmp(flag, "little")) {endianness = 0;
+ } else if (!strcmp(flag, "r") || !strcmp(flag, "rate")) {
+ EAT_ARG(A_FLOAT,rate); if (rate<0) goto usage;
+ } else goto usage;
+ }
+ if (!argc || argv->a_type != A_SYMBOL) goto usage;
+ filesym = argv->a_symbol;
+ /* check if format not specified and fill in */
+ if (filetype < 0) {
+ const char *s = filesym->name + strlen(filesym->name);
+ if (strlen(filesym->name) >= 5 && !strcasecmp(s-4, ".aif" )) filetype = FORMAT_AIFF;
+ if (strlen(filesym->name) >= 6 && !strcasecmp(s-5, ".aiff")) filetype = FORMAT_AIFF;
+ if (strlen(filesym->name) >= 5 && !strcasecmp(s-4, ".snd" )) filetype = FORMAT_NEXT;
+ if (strlen(filesym->name) >= 4 && !strcasecmp(s-3, ".au" )) filetype = FORMAT_NEXT;
+ if (filetype < 0) filetype = FORMAT_WAVE;
+ }
+ /* don't handle AIFF floating point samples */
+ if (bytespersample == 4) {
+ if (filetype == FORMAT_AIFF) {
+ error("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) error("WAVE file forced to little endian");
+ } else if (filetype == FORMAT_AIFF) {
+ bigendian = 1;
+ if (endianness == 0) error("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 = bytespersample;
+ *p_swap = swap;
+ *p_normalize = normalize;
+ *p_onset = onset;
+ *p_nframes = nframes;
+ *p_bigendian = bigendian;
+ *p_rate = rate;
+ return 0;
+usage:
+ return -1;
+}
+
+static bool strcaseends(const char *a, const char *b) {return strcasecmp(a+strlen(a)-strlen(b),b)==0;}
+
+static int create_soundfile(t_canvas *canvas, const char *filename, int filetype, int nframes, int bytespersample,
+int bigendian, int nchannels, int swap, float samplerate) {
+ char filenamebuf[strlen(filename)+10];
+ char headerbuf[WRITEHDRSIZE];
+ int fd, headersize = 0;
+ strcpy(filenamebuf, filename);
+ if (filetype == FORMAT_NEXT) {
+ t_nextstep *nexthdr = (t_nextstep *)headerbuf;
+ if (!strcaseends(filenamebuf,".snd")) strcat(filenamebuf, ".snd");
+ if (bigendian) strncpy(nexthdr->fileid, bigendian?".snd":"dns.", 4);
+ nexthdr->onset = swap4(sizeof(*nexthdr), swap);
+ nexthdr->length = 0;
+ nexthdr->format = swap4(bytespersample == 3 ? NS_FORMAT_LINEAR_24 : bytespersample == 4 ? NS_FORMAT_FLOAT : NS_FORMAT_LINEAR_16, swap);
+ nexthdr->sr = swap4((size_t)samplerate, swap);
+ nexthdr->nchans = swap4((size_t)nchannels, swap);
+ strcpy(nexthdr->info, "Pd ");
+ swapstring(nexthdr->info, swap);
+ headersize = sizeof(t_nextstep);
+ } else if (filetype == FORMAT_AIFF) {
+ long datasize = nframes * nchannels * bytespersample;
+ long longtmp;
+ static unsigned char dogdoo[] = {0x40, 0x0e, 0xac, 0x44, 0, 0, 0, 0, 0, 0, 'S', 'S', 'N', 'D'};
+ t_aiff *aiffhdr = (t_aiff *)headerbuf;
+ if (!strcaseends(filenamebuf,".aif") && !strcaseends(filenamebuf,".aiff")) strcat(filenamebuf, ".aif");
+ strncpy(aiffhdr->fileid, "FORM", 4);
+ aiffhdr->chunksize = swap4(datasize + sizeof(*aiffhdr) + 4, swap);
+ strncpy(aiffhdr->aiffid, "AIFF", 4);
+ strncpy(aiffhdr->fmtid, "COMM", 4);
+ aiffhdr->fmtchunksize = swap4(18, swap);
+ aiffhdr->nchannels = swap2(nchannels, swap);
+ longtmp = swap4(nframes, swap);
+ memcpy(&aiffhdr->nframeshi, &longtmp, 4);
+ aiffhdr->bitspersamp = swap2(8 * bytespersample, swap);
+ memcpy(aiffhdr->samprate, dogdoo, sizeof(dogdoo));
+ longtmp = swap4(datasize, swap);
+ memcpy(aiffhdr->samprate + sizeof(dogdoo), &longtmp, 4);
+ memset(aiffhdr->samprate + sizeof(dogdoo) + 4, 0, 8);
+ headersize = AIFFPLUS;
+ /* fix by matju for häfeli, 2007.07.04, but really, dogdoo should be removed */
+ while (samplerate >= 0x10000) {aiffhdr->samprate[1]++; samplerate/=2;}
+ aiffhdr->samprate[2] = (long)samplerate>>8;
+ aiffhdr->samprate[3] = (long)samplerate;
+ } else { /* WAVE format */
+ long datasize = nframes * nchannels * bytespersample;
+ if (!strcaseends(filenamebuf,".wav")) strcat(filenamebuf, ".wav");
+ t_wave *wavehdr = (t_wave *)headerbuf;
+ strncpy(wavehdr->fileid, "RIFF", 4);
+ wavehdr->chunksize = swap4(datasize + sizeof(*wavehdr) - 8, swap);
+ strncpy(wavehdr->waveid, "WAVE", 4);
+ strncpy(wavehdr->fmtid, "fmt ", 4);
+ wavehdr->fmtchunksize = swap4(16, swap);
+ wavehdr->fmttag = swap2((bytespersample == 4 ? WAV_FLOAT : WAV_INT), swap);
+ wavehdr->nchannels = swap2(nchannels, swap);
+ wavehdr->samplespersec = swap4(size_t(samplerate), swap);
+ wavehdr->navgbytespersec = swap4((int)(samplerate * nchannels * bytespersample), swap);
+ wavehdr->nblockalign = swap2(nchannels * bytespersample, swap);
+ wavehdr->nbitspersample = swap2(8 * bytespersample, swap);
+ strncpy(wavehdr->datachunkid, "data", 4);
+ wavehdr->datachunksize = swap4(datasize, swap);
+ headersize = sizeof(t_wave);
+ }
+ char *buf2 = canvas_makefilename(canvas, filenamebuf,0,0);
+ sys_bashfilename(buf2,buf2);
+ if ((fd = open(buf2, BINCREATE, 0666)) < 0) {free(buf2); 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)
+ error("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, v;
+ if (lseek(fd, ((char *)(&((t_wave *)0)->chunksize)) - (char *)0, SEEK_SET) == 0) goto baddonewrite;
+ v = swap4(datasize + sizeof(t_wave) - 8, swap);
+ if (write(fd, (char *)&v, 4) < 4) goto baddonewrite;
+ if (lseek(fd, ((char *)(&((t_wave *)0)->datachunksize)) - (char *)0, SEEK_SET) == 0) goto baddonewrite;
+ v = swap4(datasize, swap);
+ if (write(fd, (char *)&v, 4) < 4) goto baddonewrite;
+ }
+ if (filetype == FORMAT_AIFF) {
+ long v;
+ if (lseek(fd, ((char *)(&((t_aiff *)0)->nframeshi)) - (char *)0, SEEK_SET) == 0) goto baddonewrite;
+ v = swap4(itemswritten, swap);
+ if (write(fd, (char *)&v,4) < 4) goto baddonewrite;
+ if (lseek(fd, ((char *)(&((t_aiff *)0)->chunksize)) - (char *)0, SEEK_SET) == 0) goto baddonewrite;
+ v = swap4(itemswritten*bytesperframe+AIFFHDRSIZE, swap);
+ if (write(fd, (char *)&v,4) < 4) goto baddonewrite;
+ if (lseek(fd, (AIFFHDRSIZE+4), SEEK_SET) == 0) goto baddonewrite;
+ v = swap4(itemswritten*bytesperframe, swap);
+ if (write(fd, (char *)&v,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:
+ error("%s: %s", filename, strerror(errno));
+}
+
+static void soundfile_xferout(int nchannels, float **vecs, unsigned char *buf, int nitems, long onset, int bytespersample,
+int bigendian, float normalfactor) {
+ unsigned char *sp=buf, *sp2;
+ float *fp;
+ int bytesperframe = bytespersample * nchannels;
+ #define LOOP for (int j=0; j<nitems; j++, sp2 += bytesperframe, fp++)
+ for (int i = 0; i < nchannels; i++, sp += bytespersample) {
+ sp2 = sp; fp = vecs[i] + onset;
+ if (bytespersample == 2) {
+ float ff = normalfactor * 32768.;
+ if (bigendian) LOOP {
+ int xx = clip(int(32768. + *fp * ff) - 0x8000,-0x7fff,+0x7fff);
+ sp2[0] = xx>>8; sp2[1] = xx;
+ } else LOOP {
+ int xx = clip(int(32768. + *fp * ff) - 0x8000,-0x7fff,+0x7fff);
+ sp2[1] = xx>>8; sp2[0] = xx;
+ }
+ } else if (bytespersample == 3) {
+ float ff = normalfactor * 8388608.;
+ if (bigendian) LOOP {
+ int xx = clip(int(8388608. + *fp * ff) - 0x800000,-0x7fffff,+0x7fffff);
+ sp2[0] = xx>>16; sp2[1] = xx>>8; sp2[2] = xx;
+ } else LOOP {
+ int xx = clip(int(8388608. + *fp * ff) - 0x800000,-0x7fffff,+0x7fffff);
+ sp2[2] = xx>>16; sp2[1] = xx>>8; sp2[0] = xx;
+ }
+ } else if (bytespersample == 4) {
+ if (bigendian) LOOP {
+ float f2 = *fp * normalfactor; long xx = *(long *)&f2;
+ sp2[0] = xx >> 24; sp2[1] = xx >> 16; sp2[2] = xx >> 8; sp2[3] = xx;
+ } else LOOP {
+ float f2 = *fp * normalfactor; long xx = *(long *)&f2;
+ sp2[3] = xx >> 24; sp2[2] = xx >> 16; sp2[1] = xx >> 8; sp2[0] = xx;
+ }
+ }
+ }
+ #undef LOOP
+}
+
+/* ------- soundfiler - reads and writes soundfiles to/from "garrays" ---- */
+
+#define DEFMAXSIZE (16*1024*1024*4) /* default maximum 16 million floats per channel */
+#define SAMPBUFSIZE 1024
+
+static t_class *soundfiler_class;
+
+struct t_soundfiler : t_object {t_canvas *canvas;};
+
+#ifdef THREADED_SF
+#include <sched.h>
+#if (_POSIX_MEMLOCK - 0) >= 200112L
+#include <sys/mman.h>
+#else
+#define munlockall() /* ignore */
+#define mlockall() /* ignore */
+#endif /* _POSIX_MEMLOCK */
+
+static pthread_t sf_thread_id; /* id of soundfiler thread */
+
+struct t_sfprocess {
+ void (*process)(t_soundfiler *,t_symbol *, int, t_atom *); /* function to call */
+ t_soundfiler *x;
+ int argc;
+ t_atom *argv;
+ struct t_sfprocess *next; /* next object in queue */
+ pthread_mutex_t mutex;
+};
+
+/* this is the queue for all soundfiler objects */
+struct t_sfqueue {
+ t_sfprocess *begin;
+ t_sfprocess *end;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+};
+
+static t_sfqueue *soundfiler_queue;
+
+/* we fill the queue */
+void soundfiler_queue_add(void (* process) (t_soundfiler *,t_symbol *,int,t_atom *), void * x, int argc, t_atom * argv) {
+ /* preparing entry */
+ t_sfprocess * last_entry = (t_sfprocess*)getbytes(sizeof(t_sfprocess));
+ if (debug) post("adding process to queue");
+ pthread_mutex_init(&(last_entry->mutex), NULL);
+ pthread_mutex_lock(&(last_entry->mutex));
+ last_entry->process = process;
+ last_entry->x = (t_soundfiler *)x;
+ last_entry->argc = argc;
+ last_entry->argv = (t_atom *)copybytes(argv, argc * sizeof(t_atom));
+ last_entry->next = NULL;
+ pthread_mutex_unlock(&(last_entry->mutex));
+ /* add new entry to queue */
+ pthread_mutex_lock(&(soundfiler_queue->mutex));
+ if (soundfiler_queue->begin==NULL) {
+ soundfiler_queue->begin=last_entry;
+ soundfiler_queue->end=last_entry;
+ } else {
+ pthread_mutex_lock(&(soundfiler_queue->end->mutex));
+ soundfiler_queue->end->next=last_entry;
+ pthread_mutex_unlock(&(soundfiler_queue->end->mutex));
+ soundfiler_queue->end=last_entry;
+ }
+ if ( soundfiler_queue->begin == soundfiler_queue->end ) {
+ if (debug) post("signaling");
+ pthread_mutex_unlock(&(soundfiler_queue->mutex));
+ /* and signal the helper thread */
+ pthread_cond_signal(&(soundfiler_queue->cond));
+ } else {
+ if (debug) post("not signaling");
+ pthread_mutex_unlock(&(soundfiler_queue->mutex));
+ }
+ return;
+}
+
+/* global soundfiler thread ... sleeping until signaled */
+void soundfiler_thread() {
+ t_sfprocess *me;
+ t_sfprocess *next;
+ if (debug) post("soundfiler_thread ID: %d", pthread_self());
+ while (1) {
+ if (debug) post("Soundfiler sleeping");
+ pthread_cond_wait(&soundfiler_queue->cond, &soundfiler_queue->mutex);
+ if (debug) post("Soundfiler awake");
+ /* work on the queue */
+ while (soundfiler_queue->begin!=NULL) {
+ post("soundfiler: locked queue");
+ /* locking process */
+ pthread_mutex_lock(&(soundfiler_queue->begin->mutex));
+ me = soundfiler_queue->begin;
+ pthread_mutex_unlock(&(me->mutex));
+ pthread_mutex_unlock(&(soundfiler_queue->mutex));
+ if (debug) post("soundfiler: mutex unlocked, running process");
+ /* running the specific function */
+ me->process(me->x, NULL, me->argc, me->argv);
+ if (debug) post("soundfiler: process done, locking mutex");
+ pthread_mutex_lock(&(soundfiler_queue->mutex));
+ pthread_mutex_lock(&(me->mutex));
+ free(me->argv);
+ /* the process struct */
+ next=me->next;
+ soundfiler_queue->begin=next;
+ free(me);
+ }
+ soundfiler_queue->end=NULL;
+ }
+}
+
+extern int sys_hipriority; /* real-time flag, true if priority boosted */
+
+/* create soundfiler thread */
+void sys_start_sfthread() {
+ pthread_attr_t sf_attr;
+ struct sched_param sf_param;
+ int status;
+ // initialize queue
+ soundfiler_queue = (t_sfqueue *)getbytes(sizeof(t_sfqueue));
+ pthread_mutex_init(&soundfiler_queue->mutex,NULL);
+ pthread_cond_init(&soundfiler_queue->cond,NULL);
+ soundfiler_queue->begin=soundfiler_queue->end=NULL;
+/* pthread_mutex_unlock(&(soundfiler_queue->mutex)); */
+ // initialize thread
+ pthread_attr_init(&sf_attr);
+ 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 */
+ //start thread
+ status = pthread_create(&sf_thread_id, &sf_attr, (void *(*)(void *)) soundfiler_thread,NULL);
+ if (status != 0) error("Couldn't create soundfiler thread: %d",status);
+ else post("global soundfiler thread launched, priority: %d", sf_param.sched_priority);
+}
+
+static void soundfiler_t_write( t_soundfiler *x, t_symbol *s, int argc, t_atom *argv);
+static void soundfiler_t_write_addq(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) {
+ soundfiler_queue_add(soundfiler_t_write,(void *)x,argc, argv);
+}
+static void soundfiler_t_read( t_soundfiler *x, t_symbol *s, int argc, t_atom *argv);
+static void soundfiler_t_read_addq(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) {
+ soundfiler_queue_add(soundfiler_t_read,(void *)x,argc, argv);
+}
+
+/* soundfiler_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 */
+static t_int soundfiler_read_update_garray(t_int *w);
+static t_int soundfiler_read_update_graphics(t_int *w);
+static t_int soundfiler_read_output(t_int *w);
+static void soundfiler_t_read(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) {
+ t_param p;
+ int headersize = -1;
+ p.nchannels = 0; p.bytespersample = 0; p.bigendian = 0;
+ int resize = 0, i, j;
+ long skipframes = 0, nframes = 0, finalsize = 0, maxsize = DEFMAXSIZE, itemsread = 0;
+ p.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_symbol->name == '-') {
+ char *flag = argv->a_symbol->name + 1;
+ argc--; argv++;
+ if (!strcmp(flag, "skip")) {
+ EAT_ARG(A_FLOAT,skipframes); if (skipframes<0) goto usage;
+ } else if (!strcmp(flag, "nframes")) {
+ EAT_ARG(A_FLOAT,nframes); if (nframes<0) goto usage;
+ } else if (!strcmp(flag, "raw")) {
+ EAT_ARG(A_FLOAT,headersize); if (headersize<0) goto usage;
+ EAT_ARG(A_FLOAT,p.nchannels); if (p.nchannels<1) goto usage;
+ EAT_ARG(A_FLOAT,p.bytespersample); if (p.bytespersample<2 || p.bytespersample>4) goto usage;
+ EAT_ARG(A_SYMBOL,endianness); if (endianness!='b' && endianness!='l' && endianness!='n') goto usage;
+ if (endianness == 'b') p.bigendian = 1;
+ else if (endianness == 'l') p.bigendian = 0;
+ else p.bigendian = garray_ambigendian();
+ } else if (!strcmp(flag, "resize")) {
+ resize = 1;
+ } else if (!strcmp(flag, "maxsize")) {
+ EAT_ARG(A_FLOAT,maxsize); if (maxsize<0) goto usage;
+ resize = 1; /* maxsize implies resize. */
+ } else goto usage;
+ }
+ if (argc < 2 || argc > MAXSFCHANS + 1 || argv[0].a_type != A_SYMBOL) goto usage;
+ filename = argv[0].a_symbol->name;
+ argc--; argv++;
+ for (int i=0; i<argc; i++) {
+ if (argv[i].a_type != A_SYMBOL) goto usage;
+ garrays[i] = (t_garray *)pd_findbyclass(argv[i].a_symbol, garray_class);
+ if (!garrays[i]) {
+ error("%s: no such table", argv[i].a_symbol->name);
+ goto done;
+ } else if (!garray_getfloatarray(garrays[i], &vecsize[i], &vecs[i]))
+ error("%s: bad template for tabwrite", argv[i].a_symbol->name);
+ if (finalsize && finalsize != vecsize[i] && !resize) {
+ post("soundfiler_read: arrays have different lengths; resizing...");
+ resize = 1;
+ }
+ finalsize = vecsize[i];
+ }
+ fd = open_soundfile(canvas_getdir(x->canvas)->name, filename, headersize, &p, skipframes);
+ if (fd < 0) {
+ error("soundfiler_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) {error("lseek failed"); goto done;}
+ lseek(fd, poswas, SEEK_SET);
+ framesinfile = (eofis - poswas) / p.bytesperchannel();
+ if (framesinfile > maxsize) {
+ error("soundfiler_read: truncated to %d elements", maxsize);
+ framesinfile = maxsize;
+ }
+ framesinfile = min(framesinfile, p.bytelimit / p.bytesperchannel());
+ finalsize = framesinfile;
+ }
+ if (!finalsize) finalsize = 0x7fffffff;
+ finalsize = min(finalsize, p.bytelimit / p.bytesperchannel());
+ fp = fdopen(fd, "rb");
+ bufframes = SAMPBUFSIZE / p.bytesperchannel();
+ if (debug) {
+ post("buffers: %d", argc);
+ post("channels: %d", p.nchannels);
+ }
+ munlockall();
+ /* allocate memory for new array */
+ if (resize)
+ for (int i=0; i<argc; i++) {
+ nvecs[i] = (float *)getalignedbytes(finalsize * sizeof(t_float));
+ /* if we are out of memory, free it again and quit */
+ if (nvecs[i]==0) {
+ error("resize failed");
+ /* if the resizing fails, we'll have to free all arrays again */
+ for (j=0; j!=i;++j) freealignedbytes (nvecs[i],finalsize * sizeof(t_float));
+ goto done;
+ }
+ }
+ else
+ for (int i=0; i<argc; i++) {
+ nvecs[i] = (float *)getalignedbytes(vecsize[i] * sizeof(t_float));
+ /* if we are out of memory, free it again and quit */
+ if (nvecs[i]==0) {
+ error("resize failed");
+ /* if the resizing fails, we'll have to free all arrays again */
+ for (j=0; j!=i;++j) freealignedbytes (nvecs[i],vecsize[i] * sizeof(t_float));
+ goto done;
+ }
+ }
+ if(i > p.nchannels) memset(nvecs[i],0,vecsize[i] * sizeof(t_float));
+ if (debug) post("transfer soundfile");
+ for (itemsread = 0; itemsread < finalsize; ) {
+ int thisread = finalsize - itemsread;
+ thisread = (thisread > bufframes ? bufframes : thisread);
+ nitems = fread(sampbuf, p.bytesperchannel(), thisread, fp);
+ if (nitems <= 0) break;
+ soundfile_xferin(p.nchannels, argc, nvecs, itemsread, (unsigned char *)sampbuf, nitems, p.bytespersample, p.bigendian);
+ itemsread += nitems;
+ }
+ if (debug) post("zeroing remaining elements");
+ /* zero out remaining elements of vectors */
+ for (int i=0; i<argc; i++) for (int j=itemsread; j<finalsize; j++) nvecs[i][j] = 0;
+ /* set idle callback to switch pointers */
+ if (debug) post("locked");
+ for (int 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);
+ sys_callback(&soundfiler_read_update_garray, w, 4);
+ pthread_cond_wait(&resume_after_callback, &resume_after_callback_mutex);
+ }
+ if (debug) post("unlocked, doing graphics updates");
+ /* do all graphics updates. run this in the main thread via callback */
+ for (int 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;
+ sys_callback(&soundfiler_read_update_graphics, w, 2);
+ }
+ /* free the old arrays */
+ for (int i=0; i<argc; i++) freealignedbytes(vecs[i], vecsize[i] * sizeof(t_float));
+ fclose(fp);
+ fd = -1;
+ goto done;
+usage:
+ error("usage: read [flags] filename tablename...");
+ post("flags: -skip <n> -nframes <n> -resize -maxsize <n> ...");
+ post("-raw <headerbytes> <channels> <bytespersample> <endian (b, l, or n)>.");
+done:
+ if (fd>=0) close(fd);
+ mlockall(MCL_FUTURE);
+ outargs = (t_int*)getbytes(2*sizeof(t_int));
+ outargs[0] = (t_int)x->outlet;
+ outargs[1] = (t_int)itemsread;
+ sys_callback(&soundfiler_read_output, outargs, 2);
+}
+
+/* idle callback for threadsafe synchronisation */
+static t_int soundfiler_read_update_garray(t_int *w) {
+ t_garray *garray = (t_garray*)w[0];
+ t_int nvec = w[1];
+ t_int finalsize = w[2];
+ pthread_cond_t *conditional = (pthread_cond_t*) w[3];
+ t_array *a = garray_getarray(garray);
+ a->vec = (char *) nvec;
+ a->n = finalsize;
+ if (garray->usedindsp) canvas_update_dsp();
+ /* signal helper thread */
+ pthread_cond_broadcast(conditional);
+ return 0;
+}
+
+static t_int soundfiler_read_update_graphics(t_int *w) {
+ t_garray *garray = (t_garray*) w[0];
+ t_canvas *gl;
+ int n = w[1];
+ /* if this is the only array in the graph, reset the graph's coordinates */
+ if (debug) post("redraw array %p", garray);
+ gl = garray->canvas;
+ if (gl->list == garray && !garray->next) {
+ vmess(gl, gensym("bounds"), "ffff", 0., gl->y1, double(n > 1 ? n-1 : 1), gl->y2);
+ /* close any dialogs that might have the wrong info now... */
+ } else garray_redraw(garray);
+ return 0;
+}
+
+static t_int soundfiler_read_output(t_int * w) {
+ t_outlet* outlet = (t_outlet*) w[0];
+ float itemsread = (float) w[1];
+ if (debug) post("bang %p", outlet);
+ outlet_float (outlet, itemsread);
+ return 0;
+}
+
+/* this is broken out from soundfiler_write below so garray_write can call it too... not done yet though. */
+long soundfiler_t_dowrite(void *obj, t_canvas *canvas, int argc, t_atom *argv) {
+ int bytespersample, bigendian, swap, filetype, normalize, 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,
+ &bytespersample, &swap, &bigendian, &normalize, &onset, &nframes, &samplerate))
+ goto usage;
+ nchannels = argc;
+ if (nchannels < 1 || nchannels > MAXSFCHANS) goto usage;
+ if (samplerate < 0) samplerate = sys_getsr();
+ for (int 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_symbol, garray_class))) {
+ error("%s: no such table", argv[i].a_symbol->name);
+ goto fail;
+ }
+ else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i]))
+ error("%s: bad template for tabwrite", argv[i].a_symbol->name);
+ if (nframes > vecsize - onset)
+ nframes = vecsize - onset;
+ for (int 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) {
+ error("soundfiler_write: no samples at onset %ld", onset);
+ goto fail;
+ }
+ if ((fd = create_soundfile(canvas, filesym->name, filetype, nframes, bytespersample, bigendian, nchannels, swap, samplerate)) < 0) {
+ post("%s: %s", filesym->name, strerror(errno));
+ goto fail;
+ }
+ if (!normalize) {
+ if ((bytespersample != 4) && (biggest > 1)) {
+ post("%s: normalizing max amplitude %f to 1", filesym->name, biggest);
+ normalize = 1;
+ } else post("%s: biggest amplitude = %f", filesym->name, biggest);
+ }
+ if (normalize) normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1);
+ else normfactor = 1;
+ bufframes = SAMPBUFSIZE / (nchannels * bytespersample);
+ for (itemswritten = 0; itemswritten < nframes; ) {
+ int thiswrite = nframes - itemswritten, nbytes;
+ thiswrite = (thiswrite > bufframes ? bufframes : thiswrite);
+ soundfile_xferout(argc, vecs, (unsigned char *)sampbuf, thiswrite, onset, bytespersample, bigendian, normfactor);
+ nbytes = write(fd, sampbuf, nchannels * bytespersample * thiswrite);
+ if (nbytes < nchannels * bytespersample * thiswrite) {
+ post("%s: %s", filesym->name, strerror(errno));
+ if (nbytes > 0) itemswritten += nbytes / (nchannels * bytespersample);
+ break;
+ }
+ itemswritten += thiswrite;
+ onset += thiswrite;
+ }
+ if (fd >= 0) {
+ soundfile_finishwrite(obj, filesym->name, fd, filetype, nframes, itemswritten, nchannels * bytespersample, swap);
+ close (fd);
+ }
+ return itemswritten;
+usage:
+ error("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 soundfiler_t_write(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) {
+ long bozo = soundfiler_t_dowrite(x, x->canvas, argc, argv);
+ sys_lock();
+ outlet_float(x->outlet, (float)bozo);
+ sys_lock();
+}
+
+static void soundfiler_t_resize(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv);
+static void soundfiler_t_resize_addq(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) {
+ soundfiler_queue_add(soundfiler_t_resize,(void *)x,argc, argv);
+}
+
+/* TB: soundfiler_t_resize ... usage: resize table size; adapted from garray_resize */
+static void soundfiler_t_resize(t_soundfiler *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_canvas *gl;
+ int n; /* resize of n elements */
+ char *nvec; /* new array */
+ t_garray *x = (t_garray *)pd_findbyclass(argv[0].a_symbol, garray_class);
+ t_array *a = garray_getarray(x);
+ if (!x) {error("%s: no such table", argv[0].a_symbol->name); goto usage;}
+ vec = (t_float*) a->vec;
+ was = a->n;
+ if ((argv+1)->a_type == A_FLOAT) {
+ n = (int) (argv+1)->a_float;
+ } else goto usage;
+ if (n == was) return;
+ if (n < 1) n = 1;
+ elemsize = template_findbyname(a->templatesym)->t_n * sizeof(t_word);
+ munlockall();
+ if (was > n) {
+ nvec = (char *)copyalignedbytes(a->vec, was * elemsize);
+ } else {
+ nvec = (char *)getalignedbytes(n * elemsize);
+ memcpy (nvec, a->vec, was * elemsize);
+ memset(nvec + was*elemsize, 0, (n - was) * elemsize);
+ }
+ if (!nvec) {error("array resize failed: out of memory"); mlockall(MCL_FUTURE); return;}
+ /* TB: we'll have to be sure that no one is accessing the array */
+ sys_lock();
+ a->vec = nvec;
+ a->n = n;
+ if (x->usedindsp) canvas_update_dsp();
+ sys_unlock();
+ /* if this is the only array in the graph, reset the graph's coordinates */
+ gl = x->canvas;
+ if (gl->list == x && !x->next) {
+ vmess(gl, gensym("bounds"), "ffff", 0., gl->y1, (double)(n > 1 ? n-1 : 1), gl->y2);
+ /* close any dialogs that might have the wrong info now... */
+ } else garray_redraw(x);
+ freealignedbytes (vec, was * elemsize);
+ mlockall(MCL_FUTURE);
+ sys_lock();
+ outlet_float(y->outlet, (float)atom_getintarg(1,argc,argv));
+ sys_unlock();
+ return;
+usage:
+ error("usage: resize tablename size");
+}
+
+static void soundfiler_t_const(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv);
+static void soundfiler_t_const_addq(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) {
+ soundfiler_queue_add(soundfiler_t_const,(void *)x,argc, argv);
+}
+
+/* TB: soundfiler_t_const ... usage: const table value */
+static void soundfiler_t_const(t_soundfiler *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_canvas *gl;
+ int val; /* value */
+ char *nvec; /* new array */
+ t_garray * x = (t_garray *)pd_findbyclass(argv[0].a_symbol, garray_class);
+ t_array *a = garray_getarray(x);
+ if (!x) {error("%s: no such table", argv[0].a_symbol->name); goto usage;}
+ vec = (t_float*) a->vec;
+ size = a->n;
+ if ((argv+1)->a_type == A_FLOAT) {
+ val = (int) (argv+1)->a_float;
+ } else goto usage;
+ elemsize = template_findbyname(a->templatesym)->t_n * sizeof(t_word);
+ /* allocating memory */
+ munlockall();
+ nvec = (char *)getalignedbytes(size * elemsize);
+ if (!nvec) {
+ error("array resize failed: out of memory");
+ mlockall(MCL_FUTURE);
+ return;
+ }
+ /* setting array */
+ for (int i=0; i!=size; ++i) nvec[i]=val;
+ /* TB: we'll have to be sure that no one is accessing the array */
+ sys_lock();
+ a->vec = nvec;
+ if (x->usedindsp) canvas_update_dsp();
+ sys_unlock();
+ /* if this is the only array in the graph, reset the graph's coordinates */
+ gl = x->canvas;
+ if (gl->list == x && !x->next) {
+ vmess(gl, gensym("bounds"), "ffff", 0., gl->y1, (double)(size > 1 ? size-1 : 1), gl->y2);
+ /* close any dialogs that might have the wrong info now... */
+ } else garray_redraw(x);
+ freealignedbytes (vec, size * elemsize);
+ mlockall(MCL_FUTURE);
+ sys_lock();
+ outlet_float(y->outlet, size);
+ sys_unlock();
+ return;
+ usage:
+ error("usage: const tablename value");
+}
+
+#endif /* THREADED_SF */
+
+static t_soundfiler *soundfiler_new() {
+ t_soundfiler *x = (t_soundfiler *)pd_new(soundfiler_class);
+ x->canvas = canvas_getcurrent();
+ outlet_new(x,&s_float);
+#ifdef THREADED_SF
+ post("warning: threaded soundfiler is not synchronous");
+#endif /* THREADED_SF */
+ return x;
+}
+
+/* soundfiler_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> */
+static void soundfiler_read(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) {
+ t_param p;
+ p.bytespersample = 0;
+ p.bigendian = 0;
+ p.nchannels = 0;
+ p.bytelimit = 0x7fffffff;
+ int headersize = -1, resize = 0;
+ long skipframes = 0, nframes = 0, finalsize = 0, maxsize = DEFMAXSIZE, itemsread = 0;
+ int fd = -1;
+ char endianness, *filename;
+ t_garray *garrays[MAXSFCHANS];
+ t_float *vecs[MAXSFCHANS];
+ char sampbuf[SAMPBUFSIZE];
+ int bufframes, nitems;
+ FILE *fp;
+ while (argc > 0 && argv->a_type == A_SYMBOL && *argv->a_symbol->name == '-') {
+ char *flag = argv->a_symbol->name + 1;
+ argc--; argv++;
+ if (!strcmp(flag, "skip")) {
+ EAT_ARG(A_FLOAT,skipframes); if (skipframes<0) goto usage;
+ } else if (!strcmp(flag, "nframes")) {
+ EAT_ARG(A_FLOAT,nframes); if (nframes<0) goto usage;
+ } else if (!strcmp(flag, "raw")) {
+ EAT_ARG(A_FLOAT,headersize); if (headersize<0) goto usage;
+ EAT_ARG(A_FLOAT,p.nchannels); if (p.nchannels<1) goto usage;
+ EAT_ARG(A_FLOAT,p.bytespersample); if (p.bytespersample<2 || p.bytespersample>4) goto usage;
+ EAT_ARG(A_SYMBOL,endianness); if (endianness!='b' && endianness!='l' && endianness!='n') goto usage;
+ if (endianness == 'b') p.bigendian = 1;
+ else if (endianness == 'l') p.bigendian = 0;
+ else p.bigendian = garray_ambigendian();
+ } else if (!strcmp(flag, "resize")) {
+ resize = 1;
+ } else if (!strcmp(flag, "maxsize")) {
+ EAT_ARG(A_FLOAT,maxsize); if (maxsize<0) goto usage;
+ resize = 1; /* maxsize implies resize. */
+ } else goto usage;
+ }
+ if (argc < 2 || argc > MAXSFCHANS + 1 || argv[0].a_type != A_SYMBOL) goto usage;
+ filename = argv[0].a_symbol->name;
+ argc--; argv++;
+ for (int i=0; i<argc; i++) {
+ int vecsize;
+ if (argv[i].a_type != A_SYMBOL) goto usage;
+ garrays[i] = (t_garray *)pd_findbyclass(argv[i].a_symbol, garray_class);
+ if (!garrays) {
+ error("%s: no such table", argv[i].a_symbol->name);
+ goto done;
+ } else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i]))
+ error("%s: bad template for tabwrite", argv[i].a_symbol->name);
+ if (finalsize && finalsize != vecsize && !resize) {
+ post("soundfiler_read: arrays have different lengths; resizing...");
+ resize = 1;
+ }
+ finalsize = vecsize;
+ }
+ fd = open_soundfile_via_canvas(x->canvas, filename, headersize, &p, skipframes);
+ if (fd < 0) {
+ error("soundfiler_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) {error("lseek failed"); goto done;}
+ lseek(fd, poswas, SEEK_SET);
+ framesinfile = (eofis - poswas) / (p.nchannels * p.bytespersample);
+ if (framesinfile > maxsize) {
+ error("soundfiler_read: truncated to %d elements", maxsize);
+ framesinfile = maxsize;
+ }
+ framesinfile = min(framesinfile, p.bytelimit / (p.nchannels * p.bytespersample));
+ finalsize = framesinfile;
+ for (int i=0; i<argc; i++) {
+ int vecsize;
+ garray_resize(garrays[i], finalsize);
+ /* for sanity's sake let's clear the save-in-patch flag here */
+ garray_setsaveit(garrays[i], 0);
+ garray_getfloatarray(garrays[i], &vecsize, &vecs[i]);
+ /* if the resize failed, garray_resize reported the error */
+ if (vecsize != framesinfile) {error("resize failed"); goto done;}
+ }
+ }
+ if (!finalsize) finalsize = 0x7fffffff;
+ finalsize = min(finalsize, p.bytelimit / (p.nchannels * p.bytespersample));
+ fp = fdopen(fd, "rb");
+ bufframes = SAMPBUFSIZE / (p.nchannels * p.bytespersample);
+ for (itemsread = 0; itemsread < finalsize; ) {
+ int thisread = finalsize - itemsread;
+ thisread = min(thisread,bufframes);
+ nitems = fread(sampbuf, p.nchannels * p.bytespersample, thisread, fp);
+ if (nitems <= 0) break;
+ soundfile_xferin(p.nchannels, argc, vecs, itemsread, (unsigned char *)sampbuf, nitems, p.bytespersample, p.bigendian);
+ itemsread += nitems;
+ }
+ /* zero out remaining elements of vectors */
+ for (int i=0; i<argc; i++) {
+ int vecsize; garray_getfloatarray(garrays[i], &vecsize, &vecs[i]);
+ for (int j=itemsread; j<vecsize; j++) vecs[i][j]=0;
+ }
+ /* zero out vectors in excess of number of channels */
+ for (int i=p.nchannels; i<argc; i++) {
+ int vecsize; float *foo; garray_getfloatarray(garrays[i], &vecsize, &foo);
+ for (int j=0; j<vecsize; j++) foo[j]=0;
+ }
+ /* do all graphics updates */
+ for (int i=0; i<argc; i++) garray_redraw(garrays[i]);
+ fclose(fp);
+ fd = -1;
+ goto done;
+usage:
+ error("usage: read [flags] filename tablename...");
+ post("flags: -skip <n> -nframes <n> -resize -maxsize <n> ...");
+ post("-raw <headerbytes> <channels> <bytespersample> <endian (b, l, or n)>.");
+done:
+ if (fd >= 0) close (fd);
+ outlet_float(x->outlet, (float)itemsread);
+}
+
+/* this is broken out from soundfiler_write below so garray_write can
+ call it too... not done yet though. */
+long soundfiler_dowrite(void *obj, t_canvas *canvas, int argc, t_atom *argv) {
+ int bytespersample, bigendian, swap, filetype, normalize, 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,
+ &bytespersample, &swap, &bigendian, &normalize, &onset, &nframes,
+ &samplerate))
+ goto usage;
+ nchannels = argc;
+ if (nchannels < 1 || nchannels > MAXSFCHANS) goto usage;
+ if (samplerate < 0) samplerate = sys_getsr();
+ for (int 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_symbol, garray_class))) {
+ error("%s: no such table", argv[i].a_symbol->name);
+ goto fail;
+ }
+ else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i]))
+ error("%s: bad template for tabwrite", argv[i].a_symbol->name);
+ nframes = min(nframes, vecsize - onset);
+ for (int 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) {
+ error("soundfiler_write: no samples at onset %ld", onset);
+ goto fail;
+ }
+ if ((fd = create_soundfile(canvas, filesym->name, filetype, nframes, bytespersample, bigendian, nchannels, swap, samplerate)) < 0) {
+ post("%s: %s", filesym->name, strerror(errno));
+ goto fail;
+ }
+ if (!normalize) {
+ if ((bytespersample != 4) && (biggest > 1)) {
+ post("%s: normalizing max amplitude %f to 1", filesym->name, biggest);
+ normalize = 1;
+ } else post("%s: biggest amplitude = %f", filesym->name, biggest);
+ }
+ if (normalize) normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1); else normfactor = 1;
+ bufframes = SAMPBUFSIZE / (nchannels * bytespersample);
+ for (itemswritten = 0; itemswritten < nframes; ) {
+ int thiswrite = nframes - itemswritten, nbytes;
+ thiswrite = (thiswrite > bufframes ? bufframes : thiswrite);
+ soundfile_xferout(argc, vecs, (unsigned char *)sampbuf, thiswrite, onset, bytespersample, bigendian, normfactor);
+ nbytes = write(fd, sampbuf, nchannels * bytespersample * thiswrite);
+ if (nbytes < nchannels * bytespersample * thiswrite) {
+ post("%s: %s", filesym->name, strerror(errno));
+ if (nbytes > 0) itemswritten += nbytes / (nchannels * bytespersample);
+ break;
+ }
+ itemswritten += thiswrite;
+ onset += thiswrite;
+ }
+ if (fd >= 0) {
+ soundfile_finishwrite(obj, filesym->name, fd, filetype, nframes, itemswritten, nchannels * bytespersample, swap);
+ close (fd);
+ }
+ return itemswritten;
+usage:
+ error("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 soundfiler_write(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) {
+ long bozo = soundfiler_dowrite(x, x->canvas, argc, argv);
+ outlet_float(x->outlet, (float)bozo);
+}
+
+static void soundfiler_setup() {
+ t_class *c = soundfiler_class = class_new2("soundfiler", (t_newmethod)soundfiler_new, 0, sizeof(t_soundfiler), 0, "");
+#ifdef THREADED_SF
+ class_addmethod2(c, (t_method)soundfiler_t_read_addq, "read", "*");
+/* class_addmethod2(c, (t_method)soundfiler_t_write_addq, "write", "*"); */
+ class_addmethod2(c, (t_method)soundfiler_t_resize_addq, "resize", "*");
+ class_addmethod2(c, (t_method)soundfiler_t_const_addq, "const", "*");
+#else
+ class_addmethod2(c, (t_method)soundfiler_read, "read", "*");
+#endif /* THREADED_SF */
+ class_addmethod2(c, (t_method)soundfiler_write, "write", "*");
+}
+
+/************************* readsf object ******************************/
+
+/* READSF uses the Posix threads package; for the moment we're Linux
+only although this should be portable to the other platforms.
+
+Each instance of readsf~ owns a "child" thread for doing the unix (MSW?) file
+reading. The parent thread signals the child each time:
+ (1) a file wants opening or closing;
+ (2) we've eaten another 1/16 of the shared buffer (so that the
+ child thread should check if it's time to read some more.)
+The child signals the parent whenever a read has completed. Signalling
+is done by setting "conditions" and putting data in mutex-controlled common
+areas.
+*/
+
+#define MAXBYTESPERSAMPLE 4
+#define MAXVECSIZE 128
+
+#define READSIZE 65536
+#define WRITESIZE 65536
+#define DEFBUFPERCHAN 262144
+#define MINBUFSIZE (4 * READSIZE)
+#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */
+
+#define REQUEST_NOTHING 0
+#define REQUEST_OPEN 1
+#define REQUEST_CLOSE 2
+#define REQUEST_QUIT 3
+#define REQUEST_BUSY 4
+
+#define STATE_IDLE 0
+#define STATE_STARTUP 1
+#define STATE_STREAM 2
+
+static t_class *readsf_class;
+
+struct t_readsf : t_object {
+ t_canvas *canvas;
+ t_clock *clock;
+ char *buf; /* soundfile buffer */
+ int bufsize; /* buffer size in bytes */
+ int noutlets; /* number of audio outlets */
+ t_sample *(outvec[MAXSFCHANS]); /* audio vectors */
+ int vecsize; /* vector size for transfers */
+ t_outlet *bangout; /* bang-on-done outlet */
+ int state; /* opened, running, or idle */
+ float insamplerate; /* sample rate of input signal if known */
+ /* parameters to communicate with subthread */
+ int requestcode; /* pending request from parent to I/O thread */
+ char *filename; /* file to open (string is permanently allocated) */
+ int fileerror; /* slot for "errno" return */
+ int skipheaderbytes; /* size of header we'll skip */
+ t_param p;
+ float samplerate; /* sample rate of soundfile */
+ long onsetframes; /* number of sample frames to skip */
+ int fd; /* filedesc */
+ int fifosize; /* buffer size appropriately rounded down */
+ int fifohead; /* index of next byte to get from file */
+ int fifotail; /* index of next byte the ugen will read */
+ int eof; /* true if fifohead has stopped changing */
+ int sigcountdown; /* counter for signalling child for more data */
+ int sigperiod; /* number of ticks per signal */
+ int filetype; /* writesf~ only; type of file to create */
+ int itemswritten; /* writesf~ only; items writen */
+ int swap; /* writesf~ only; true if byte swapping */
+ float f; /* writesf~ only; scalar for signal inlet */
+ pthread_mutex_t mutex;
+ pthread_cond_t requestcondition;
+ pthread_cond_t answercondition;
+ pthread_t childthread;
+};
+
+
+/************** the child thread which performs file I/O ***********/
+
+#if 1
+#define sfread_cond_wait pthread_cond_wait
+#define sfread_cond_signal pthread_cond_signal
+#else
+#include <sys/time.h> /* debugging version... */
+#include <sys/types.h>
+static void readsf_fakewait(pthread_mutex_t *b) {
+ struct timeval timout;
+ timout.tv_sec = 0;
+ timout.tv_usec = 1000000;
+ pthread_mutex_unlock(b);
+ select(0, 0, 0, 0, &timout);
+ pthread_mutex_lock(b);
+}
+
+#define sfread_cond_wait(a,b) readsf_fakewait(b)
+#define sfread_cond_signal(a)
+#endif
+
+static void *readsf_child_main(void *zz) {
+ t_readsf *x = (t_readsf *)zz;
+ pthread_mutex_lock(&x->mutex);
+ while (1) {
+ int fd, fifohead;
+ char *buf;
+ if (x->requestcode == REQUEST_NOTHING) {
+ sfread_cond_signal(&x->answercondition);
+ sfread_cond_wait(&x->requestcondition, &x->mutex);
+ } else if (x->requestcode == REQUEST_OPEN) {
+ int sysrtn, wantbytes;
+ /* copy file stuff out of the data structure so we can
+ relinquish the mutex while we're in open_soundfile(). */
+ long onsetframes = x->onsetframes;
+ t_param p;
+ p.bytelimit = 0x7fffffff;
+ int skipheaderbytes = x->skipheaderbytes;
+ p.bytespersample = x->p.bytespersample;
+ p.bigendian = x->p.bigendian;
+ /* alter the request code so that an ensuing "open" will get noticed. */
+ x->requestcode = REQUEST_BUSY;
+ x->fileerror = 0;
+ /* if there's already a file open, close it */
+ if (x->fd >= 0) {
+ fd = x->fd;
+ pthread_mutex_unlock(&x->mutex);
+ close (fd);
+ pthread_mutex_lock(&x->mutex);
+ x->fd = -1;
+ if (x->requestcode != REQUEST_BUSY) goto lost;
+ }
+ /* open the soundfile with the mutex unlocked */
+ pthread_mutex_unlock(&x->mutex);
+ fd = open_soundfile(canvas_getdir(x->canvas)->name, x->filename, skipheaderbytes, &p, onsetframes);
+ pthread_mutex_lock(&x->mutex);
+ /* copy back into the instance structure. */
+ x->p = p;
+ x->fd = fd;
+ if (fd < 0) {
+ x->fileerror = errno;
+ x->eof = 1;
+ goto lost;
+ }
+ /* check if another request has been made; if so, field it */
+ if (x->requestcode != REQUEST_BUSY) goto lost;
+ x->fifohead = 0;
+ /* set fifosize from bufsize. fifosize must be a multiple of the number of bytes eaten for each DSP
+ tick. We pessimistically assume MAXVECSIZE samples per tick since that could change. There could be a
+ problem here if the vector size increases while a soundfile is being played... */
+ x->fifosize = x->bufsize - (x->bufsize % (x->p.bytesperchannel() * MAXVECSIZE));
+ /* arrange for the "request" condition to be signalled 16 times per buffer */
+ x->sigcountdown = x->sigperiod = x->fifosize / (16 * x->p.bytesperchannel() * x->vecsize);
+ /* in a loop, wait for the fifo to get hungry and feed it */
+ while (x->requestcode == REQUEST_BUSY) {
+ int fifosize = x->fifosize;
+ if (x->eof) break;
+ if (x->fifohead >= x->fifotail) {
+ /* if the head is >= the tail, we can immediately read to the end of the fifo. Unless, that is, we
+ would read all the way to the end of the buffer and the "tail" is zero; this would fill the buffer completely
+ which isn't allowed because you can't tell a completely full buffer from an empty one. */
+ if (x->fifotail || (fifosize - x->fifohead > READSIZE)) {
+ wantbytes = min(min(fifosize - x->fifohead, READSIZE),(int)x->p.bytelimit);
+ } else {
+ sfread_cond_signal(&x->answercondition);
+ sfread_cond_wait(&x->requestcondition, &x->mutex);
+ continue;
+ }
+ } else {
+ /* otherwise check if there are at least READSIZE bytes to read. If not, wait and loop back. */
+ wantbytes = x->fifotail - x->fifohead - 1;
+ if (wantbytes < READSIZE) {
+ sfread_cond_signal(&x->answercondition);
+ sfread_cond_wait(&x->requestcondition, &x->mutex);
+ continue;
+ } else wantbytes = READSIZE;
+ wantbytes = min(wantbytes,(int)x->p.bytelimit);
+ }
+ fd = x->fd;
+ buf = x->buf;
+ fifohead = x->fifohead;
+ pthread_mutex_unlock(&x->mutex);
+ sysrtn = read(fd, buf + fifohead, wantbytes);
+ pthread_mutex_lock(&x->mutex);
+ if (x->requestcode != REQUEST_BUSY) break;
+ if (sysrtn < 0) {x->fileerror = errno; break;}
+ else if (sysrtn == 0) {x->eof = 1; break;}
+ else {
+ x->fifohead += sysrtn;
+ x->p.bytelimit -= sysrtn;
+ if (x->p.bytelimit <= 0) {x->eof = 1; break;}
+ if (x->fifohead == fifosize) x->fifohead = 0;
+ }
+ /* signal parent in case it's waiting for data */
+ sfread_cond_signal(&x->answercondition);
+ }
+ lost:
+ if (x->requestcode == REQUEST_BUSY) x->requestcode = REQUEST_NOTHING;
+ /* fell out of read loop: close file if necessary, set EOF and signal once more */
+ if (x->fd >= 0) {
+ fd = x->fd;
+ pthread_mutex_unlock(&x->mutex);
+ close (fd);
+ pthread_mutex_lock(&x->mutex);
+ x->fd = -1;
+ }
+ sfread_cond_signal(&x->answercondition);
+ } else if (x->requestcode == REQUEST_CLOSE || x->requestcode == REQUEST_QUIT) {
+ if (x->fd >= 0) {
+ fd = x->fd;
+ pthread_mutex_unlock(&x->mutex);
+ close (fd);
+ pthread_mutex_lock(&x->mutex);
+ x->fd = -1;
+ }
+ x->requestcode = REQUEST_NOTHING;
+ sfread_cond_signal(&x->answercondition);
+ }
+ if (x->requestcode == REQUEST_QUIT) break;
+ }
+ pthread_mutex_unlock(&x->mutex);
+ return 0;
+}
+
+/******** the object proper runs in the calling (parent) thread ****/
+
+static void readsf_tick(t_readsf *x);
+
+static void *readsf_new(t_floatarg fnchannels, t_floatarg fbufsize) {
+ int nchannels = int(fnchannels), bufsize = int(fbufsize);
+ if (nchannels < 1) nchannels = 1;
+ else if (nchannels > MAXSFCHANS) nchannels = MAXSFCHANS;
+ if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels;
+ else if (bufsize < MINBUFSIZE) bufsize = MINBUFSIZE;
+ else if (bufsize > MAXBUFSIZE) bufsize = MAXBUFSIZE;
+ char *buf = (char *)getbytes(bufsize);
+ if (!buf) return 0;
+ t_readsf *x = (t_readsf *)pd_new(readsf_class);
+ for (int i=0; i<nchannels; i++) outlet_new(x,gensym("signal"));
+ x->noutlets = nchannels;
+ x->bangout = outlet_new(x,&s_bang);
+ pthread_mutex_init(&x->mutex, 0);
+ pthread_cond_init(&x->requestcondition, 0);
+ pthread_cond_init(&x->answercondition, 0);
+ x->vecsize = MAXVECSIZE;
+ x->state = STATE_IDLE;
+ x->clock = clock_new(x, (t_method)readsf_tick);
+ x->canvas = canvas_getcurrent();
+ x->p.bytespersample = 2;
+ x->p.nchannels = 1;
+ x->fd = -1;
+ x->buf = buf;
+ x->bufsize = bufsize;
+ x->fifosize = x->fifohead = x->fifotail = x->requestcode = 0;
+ pthread_create(&x->childthread, 0, readsf_child_main, x);
+ return x;
+}
+
+static void readsf_tick(t_readsf *x) {outlet_bang(x->bangout);}
+
+static t_int *readsf_perform(t_int *w) {
+ t_readsf *x = (t_readsf *)(w[1]);
+ int vecsize = x->vecsize, noutlets = x->noutlets;
+ if (x->state == STATE_STREAM) {
+ int wantbytes;
+ pthread_mutex_lock(&x->mutex);
+ wantbytes = vecsize * x->p.bytesperchannel();
+ while (!x->eof && x->fifohead >= x->fifotail && x->fifohead < x->fifotail + wantbytes-1) {
+ sfread_cond_signal(&x->requestcondition);
+ sfread_cond_wait(&x->answercondition, &x->mutex);
+ }
+ if (x->eof && x->fifohead >= x->fifotail && x->fifohead < x->fifotail + wantbytes-1) {
+ if (x->fileerror) {
+ error("dsp: %s: %s", x->filename, x->fileerror == EIO ? "unknown or bad header format" : strerror(x->fileerror));
+ }
+ clock_delay(x->clock, 0);
+ x->state = STATE_IDLE;
+ /* if there's a partial buffer left, copy it out. */
+ int xfersize = (x->fifohead - x->fifotail + 1) / x->p.bytesperchannel();
+ if (xfersize) {
+ soundfile_xferin(x->p.nchannels, noutlets, x->outvec, 0,
+ (unsigned char *)(x->buf + x->fifotail), xfersize, x->p.bytespersample, x->p.bigendian);
+ vecsize -= xfersize;
+ }
+ /* then zero out the (rest of the) output */
+ for (int i=0; i<noutlets; i++) {
+ float *fp = x->outvec[i] + xfersize;
+ for (int j=vecsize; j--; ) *fp++ = 0;
+ }
+ sfread_cond_signal(&x->requestcondition);
+ pthread_mutex_unlock(&x->mutex);
+ return w+2;
+ }
+ soundfile_xferin(x->p.nchannels, noutlets, x->outvec, 0, (unsigned char *)(x->buf + x->fifotail), vecsize, x->p.bytespersample, x->p.bigendian);
+ x->fifotail += wantbytes;
+ if (x->fifotail >= x->fifosize) x->fifotail = 0;
+ if ((--x->sigcountdown) <= 0) {
+ sfread_cond_signal(&x->requestcondition);
+ x->sigcountdown = x->sigperiod;
+ }
+ pthread_mutex_unlock(&x->mutex);
+ } else {
+ for (int i=0; i<noutlets; i++) {
+ float *fp = x->outvec[i];
+ for (int j=vecsize; j--; ) *fp++=0;
+ }
+ }
+ return w+2;
+}
+
+static void readsf_start(t_readsf *x) {
+ /* start making output. If we're in the "startup" state change
+ to the "running" state. */
+ if (x->state == STATE_STARTUP) x->state = STATE_STREAM;
+ else error("readsf: start requested with no prior 'open'");
+}
+
+static void readsf_stop(t_readsf *x) {
+ /* LATER rethink whether you need the mutex just to set a variable? */
+ pthread_mutex_lock(&x->mutex);
+ x->state = STATE_IDLE;
+ x->requestcode = REQUEST_CLOSE;
+ sfread_cond_signal(&x->requestcondition);
+ pthread_mutex_unlock(&x->mutex);
+}
+
+static void readsf_float(t_readsf *x, t_floatarg f) {
+ if (f != 0) readsf_start(x); else readsf_stop(x);
+}
+
+/* open method. Called as: open filename [skipframes headersize channels bytespersample endianness]
+ (if headersize is zero, header is taken to be automatically detected; thus, use the special "-1" to mean a truly headerless file.) */
+static void readsf_open(t_readsf *x, t_symbol *s, int argc, t_atom *argv) {
+ t_symbol *filesym = atom_getsymbolarg(0, argc, argv);
+ t_float onsetframes = atom_getfloatarg(1, argc, argv);
+ t_float headerbytes = atom_getfloatarg(2, argc, argv);
+ t_float channels = atom_getfloatarg(3, argc, argv);
+ t_float bytespersample = atom_getfloatarg(4, argc, argv);
+ t_symbol *endian = atom_getsymbolarg(5, argc, argv);
+ if (!*filesym->name) return;
+ pthread_mutex_lock(&x->mutex);
+ x->requestcode = REQUEST_OPEN;
+ x->filename = filesym->name;
+ x->fifotail = 0;
+ x->fifohead = 0;
+ if (*endian->name == 'b') x->p.bigendian = 1;
+ else if (*endian->name == 'l') x->p.bigendian = 0;
+ else if (*endian->name) error("endianness neither 'b' nor 'l'");
+ else x->p.bigendian = garray_ambigendian();
+ x->onsetframes = max(long(onsetframes),0L);
+ x->skipheaderbytes = int(headerbytes > 0 ? headerbytes : headerbytes == 0 ? -1 : 0);
+ x->p.nchannels = max(int(channels),1);
+ x->p.bytespersample = max(int(bytespersample),2);
+ x->eof = 0;
+ x->fileerror = 0;
+ x->state = STATE_STARTUP;
+ sfread_cond_signal(&x->requestcondition);
+ pthread_mutex_unlock(&x->mutex);
+}
+
+static void readsf_dsp(t_readsf *x, t_signal **sp) {
+ int i, noutlets = x->noutlets;
+ pthread_mutex_lock(&x->mutex);
+ x->vecsize = sp[0]->n;
+ x->sigperiod = (x->fifosize / (x->p.bytesperchannel() * x->vecsize));
+ for (i = 0; i < noutlets; i++) x->outvec[i] = sp[i]->v;
+ pthread_mutex_unlock(&x->mutex);
+ dsp_add(readsf_perform, 1, x);
+}
+
+static void readsf_print(t_readsf *x) {
+ post("state %d", x->state);
+ post("fifo head %d", x->fifohead);
+ post("fifo tail %d", x->fifotail);
+ post("fifo size %d", x->fifosize);
+ post("fd %d", x->fd);
+ post("eof %d", x->eof);
+}
+
+static void readsf_free(t_readsf *x) {
+ /* request QUIT and wait for acknowledge */
+ void *threadrtn;
+ pthread_mutex_lock(&x->mutex);
+ x->requestcode = REQUEST_QUIT;
+ sfread_cond_signal(&x->requestcondition);
+ while (x->requestcode != REQUEST_NOTHING) {
+ sfread_cond_signal(&x->requestcondition);
+ sfread_cond_wait(&x->answercondition, &x->mutex);
+ }
+ pthread_mutex_unlock(&x->mutex);
+ if (pthread_join(x->childthread, &threadrtn)) error("readsf_free: join failed");
+ pthread_cond_destroy(&x->requestcondition);
+ pthread_cond_destroy(&x->answercondition);
+ pthread_mutex_destroy(&x->mutex);
+ free(x->buf);
+ clock_free(x->clock);
+}
+
+static void readsf_setup() {
+ t_class *c = readsf_class = class_new2("readsf~", (t_newmethod)readsf_new, (t_method)readsf_free, sizeof(t_readsf), 0, "FF");
+ class_addfloat(c, (t_method)readsf_float);
+ class_addmethod2(c, (t_method)readsf_start, "start","");
+ class_addmethod2(c, (t_method)readsf_stop, "stop","");
+ class_addmethod2(c, (t_method)readsf_dsp, "dsp","");
+ class_addmethod2(c, (t_method)readsf_open, "open","*");
+ class_addmethod2(c, (t_method)readsf_print, "print","");
+}
+
+/******************************* writesf *******************/
+
+static t_class *writesf_class;
+typedef t_readsf t_writesf; /* just re-use the structure */
+
+/************** the child thread which performs file I/O ***********/
+
+static void *writesf_child_main(void *zz) {
+ t_writesf *x = (t_writesf*)zz;
+ pthread_mutex_lock(&x->mutex);
+ while (1) {
+ if (x->requestcode == REQUEST_NOTHING) {
+ sfread_cond_signal(&x->answercondition);
+ sfread_cond_wait(&x->requestcondition, &x->mutex);
+ } else if (x->requestcode == REQUEST_OPEN) {
+ int fd, sysrtn, writebytes;
+ /* copy file stuff out of the data structure so we can relinquish the mutex while we're in open_soundfile(). */
+ int filetype = x->filetype;
+ char *filename = x->filename;
+ t_canvas *canvas = x->canvas;
+ float samplerate = x->samplerate;
+ /* alter the request code so that an ensuing "open" will get noticed. */
+ x->requestcode = REQUEST_BUSY;
+ x->fileerror = 0;
+ /* if there's already a file open, close it. This should never happen since
+ writesf_open() calls stop if needed and then waits until we're idle. */
+ if (x->fd >= 0) {
+ int bytesperframe = x->p.bytesperchannel();
+ char *filename = x->filename;
+ int fd = x->fd;
+ int filetype = x->filetype;
+ int itemswritten = x->itemswritten;
+ int swap = x->swap;
+ pthread_mutex_unlock(&x->mutex);
+ soundfile_finishwrite(x, filename, fd, filetype, 0x7fffffff, itemswritten, bytesperframe, swap);
+ close (fd);
+ pthread_mutex_lock(&x->mutex);
+ x->fd = -1;
+ if (x->requestcode != REQUEST_BUSY) continue;
+ }
+ /* open the soundfile with the mutex unlocked */
+ pthread_mutex_unlock(&x->mutex);
+ fd = create_soundfile(canvas, filename, filetype, 0,
+ x->p.bytespersample, x->p.bigendian, x->p.nchannels, garray_ambigendian() != x->p.bigendian, samplerate);
+ pthread_mutex_lock(&x->mutex);
+ if (fd < 0) {
+ x->fd = -1;
+ x->eof = 1;
+ x->fileerror = errno;
+ x->requestcode = REQUEST_NOTHING;
+ continue;
+ }
+ /* check if another request has been made; if so, field it */
+ if (x->requestcode != REQUEST_BUSY) continue;
+ x->fd = fd;
+ x->fifotail = 0;
+ x->itemswritten = 0;
+ x->swap = garray_ambigendian() != x->p.bigendian;
+ /* in a loop, wait for the fifo to have data and write it to disk */
+ while (x->requestcode == REQUEST_BUSY || (x->requestcode == REQUEST_CLOSE && x->fifohead != x->fifotail)) {
+ int fifosize = x->fifosize, fifotail;
+ char *buf = x->buf;
+ /* if the head is < the tail, we can immediately write
+ from tail to end of fifo to disk; otherwise we hold off
+ writing until there are at least WRITESIZE bytes in the
+ buffer */
+ if (x->fifohead < x->fifotail || x->fifohead >= x->fifotail + WRITESIZE
+ || (x->requestcode == REQUEST_CLOSE && x->fifohead != x->fifotail)) {
+ writebytes = (x->fifohead < x->fifotail ? fifosize : x->fifohead) - x->fifotail;
+ if (writebytes > READSIZE) writebytes = READSIZE;
+ } else {
+ sfread_cond_signal(&x->answercondition);
+ sfread_cond_wait(&x->requestcondition,&x->mutex);
+ continue;
+ }
+ fifotail = x->fifotail;
+ fd = x->fd;
+ pthread_mutex_unlock(&x->mutex);
+ sysrtn = write(fd, buf + fifotail, writebytes);
+ pthread_mutex_lock(&x->mutex);
+ if (x->requestcode != REQUEST_BUSY && x->requestcode != REQUEST_CLOSE) break;
+ if (sysrtn < writebytes) {x->fileerror = errno; break;}
+ else {
+ x->fifotail += sysrtn;
+ if (x->fifotail == fifosize) x->fifotail = 0;
+ }
+ x->itemswritten += sysrtn / x->p.bytesperchannel();
+ /* signal parent in case it's waiting for data */
+ sfread_cond_signal(&x->answercondition);
+ }
+ } else if (x->requestcode == REQUEST_CLOSE || x->requestcode == REQUEST_QUIT) {
+ if (x->fd >= 0) {
+ int bytesperframe = x->p.bytesperchannel();
+ char *filename = x->filename;
+ int fd = x->fd;
+ int filetype = x->filetype;
+ int itemswritten = x->itemswritten;
+ int swap = x->swap;
+ pthread_mutex_unlock(&x->mutex);
+ soundfile_finishwrite(x, filename, fd, filetype, 0x7fffffff, itemswritten, bytesperframe, swap);
+ close(fd);
+ pthread_mutex_lock(&x->mutex);
+ x->fd = -1;
+ }
+ x->requestcode = REQUEST_NOTHING;
+ sfread_cond_signal(&x->answercondition);
+ }
+ if (x->requestcode == REQUEST_QUIT) break;
+ }
+ pthread_mutex_unlock(&x->mutex);
+ return 0;
+}
+
+/******** the object proper runs in the calling (parent) thread ****/
+
+static void *writesf_new(t_floatarg fnchannels, t_floatarg fbufsize) {
+ int nchannels = int(fnchannels), bufsize = int(fbufsize), i;
+ if (nchannels < 1) nchannels = 1;
+ else if (nchannels > MAXSFCHANS) nchannels = MAXSFCHANS;
+ if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels;
+ else if (bufsize < MINBUFSIZE) bufsize = MINBUFSIZE;
+ else if (bufsize > MAXBUFSIZE) bufsize = MAXBUFSIZE;
+ char *buf = (char *)getbytes(bufsize);
+ if (!buf) return 0;
+ t_writesf *x = (t_writesf *)pd_new(writesf_class);
+ for (i = 1; i < nchannels; i++) inlet_new(x,x, &s_signal, &s_signal);
+ x->f = 0;
+ x->p.nchannels = nchannels;
+ pthread_mutex_init(&x->mutex, 0);
+ pthread_cond_init(&x->requestcondition, 0);
+ pthread_cond_init(&x->answercondition, 0);
+ x->vecsize = MAXVECSIZE;
+ x->insamplerate = x->samplerate = 0;
+ x->state = STATE_IDLE;
+ x->clock = 0; /* no callback needed here */
+ x->canvas = canvas_getcurrent();
+ x->p.bytespersample = 2;
+ x->fd = -1;
+ x->buf = buf;
+ x->bufsize = bufsize;
+ x->fifosize = x->fifohead = x->fifotail = x->requestcode = 0;
+ pthread_create(&x->childthread, 0, writesf_child_main, x);
+ return x;
+}
+
+static t_int *writesf_perform(t_int *w) {
+ t_writesf *x = (t_writesf *)(w[1]);
+ if (x->state == STATE_STREAM) {
+ int wantbytes;
+ pthread_mutex_lock(&x->mutex);
+ wantbytes = x->vecsize * x->p.bytesperchannel();
+ while (x->fifotail > x->fifohead && x->fifotail < x->fifohead + wantbytes + 1) {
+ sfread_cond_signal(&x->requestcondition);
+ sfread_cond_wait(&x->answercondition, &x->mutex);
+ /* resync local cariables -- bug fix thanks to Shahrokh */
+ wantbytes = x->vecsize * x->p.bytesperchannel();
+ }
+ soundfile_xferout(x->p.nchannels, x->outvec, (unsigned char *)(x->buf + x->fifohead), x->vecsize, 0, x->p.bytespersample, x->p.bigendian, 1.);
+ x->fifohead += wantbytes;
+ if (x->fifohead >= x->fifosize) x->fifohead = 0;
+ if ((--x->sigcountdown) <= 0) {
+ sfread_cond_signal(&x->requestcondition);
+ x->sigcountdown = x->sigperiod;
+ }
+ pthread_mutex_unlock(&x->mutex);
+ }
+ return w+2;
+}
+
+static void writesf_start(t_writesf *x) {
+ /* start making output. If we're in the "startup" state change to the "running" state. */
+ if (x->state == STATE_STARTUP) x->state = STATE_STREAM;
+ else error("writesf: start requested with no prior 'open'");
+}
+
+static void writesf_stop(t_writesf *x) {
+ /* LATER rethink whether you need the mutex just to set a Svariable? */
+ pthread_mutex_lock(&x->mutex);
+ x->state = STATE_IDLE;
+ x->requestcode = REQUEST_CLOSE;
+ sfread_cond_signal(&x->requestcondition);
+ pthread_mutex_unlock(&x->mutex);
+}
+
+/* open method. Called as: open [args] filename with args as in soundfiler_writeargparse(). */
+static void writesf_open(t_writesf *x, t_symbol *s, int argc, t_atom *argv) {
+ t_symbol *filesym;
+ int filetype, bytespersample, swap, bigendian, normalize;
+ long onset, nframes;
+ float samplerate;
+ if (x->state != STATE_IDLE) writesf_stop(x);
+ if (soundfiler_writeargparse(x, &argc, &argv, &filesym, &filetype, &bytespersample, &swap, &bigendian,
+ &normalize, &onset, &nframes, &samplerate)) {
+ error("writesf~: usage: open [-bytes [234]] [-wave,-nextstep,-aiff] ...");
+ post("... [-big,-little] [-rate ####] filename");
+ return;
+ }
+ if (normalize || onset || (nframes != 0x7fffffff))
+ error("normalize/onset/nframes argument to writesf~: ignored");
+ if (argc) error("extra argument(s) to writesf~: ignored");
+ pthread_mutex_lock(&x->mutex);
+ while (x->requestcode != REQUEST_NOTHING) {
+ sfread_cond_signal(&x->requestcondition);
+ sfread_cond_wait(&x->answercondition, &x->mutex);
+ }
+ x->p.bytespersample = max(bytespersample,2);
+ x->swap = swap;
+ x->p.bigendian = bigendian;
+ x->filename = filesym->name;
+ x->filetype = filetype;
+ x->itemswritten = 0;
+ x->requestcode = REQUEST_OPEN;
+ x->fifotail = 0;
+ x->fifohead = 0;
+ x->eof = 0;
+ x->fileerror = 0;
+ x->state = STATE_STARTUP;
+ x->samplerate = samplerate>0 ? samplerate : x->insamplerate>0 ? x->insamplerate : sys_getsr();
+ /* set fifosize from bufsize. fifosize must be a multiple of the number of bytes eaten for each DSP tick. */
+ x->fifosize = x->bufsize - x->bufsize % (x->p.bytesperchannel() * MAXVECSIZE);
+ /* arrange for the "request" condition to be signalled 16 times per buffer */
+ x->sigcountdown = x->sigperiod = x->fifosize / (16 * x->p.bytesperchannel() * x->vecsize);
+ sfread_cond_signal(&x->requestcondition);
+ pthread_mutex_unlock(&x->mutex);
+}
+
+static void writesf_dsp(t_writesf *x, t_signal **sp) {
+ int ninlets = x->p.nchannels;
+ pthread_mutex_lock(&x->mutex);
+ x->vecsize = sp[0]->n;
+ x->sigperiod = x->fifosize / (x->p.bytesperchannel() * x->vecsize);
+ for (int i=0; i<ninlets; i++) x->outvec[i] = sp[i]->v;
+ x->insamplerate = sp[0]->sr;
+ pthread_mutex_unlock(&x->mutex);
+ dsp_add(writesf_perform, 1, x);
+}
+
+static void writesf_print(t_writesf *x) {
+ post("state %d", x->state);
+ post("fifo head %d", x->fifohead);
+ post("fifo tail %d", x->fifotail);
+ post("fifo size %d", x->fifosize);
+ post("fd %d", x->fd);
+ post("eof %d", x->eof);
+}
+
+static void writesf_free(t_writesf *x) {
+ /* request QUIT and wait for acknowledge */
+ void *threadrtn;
+ pthread_mutex_lock(&x->mutex);
+ x->requestcode = REQUEST_QUIT;
+ /* post("stopping writesf thread..."); */
+ sfread_cond_signal(&x->requestcondition);
+ while (x->requestcode != REQUEST_NOTHING) {
+ /* post("signalling..."); */
+ sfread_cond_signal(&x->requestcondition);
+ sfread_cond_wait(&x->answercondition, &x->mutex);
+ }
+ pthread_mutex_unlock(&x->mutex);
+ if (pthread_join(x->childthread, &threadrtn)) error("writesf_free: join failed");
+ /* post("... done."); */
+ pthread_cond_destroy(&x->requestcondition);
+ pthread_cond_destroy(&x->answercondition);
+ pthread_mutex_destroy(&x->mutex);
+ free(x->buf);
+}
+
+static void writesf_setup() {
+ t_class *c = writesf_class = class_new2("writesf~", (t_newmethod)writesf_new, (t_method)writesf_free, sizeof(t_writesf), 0, "FF");
+ class_addmethod2(c, (t_method)writesf_start, "start", "");
+ class_addmethod2(c, (t_method)writesf_stop, "stop", "");
+ class_addmethod2(c, (t_method)writesf_dsp, "dsp", "");
+ class_addmethod2(c, (t_method)writesf_open, "open", "*");
+ class_addmethod2(c, (t_method)writesf_print, "print", "");
+ CLASS_MAINSIGNALIN(c, t_writesf, f);
+}
+
+/* ------------------------ global setup routine ------------------------- */
+
+void d_soundfile_setup() {
+ if (sizeof(uint16)!=2) bug("uint16 should really be 16 bits");
+ if (sizeof(uint32)!=4) bug("uint32 should really be 32 bits");
+ soundfiler_setup();
+ readsf_setup();
+ writesf_setup();
+}