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