diff options
author | N.N. <matju@users.sourceforge.net> | 2010-01-05 22:49:36 +0000 |
---|---|---|
committer | N.N. <matju@users.sourceforge.net> | 2010-01-05 22:49:36 +0000 |
commit | 8dbec761cf858ea65900c8a094599857208d8c3a (patch) | |
tree | 3228c023f87f23a354da3b57fdc2afe5b7052032 /desiredata/src/d_soundfile.c | |
parent | 529e59635598e2d90a7a49f6b4c676f8366109ba (diff) |
svn path=/trunk/; revision=12907
Diffstat (limited to 'desiredata/src/d_soundfile.c')
-rw-r--r-- | desiredata/src/d_soundfile.c | 2059 |
1 files changed, 0 insertions, 2059 deletions
diff --git a/desiredata/src/d_soundfile.c b/desiredata/src/d_soundfile.c deleted file mode 100644 index d4162115..00000000 --- a/desiredata/src/d_soundfile.c +++ /dev/null @@ -1,2059 +0,0 @@ -/* 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" -using namespace desire; -#define a_symbol a_w.w_symbol -#define a_float a_w.w_float - -#define MAXSFCHANS 256 - -#ifdef _LARGEFILE64_SOURCE -# define open open64 -# define lseek lseek64 -#endif - -#if 0 -static bool debug=0; -#endif - -#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: %ld out of %ld 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->send(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(); - x->outlet->send(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(); - y->outlet->send((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(); - y->outlet->send(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[i]) { - 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 %ld 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); - x->outlet->send(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); - x->outlet->send(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, soundfiler_t_read_addq, "read", "*"); -/* class_addmethod2(c, soundfiler_t_write_addq, "write", "*"); */ - class_addmethod2(c, soundfiler_t_resize_addq, "resize", "*"); - class_addmethod2(c, soundfiler_t_const_addq, "const", "*"); -#else - class_addmethod2(c, soundfiler_read, "read", "*"); -#endif /* THREADED_SF */ - class_addmethod2(c, 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) {x->bangout->send();} - -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(); -} |