summaryrefslogtreecommitdiff
path: root/apps/plugins/pdbox/PDa/src/d_soundfile.c
diff options
context:
space:
mode:
authorPeter D'Hoye <peter.dhoye@gmail.com>2009-05-22 21:58:48 +0000
committerPeter D'Hoye <peter.dhoye@gmail.com>2009-05-22 21:58:48 +0000
commit513389b4c1bc8afe4b2dc9947c534bfeb105e3da (patch)
tree10e673b35651ac567fed2eda0c679c7ade64cbc6 /apps/plugins/pdbox/PDa/src/d_soundfile.c
parent95fa7f6a2ef466444fbe3fe87efc6d5db6b77b36 (diff)
downloadrockbox-513389b4c1bc8afe4b2dc9947c534bfeb105e3da.tar.gz
rockbox-513389b4c1bc8afe4b2dc9947c534bfeb105e3da.zip
Add FS #10214. Initial commit of the original PDa code for the GSoC Pure Data plugin project of Wincent Balin. Stripped some non-sourcefiles and added a rockbox readme that needs a bit more info from Wincent. Is added to CATEGORIES and viewers, but not yet to SUBDIRS (ie doesn't build yet)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21044 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/d_soundfile.c')
-rw-r--r--apps/plugins/pdbox/PDa/src/d_soundfile.c4734
1 files changed, 4734 insertions, 0 deletions
diff --git a/apps/plugins/pdbox/PDa/src/d_soundfile.c b/apps/plugins/pdbox/PDa/src/d_soundfile.c
new file mode 100644
index 0000000000..872a44a923
--- /dev/null
+++ b/apps/plugins/pdbox/PDa/src/d_soundfile.c
@@ -0,0 +1,4734 @@
1/* Copyright (c) 1997-1999 Miller Puckette.
2* For information on usage and redistribution, and for a DISCLAIMER OF ALL
3* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
4
5/* this file contains, first, a collection of soundfile access routines, a
6sort of soundfile library. Second, the "soundfiler" object is defined which
7uses the routines to read or write soundfiles, synchronously, from garrays.
8These operations are not to be done in "real time" as they may have to wait
9for disk accesses (even the write routine.) Finally, the realtime objects
10readsf~ and writesf~ are defined which confine disk operations to a separate
11thread so that they can be used in real time. The readsf~ and writesf~
12objects use Posix-like threads. */
13
14#ifdef UNIX
15#include <unistd.h>
16#include <fcntl.h>
17#endif
18#include <pthread.h>
19#ifdef MSW
20#include <io.h>
21#endif
22#include <stdio.h>
23#include <string.h>
24#include <errno.h>
25
26#include "m_pd.h"
27
28#define MAXSFCHANS 64
29
30/***************** soundfile header structures ************************/
31
32typedef unsigned short uint16;
33typedef unsigned long uint32;
34
35#define FORMAT_WAVE 0
36#define FORMAT_AIFF 1
37#define FORMAT_NEXT 2
38
39/* the NeXTStep sound header structure; can be big or little endian */
40
41typedef struct _nextstep
42{
43 char ns_fileid[4]; /* magic number '.snd' if file is big-endian */
44 uint32 ns_onset; /* byte offset of first sample */
45 uint32 ns_length; /* length of sound in bytes */
46 uint32 ns_format; /* format; see below */
47 uint32 ns_sr; /* sample rate */
48 uint32 ns_nchans; /* number of channels */
49 char ns_info[4]; /* comment */
50} t_nextstep;
51
52#define NS_FORMAT_LINEAR_16 3
53#define NS_FORMAT_LINEAR_24 4
54#define NS_FORMAT_FLOAT 6
55#define SCALE (1./(1024. * 1024. * 1024. * 2.))
56
57/* the WAVE header. All Wave files are little endian. We assume
58 the "fmt" chunk comes first which is usually the case but perhaps not
59 always; same for AIFF and the "COMM" chunk. */
60
61typedef unsigned word;
62typedef unsigned long dword;
63
64typedef struct _wave
65{
66 char w_fileid[4]; /* chunk id 'RIFF' */
67 uint32 w_chunksize; /* chunk size */
68 char w_waveid[4]; /* wave chunk id 'WAVE' */
69 char w_fmtid[4]; /* format chunk id 'fmt ' */
70 uint32 w_fmtchunksize; /* format chunk size */
71 uint16 w_fmttag; /* format tag (WAV_INT etc) */
72 uint16 w_nchannels; /* number of channels */
73 uint32 w_samplespersec; /* sample rate in hz */
74 uint32 w_navgbytespersec; /* average bytes per second */
75 uint16 w_nblockalign; /* number of bytes per frame */
76 uint16 w_nbitspersample; /* number of bits in a sample */
77 char w_datachunkid[4]; /* data chunk id 'data' */
78 uint32 w_datachunksize; /* length of data chunk */
79} t_wave;
80
81typedef struct _fmt /* format chunk */
82{
83 uint16 f_fmttag; /* format tag, 1 for PCM */
84 uint16 f_nchannels; /* number of channels */
85 uint32 f_samplespersec; /* sample rate in hz */
86 uint32 f_navgbytespersec; /* average bytes per second */
87 uint16 f_nblockalign; /* number of bytes per frame */
88 uint16 f_nbitspersample; /* number of bits in a sample */
89} t_fmt;
90
91typedef struct _wavechunk /* ... and the last two items */
92{
93 char wc_id[4]; /* data chunk id, e.g., 'data' or 'fmt ' */
94 uint32 wc_size; /* length of data chunk */
95} t_wavechunk;
96
97#define WAV_INT 1
98#define WAV_FLOAT 3
99
100/* the AIFF header. I'm assuming AIFC is compatible but don't really know
101 that. */
102
103typedef struct _datachunk
104{
105 char dc_id[4]; /* data chunk id 'SSND' */
106 uint32 dc_size; /* length of data chunk */
107} t_datachunk;
108
109typedef struct _comm
110{
111 uint16 c_nchannels; /* number of channels */
112 uint16 c_nframeshi; /* # of sample frames (hi) */
113 uint16 c_nframeslo; /* # of sample frames (lo) */
114 uint16 c_bitspersamp; /* bits per sample */
115 unsigned char c_samprate[10]; /* sample rate, 80-bit float! */
116} t_comm;
117
118 /* this version is more convenient for writing them out: */
119typedef struct _aiff
120{
121 char a_fileid[4]; /* chunk id 'FORM' */
122 uint32 a_chunksize; /* chunk size */
123 char a_aiffid[4]; /* aiff chunk id 'AIFF' */
124 char a_fmtid[4]; /* format chunk id 'COMM' */
125 uint32 a_fmtchunksize; /* format chunk size, 18 */
126 uint16 a_nchannels; /* number of channels */
127 uint16 a_nframeshi; /* # of sample frames (hi) */
128 uint16 a_nframeslo; /* # of sample frames (lo) */
129 uint16 a_bitspersamp; /* bits per sample */
130 unsigned char a_samprate[10]; /* sample rate, 80-bit float! */
131} t_aiff;
132
133#define AIFFHDRSIZE 38 /* probably not what sizeof() gives */
134
135
136#define AIFFPLUS (AIFFHDRSIZE + 8) /* header size including first chunk hdr */
137
138#define WHDR1 sizeof(t_nextstep)
139#define WHDR2 (sizeof(t_wave) > WHDR1 ? sizeof (t_wave) : WHDR1)
140#define WRITEHDRSIZE (AIFFPLUS > WHDR2 ? AIFFPLUS : WHDR2)
141
142#define READHDRSIZE (16 > WHDR2 + 2 ? 16 : WHDR2 + 2)
143
144#define OBUFSIZE MAXPDSTRING /* assume MAXPDSTRING is bigger than headers */
145
146#ifdef MSW
147#include <fcntl.h>
148#define BINCREATE _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY
149#else
150#define BINCREATE O_WRONLY | O_CREAT | O_TRUNC
151#endif
152
153/* this routine returns 1 if the high order byte comes at the lower
154address on our architecture (big-endianness.). It's 1 for Motorola,
1550 for Intel: */
156
157extern int garray_ambigendian(void);
158
159/* byte swappers */
160
161static uint32 swap4(uint32 n, int doit)
162{
163 if (doit)
164 return (((n & 0xff) << 24) | ((n & 0xff00) << 8) |
165 ((n & 0xff0000) >> 8) | ((n & 0xff000000) >> 24));
166 else return (n);
167}
168
169static uint16 swap2(uint32 n, int doit)
170{
171 if (doit)
172 return (((n & 0xff) << 8) | ((n & 0xff00) >> 8));
173 else return (n);
174}
175
176static void swapstring(char *foo, int doit)
177{
178 if (doit)
179 {
180 char a = foo[0], b = foo[1], c = foo[2], d = foo[3];
181 foo[0] = d; foo[1] = c; foo[2] = b; foo[3] = a;
182 }
183}
184
185/******************** soundfile access routines **********************/
186
187/* This routine opens a file, looks for either a nextstep or "wave" header,
188* seeks to end of it, and fills in bytes per sample and number of channels.
189* Only 2- and 3-byte fixed-point samples and 4-byte floating point samples
190* are supported. If "headersize" is nonzero, the
191* caller should supply the number of channels, endinanness, and bytes per
192* sample; the header is ignored. Otherwise, the routine tries to read the
193* header and fill in the properties.
194*/
195
196int open_soundfile(const char *dirname, const char *filename, int headersize,
197 int *p_bytespersamp, int *p_bigendian, int *p_nchannels, long *p_bytelimit,
198 long skipframes)
199{
200 char buf[OBUFSIZE], *bufptr;
201 int fd, format, nchannels, bigendian, bytespersamp, swap, sysrtn;
202 long bytelimit = 0x7fffffff;
203 errno = 0;
204 fd = open_via_path(dirname, filename,
205 "", buf, &bufptr, MAXPDSTRING, 1);
206 if (fd < 0)
207 return (-1);
208 if (headersize >= 0) /* header detection overridden */
209 {
210 bigendian = *p_bigendian;
211 nchannels = *p_nchannels;
212 bytespersamp = *p_bytespersamp;
213 bytelimit = *p_bytelimit;
214 }
215 else
216 {
217 int bytesread = read(fd, buf, READHDRSIZE);
218 int format;
219 if (bytesread < 4)
220 goto badheader;
221 if (!strncmp(buf, ".snd", 4))
222 format = FORMAT_NEXT, bigendian = 1;
223 else if (!strncmp(buf, "dns.", 4))
224 format = FORMAT_NEXT, bigendian = 0;
225 else if (!strncmp(buf, "RIFF", 4))
226 {
227 if (bytesread < 12 || strncmp(buf + 8, "WAVE", 4))
228 goto badheader;
229 format = FORMAT_WAVE, bigendian = 0;
230 }
231 else if (!strncmp(buf, "FORM", 4))
232 {
233 if (bytesread < 12 || strncmp(buf + 8, "AIFF", 4))
234 goto badheader;
235 format = FORMAT_AIFF, bigendian = 1;
236 }
237 else
238 goto badheader;
239 swap = (bigendian != garray_ambigendian());
240 if (format == FORMAT_NEXT) /* nextstep header */
241 {
242 uint32 param;
243 if (bytesread < (int)sizeof(t_nextstep))
244 goto badheader;
245 nchannels = swap4(((t_nextstep *)buf)->ns_nchans, swap);
246 format = swap4(((t_nextstep *)buf)->ns_format, swap);
247 headersize = swap4(((t_nextstep *)buf)->ns_onset, swap);
248 if (format == NS_FORMAT_LINEAR_16)
249 bytespersamp = 2;
250 else if (format == NS_FORMAT_LINEAR_24)
251 bytespersamp = 3;
252 else if (format == NS_FORMAT_FLOAT)
253 bytespersamp = 4;
254 else goto badheader;
255 bytelimit = 0x7fffffff;
256 }
257 else if (format == FORMAT_WAVE) /* wave header */
258 {
259 /* This is awful. You have to skip over chunks,
260 except that if one happens to be a "fmt" chunk, you want to
261 find out the format from that one. The case where the
262 "fmt" chunk comes after the audio isn't handled. */
263 headersize = 12;
264 if (bytesread < 20)
265 goto badheader;
266 /* First we guess a number of channels, etc., in case there's
267 no "fmt" chunk to follow. */
268 nchannels = 1;
269 bytespersamp = 2;
270 /* copy the first chunk header to beginnning of buffer. */
271 memcpy(buf, buf + headersize, sizeof(t_wavechunk));
272 /* post("chunk %c %c %c %c",
273 ((t_wavechunk *)buf)->wc_id[0],
274 ((t_wavechunk *)buf)->wc_id[1],
275 ((t_wavechunk *)buf)->wc_id[2],
276 ((t_wavechunk *)buf)->wc_id[3]); */
277 /* read chunks in loop until we get to the data chunk */
278 while (strncmp(((t_wavechunk *)buf)->wc_id, "data", 4))
279 {
280 long chunksize = swap4(((t_wavechunk *)buf)->wc_size,
281 swap), seekto = headersize + chunksize + 8, seekout;
282
283 if (!strncmp(((t_wavechunk *)buf)->wc_id, "fmt ", 4))
284 {
285 long commblockonset = headersize + 8;
286 seekout = lseek(fd, commblockonset, SEEK_SET);
287 if (seekout != commblockonset)
288 goto badheader;
289 if (read(fd, buf, sizeof(t_fmt)) < (int) sizeof(t_fmt))
290 goto badheader;
291 nchannels = swap2(((t_fmt *)buf)->f_nchannels, swap);
292 format = swap2(((t_fmt *)buf)->f_nbitspersample, swap);
293 if (format == 16)
294 bytespersamp = 2;
295 else if (format == 24)
296 bytespersamp = 3;
297 else if (format == 32)
298 bytespersamp = 4;
299 else goto badheader;
300 }
301 seekout = lseek(fd, seekto, SEEK_SET);
302 if (seekout != seekto)
303 goto badheader;
304 if (read(fd, buf, sizeof(t_wavechunk)) <
305 (int) sizeof(t_wavechunk))
306 goto badheader;
307 /* post("new chunk %c %c %c %c at %d",
308 ((t_wavechunk *)buf)->wc_id[0],
309 ((t_wavechunk *)buf)->wc_id[1],
310 ((t_wavechunk *)buf)->wc_id[2],
311 ((t_wavechunk *)buf)->wc_id[3], seekto); */
312 headersize = seekto;
313 }
314 bytelimit = swap4(((t_wavechunk *)buf)->wc_size, swap);
315 headersize += 8;
316 }
317 else
318 {
319 /* AIFF. same as WAVE; actually predates it. Disgusting. */
320 headersize = 12;
321 if (bytesread < 20)
322 goto badheader;
323 /* First we guess a number of channels, etc., in case there's
324 no COMM block to follow. */
325 nchannels = 1;
326 bytespersamp = 2;
327 /* copy the first chunk header to beginnning of buffer. */
328 memcpy(buf, buf + headersize, sizeof(t_datachunk));
329 /* read chunks in loop until we get to the data chunk */
330 while (strncmp(((t_datachunk *)buf)->dc_id, "SSND", 4))
331 {
332 long chunksize = swap4(((t_datachunk *)buf)->dc_size,
333 swap), seekto = headersize + chunksize + 8, seekout;
334 /* post("chunk %c %c %c %c seek %d",
335 ((t_datachunk *)buf)->dc_id[0],
336 ((t_datachunk *)buf)->dc_id[1],
337 ((t_datachunk *)buf)->dc_id[2],
338 ((t_datachunk *)buf)->dc_id[3], seekto); */
339 if (!strncmp(((t_datachunk *)buf)->dc_id, "COMM", 4))
340 {
341 long commblockonset = headersize + 8;
342 seekout = lseek(fd, commblockonset, SEEK_SET);
343 if (seekout != commblockonset)
344 goto badheader;
345 if (read(fd, buf, sizeof(t_comm)) <
346 (int) sizeof(t_comm))
347 goto badheader;
348 nchannels = swap2(((t_comm *)buf)->c_nchannels, swap);
349 format = swap2(((t_comm *)buf)->c_bitspersamp, swap);
350 if (format == 16)
351 bytespersamp = 2;
352 else if (format == 24)
353 bytespersamp = 3;
354 else goto badheader;
355 }
356 seekout = lseek(fd, seekto, SEEK_SET);
357 if (seekout != seekto)
358 goto badheader;
359 if (read(fd, buf, sizeof(t_datachunk)) <
360 (int) sizeof(t_datachunk))
361 goto badheader;
362 headersize = seekto;
363 }
364 bytelimit = swap4(((t_datachunk *)buf)->dc_size, swap);
365 headersize += 8;
366 }
367 }
368 /* seek past header and any sample frames to skip */
369 sysrtn = lseek(fd, nchannels * bytespersamp * skipframes + headersize, 0);
370 if (sysrtn != nchannels * bytespersamp * skipframes + headersize)
371 return (-1);
372 bytelimit -= nchannels * bytespersamp * skipframes;
373 if (bytelimit < 0)
374 bytelimit = 0;
375 /* copy sample format back to caller */
376 *p_bigendian = bigendian;
377 *p_nchannels = nchannels;
378 *p_bytespersamp = bytespersamp;
379 *p_bytelimit = bytelimit;
380 return (fd);
381badheader:
382 /* the header wasn't recognized. We're threadable here so let's not
383 print out the error... */
384 errno = EIO;
385 return (-1);
386}
387
388static void soundfile_xferin(int sfchannels, int nvecs, t_sample **vecs,
389 long itemsread, unsigned char *buf, int nitems, int bytespersamp,
390 int bigendian)
391{
392 int i, j;
393 unsigned char *sp, *sp2;
394 t_sample *fp;
395 int nchannels = (sfchannels < nvecs ? sfchannels : nvecs);
396 int bytesperframe = bytespersamp * sfchannels;
397 for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp)
398 {
399 if (bytespersamp == 2)
400 {
401 if (bigendian)
402 {
403 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
404 j < nitems; j++, sp2 += bytesperframe, fp++)
405 *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16));
406 }
407 else
408 {
409 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
410 j < nitems; j++, sp2 += bytesperframe, fp++)
411 *fp = ((short*)sp2)[0]<<(fix1-16);
412 }
413 }
414 else if (bytespersamp == 3)
415 {
416 if (bigendian)
417 {
418 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
419 j < nitems; j++, sp2 += bytesperframe, fp++)
420 *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16)
421 | (sp2[2] << 8));
422 }
423 else
424 {
425 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
426 j < nitems; j++, sp2 += bytesperframe, fp++)
427 *fp = SCALE * ((sp2[2] << 24) | (sp2[1] << 16)
428 | (sp2[0] << 8));
429 }
430 }
431 else if (bytespersamp == 4)
432 {
433 if (bigendian)
434 {
435 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
436 j < nitems; j++, sp2 += bytesperframe, fp++)
437 *(long *)fp = ((sp2[0] << 24) | (sp2[1] << 16)
438 | (sp2[2] << 8) | sp2[3]);
439 }
440 else
441 {
442 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
443 j < nitems; j++, sp2 += bytesperframe, fp++)
444 *(long *)fp = ((sp2[3] << 24) | (sp2[2] << 16)
445 | (sp2[1] << 8) | sp2[0]);
446 }
447 }
448 }
449 /* zero out other outputs */
450 for (i = sfchannels; i < nvecs; i++)
451 for (j = nitems, fp = vecs[i]; j--; )
452 *fp++ = 0;
453
454}
455
456 /* soundfiler_write ...
457
458 usage: write [flags] filename table ...
459 flags:
460 -nframes <frames>
461 -skip <frames>
462 -bytes <bytes per sample>
463 -normalize
464 -nextstep
465 -wave
466 -big
467 -little
468 */
469
470 /* the routine which actually does the work should LATER also be called
471 from garray_write16. */
472
473
474 /* Parse arguments for writing. The "obj" argument is only for flagging
475 errors. For streaming to a file the "normalize", "onset" and "nframes"
476 arguments shouldn't be set but the calling routine flags this. */
477
478static int soundfiler_writeargparse(void *obj, int *p_argc, t_atom **p_argv,
479 t_symbol **p_filesym,
480 int *p_filetype, int *p_bytespersamp, int *p_swap, int *p_bigendian,
481 int *p_normalize, long *p_onset, long *p_nframes, float *p_rate)
482{
483 int argc = *p_argc;
484 t_atom *argv = *p_argv;
485 int bytespersamp = 2, bigendian = 0,
486 endianness = -1, swap, filetype = -1, normalize = 0;
487 long onset = 0, nframes = 0x7fffffff;
488 t_symbol *filesym;
489 float rate = -1;
490
491 while (argc > 0 && argv->a_type == A_SYMBOL &&
492 *argv->a_w.w_symbol->s_name == '-')
493 {
494 char *flag = argv->a_w.w_symbol->s_name + 1;
495 if (!strcmp(flag, "skip"))
496 {
497 if (argc < 2 || argv[1].a_type != A_FLOAT ||
498 ((onset = argv[1].a_w.w_float) < 0))
499 goto usage;
500 argc -= 2; argv += 2;
501 }
502 else if (!strcmp(flag, "nframes"))
503 {
504 if (argc < 2 || argv[1].a_type != A_FLOAT ||
505 ((nframes = argv[1].a_w.w_float) < 0))
506 goto usage;
507 argc -= 2; argv += 2;
508 }
509 else if (!strcmp(flag, "bytes"))
510 {
511 if (argc < 2 || argv[1].a_type != A_FLOAT ||
512 ((bytespersamp = argv[1].a_w.w_float) < 2) ||
513 bytespersamp > 4)
514 goto usage;
515 argc -= 2; argv += 2;
516 }
517 else if (!strcmp(flag, "normalize"))
518 {
519 normalize = 1;
520 argc -= 1; argv += 1;
521 }
522 else if (!strcmp(flag, "wave"))
523 {
524 filetype = FORMAT_WAVE;
525 argc -= 1; argv += 1;
526 }
527 else if (!strcmp(flag, "nextstep"))
528 {
529 filetype = FORMAT_NEXT;
530 argc -= 1; argv += 1;
531 }
532 else if (!strcmp(flag, "aiff"))
533 {
534 filetype = FORMAT_AIFF;
535 argc -= 1; argv += 1;
536 }
537 else if (!strcmp(flag, "big"))
538 {
539 endianness = 1;
540 argc -= 1; argv += 1;
541 }
542 else if (!strcmp(flag, "little"))
543 {
544 endianness = 0;
545 argc -= 1; argv += 1;
546 }
547 else if (!strcmp(flag, "r") || !strcmp(flag, "rate"))
548 {
549 if (argc < 2 || argv[1].a_type != A_FLOAT ||
550 ((rate = argv[1].a_w.w_float) <= 0))
551 goto usage;
552 argc -= 2; argv += 2;
553 }
554 else goto usage;
555 }
556 if (!argc || argv->a_type != A_SYMBOL)
557 goto usage;
558 filesym = argv->a_w.w_symbol;
559
560 /* check if format not specified and fill in */
561 if (filetype < 0)
562 {
563 if (strlen(filesym->s_name) >= 5 &&
564 (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".aif") ||
565 !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".AIF")))
566 filetype = FORMAT_AIFF;
567 if (strlen(filesym->s_name) >= 6 &&
568 (!strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".aiff") ||
569 !strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".AIFF")))
570 filetype = FORMAT_AIFF;
571 if (strlen(filesym->s_name) >= 5 &&
572 (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".snd") ||
573 !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".SND")))
574 filetype = FORMAT_NEXT;
575 if (strlen(filesym->s_name) >= 4 &&
576 (!strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".au") ||
577 !strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".AU")))
578 filetype = FORMAT_NEXT;
579 if (filetype < 0)
580 filetype = FORMAT_WAVE;
581 }
582 /* don't handle AIFF floating point samples */
583 if (bytespersamp == 4)
584 {
585 if (filetype == FORMAT_AIFF)
586 {
587 pd_error(obj, "AIFF floating-point file format unavailable");
588 goto usage;
589 }
590 }
591 /* for WAVE force little endian; for nextstep use machine native */
592 if (filetype == FORMAT_WAVE)
593 {
594 bigendian = 0;
595 if (endianness == 1)
596 pd_error(obj, "WAVE file forced to little endian");
597 }
598 else if (filetype == FORMAT_AIFF)
599 {
600 bigendian = 1;
601 if (endianness == 0)
602 pd_error(obj, "AIFF file forced to big endian");
603 }
604 else if (endianness == -1)
605 {
606 bigendian = garray_ambigendian();
607 }
608 else bigendian = endianness;
609 swap = (bigendian != garray_ambigendian());
610
611 argc--; argv++;
612
613 *p_argc = argc;
614 *p_argv = argv;
615 *p_filesym = filesym;
616 *p_filetype = filetype;
617 *p_bytespersamp = bytespersamp;
618 *p_swap = swap;
619 *p_normalize = normalize;
620 *p_onset = onset;
621 *p_nframes = nframes;
622 *p_bigendian = bigendian;
623 *p_rate = rate;
624 return (0);
625usage:
626 return (-1);
627}
628
629static int create_soundfile(t_canvas *canvas, const char *filename,
630 int filetype, int nframes, int bytespersamp,
631 int bigendian, int nchannels, int swap, float samplerate)
632{
633 char filenamebuf[MAXPDSTRING], buf2[MAXPDSTRING];
634 char headerbuf[WRITEHDRSIZE];
635 t_wave *wavehdr = (t_wave *)headerbuf;
636 t_nextstep *nexthdr = (t_nextstep *)headerbuf;
637 t_aiff *aiffhdr = (t_aiff *)headerbuf;
638 int fd, headersize = 0;
639
640 strncpy(filenamebuf, filename, MAXPDSTRING-10);
641 filenamebuf[MAXPDSTRING-10] = 0;
642
643 if (filetype == FORMAT_NEXT)
644 {
645 if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".snd"))
646 strcat(filenamebuf, ".snd");
647 if (bigendian)
648 strncpy(nexthdr->ns_fileid, ".snd", 4);
649 else strncpy(nexthdr->ns_fileid, "dns.", 4);
650 nexthdr->ns_onset = swap4(sizeof(*nexthdr), swap);
651 nexthdr->ns_length = 0;
652 nexthdr->ns_format = swap4((bytespersamp == 3 ? NS_FORMAT_LINEAR_24 :
653 (bytespersamp == 4 ? NS_FORMAT_FLOAT : NS_FORMAT_LINEAR_16)), swap);
654 nexthdr->ns_sr = swap4(samplerate, swap);
655 nexthdr->ns_nchans = swap4(nchannels, swap);
656 strcpy(nexthdr->ns_info, "Pd ");
657 swapstring(nexthdr->ns_info, swap);
658 headersize = sizeof(t_nextstep);
659 }
660 else if (filetype == FORMAT_AIFF)
661 {
662 long datasize = nframes * nchannels * bytespersamp;
663 long longtmp;
664 static unsigned char dogdoo[] =
665 {0x40, 0x0e, 0xac, 0x44, 0, 0, 0, 0, 0, 0, 'S', 'S', 'N', 'D'};
666 if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".aif") &&
667 strcmp(filenamebuf + strlen(filenamebuf)-5, ".aiff"))
668 strcat(filenamebuf, ".aif");
669 strncpy(aiffhdr->a_fileid, "FORM", 4);
670 aiffhdr->a_chunksize = swap4(datasize + sizeof(*aiffhdr) + 4, swap);
671 strncpy(aiffhdr->a_aiffid, "AIFF", 4);
672 strncpy(aiffhdr->a_fmtid, "COMM", 4);
673 aiffhdr->a_fmtchunksize = swap4(18, swap);
674 aiffhdr->a_nchannels = swap2(nchannels, swap);
675 longtmp = swap4(nframes, swap);
676 memcpy(&aiffhdr->a_nframeshi, &longtmp, 4);
677 aiffhdr->a_bitspersamp = swap2(8 * bytespersamp, swap);
678 memcpy(aiffhdr->a_samprate, dogdoo, sizeof(dogdoo));
679 longtmp = swap4(datasize, swap);
680 memcpy(aiffhdr->a_samprate + sizeof(dogdoo), &longtmp, 4);
681 headersize = AIFFPLUS;
682 }
683 else /* WAVE format */
684 {
685 long datasize = nframes * nchannels * bytespersamp;
686 if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".wav"))
687 strcat(filenamebuf, ".wav");
688 strncpy(wavehdr->w_fileid, "RIFF", 4);
689 wavehdr->w_chunksize = swap4(datasize + sizeof(*wavehdr) - 8, swap);
690 strncpy(wavehdr->w_waveid, "WAVE", 4);
691 strncpy(wavehdr->w_fmtid, "fmt ", 4);
692 wavehdr->w_fmtchunksize = swap4(16, swap);
693 wavehdr->w_fmttag =
694 swap2((bytespersamp == 4 ? WAV_FLOAT : WAV_INT), swap);
695 wavehdr->w_nchannels = swap2(nchannels, swap);
696 wavehdr->w_samplespersec = swap4(samplerate, swap);
697 wavehdr->w_navgbytespersec =
698 swap4((int)(samplerate * nchannels * bytespersamp), swap);
699 wavehdr->w_nblockalign = swap2(nchannels * bytespersamp, swap);
700 wavehdr->w_nbitspersample = swap2(8 * bytespersamp, swap);
701 strncpy(wavehdr->w_datachunkid, "data", 4);
702 wavehdr->w_datachunksize = swap4(datasize, swap);
703 headersize = sizeof(t_wave);
704 }
705
706 canvas_makefilename(canvas, filenamebuf, buf2, MAXPDSTRING);
707 sys_bashfilename(buf2, buf2);
708 if ((fd = open(buf2, BINCREATE, 0666)) < 0)
709 return (-1);
710
711 if (write(fd, headerbuf, headersize) < headersize)
712 {
713 close (fd);
714 return (-1);
715 }
716 return (fd);
717}
718
719static void soundfile_finishwrite(void *obj, char *filename, int fd,
720 int filetype, long nframes, long itemswritten, int bytesperframe, int swap)
721{
722 if (itemswritten < nframes)
723 {
724 if (nframes < 0x7fffffff)
725 pd_error(obj, "soundfiler_write: %d out of %d bytes written",
726 itemswritten, nframes);
727 /* try to fix size fields in header */
728 if (filetype == FORMAT_WAVE)
729 {
730 long datasize = itemswritten * bytesperframe, mofo;
731
732 if (lseek(fd,
733 ((char *)(&((t_wave *)0)->w_chunksize)) - (char *)0,
734 SEEK_SET) == 0)
735 goto baddonewrite;
736 mofo = swap4(datasize + sizeof(t_wave) - 8, swap);
737 if (write(fd, (char *)(&mofo), 4) < 4)
738 goto baddonewrite;
739 if (lseek(fd,
740 ((char *)(&((t_wave *)0)->w_datachunksize)) - (char *)0,
741 SEEK_SET) == 0)
742 goto baddonewrite;
743 mofo = swap4(datasize, swap);
744 if (write(fd, (char *)(&mofo), 4) < 4)
745 goto baddonewrite;
746 }
747 if (filetype == FORMAT_AIFF)
748 {
749 long mofo;
750 if (lseek(fd,
751 ((char *)(&((t_aiff *)0)->a_nframeshi)) - (char *)0,
752 SEEK_SET) == 0)
753 goto baddonewrite;
754 mofo = swap4(nframes, swap);
755 if (write(fd, (char *)(&mofo), 4) < 4)
756 goto baddonewrite;
757 }
758 if (filetype == FORMAT_NEXT)
759 {
760 /* do it the lazy way: just set the size field to 'unknown size'*/
761 uint32 nextsize = 0xffffffff;
762 if (lseek(fd, 8, SEEK_SET) == 0)
763 {
764 goto baddonewrite;
765 }
766 if (write(fd, &nextsize, 4) < 4)
767 {
768 goto baddonewrite;
769 }
770 }
771 }
772 return;
773baddonewrite:
774 post("%s: %s", filename, strerror(errno));
775}
776
777static void soundfile_xferout(int nchannels, t_sample **vecs,
778 unsigned char *buf, int nitems, long onset, int bytespersamp,
779 int bigendian, float normalfactor)
780{
781 int i, j;
782 unsigned char *sp, *sp2;
783 t_sample *fp;
784 int bytesperframe = bytespersamp * nchannels;
785 long xx;
786 for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp)
787 {
788 if (bytespersamp == 2)
789 {
790 float ff = normalfactor * 32768.;
791 if (bigendian)
792 {
793 for (j = 0, sp2 = sp, fp = vecs[i] + onset;
794 j < nitems; j++, sp2 += bytesperframe, fp++)
795 {
796 int xx = 32768. + (*fp * ff);
797 xx -= 32768;
798 if (xx < -32767)
799 xx = -32767;
800 if (xx > 32767)
801 xx = 32767;
802 sp2[0] = (xx >> 8);
803 sp2[1] = xx;
804 }
805 }
806 else
807 {
808 for (j = 0, sp2 = sp, fp=vecs[i] + onset;
809 j < nitems; j++, sp2 += bytesperframe, fp++)
810 {
811 int xx = 32768. + (*fp * ff);
812 xx -= 32768;
813 if (xx < -32767)
814 xx = -32767;
815 if (xx > 32767)
816 xx = 32767;
817 sp2[1] = (xx >> 8);
818 sp2[0] = xx;
819 }
820 }
821 }
822 else if (bytespersamp == 3)
823 {
824 float ff = normalfactor * 8388608.;
825 if (bigendian)
826 {
827 for (j = 0, sp2 = sp, fp=vecs[i] + onset;
828 j < nitems; j++, sp2 += bytesperframe, fp++)
829 {
830 int xx = 8388608. + (*fp * ff);
831 xx -= 8388608;
832 if (xx < -8388607)
833 xx = -8388607;
834 if (xx > 8388607)
835 xx = 8388607;
836 sp2[0] = (xx >> 16);
837 sp2[1] = (xx >> 8);
838 sp2[2] = xx;
839 }
840 }
841 else
842 {
843 for (j = 0, sp2 = sp, fp=vecs[i] + onset;
844 j < nitems; j++, sp2 += bytesperframe, fp++)
845 {
846 int xx = 8388608. + (*fp * ff);
847 xx -= 8388608;
848 if (xx < -8388607)
849 xx = -8388607;
850 if (xx > 8388607)
851 xx = 8388607;
852 sp2[2] = (xx >> 16);
853 sp2[1] = (xx >> 8);
854 sp2[0] = xx;
855 }
856 }
857 }
858 else if (bytespersamp == 4)
859 {
860 if (bigendian)
861 {
862 for (j = 0, sp2 = sp, fp=vecs[i] + onset;
863 j < nitems; j++, sp2 += bytesperframe, fp++)
864 {
865 float f2 = *fp * normalfactor;
866 xx = *(long *)&f2;
867 sp2[0] = (xx >> 24); sp2[1] = (xx >> 16);
868 sp2[2] = (xx >> 8); sp2[3] = xx;
869 }
870 }
871 else
872 {
873 for (j = 0, sp2 = sp, fp=vecs[i] + onset;
874 j < nitems; j++, sp2 += bytesperframe, fp++)
875 {
876 float f2 = *fp * normalfactor;
877 xx = *(long *)&f2;
878 sp2[3] = (xx >> 24); sp2[2] = (xx >> 16);
879 sp2[1] = (xx >> 8); sp2[0] = xx;
880 }
881 }
882 }
883 }
884}
885
886
887/* ------- soundfiler - reads and writes soundfiles to/from "garrays" ---- */
888#define DEFMAXSIZE 4000000 /* default maximum 16 MB per channel */
889#define SAMPBUFSIZE 1024
890
891
892static t_class *soundfiler_class;
893
894typedef struct _soundfiler
895{
896 t_object x_obj;
897 t_canvas *x_canvas;
898} t_soundfiler;
899
900static t_soundfiler *soundfiler_new(void)
901{
902 t_soundfiler *x = (t_soundfiler *)pd_new(soundfiler_class);
903 x->x_canvas = canvas_getcurrent();
904 outlet_new(&x->x_obj, &s_float);
905 return (x);
906}
907
908 /* soundfiler_read ...
909
910 usage: read [flags] filename table ...
911 flags:
912 -skip <frames> ... frames to skip in file
913 -nframes <frames>
914 -onset <frames> ... onset in table to read into (NOT DONE YET)
915 -raw <headersize channels bytes endian>
916 -resize
917 -maxsize <max-size>
918 */
919
920static void soundfiler_read(t_soundfiler *x, t_symbol *s,
921 int argc, t_atom *argv)
922{
923 int headersize = -1, channels = 0, bytespersamp = 0, bigendian = 0,
924 resize = 0, i, j;
925 long skipframes = 0, nframes = 0, finalsize = 0, itemsleft,
926 maxsize = DEFMAXSIZE, itemsread = 0, bytelimit = 0x7fffffff;
927 int fd = -1;
928 char endianness, *filename;
929 t_garray *garrays[MAXSFCHANS];
930 t_sample *vecs[MAXSFCHANS];
931 char sampbuf[SAMPBUFSIZE];
932 int bufframes, nitems;
933 FILE *fp;
934 while (argc > 0 && argv->a_type == A_SYMBOL &&
935 *argv->a_w.w_symbol->s_name == '-')
936 {
937 char *flag = argv->a_w.w_symbol->s_name + 1;
938 if (!strcmp(flag, "skip"))
939 {
940 if (argc < 2 || argv[1].a_type != A_FLOAT ||
941 ((skipframes = argv[1].a_w.w_float) < 0))
942 goto usage;
943 argc -= 2; argv += 2;
944 }
945 else if (!strcmp(flag, "nframes"))
946 {
947 if (argc < 2 || argv[1].a_type != A_FLOAT ||
948 ((nframes = argv[1].a_w.w_float) < 0))
949 goto usage;
950 argc -= 2; argv += 2;
951 }
952 else if (!strcmp(flag, "raw"))
953 {
954 if (argc < 5 ||
955 argv[1].a_type != A_FLOAT ||
956 ((headersize = argv[1].a_w.w_float) < 0) ||
957 argv[2].a_type != A_FLOAT ||
958 ((channels = argv[2].a_w.w_float) < 1) ||
959 (channels > MAXSFCHANS) ||
960 argv[3].a_type != A_FLOAT ||
961 ((bytespersamp = argv[3].a_w.w_float) < 2) ||
962 (bytespersamp > 4) ||
963 argv[4].a_type != A_SYMBOL ||
964 ((endianness = argv[4].a_w.w_symbol->s_name[0]) != 'b'
965 && endianness != 'l' && endianness != 'n'))
966 goto usage;
967 if (endianness == 'b')
968 bigendian = 1;
969 else if (endianness == 'l')
970 bigendian = 0;
971 else
972 bigendian = garray_ambigendian();
973 argc -= 5; argv += 5;
974 }
975 else if (!strcmp(flag, "resize"))
976 {
977 resize = 1;
978 argc -= 1; argv += 1;
979 }
980 else if (!strcmp(flag, "maxsize"))
981 {
982 if (argc < 2 || argv[1].a_type != A_FLOAT ||
983 ((maxsize = argv[1].a_w.w_float) < 0))
984 goto usage;
985 resize = 1; /* maxsize implies resize. */
986 argc -= 2; argv += 2;
987 }
988 else goto usage;
989 }
990 if (argc < 2 || argc > MAXSFCHANS + 1 || argv[0].a_type != A_SYMBOL)
991 goto usage;
992 filename = argv[0].a_w.w_symbol->s_name;
993 argc--; argv++;
994
995 for (i = 0; i < argc; i++)
996 {
997 int vecsize;
998 if (argv[i].a_type != A_SYMBOL)
999 goto usage;
1000 if (!(garrays[i] =
1001 (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class)))
1002 {
1003 pd_error(x, "%s: no such table", argv[i].a_w.w_symbol->s_name);
1004 goto done;
1005 }
1006 else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i]))
1007 error("%s: bad template for tabwrite",
1008 argv[i].a_w.w_symbol->s_name);
1009 if (finalsize && finalsize != vecsize && !resize)
1010 {
1011 post("soundfiler_read: arrays have different lengths; resizing...");
1012 resize = 1;
1013 }
1014 finalsize = vecsize;
1015 }
1016 fd = open_soundfile(canvas_getdir(x->x_canvas)->s_name, filename,
1017 headersize, &bytespersamp, &bigendian, &channels, &bytelimit,
1018 skipframes);
1019
1020 if (fd < 0)
1021 {
1022 pd_error(x, "soundfiler_read: %s: %s", filename, (errno == EIO ?
1023 "unknown or bad header format" : strerror(errno)));
1024 goto done;
1025 }
1026
1027 if (resize)
1028 {
1029 /* figure out what to resize to */
1030 long poswas, eofis, framesinfile;
1031
1032 poswas = lseek(fd, 0, SEEK_CUR);
1033 eofis = lseek(fd, 0, SEEK_END);
1034 if (poswas < 0 || eofis < 0)
1035 {
1036 pd_error(x, "lseek failed");
1037 goto done;
1038 }
1039 lseek(fd, poswas, SEEK_SET);
1040 framesinfile = (eofis - poswas) / (channels * bytespersamp);
1041 if (framesinfile > maxsize)
1042 {
1043 pd_error(x, "soundfiler_read: truncated to %d elements", maxsize);
1044 framesinfile = maxsize;
1045 }
1046 if (framesinfile > bytelimit / (channels * bytespersamp))
1047 framesinfile = bytelimit / (channels * bytespersamp);
1048 finalsize = framesinfile;
1049 for (i = 0; i < argc; i++)
1050 {
1051 int vecsize;
1052
1053 garray_resize(garrays[i], finalsize);
1054 /* for sanity's sake let's clear the save-in-patch flag here */
1055 garray_setsaveit(garrays[i], 0);
1056 garray_getfloatarray(garrays[i], &vecsize, &vecs[i]);
1057 /* if the resize failed, garray_resize reported the error */
1058 if (vecsize != framesinfile)
1059 {
1060 pd_error(x, "resize failed");
1061 goto done;
1062 }
1063 }
1064 }
1065 if (!finalsize) finalsize = 0x7fffffff;
1066 if (finalsize > bytelimit / (channels * bytespersamp))
1067 finalsize = bytelimit / (channels * bytespersamp);
1068 fp = fdopen(fd, "rb");
1069 bufframes = SAMPBUFSIZE / (channels * bytespersamp);
1070
1071 for (itemsread = 0; itemsread < finalsize; )
1072 {
1073 int thisread = finalsize - itemsread;
1074 thisread = (thisread > bufframes ? bufframes : thisread);
1075 nitems = fread(sampbuf, channels * bytespersamp, thisread, fp);
1076 if (nitems <= 0) break;
1077 soundfile_xferin(channels, argc, vecs, itemsread,
1078 (unsigned char *)sampbuf, nitems, bytespersamp, bigendian);
1079 itemsread += nitems;
1080 }
1081 /* zero out remaining elements of vectors */
1082
1083 for (i = 0; i < argc; i++)
1084 {
1085 int nzero, vecsize;
1086 garray_getfloatarray(garrays[i], &vecsize, &vecs[i]);
1087 for (j = itemsread; j < vecsize; j++)
1088 vecs[i][j] = 0;
1089 }
1090 /* zero out vectors in excess of number of channels */
1091 for (i = channels; i < argc; i++)
1092 {
1093 int vecsize;
1094 t_sample *foo;
1095 garray_getfloatarray(garrays[i], &vecsize, &foo);
1096 for (j = 0; j < vecsize; j++)
1097 foo[j] = 0;
1098 }
1099 /* do all graphics updates */
1100 for (i = 0; i < argc; i++)
1101 garray_redraw(garrays[i]);
1102 fclose(fp);
1103 fd = -1;
1104 goto done;
1105usage:
1106 pd_error(x, "usage: read [flags] filename tablename...");
1107 post("flags: -skip <n> -nframes <n> -resize -maxsize <n> ...");
1108 post("-raw <headerbytes> <channels> <bytespersamp> <endian (b, l, or n)>.");
1109done:
1110 if (fd >= 0)
1111 close (fd);
1112 outlet_float(x->x_obj.ob_outlet, (float)itemsread);
1113}
1114
1115 /* this is broken out from soundfiler_write below so garray_write can
1116 call it too... not done yet though. */
1117
1118long soundfiler_dowrite(void *obj, t_canvas *canvas,
1119 int argc, t_atom *argv)
1120{
1121 int headersize, bytespersamp, bigendian,
1122 endianness, swap, filetype, normalize, i, j, nchannels;
1123 long onset, nframes, itemsleft,
1124 maxsize = DEFMAXSIZE, itemswritten = 0;
1125 t_garray *garrays[MAXSFCHANS];
1126 t_sample *vecs[MAXSFCHANS];
1127 char sampbuf[SAMPBUFSIZE];
1128 int bufframes, nitems;
1129 int fd = -1;
1130 float normfactor, biggest = 0, samplerate;
1131 t_symbol *filesym;
1132
1133 if (soundfiler_writeargparse(obj, &argc, &argv, &filesym, &filetype,
1134 &bytespersamp, &swap, &bigendian, &normalize, &onset, &nframes,
1135 &samplerate))
1136 goto usage;
1137 nchannels = argc;
1138 if (nchannels < 1 || nchannels > MAXSFCHANS)
1139 goto usage;
1140 if (samplerate < 0)
1141 samplerate = sys_getsr();
1142 for (i = 0; i < nchannels; i++)
1143 {
1144 int vecsize;
1145 if (argv[i].a_type != A_SYMBOL)
1146 goto usage;
1147 if (!(garrays[i] =
1148 (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class)))
1149 {
1150 pd_error(obj, "%s: no such table", argv[i].a_w.w_symbol->s_name);
1151 goto fail;
1152 }
1153 else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i]))
1154 error("%s: bad template for tabwrite",
1155 argv[i].a_w.w_symbol->s_name);
1156 if (nframes > vecsize - onset)
1157 nframes = vecsize - onset;
1158
1159 for (j = 0; j < vecsize; j++)
1160 {
1161 if (vecs[i][j] > biggest)
1162 biggest = vecs[i][j];
1163 else if (-vecs[i][j] > biggest)
1164 biggest = -vecs[i][j];
1165 }
1166 }
1167 if (nframes <= 0)
1168 {
1169 pd_error(obj, "soundfiler_write: no samples at onset %ld", onset);
1170 goto fail;
1171 }
1172
1173 if ((fd = create_soundfile(canvas, filesym->s_name, filetype,
1174 nframes, bytespersamp, bigendian, nchannels,
1175 swap, samplerate)) < 0)
1176 {
1177 post("%s: %s\n", filesym->s_name, strerror(errno));
1178 goto fail;
1179 }
1180 if (!normalize)
1181 {
1182 if ((bytespersamp != 4) && (biggest > 1))
1183 {
1184 post("%s: normalizing max amplitude %f to 1", filesym->s_name, biggest);
1185 normalize = 1;
1186 }
1187 else post("%s: biggest amplitude = %f", filesym->s_name, biggest);
1188 }
1189 if (normalize)
1190 normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1);
1191 else normfactor = 1;
1192
1193 bufframes = SAMPBUFSIZE / (nchannels * bytespersamp);
1194
1195 for (itemswritten = 0; itemswritten < nframes; )
1196 {
1197 int thiswrite = nframes - itemswritten, nitems, nbytes;
1198 thiswrite = (thiswrite > bufframes ? bufframes : thiswrite);
1199 soundfile_xferout(argc, vecs, (unsigned char *)sampbuf, thiswrite,
1200 onset, bytespersamp, bigendian, normfactor);
1201 nbytes = write(fd, sampbuf, nchannels * bytespersamp * thiswrite);
1202 if (nbytes < nchannels * bytespersamp * thiswrite)
1203 {
1204 post("%s: %s", filesym->s_name, strerror(errno));
1205 if (nbytes > 0)
1206 itemswritten += nbytes / (nchannels * bytespersamp);
1207 break;
1208 }
1209 itemswritten += thiswrite;
1210 onset += thiswrite;
1211 }
1212 if (fd >= 0)
1213 {
1214 soundfile_finishwrite(obj, filesym->s_name, fd,
1215 filetype, nframes, itemswritten, nchannels * bytespersamp, swap);
1216 close (fd);
1217 }
1218 return ((float)itemswritten);
1219usage:
1220 pd_error(obj, "usage: write [flags] filename tablename...");
1221 post("flags: -skip <n> -nframes <n> -bytes <n> -wave -aiff -nextstep ...");
1222 post("-big -little -normalize");
1223 post("(defaults to a 16-bit wave file).");
1224fail:
1225 if (fd >= 0)
1226 close (fd);
1227 return (0);
1228}
1229
1230static void soundfiler_write(t_soundfiler *x, t_symbol *s,
1231 int argc, t_atom *argv)
1232{
1233 long bozo = soundfiler_dowrite(x, x->x_canvas,
1234 argc, argv);
1235 outlet_float(x->x_obj.ob_outlet, (float)bozo);
1236}
1237
1238static void soundfiler_setup(void)
1239{
1240 soundfiler_class = class_new(gensym("soundfiler"), (t_newmethod)soundfiler_new,
1241 0, sizeof(t_soundfiler), 0, 0);
1242 class_addmethod(soundfiler_class, (t_method)soundfiler_read, gensym("read"),
1243 A_GIMME, 0);
1244 class_addmethod(soundfiler_class, (t_method)soundfiler_write,
1245 gensym("write"), A_GIMME, 0);
1246}
1247
1248
1249#ifndef FIXEDPOINT
1250/************************* readsf object ******************************/
1251
1252/* READSF uses the Posix threads package; for the moment we're Linux
1253only although this should be portable to the other platforms.
1254
1255Each instance of readsf~ owns a "child" thread for doing the UNIX (MSW?) file
1256reading. The parent thread signals the child each time:
1257 (1) a file wants opening or closing;
1258 (2) we've eaten another 1/16 of the shared buffer (so that the
1259 child thread should check if it's time to read some more.)
1260The child signals the parent whenever a read has completed. Signalling
1261is done by setting "conditions" and putting data in mutex-controlled common
1262areas.
1263*/
1264
1265#define MAXBYTESPERSAMPLE 4
1266#define MAXVECSIZE 128
1267
1268#define READSIZE 65536
1269#define WRITESIZE 65536
1270#define DEFBUFPERCHAN 262144
1271#define MINBUFSIZE (4 * READSIZE)
1272#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */
1273
1274#define REQUEST_NOTHING 0
1275#define REQUEST_OPEN 1
1276#define REQUEST_CLOSE 2
1277#define REQUEST_QUIT 3
1278#define REQUEST_BUSY 4
1279
1280#define STATE_IDLE 0
1281#define STATE_STARTUP 1
1282#define STATE_STREAM 2
1283
1284static t_class *readsf_class;
1285
1286typedef struct _readsf
1287{
1288 t_object x_obj;
1289 t_canvas *x_canvas;
1290 t_clock *x_clock;
1291 char *x_buf; /* soundfile buffer */
1292 int x_bufsize; /* buffer size in bytes */
1293 int x_noutlets; /* number of audio outlets */
1294 t_sample *(x_outvec[MAXSFCHANS]); /* audio vectors */
1295 int x_vecsize; /* vector size for transfers */
1296 t_outlet *x_bangout; /* bang-on-done outlet */
1297 int x_state; /* opened, running, or idle */
1298 float x_insamplerate; /* sample rate of input signal if known */
1299 /* parameters to communicate with subthread */
1300 int x_requestcode; /* pending request from parent to I/O thread */
1301 char *x_filename; /* file to open (string is permanently allocated) */
1302 int x_fileerror; /* slot for "errno" return */
1303 int x_skipheaderbytes; /* size of header we'll skip */
1304 int x_bytespersample; /* bytes per sample (2 or 3) */
1305 int x_bigendian; /* true if file is big-endian */
1306 int x_sfchannels; /* number of channels in soundfile */
1307 float x_samplerate; /* sample rate of soundfile */
1308 long x_onsetframes; /* number of sample frames to skip */
1309 long x_bytelimit; /* max number of data bytes to read */
1310 int x_fd; /* filedesc */
1311 int x_fifosize; /* buffer size appropriately rounded down */
1312 int x_fifohead; /* index of next byte to get from file */
1313 int x_fifotail; /* index of next byte the ugen will read */
1314 int x_eof; /* true if fifohead has stopped changing */
1315 int x_sigcountdown; /* counter for signalling child for more data */
1316 int x_sigperiod; /* number of ticks per signal */
1317 int x_filetype; /* writesf~ only; type of file to create */
1318 int x_itemswritten; /* writesf~ only; items writen */
1319 int x_swap; /* writesf~ only; true if byte swapping */
1320 float x_f; /* writesf~ only; scalar for signal inlet */
1321 pthread_mutex_t x_mutex;
1322 pthread_cond_t x_requestcondition;
1323 pthread_cond_t x_answercondition;
1324 pthread_t x_childthread;
1325} t_readsf;
1326
1327
1328/************** the child thread which performs file I/O ***********/
1329
1330#if 0
1331static void pute(char *s) /* debug routine */
1332{
1333 write(2, s, strlen(s));
1334}
1335#define DEBUG_SOUNDFILE
1336#endif
1337
1338#if 1
1339#define sfread_cond_wait pthread_cond_wait
1340#define sfread_cond_signal pthread_cond_signal
1341#else
1342#include <sys/time.h> /* debugging version... */
1343#include <sys/types.h>
1344static void readsf_fakewait(pthread_mutex_t *b)
1345{
1346 struct timeval timout;
1347 timout.tv_sec = 0;
1348 timout.tv_usec = 1000000;
1349 pthread_mutex_unlock(b);
1350 select(0, 0, 0, 0, &timout);
1351 pthread_mutex_lock(b);
1352}
1353
1354#define sfread_cond_wait(a,b) readsf_fakewait(b)
1355#define sfread_cond_signal(a)
1356#endif
1357
1358static void *readsf_child_main(void *zz)
1359{
1360 t_readsf *x = zz;
1361#ifdef DEBUG_SOUNDFILE
1362 pute("1\n");
1363#endif
1364 pthread_mutex_lock(&x->x_mutex);
1365 while (1)
1366 {
1367 int fd, fifohead;
1368 char *buf;
1369#ifdef DEBUG_SOUNDFILE
1370 pute("0\n");
1371#endif
1372 if (x->x_requestcode == REQUEST_NOTHING)
1373 {
1374#ifdef DEBUG_SOUNDFILE
1375 pute("wait 2\n");
1376#endif
1377 sfread_cond_signal(&x->x_answercondition);
1378 sfread_cond_wait(&x->x_requestcondition, &x->x_mutex);
1379#ifdef DEBUG_SOUNDFILE
1380 pute("3\n");
1381#endif
1382 }
1383 else if (x->x_requestcode == REQUEST_OPEN)
1384 {
1385 char boo[80];
1386 int sysrtn, wantbytes;
1387
1388 /* copy file stuff out of the data structure so we can
1389 relinquish the mutex while we're in open_soundfile(). */
1390 long onsetframes = x->x_onsetframes;
1391 long bytelimit = 0x7fffffff;
1392 int skipheaderbytes = x->x_skipheaderbytes;
1393 int bytespersample = x->x_bytespersample;
1394 int sfchannels = x->x_sfchannels;
1395 int bigendian = x->x_bigendian;
1396 char *filename = x->x_filename;
1397 char *dirname = canvas_getdir(x->x_canvas)->s_name;
1398 /* alter the request code so that an ensuing "open" will get
1399 noticed. */
1400#ifdef DEBUG_SOUNDFILE
1401 pute("4\n");
1402#endif
1403 x->x_requestcode = REQUEST_BUSY;
1404 x->x_fileerror = 0;
1405
1406 /* if there's already a file open, close it */
1407 if (x->x_fd >= 0)
1408 {
1409 fd = x->x_fd;
1410 pthread_mutex_unlock(&x->x_mutex);
1411 close (fd);
1412 pthread_mutex_lock(&x->x_mutex);
1413 x->x_fd = -1;
1414 if (x->x_requestcode != REQUEST_BUSY)
1415 goto lost;
1416 }
1417 /* open the soundfile with the mutex unlocked */
1418 pthread_mutex_unlock(&x->x_mutex);
1419 fd = open_soundfile(dirname, filename,
1420 skipheaderbytes, &bytespersample, &bigendian,
1421 &sfchannels, &bytelimit, onsetframes);
1422 pthread_mutex_lock(&x->x_mutex);
1423
1424#ifdef DEBUG_SOUNDFILE
1425 pute("5\n");
1426#endif
1427 /* copy back into the instance structure. */
1428 x->x_bytespersample = bytespersample;
1429 x->x_sfchannels = sfchannels;
1430 x->x_bigendian = bigendian;
1431 x->x_fd = fd;
1432 x->x_bytelimit = bytelimit;
1433 if (fd < 0)
1434 {
1435 x->x_fileerror = errno;
1436 x->x_eof = 1;
1437#ifdef DEBUG_SOUNDFILE
1438 pute("open failed\n");
1439 pute(filename);
1440 pute(dirname);
1441#endif
1442 goto lost;
1443 }
1444 /* check if another request has been made; if so, field it */
1445 if (x->x_requestcode != REQUEST_BUSY)
1446 goto lost;
1447#ifdef DEBUG_SOUNDFILE
1448 pute("6\n");
1449#endif
1450 x->x_fifohead = 0;
1451 /* set fifosize from bufsize. fifosize must be a
1452 multiple of the number of bytes eaten for each DSP
1453 tick. We pessimistically assume MAXVECSIZE samples
1454 per tick since that could change. There could be a
1455 problem here if the vector size increases while a
1456 soundfile is being played... */
1457 x->x_fifosize = x->x_bufsize - (x->x_bufsize %
1458 (x->x_bytespersample * x->x_sfchannels * MAXVECSIZE));
1459 /* arrange for the "request" condition to be signalled 16
1460 times per buffer */
1461#ifdef DEBUG_SOUNDFILE
1462 sprintf(boo, "fifosize %d\n",
1463 x->x_fifosize);
1464 pute(boo);
1465#endif
1466 x->x_sigcountdown = x->x_sigperiod =
1467 (x->x_fifosize /
1468 (16 * x->x_bytespersample * x->x_sfchannels *
1469 x->x_vecsize));
1470 /* in a loop, wait for the fifo to get hungry and feed it */
1471
1472 while (x->x_requestcode == REQUEST_BUSY)
1473 {
1474 int fifosize = x->x_fifosize;
1475#ifdef DEBUG_SOUNDFILE
1476 pute("77\n");
1477#endif
1478 if (x->x_eof)
1479 break;
1480 if (x->x_fifohead >= x->x_fifotail)
1481 {
1482 /* if the head is >= the tail, we can immediately read
1483 to the end of the fifo. Unless, that is, we would
1484 read all the way to the end of the buffer and the
1485 "tail" is zero; this would fill the buffer completely
1486 which isn't allowed because you can't tell a completely
1487 full buffer from an empty one. */
1488 if (x->x_fifotail || (fifosize - x->x_fifohead > READSIZE))
1489 {
1490 wantbytes = fifosize - x->x_fifohead;
1491 if (wantbytes > READSIZE)
1492 wantbytes = READSIZE;
1493 if (wantbytes > x->x_bytelimit)
1494 wantbytes = x->x_bytelimit;
1495#ifdef DEBUG_SOUNDFILE
1496 sprintf(boo, "head %d, tail %d, size %d\n",
1497 x->x_fifohead, x->x_fifotail, wantbytes);
1498 pute(boo);
1499#endif
1500 }
1501 else
1502 {
1503#ifdef DEBUG_SOUNDFILE
1504 pute("wait 7a ...\n");
1505#endif
1506 sfread_cond_signal(&x->x_answercondition);
1507#ifdef DEBUG_SOUNDFILE
1508 pute("signalled\n");
1509#endif
1510 sfread_cond_wait(&x->x_requestcondition,
1511 &x->x_mutex);
1512#ifdef DEBUG_SOUNDFILE
1513 pute("7a done\n");
1514#endif
1515 continue;
1516 }
1517 }
1518 else
1519 {
1520 /* otherwise check if there are at least READSIZE
1521 bytes to read. If not, wait and loop back. */
1522 wantbytes = x->x_fifotail - x->x_fifohead - 1;
1523 if (wantbytes < READSIZE)
1524 {
1525#ifdef DEBUG_SOUNDFILE
1526 pute("wait 7...\n");
1527#endif
1528 sfread_cond_signal(&x->x_answercondition);
1529 sfread_cond_wait(&x->x_requestcondition,
1530 &x->x_mutex);
1531#ifdef DEBUG_SOUNDFILE
1532 pute("7 done\n");
1533#endif
1534 continue;
1535 }
1536 else wantbytes = READSIZE;
1537 if (wantbytes > x->x_bytelimit)
1538 wantbytes = x->x_bytelimit;
1539 }
1540#ifdef DEBUG_SOUNDFILE
1541 pute("8\n");
1542#endif
1543 fd = x->x_fd;
1544 buf = x->x_buf;
1545 fifohead = x->x_fifohead;
1546 pthread_mutex_unlock(&x->x_mutex);
1547 sysrtn = read(fd, buf + fifohead, wantbytes);
1548 pthread_mutex_lock(&x->x_mutex);
1549 if (x->x_requestcode != REQUEST_BUSY)
1550 break;
1551 if (sysrtn < 0)
1552 {
1553#ifdef DEBUG_SOUNDFILE
1554 pute("fileerror\n");
1555#endif
1556 x->x_fileerror = errno;
1557 break;
1558 }
1559 else if (sysrtn == 0)
1560 {
1561 x->x_eof = 1;
1562 break;
1563 }
1564 else
1565 {
1566 x->x_fifohead += sysrtn;
1567 x->x_bytelimit -= sysrtn;
1568 if (x->x_bytelimit <= 0)
1569 {
1570 x->x_eof = 1;
1571 break;
1572 }
1573 if (x->x_fifohead == fifosize)
1574 x->x_fifohead = 0;
1575 }
1576#ifdef DEBUG_SOUNDFILE
1577 sprintf(boo, "after: head %d, tail %d\n",
1578 x->x_fifohead, x->x_fifotail);
1579 pute(boo);
1580#endif
1581 /* signal parent in case it's waiting for data */
1582 sfread_cond_signal(&x->x_answercondition);
1583 }
1584 lost:
1585
1586 if (x->x_requestcode == REQUEST_BUSY)
1587 x->x_requestcode = REQUEST_NOTHING;
1588 /* fell out of read loop: close file if necessary,
1589 set EOF and signal once more */
1590 if (x->x_fd >= 0)
1591 {
1592 fd = x->x_fd;
1593 pthread_mutex_unlock(&x->x_mutex);
1594 close (fd);
1595 pthread_mutex_lock(&x->x_mutex);
1596 x->x_fd = -1;
1597 }
1598 sfread_cond_signal(&x->x_answercondition);
1599
1600 }
1601 else if (x->x_requestcode == REQUEST_CLOSE)
1602 {
1603 if (x->x_fd >= 0)
1604 {
1605 fd = x->x_fd;
1606 pthread_mutex_unlock(&x->x_mutex);
1607 close (fd);
1608 pthread_mutex_lock(&x->x_mutex);
1609 x->x_fd = -1;
1610 }
1611 if (x->x_requestcode == REQUEST_CLOSE)
1612 x->x_requestcode = REQUEST_NOTHING;
1613 sfread_cond_signal(&x->x_answercondition);
1614 }
1615 else if (x->x_requestcode == REQUEST_QUIT)
1616 {
1617 if (x->x_fd >= 0)
1618 {
1619 fd = x->x_fd;
1620 pthread_mutex_unlock(&x->x_mutex);
1621 close (fd);
1622 pthread_mutex_lock(&x->x_mutex);
1623 x->x_fd = -1;
1624 }
1625 x->x_requestcode = REQUEST_NOTHING;
1626 sfread_cond_signal(&x->x_answercondition);
1627 break;
1628 }
1629 else
1630 {
1631#ifdef DEBUG_SOUNDFILE
1632 pute("13\n");
1633#endif
1634 }
1635 }
1636#ifdef DEBUG_SOUNDFILE
1637 pute("thread exit\n");
1638#endif
1639 pthread_mutex_unlock(&x->x_mutex);
1640 return (0);
1641}
1642
1643/******** the object proper runs in the calling (parent) thread ****/
1644
1645static void readsf_tick(t_readsf *x);
1646
1647static void *readsf_new(t_floatarg fnchannels, t_floatarg fbufsize)
1648{
1649 t_readsf *x;
1650 int nchannels = fnchannels, bufsize = fbufsize, i;
1651 char *buf;
1652
1653 if (nchannels < 1)
1654 nchannels = 1;
1655 else if (nchannels > MAXSFCHANS)
1656 nchannels = MAXSFCHANS;
1657 if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels;
1658 else if (bufsize < MINBUFSIZE)
1659 bufsize = MINBUFSIZE;
1660 else if (bufsize > MAXBUFSIZE)
1661 bufsize = MAXBUFSIZE;
1662 buf = getbytes(bufsize);
1663 if (!buf) return (0);
1664
1665 x = (t_readsf *)pd_new(readsf_class);
1666
1667 for (i = 0; i < nchannels; i++)
1668 outlet_new(&x->x_obj, gensym("signal"));
1669 x->x_noutlets = nchannels;
1670 x->x_bangout = outlet_new(&x->x_obj, &s_bang);
1671 pthread_mutex_init(&x->x_mutex, 0);
1672 pthread_cond_init(&x->x_requestcondition, 0);
1673 pthread_cond_init(&x->x_answercondition, 0);
1674 x->x_vecsize = MAXVECSIZE;
1675 x->x_state = STATE_IDLE;
1676 x->x_clock = clock_new(x, (t_method)readsf_tick);
1677 x->x_canvas = canvas_getcurrent();
1678 x->x_bytespersample = 2;
1679 x->x_sfchannels = 1;
1680 x->x_fd = -1;
1681 x->x_buf = buf;
1682 x->x_bufsize = bufsize;
1683 x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0;
1684 pthread_create(&x->x_childthread, 0, readsf_child_main, x);
1685 return (x);
1686}
1687
1688static void readsf_tick(t_readsf *x)
1689{
1690 outlet_bang(x->x_bangout);
1691}
1692
1693static t_int *readsf_perform(t_int *w)
1694{
1695 t_readsf *x = (t_readsf *)(w[1]);
1696 int vecsize = x->x_vecsize, noutlets = x->x_noutlets, i, j,
1697 bytespersample = x->x_bytespersample,
1698 bigendian = x->x_bigendian;
1699 float *fp;
1700 if (x->x_state == STATE_STREAM)
1701 {
1702 int wantbytes, nchannels, sfchannels = x->x_sfchannels;
1703 pthread_mutex_lock(&x->x_mutex);
1704 wantbytes = sfchannels * vecsize * bytespersample;
1705 while (
1706 !x->x_eof && x->x_fifohead >= x->x_fifotail &&
1707 x->x_fifohead < x->x_fifotail + wantbytes-1)
1708 {
1709#ifdef DEBUG_SOUNDFILE
1710 pute("wait...\n");
1711#endif
1712 sfread_cond_signal(&x->x_requestcondition);
1713 sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
1714#ifdef DEBUG_SOUNDFILE
1715 pute("done\n");
1716#endif
1717 }
1718 if (x->x_eof && x->x_fifohead >= x->x_fifotail &&
1719 x->x_fifohead < x->x_fifotail + wantbytes-1)
1720 {
1721 int xfersize;
1722 if (x->x_fileerror)
1723 {
1724 pd_error(x, "dsp: %s: %s", x->x_filename,
1725 (x->x_fileerror == EIO ?
1726 "unknown or bad header format" :
1727 strerror(x->x_fileerror)));
1728 }
1729 clock_delay(x->x_clock, 0);
1730 x->x_state = STATE_IDLE;
1731
1732 /* if there's a partial buffer left, copy it out. */
1733 xfersize = (x->x_fifohead - x->x_fifotail + 1) /
1734 (sfchannels * bytespersample);
1735 if (xfersize)
1736 {
1737 soundfile_xferin(sfchannels, noutlets, x->x_outvec, 0,
1738 (unsigned char *)(x->x_buf + x->x_fifotail), xfersize,
1739 bytespersample, bigendian);
1740 vecsize -= xfersize;
1741 }
1742 /* then zero out the (rest of the) output */
1743 for (i = 0; i < noutlets; i++)
1744 for (j = vecsize, fp = x->x_outvec[i] + xfersize; j--; )
1745 *fp++ = 0;
1746
1747 sfread_cond_signal(&x->x_requestcondition);
1748 pthread_mutex_unlock(&x->x_mutex);
1749 return (w+2);
1750 }
1751
1752 soundfile_xferin(sfchannels, noutlets, x->x_outvec, 0,
1753 (unsigned char *)(x->x_buf + x->x_fifotail), vecsize,
1754 bytespersample, bigendian);
1755
1756 x->x_fifotail += wantbytes;
1757 if (x->x_fifotail >= x->x_fifosize)
1758 x->x_fifotail = 0;
1759 if ((--x->x_sigcountdown) <= 0)
1760 {
1761 sfread_cond_signal(&x->x_requestcondition);
1762 x->x_sigcountdown = x->x_sigperiod;
1763 }
1764 pthread_mutex_unlock(&x->x_mutex);
1765 }
1766 else
1767 {
1768 idle:
1769 for (i = 0; i < noutlets; i++)
1770 for (j = vecsize, fp = x->x_outvec[i]; j--; )
1771 *fp++ = 0;
1772 }
1773 return (w+2);
1774}
1775
1776static void readsf_start(t_readsf *x)
1777{
1778 /* start making output. If we're in the "startup" state change
1779 to the "running" state. */
1780 if (x->x_state == STATE_STARTUP)
1781 x->x_state = STATE_STREAM;
1782 else pd_error(x, "readsf: start requested with no prior 'open'");
1783}
1784
1785static void readsf_stop(t_readsf *x)
1786{
1787 /* LATER rethink whether you need the mutex just to set a variable? */
1788 pthread_mutex_lock(&x->x_mutex);
1789 x->x_state = STATE_IDLE;
1790 x->x_requestcode = REQUEST_CLOSE;
1791 sfread_cond_signal(&x->x_requestcondition);
1792 pthread_mutex_unlock(&x->x_mutex);
1793}
1794
1795static void readsf_float(t_readsf *x, t_floatarg f)
1796{
1797 if (f != 0)
1798 readsf_start(x);
1799 else readsf_stop(x);
1800}
1801
1802 /* open method. Called as:
1803 open filename [skipframes headersize channels bytespersamp endianness]
1804 (if headersize is zero, header is taken to be automatically
1805 detected; thus, use the special "-1" to mean a truly headerless file.)
1806 */
1807
1808static void readsf_open(t_readsf *x, t_symbol *s, int argc, t_atom *argv)
1809{
1810 t_symbol *filesym = atom_getsymbolarg(0, argc, argv);
1811 t_float onsetframes = atom_getfloatarg(1, argc, argv);
1812 t_float headerbytes = atom_getfloatarg(2, argc, argv);
1813 t_float channels = atom_getfloatarg(3, argc, argv);
1814 t_float bytespersamp = atom_getfloatarg(4, argc, argv);
1815 t_symbol *endian = atom_getsymbolarg(5, argc, argv);
1816 if (!*filesym->s_name)
1817 return;
1818 pthread_mutex_lock(&x->x_mutex);
1819 x->x_requestcode = REQUEST_OPEN;
1820 x->x_filename = filesym->s_name;
1821 x->x_fifotail = 0;
1822 x->x_fifohead = 0;
1823 if (*endian->s_name == 'b')
1824 x->x_bigendian = 1;
1825 else if (*endian->s_name == 'l')
1826 x->x_bigendian = 0;
1827 else if (*endian->s_name)
1828 pd_error(x, "endianness neither 'b' nor 'l'");
1829 else x->x_bigendian = garray_ambigendian();
1830 x->x_onsetframes = (onsetframes > 0 ? onsetframes : 0);
1831 x->x_skipheaderbytes = (headerbytes > 0 ? headerbytes :
1832 (headerbytes == 0 ? -1 : 0));
1833 x->x_sfchannels = (channels >= 1 ? channels : 1);
1834 x->x_bytespersample = (bytespersamp > 2 ? bytespersamp : 2);
1835 x->x_eof = 0;
1836 x->x_fileerror = 0;
1837 x->x_state = STATE_STARTUP;
1838 sfread_cond_signal(&x->x_requestcondition);
1839 pthread_mutex_unlock(&x->x_mutex);
1840}
1841
1842static void readsf_dsp(t_readsf *x, t_signal **sp)
1843{
1844 int i, noutlets = x->x_noutlets;
1845 pthread_mutex_lock(&x->x_mutex);
1846 x->x_vecsize = sp[0]->s_n;
1847
1848 x->x_sigperiod = (x->x_fifosize /
1849 (x->x_bytespersample * x->x_sfchannels * x->x_vecsize));
1850 for (i = 0; i < noutlets; i++)
1851 x->x_outvec[i] = sp[i]->s_vec;
1852 pthread_mutex_unlock(&x->x_mutex);
1853 dsp_add(readsf_perform, 1, x);
1854}
1855
1856static void readsf_print(t_readsf *x)
1857{
1858 post("state %d", x->x_state);
1859 post("fifo head %d", x->x_fifohead);
1860 post("fifo tail %d", x->x_fifotail);
1861 post("fifo size %d", x->x_fifosize);
1862 post("fd %d", x->x_fd);
1863 post("eof %d", x->x_eof);
1864}
1865
1866static void readsf_free(t_readsf *x)
1867{
1868 /* request QUIT and wait for acknowledge */
1869 void *threadrtn;
1870 pthread_mutex_lock(&x->x_mutex);
1871 x->x_requestcode = REQUEST_QUIT;
1872 sfread_cond_signal(&x->x_requestcondition);
1873 while (x->x_requestcode != REQUEST_NOTHING)
1874 {
1875 sfread_cond_signal(&x->x_requestcondition);
1876 sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
1877 }
1878 pthread_mutex_unlock(&x->x_mutex);
1879 if (pthread_join(x->x_childthread, &threadrtn))
1880 error("readsf_free: join failed");
1881
1882 pthread_cond_destroy(&x->x_requestcondition);
1883 pthread_cond_destroy(&x->x_answercondition);
1884 pthread_mutex_destroy(&x->x_mutex);
1885 freebytes(x->x_buf, x->x_bufsize);
1886 clock_free(x->x_clock);
1887}
1888
1889static void readsf_setup(void)
1890{
1891 readsf_class = class_new(gensym("readsf~"), (t_newmethod)readsf_new,
1892 (t_method)readsf_free, sizeof(t_readsf), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
1893 class_addfloat(readsf_class, (t_method)readsf_float);
1894 class_addmethod(readsf_class, (t_method)readsf_start, gensym("start"), 0);
1895 class_addmethod(readsf_class, (t_method)readsf_stop, gensym("stop"), 0);
1896 class_addmethod(readsf_class, (t_method)readsf_dsp, gensym("dsp"), 0);
1897 class_addmethod(readsf_class, (t_method)readsf_open, gensym("open"),
1898 A_GIMME, 0);
1899 class_addmethod(readsf_class, (t_method)readsf_print, gensym("print"), 0);
1900}
1901
1902/******************************* writesf *******************/
1903
1904static t_class *writesf_class;
1905
1906#define t_writesf t_readsf /* just re-use the structure */
1907
1908/************** the child thread which performs file I/O ***********/
1909
1910static void *writesf_child_main(void *zz)
1911{
1912 t_writesf *x = zz;
1913#ifdef DEBUG_SOUNDFILE
1914 pute("1\n");
1915#endif
1916 pthread_mutex_lock(&x->x_mutex);
1917 while (1)
1918 {
1919#ifdef DEBUG_SOUNDFILE
1920 pute("0\n");
1921#endif
1922 if (x->x_requestcode == REQUEST_NOTHING)
1923 {
1924#ifdef DEBUG_SOUNDFILE
1925 pute("wait 2\n");
1926#endif
1927 sfread_cond_signal(&x->x_answercondition);
1928 sfread_cond_wait(&x->x_requestcondition, &x->x_mutex);
1929#ifdef DEBUG_SOUNDFILE
1930 pute("3\n");
1931#endif
1932 }
1933 else if (x->x_requestcode == REQUEST_OPEN)
1934 {
1935 char boo[80];
1936 int fd, sysrtn, writebytes;
1937
1938 /* copy file stuff out of the data structure so we can
1939 relinquish the mutex while we're in open_soundfile(). */
1940 long onsetframes = x->x_onsetframes;
1941 long bytelimit = 0x7fffffff;
1942 int skipheaderbytes = x->x_skipheaderbytes;
1943 int bytespersample = x->x_bytespersample;
1944 int sfchannels = x->x_sfchannels;
1945 int bigendian = x->x_bigendian;
1946 int filetype = x->x_filetype;
1947 char *filename = x->x_filename;
1948 t_canvas *canvas = x->x_canvas;
1949 float samplerate = x->x_samplerate;
1950
1951 /* alter the request code so that an ensuing "open" will get
1952 noticed. */
1953#ifdef DEBUG_SOUNDFILE
1954 pute("4\n");
1955#endif
1956 x->x_requestcode = REQUEST_BUSY;
1957 x->x_fileerror = 0;
1958
1959 /* if there's already a file open, close it */
1960 if (x->x_fd >= 0)
1961 {
1962 pthread_mutex_unlock(&x->x_mutex);
1963 close (x->x_fd);
1964 pthread_mutex_lock(&x->x_mutex);
1965 x->x_fd = -1;
1966 if (x->x_requestcode != REQUEST_BUSY)
1967 continue;
1968 }
1969 /* open the soundfile with the mutex unlocked */
1970 pthread_mutex_unlock(&x->x_mutex);
1971 fd = create_soundfile(canvas, filename, filetype, 0,
1972 bytespersample, bigendian, sfchannels,
1973 garray_ambigendian() != bigendian, samplerate);
1974 pthread_mutex_lock(&x->x_mutex);
1975#ifdef DEBUG_SOUNDFILE
1976 pute("5\n");
1977#endif
1978
1979 if (fd < 0)
1980 {
1981 x->x_fd = -1;
1982 x->x_eof = 1;
1983 x->x_fileerror = errno;
1984#ifdef DEBUG_SOUNDFILE
1985 pute("open failed\n");
1986 pute(filename);
1987#endif
1988 x->x_requestcode = REQUEST_NOTHING;
1989 continue;
1990 }
1991 /* check if another request has been made; if so, field it */
1992 if (x->x_requestcode != REQUEST_BUSY)
1993 continue;
1994#ifdef DEBUG_SOUNDFILE
1995 pute("6\n");
1996#endif
1997 x->x_fd = fd;
1998 x->x_fifotail = 0;
1999 x->x_itemswritten = 0;
2000 x->x_swap = garray_ambigendian() != bigendian;
2001 /* in a loop, wait for the fifo to have data and write it
2002 to disk */
2003 while (x->x_requestcode == REQUEST_BUSY ||
2004 (x->x_requestcode == REQUEST_CLOSE &&
2005 x->x_fifohead != x->x_fifotail))
2006 {
2007 int fifosize = x->x_fifosize, fifotail;
2008 char *buf = x->x_buf;
2009#ifdef DEBUG_SOUNDFILE
2010 pute("77\n");
2011#endif
2012
2013 /* if the head is < the tail, we can immediately write
2014 from tail to end of fifo to disk; otherwise we hold off
2015 writing until there are at least WRITESIZE bytes in the
2016 buffer */
2017 if (x->x_fifohead < x->x_fifotail ||
2018 x->x_fifohead >= x->x_fifotail + WRITESIZE
2019 || (x->x_requestcode == REQUEST_CLOSE &&
2020 x->x_fifohead != x->x_fifotail))
2021 {
2022 writebytes = (x->x_fifohead < x->x_fifotail ?
2023 fifosize : x->x_fifohead) - x->x_fifotail;
2024 if (writebytes > READSIZE)
2025 writebytes = READSIZE;
2026 }
2027 else
2028 {
2029#ifdef DEBUG_SOUNDFILE
2030 pute("wait 7a ...\n");
2031#endif
2032 sfread_cond_signal(&x->x_answercondition);
2033#ifdef DEBUG_SOUNDFILE
2034 pute("signalled\n");
2035#endif
2036 sfread_cond_wait(&x->x_requestcondition,
2037 &x->x_mutex);
2038#ifdef DEBUG_SOUNDFILE
2039 pute("7a done\n");
2040#endif
2041 continue;
2042 }
2043#ifdef DEBUG_SOUNDFILE
2044 pute("8\n");
2045#endif
2046 fifotail = x->x_fifotail;
2047 fd = x->x_fd;
2048 pthread_mutex_unlock(&x->x_mutex);
2049 sysrtn = write(fd, buf + fifotail, writebytes);
2050 pthread_mutex_lock(&x->x_mutex);
2051 if (x->x_requestcode != REQUEST_BUSY &&
2052 x->x_requestcode != REQUEST_CLOSE)
2053 break;
2054 if (sysrtn < writebytes)
2055 {
2056#ifdef DEBUG_SOUNDFILE
2057 pute("fileerror\n");
2058#endif
2059 x->x_fileerror = errno;
2060 break;
2061 }
2062 else
2063 {
2064 x->x_fifotail += sysrtn;
2065 if (x->x_fifotail == fifosize)
2066 x->x_fifotail = 0;
2067 }
2068 x->x_itemswritten +=
2069 sysrtn / (x->x_bytespersample * x->x_sfchannels);
2070 sprintf(boo, "after: head %d, tail %d\n",
2071 x->x_fifohead, x->x_fifotail);
2072#ifdef DEBUG_SOUNDFILE
2073 pute(boo);
2074#endif
2075 /* signal parent in case it's waiting for data */
2076 sfread_cond_signal(&x->x_answercondition);
2077 }
2078 }
2079 else if (x->x_requestcode == REQUEST_CLOSE ||
2080 x->x_requestcode == REQUEST_QUIT)
2081 {
2082 int quit = (x->x_requestcode == REQUEST_QUIT);
2083 if (x->x_fd >= 0)
2084 {
2085 int bytesperframe = x->x_bytespersample * x->x_sfchannels;
2086 int bigendian = x->x_bigendian;
2087 char *filename = x->x_filename;
2088 int fd = x->x_fd;
2089 int filetype = x->x_filetype;
2090 int itemswritten = x->x_itemswritten;
2091 int swap = x->x_swap;
2092 pthread_mutex_unlock(&x->x_mutex);
2093
2094 soundfile_finishwrite(x, filename, fd,
2095 filetype, 0x7fffffff, itemswritten,
2096 bytesperframe, swap);
2097 close (fd);
2098
2099 pthread_mutex_lock(&x->x_mutex);
2100 x->x_fd = -1;
2101 }
2102 x->x_requestcode = REQUEST_NOTHING;
2103 sfread_cond_signal(&x->x_answercondition);
2104 if (quit)
2105 break;
2106 }
2107 else
2108 {
2109#ifdef DEBUG_SOUNDFILE
2110 pute("13\n");
2111#endif
2112 }
2113 }
2114#ifdef DEBUG_SOUNDFILE
2115 pute("thread exit\n");
2116#endif
2117 pthread_mutex_unlock(&x->x_mutex);
2118 return (0);
2119}
2120
2121/******** the object proper runs in the calling (parent) thread ****/
2122
2123static void writesf_tick(t_writesf *x);
2124
2125static void *writesf_new(t_floatarg fnchannels, t_floatarg fbufsize)
2126{
2127 t_writesf *x;
2128 int nchannels = fnchannels, bufsize = fbufsize, i;
2129 char *buf;
2130
2131 if (nchannels < 1)
2132 nchannels = 1;
2133 else if (nchannels > MAXSFCHANS)
2134 nchannels = MAXSFCHANS;
2135 if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels;
2136 else if (bufsize < MINBUFSIZE)
2137 bufsize = MINBUFSIZE;
2138 else if (bufsize > MAXBUFSIZE)
2139 bufsize = MAXBUFSIZE;
2140 buf = getbytes(bufsize);
2141 if (!buf) return (0);
2142
2143 x = (t_writesf *)pd_new(writesf_class);
2144
2145 for (i = 1; i < nchannels; i++)
2146 inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
2147
2148 x->x_f = 0;
2149 x->x_sfchannels = nchannels;
2150 pthread_mutex_init(&x->x_mutex, 0);
2151 pthread_cond_init(&x->x_requestcondition, 0);
2152 pthread_cond_init(&x->x_answercondition, 0);
2153 x->x_vecsize = MAXVECSIZE;
2154 x->x_insamplerate = x->x_samplerate = 0;
2155 x->x_state = STATE_IDLE;
2156 x->x_clock = 0; /* no callback needed here */
2157 x->x_canvas = canvas_getcurrent();
2158 x->x_bytespersample = 2;
2159 x->x_fd = -1;
2160 x->x_buf = buf;
2161 x->x_bufsize = bufsize;
2162 x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0;
2163 pthread_create(&x->x_childthread, 0, writesf_child_main, x);
2164 return (x);
2165}
2166
2167static t_int *writesf_perform(t_int *w)
2168{
2169 t_writesf *x = (t_writesf *)(w[1]);
2170 int vecsize = x->x_vecsize, sfchannels = x->x_sfchannels, i, j,
2171 bytespersample = x->x_bytespersample,
2172 bigendian = x->x_bigendian;
2173 float *fp;
2174 if (x->x_state == STATE_STREAM)
2175 {
2176 int wantbytes;
2177 pthread_mutex_lock(&x->x_mutex);
2178 wantbytes = sfchannels * vecsize * bytespersample;
2179 while (x->x_fifotail > x->x_fifohead &&
2180 x->x_fifotail < x->x_fifohead + wantbytes + 1)
2181 {
2182#ifdef DEBUG_SOUNDFILE
2183 pute("wait...\n");
2184#endif
2185 sfread_cond_signal(&x->x_requestcondition);
2186 sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
2187#ifdef DEBUG_SOUNDFILE
2188 pute("done\n");
2189#endif
2190 }
2191
2192 soundfile_xferout(sfchannels, x->x_outvec,
2193 (unsigned char *)(x->x_buf + x->x_fifohead), vecsize, 0,
2194 bytespersample, bigendian, 1.);
2195
2196 x->x_fifohead += wantbytes;
2197 if (x->x_fifohead >= x->x_fifosize)
2198 x->x_fifohead = 0;
2199 if ((--x->x_sigcountdown) <= 0)
2200 {
2201#ifdef DEBUG_SOUNDFILE
2202 pute("signal 1\n");
2203#endif
2204 sfread_cond_signal(&x->x_requestcondition);
2205 x->x_sigcountdown = x->x_sigperiod;
2206 }
2207 pthread_mutex_unlock(&x->x_mutex);
2208 }
2209 return (w+2);
2210}
2211
2212static void writesf_start(t_writesf *x)
2213{
2214 /* start making output. If we're in the "startup" state change
2215 to the "running" state. */
2216 if (x->x_state == STATE_STARTUP)
2217 x->x_state = STATE_STREAM;
2218 else
2219 pd_error(x, "writesf: start requested with no prior 'open'");
2220}
2221
2222static void writesf_stop(t_writesf *x)
2223{
2224 /* LATER rethink whether you need the mutex just to set a Svariable? */
2225 pthread_mutex_lock(&x->x_mutex);
2226 x->x_state = STATE_IDLE;
2227 x->x_requestcode = REQUEST_CLOSE;
2228#ifdef DEBUG_SOUNDFILE
2229 pute("signal 2\n");
2230#endif
2231 sfread_cond_signal(&x->x_requestcondition);
2232 pthread_mutex_unlock(&x->x_mutex);
2233}
2234
2235
2236 /* open method. Called as: open [args] filename with args as in
2237 soundfiler_writeargparse().
2238 */
2239
2240static void writesf_open(t_writesf *x, t_symbol *s, int argc, t_atom *argv)
2241{
2242 t_symbol *filesym;
2243 int filetype, bytespersamp, swap, bigendian, normalize;
2244 long onset, nframes;
2245 float samplerate;
2246 if (soundfiler_writeargparse(x, &argc,
2247 &argv, &filesym, &filetype, &bytespersamp, &swap, &bigendian,
2248 &normalize, &onset, &nframes, &samplerate))
2249 {
2250 pd_error(x,
2251 "writesf~: usage: open [-bytes [234]] [-wave,-nextstep,-aiff] ...");
2252 post("... [-big,-little] [-rate ####] filename");
2253 }
2254 if (normalize || onset || (nframes != 0x7fffffff))
2255 pd_error(x, "normalize/onset/nframes argument to writesf~: ignored");
2256 if (argc)
2257 pd_error(x, "extra argument(s) to writesf~: ignored");
2258 pthread_mutex_lock(&x->x_mutex);
2259 x->x_bytespersample = bytespersamp;
2260 x->x_swap = swap;
2261 x->x_bigendian = bigendian;
2262 x->x_filename = filesym->s_name;
2263 x->x_filetype = filetype;
2264 x->x_itemswritten = 0;
2265 x->x_requestcode = REQUEST_OPEN;
2266 x->x_fifotail = 0;
2267 x->x_fifohead = 0;
2268 x->x_eof = 0;
2269 x->x_fileerror = 0;
2270 x->x_state = STATE_STARTUP;
2271 x->x_bytespersample = (bytespersamp > 2 ? bytespersamp : 2);
2272 if (samplerate > 0)
2273 x->x_samplerate = samplerate;
2274 else if (x->x_insamplerate > 0)
2275 x->x_samplerate = x->x_insamplerate;
2276 else x->x_samplerate = sys_getsr();
2277 /* set fifosize from bufsize. fifosize must be a
2278 multiple of the number of bytes eaten for each DSP
2279 tick. */
2280 x->x_fifosize = x->x_bufsize - (x->x_bufsize %
2281 (x->x_bytespersample * x->x_sfchannels * MAXVECSIZE));
2282 /* arrange for the "request" condition to be signalled 16
2283 times per buffer */
2284 x->x_sigcountdown = x->x_sigperiod =
2285 (x->x_fifosize /
2286 (16 * x->x_bytespersample * x->x_sfchannels *
2287 x->x_vecsize));
2288 sfread_cond_signal(&x->x_requestcondition);
2289 pthread_mutex_unlock(&x->x_mutex);
2290}
2291
2292static void writesf_dsp(t_writesf *x, t_signal **sp)
2293{
2294 int i, ninlets = x->x_sfchannels;
2295 pthread_mutex_lock(&x->x_mutex);
2296 x->x_vecsize = sp[0]->s_n;
2297
2298 x->x_sigperiod = (x->x_fifosize /
2299 (x->x_bytespersample * ninlets * x->x_vecsize));
2300 for (i = 0; i < ninlets; i++)
2301 x->x_outvec[i] = sp[i]->s_vec;
2302 x->x_insamplerate = sp[0]->s_sr;
2303 pthread_mutex_unlock(&x->x_mutex);
2304 dsp_add(writesf_perform, 1, x);
2305}
2306
2307static void writesf_print(t_writesf *x)
2308{
2309 post("state %d", x->x_state);
2310 post("fifo head %d", x->x_fifohead);
2311 post("fifo tail %d", x->x_fifotail);
2312 post("fifo size %d", x->x_fifosize);
2313 post("fd %d", x->x_fd);
2314 post("eof %d", x->x_eof);
2315}
2316
2317static void writesf_free(t_writesf *x)
2318{
2319 /* request QUIT and wait for acknowledge */
2320 void *threadrtn;
2321 pthread_mutex_lock(&x->x_mutex);
2322 x->x_requestcode = REQUEST_QUIT;
2323 /* post("stopping writesf thread..."); */
2324 sfread_cond_signal(&x->x_requestcondition);
2325 while (x->x_requestcode != REQUEST_NOTHING)
2326 {
2327 /* post("signalling..."); */
2328 sfread_cond_signal(&x->x_requestcondition);
2329 sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
2330 }
2331 pthread_mutex_unlock(&x->x_mutex);
2332 if (pthread_join(x->x_childthread, &threadrtn))
2333 error("writesf_free: join failed");
2334 /* post("... done."); */
2335
2336 pthread_cond_destroy(&x->x_requestcondition);
2337 pthread_cond_destroy(&x->x_answercondition);
2338 pthread_mutex_destroy(&x->x_mutex);
2339 freebytes(x->x_buf, x->x_bufsize);
2340}
2341
2342static void writesf_setup(void)
2343{
2344 writesf_class = class_new(gensym("writesf~"), (t_newmethod)writesf_new,
2345 (t_method)writesf_free, sizeof(t_writesf), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
2346 class_addmethod(writesf_class, (t_method)writesf_start, gensym("start"), 0);
2347 class_addmethod(writesf_class, (t_method)writesf_stop, gensym("stop"), 0);
2348 class_addmethod(writesf_class, (t_method)writesf_dsp, gensym("dsp"), 0);
2349 class_addmethod(writesf_class, (t_method)writesf_open, gensym("open"),
2350 A_GIMME, 0);
2351 class_addmethod(writesf_class, (t_method)writesf_print, gensym("print"), 0);
2352 CLASS_MAINSIGNALIN(writesf_class, t_writesf, x_f);
2353}
2354
2355#endif
2356
2357/* ------------------------ global setup routine ------------------------- */
2358
2359void d_soundfile_setup(void)
2360{
2361 soundfiler_setup();
2362#ifndef FIXEDPOINT
2363 readsf_setup();
2364 writesf_setup();
2365#endif
2366}
2367
2368/* Copyright (c) 1997-1999 Miller Puckette.
2369* For information on usage and redistribution, and for a DISCLAIMER OF ALL
2370* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
2371
2372/* this file contains, first, a collection of soundfile access routines, a
2373sort of soundfile library. Second, the "soundfiler" object is defined which
2374uses the routines to read or write soundfiles, synchronously, from garrays.
2375These operations are not to be done in "real time" as they may have to wait
2376for disk accesses (even the write routine.) Finally, the realtime objects
2377readsf~ and writesf~ are defined which confine disk operations to a separate
2378thread so that they can be used in real time. The readsf~ and writesf~
2379objects use Posix-like threads. */
2380
2381#ifdef UNIX
2382#include <unistd.h>
2383#include <fcntl.h>
2384#endif
2385#include <pthread.h>
2386#ifdef MSW
2387#include <io.h>
2388#endif
2389#include <stdio.h>
2390#include <string.h>
2391#include <errno.h>
2392
2393#include "m_pd.h"
2394
2395#define MAXSFCHANS 64
2396
2397/***************** soundfile header structures ************************/
2398
2399typedef unsigned short uint16;
2400typedef unsigned long uint32;
2401
2402#define FORMAT_WAVE 0
2403#define FORMAT_AIFF 1
2404#define FORMAT_NEXT 2
2405
2406/* the NeXTStep sound header structure; can be big or little endian */
2407
2408typedef struct _nextstep
2409{
2410 char ns_fileid[4]; /* magic number '.snd' if file is big-endian */
2411 uint32 ns_onset; /* byte offset of first sample */
2412 uint32 ns_length; /* length of sound in bytes */
2413 uint32 ns_format; /* format; see below */
2414 uint32 ns_sr; /* sample rate */
2415 uint32 ns_nchans; /* number of channels */
2416 char ns_info[4]; /* comment */
2417} t_nextstep;
2418
2419#define NS_FORMAT_LINEAR_16 3
2420#define NS_FORMAT_LINEAR_24 4
2421#define NS_FORMAT_FLOAT 6
2422#define SCALE (1./(1024. * 1024. * 1024. * 2.))
2423
2424/* the WAVE header. All Wave files are little endian. We assume
2425 the "fmt" chunk comes first which is usually the case but perhaps not
2426 always; same for AIFF and the "COMM" chunk. */
2427
2428typedef unsigned word;
2429typedef unsigned long dword;
2430
2431typedef struct _wave
2432{
2433 char w_fileid[4]; /* chunk id 'RIFF' */
2434 uint32 w_chunksize; /* chunk size */
2435 char w_waveid[4]; /* wave chunk id 'WAVE' */
2436 char w_fmtid[4]; /* format chunk id 'fmt ' */
2437 uint32 w_fmtchunksize; /* format chunk size */
2438 uint16 w_fmttag; /* format tag (WAV_INT etc) */
2439 uint16 w_nchannels; /* number of channels */
2440 uint32 w_samplespersec; /* sample rate in hz */
2441 uint32 w_navgbytespersec; /* average bytes per second */
2442 uint16 w_nblockalign; /* number of bytes per frame */
2443 uint16 w_nbitspersample; /* number of bits in a sample */
2444 char w_datachunkid[4]; /* data chunk id 'data' */
2445 uint32 w_datachunksize; /* length of data chunk */
2446} t_wave;
2447
2448typedef struct _fmt /* format chunk */
2449{
2450 uint16 f_fmttag; /* format tag, 1 for PCM */
2451 uint16 f_nchannels; /* number of channels */
2452 uint32 f_samplespersec; /* sample rate in hz */
2453 uint32 f_navgbytespersec; /* average bytes per second */
2454 uint16 f_nblockalign; /* number of bytes per frame */
2455 uint16 f_nbitspersample; /* number of bits in a sample */
2456} t_fmt;
2457
2458typedef struct _wavechunk /* ... and the last two items */
2459{
2460 char wc_id[4]; /* data chunk id, e.g., 'data' or 'fmt ' */
2461 uint32 wc_size; /* length of data chunk */
2462} t_wavechunk;
2463
2464#define WAV_INT 1
2465#define WAV_FLOAT 3
2466
2467/* the AIFF header. I'm assuming AIFC is compatible but don't really know
2468 that. */
2469
2470typedef struct _datachunk
2471{
2472 char dc_id[4]; /* data chunk id 'SSND' */
2473 uint32 dc_size; /* length of data chunk */
2474} t_datachunk;
2475
2476typedef struct _comm
2477{
2478 uint16 c_nchannels; /* number of channels */
2479 uint16 c_nframeshi; /* # of sample frames (hi) */
2480 uint16 c_nframeslo; /* # of sample frames (lo) */
2481 uint16 c_bitspersamp; /* bits per sample */
2482 unsigned char c_samprate[10]; /* sample rate, 80-bit float! */
2483} t_comm;
2484
2485 /* this version is more convenient for writing them out: */
2486typedef struct _aiff
2487{
2488 char a_fileid[4]; /* chunk id 'FORM' */
2489 uint32 a_chunksize; /* chunk size */
2490 char a_aiffid[4]; /* aiff chunk id 'AIFF' */
2491 char a_fmtid[4]; /* format chunk id 'COMM' */
2492 uint32 a_fmtchunksize; /* format chunk size, 18 */
2493 uint16 a_nchannels; /* number of channels */
2494 uint16 a_nframeshi; /* # of sample frames (hi) */
2495 uint16 a_nframeslo; /* # of sample frames (lo) */
2496 uint16 a_bitspersamp; /* bits per sample */
2497 unsigned char a_samprate[10]; /* sample rate, 80-bit float! */
2498} t_aiff;
2499
2500#define AIFFHDRSIZE 38 /* probably not what sizeof() gives */
2501
2502
2503#define AIFFPLUS (AIFFHDRSIZE + 8) /* header size including first chunk hdr */
2504
2505#define WHDR1 sizeof(t_nextstep)
2506#define WHDR2 (sizeof(t_wave) > WHDR1 ? sizeof (t_wave) : WHDR1)
2507#define WRITEHDRSIZE (AIFFPLUS > WHDR2 ? AIFFPLUS : WHDR2)
2508
2509#define READHDRSIZE (16 > WHDR2 + 2 ? 16 : WHDR2 + 2)
2510
2511#define OBUFSIZE MAXPDSTRING /* assume MAXPDSTRING is bigger than headers */
2512
2513#ifdef MSW
2514#include <fcntl.h>
2515#define BINCREATE _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY
2516#else
2517#define BINCREATE O_WRONLY | O_CREAT | O_TRUNC
2518#endif
2519
2520/* this routine returns 1 if the high order byte comes at the lower
2521address on our architecture (big-endianness.). It's 1 for Motorola,
25220 for Intel: */
2523
2524extern int garray_ambigendian(void);
2525
2526/* byte swappers */
2527
2528static uint32 swap4(uint32 n, int doit)
2529{
2530 if (doit)
2531 return (((n & 0xff) << 24) | ((n & 0xff00) << 8) |
2532 ((n & 0xff0000) >> 8) | ((n & 0xff000000) >> 24));
2533 else return (n);
2534}
2535
2536static uint16 swap2(uint32 n, int doit)
2537{
2538 if (doit)
2539 return (((n & 0xff) << 8) | ((n & 0xff00) >> 8));
2540 else return (n);
2541}
2542
2543static void swapstring(char *foo, int doit)
2544{
2545 if (doit)
2546 {
2547 char a = foo[0], b = foo[1], c = foo[2], d = foo[3];
2548 foo[0] = d; foo[1] = c; foo[2] = b; foo[3] = a;
2549 }
2550}
2551
2552/******************** soundfile access routines **********************/
2553
2554/* This routine opens a file, looks for either a nextstep or "wave" header,
2555* seeks to end of it, and fills in bytes per sample and number of channels.
2556* Only 2- and 3-byte fixed-point samples and 4-byte floating point samples
2557* are supported. If "headersize" is nonzero, the
2558* caller should supply the number of channels, endinanness, and bytes per
2559* sample; the header is ignored. Otherwise, the routine tries to read the
2560* header and fill in the properties.
2561*/
2562
2563int open_soundfile(const char *dirname, const char *filename, int headersize,
2564 int *p_bytespersamp, int *p_bigendian, int *p_nchannels, long *p_bytelimit,
2565 long skipframes)
2566{
2567 char buf[OBUFSIZE], *bufptr;
2568 int fd, format, nchannels, bigendian, bytespersamp, swap, sysrtn;
2569 long bytelimit = 0x7fffffff;
2570 errno = 0;
2571 fd = open_via_path(dirname, filename,
2572 "", buf, &bufptr, MAXPDSTRING, 1);
2573 if (fd < 0)
2574 return (-1);
2575 if (headersize >= 0) /* header detection overridden */
2576 {
2577 bigendian = *p_bigendian;
2578 nchannels = *p_nchannels;
2579 bytespersamp = *p_bytespersamp;
2580 bytelimit = *p_bytelimit;
2581 }
2582 else
2583 {
2584 int bytesread = read(fd, buf, READHDRSIZE);
2585 int format;
2586 if (bytesread < 4)
2587 goto badheader;
2588 if (!strncmp(buf, ".snd", 4))
2589 format = FORMAT_NEXT, bigendian = 1;
2590 else if (!strncmp(buf, "dns.", 4))
2591 format = FORMAT_NEXT, bigendian = 0;
2592 else if (!strncmp(buf, "RIFF", 4))
2593 {
2594 if (bytesread < 12 || strncmp(buf + 8, "WAVE", 4))
2595 goto badheader;
2596 format = FORMAT_WAVE, bigendian = 0;
2597 }
2598 else if (!strncmp(buf, "FORM", 4))
2599 {
2600 if (bytesread < 12 || strncmp(buf + 8, "AIFF", 4))
2601 goto badheader;
2602 format = FORMAT_AIFF, bigendian = 1;
2603 }
2604 else
2605 goto badheader;
2606 swap = (bigendian != garray_ambigendian());
2607 if (format == FORMAT_NEXT) /* nextstep header */
2608 {
2609 uint32 param;
2610 if (bytesread < (int)sizeof(t_nextstep))
2611 goto badheader;
2612 nchannels = swap4(((t_nextstep *)buf)->ns_nchans, swap);
2613 format = swap4(((t_nextstep *)buf)->ns_format, swap);
2614 headersize = swap4(((t_nextstep *)buf)->ns_onset, swap);
2615 if (format == NS_FORMAT_LINEAR_16)
2616 bytespersamp = 2;
2617 else if (format == NS_FORMAT_LINEAR_24)
2618 bytespersamp = 3;
2619 else if (format == NS_FORMAT_FLOAT)
2620 bytespersamp = 4;
2621 else goto badheader;
2622 bytelimit = 0x7fffffff;
2623 }
2624 else if (format == FORMAT_WAVE) /* wave header */
2625 {
2626 /* This is awful. You have to skip over chunks,
2627 except that if one happens to be a "fmt" chunk, you want to
2628 find out the format from that one. The case where the
2629 "fmt" chunk comes after the audio isn't handled. */
2630 headersize = 12;
2631 if (bytesread < 20)
2632 goto badheader;
2633 /* First we guess a number of channels, etc., in case there's
2634 no "fmt" chunk to follow. */
2635 nchannels = 1;
2636 bytespersamp = 2;
2637 /* copy the first chunk header to beginnning of buffer. */
2638 memcpy(buf, buf + headersize, sizeof(t_wavechunk));
2639 /* post("chunk %c %c %c %c",
2640 ((t_wavechunk *)buf)->wc_id[0],
2641 ((t_wavechunk *)buf)->wc_id[1],
2642 ((t_wavechunk *)buf)->wc_id[2],
2643 ((t_wavechunk *)buf)->wc_id[3]); */
2644 /* read chunks in loop until we get to the data chunk */
2645 while (strncmp(((t_wavechunk *)buf)->wc_id, "data", 4))
2646 {
2647 long chunksize = swap4(((t_wavechunk *)buf)->wc_size,
2648 swap), seekto = headersize + chunksize + 8, seekout;
2649
2650 if (!strncmp(((t_wavechunk *)buf)->wc_id, "fmt ", 4))
2651 {
2652 long commblockonset = headersize + 8;
2653 seekout = lseek(fd, commblockonset, SEEK_SET);
2654 if (seekout != commblockonset)
2655 goto badheader;
2656 if (read(fd, buf, sizeof(t_fmt)) < (int) sizeof(t_fmt))
2657 goto badheader;
2658 nchannels = swap2(((t_fmt *)buf)->f_nchannels, swap);
2659 format = swap2(((t_fmt *)buf)->f_nbitspersample, swap);
2660 if (format == 16)
2661 bytespersamp = 2;
2662 else if (format == 24)
2663 bytespersamp = 3;
2664 else if (format == 32)
2665 bytespersamp = 4;
2666 else goto badheader;
2667 }
2668 seekout = lseek(fd, seekto, SEEK_SET);
2669 if (seekout != seekto)
2670 goto badheader;
2671 if (read(fd, buf, sizeof(t_wavechunk)) <
2672 (int) sizeof(t_wavechunk))
2673 goto badheader;
2674 /* post("new chunk %c %c %c %c at %d",
2675 ((t_wavechunk *)buf)->wc_id[0],
2676 ((t_wavechunk *)buf)->wc_id[1],
2677 ((t_wavechunk *)buf)->wc_id[2],
2678 ((t_wavechunk *)buf)->wc_id[3], seekto); */
2679 headersize = seekto;
2680 }
2681 bytelimit = swap4(((t_wavechunk *)buf)->wc_size, swap);
2682 headersize += 8;
2683 }
2684 else
2685 {
2686 /* AIFF. same as WAVE; actually predates it. Disgusting. */
2687 headersize = 12;
2688 if (bytesread < 20)
2689 goto badheader;
2690 /* First we guess a number of channels, etc., in case there's
2691 no COMM block to follow. */
2692 nchannels = 1;
2693 bytespersamp = 2;
2694 /* copy the first chunk header to beginnning of buffer. */
2695 memcpy(buf, buf + headersize, sizeof(t_datachunk));
2696 /* read chunks in loop until we get to the data chunk */
2697 while (strncmp(((t_datachunk *)buf)->dc_id, "SSND", 4))
2698 {
2699 long chunksize = swap4(((t_datachunk *)buf)->dc_size,
2700 swap), seekto = headersize + chunksize + 8, seekout;
2701 /* post("chunk %c %c %c %c seek %d",
2702 ((t_datachunk *)buf)->dc_id[0],
2703 ((t_datachunk *)buf)->dc_id[1],
2704 ((t_datachunk *)buf)->dc_id[2],
2705 ((t_datachunk *)buf)->dc_id[3], seekto); */
2706 if (!strncmp(((t_datachunk *)buf)->dc_id, "COMM", 4))
2707 {
2708 long commblockonset = headersize + 8;
2709 seekout = lseek(fd, commblockonset, SEEK_SET);
2710 if (seekout != commblockonset)
2711 goto badheader;
2712 if (read(fd, buf, sizeof(t_comm)) <
2713 (int) sizeof(t_comm))
2714 goto badheader;
2715 nchannels = swap2(((t_comm *)buf)->c_nchannels, swap);
2716 format = swap2(((t_comm *)buf)->c_bitspersamp, swap);
2717 if (format == 16)
2718 bytespersamp = 2;
2719 else if (format == 24)
2720 bytespersamp = 3;
2721 else goto badheader;
2722 }
2723 seekout = lseek(fd, seekto, SEEK_SET);
2724 if (seekout != seekto)
2725 goto badheader;
2726 if (read(fd, buf, sizeof(t_datachunk)) <
2727 (int) sizeof(t_datachunk))
2728 goto badheader;
2729 headersize = seekto;
2730 }
2731 bytelimit = swap4(((t_datachunk *)buf)->dc_size, swap);
2732 headersize += 8;
2733 }
2734 }
2735 /* seek past header and any sample frames to skip */
2736 sysrtn = lseek(fd, nchannels * bytespersamp * skipframes + headersize, 0);
2737 if (sysrtn != nchannels * bytespersamp * skipframes + headersize)
2738 return (-1);
2739 bytelimit -= nchannels * bytespersamp * skipframes;
2740 if (bytelimit < 0)
2741 bytelimit = 0;
2742 /* copy sample format back to caller */
2743 *p_bigendian = bigendian;
2744 *p_nchannels = nchannels;
2745 *p_bytespersamp = bytespersamp;
2746 *p_bytelimit = bytelimit;
2747 return (fd);
2748badheader:
2749 /* the header wasn't recognized. We're threadable here so let's not
2750 print out the error... */
2751 errno = EIO;
2752 return (-1);
2753}
2754
2755static void soundfile_xferin(int sfchannels, int nvecs, t_sample **vecs,
2756 long itemsread, unsigned char *buf, int nitems, int bytespersamp,
2757 int bigendian)
2758{
2759 int i, j;
2760 unsigned char *sp, *sp2;
2761 t_sample *fp;
2762 int nchannels = (sfchannels < nvecs ? sfchannels : nvecs);
2763 int bytesperframe = bytespersamp * sfchannels;
2764 for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp)
2765 {
2766 if (bytespersamp == 2)
2767 {
2768 if (bigendian)
2769 {
2770 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
2771 j < nitems; j++, sp2 += bytesperframe, fp++)
2772 *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16));
2773 }
2774 else
2775 {
2776 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
2777 j < nitems; j++, sp2 += bytesperframe, fp++)
2778 *fp = ((short*)sp2)[0]<<(fix1-16);
2779 }
2780 }
2781 else if (bytespersamp == 3)
2782 {
2783 if (bigendian)
2784 {
2785 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
2786 j < nitems; j++, sp2 += bytesperframe, fp++)
2787 *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16)
2788 | (sp2[2] << 8));
2789 }
2790 else
2791 {
2792 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
2793 j < nitems; j++, sp2 += bytesperframe, fp++)
2794 *fp = SCALE * ((sp2[2] << 24) | (sp2[1] << 16)
2795 | (sp2[0] << 8));
2796 }
2797 }
2798 else if (bytespersamp == 4)
2799 {
2800 if (bigendian)
2801 {
2802 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
2803 j < nitems; j++, sp2 += bytesperframe, fp++)
2804 *(long *)fp = ((sp2[0] << 24) | (sp2[1] << 16)
2805 | (sp2[2] << 8) | sp2[3]);
2806 }
2807 else
2808 {
2809 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
2810 j < nitems; j++, sp2 += bytesperframe, fp++)
2811 *(long *)fp = ((sp2[3] << 24) | (sp2[2] << 16)
2812 | (sp2[1] << 8) | sp2[0]);
2813 }
2814 }
2815 }
2816 /* zero out other outputs */
2817 for (i = sfchannels; i < nvecs; i++)
2818 for (j = nitems, fp = vecs[i]; j--; )
2819 *fp++ = 0;
2820
2821}
2822
2823 /* soundfiler_write ...
2824
2825 usage: write [flags] filename table ...
2826 flags:
2827 -nframes <frames>
2828 -skip <frames>
2829 -bytes <bytes per sample>
2830 -normalize
2831 -nextstep
2832 -wave
2833 -big
2834 -little
2835 */
2836
2837 /* the routine which actually does the work should LATER also be called
2838 from garray_write16. */
2839
2840
2841 /* Parse arguments for writing. The "obj" argument is only for flagging
2842 errors. For streaming to a file the "normalize", "onset" and "nframes"
2843 arguments shouldn't be set but the calling routine flags this. */
2844
2845static int soundfiler_writeargparse(void *obj, int *p_argc, t_atom **p_argv,
2846 t_symbol **p_filesym,
2847 int *p_filetype, int *p_bytespersamp, int *p_swap, int *p_bigendian,
2848 int *p_normalize, long *p_onset, long *p_nframes, float *p_rate)
2849{
2850 int argc = *p_argc;
2851 t_atom *argv = *p_argv;
2852 int bytespersamp = 2, bigendian = 0,
2853 endianness = -1, swap, filetype = -1, normalize = 0;
2854 long onset = 0, nframes = 0x7fffffff;
2855 t_symbol *filesym;
2856 float rate = -1;
2857
2858 while (argc > 0 && argv->a_type == A_SYMBOL &&
2859 *argv->a_w.w_symbol->s_name == '-')
2860 {
2861 char *flag = argv->a_w.w_symbol->s_name + 1;
2862 if (!strcmp(flag, "skip"))
2863 {
2864 if (argc < 2 || argv[1].a_type != A_FLOAT ||
2865 ((onset = argv[1].a_w.w_float) < 0))
2866 goto usage;
2867 argc -= 2; argv += 2;
2868 }
2869 else if (!strcmp(flag, "nframes"))
2870 {
2871 if (argc < 2 || argv[1].a_type != A_FLOAT ||
2872 ((nframes = argv[1].a_w.w_float) < 0))
2873 goto usage;
2874 argc -= 2; argv += 2;
2875 }
2876 else if (!strcmp(flag, "bytes"))
2877 {
2878 if (argc < 2 || argv[1].a_type != A_FLOAT ||
2879 ((bytespersamp = argv[1].a_w.w_float) < 2) ||
2880 bytespersamp > 4)
2881 goto usage;
2882 argc -= 2; argv += 2;
2883 }
2884 else if (!strcmp(flag, "normalize"))
2885 {
2886 normalize = 1;
2887 argc -= 1; argv += 1;
2888 }
2889 else if (!strcmp(flag, "wave"))
2890 {
2891 filetype = FORMAT_WAVE;
2892 argc -= 1; argv += 1;
2893 }
2894 else if (!strcmp(flag, "nextstep"))
2895 {
2896 filetype = FORMAT_NEXT;
2897 argc -= 1; argv += 1;
2898 }
2899 else if (!strcmp(flag, "aiff"))
2900 {
2901 filetype = FORMAT_AIFF;
2902 argc -= 1; argv += 1;
2903 }
2904 else if (!strcmp(flag, "big"))
2905 {
2906 endianness = 1;
2907 argc -= 1; argv += 1;
2908 }
2909 else if (!strcmp(flag, "little"))
2910 {
2911 endianness = 0;
2912 argc -= 1; argv += 1;
2913 }
2914 else if (!strcmp(flag, "r") || !strcmp(flag, "rate"))
2915 {
2916 if (argc < 2 || argv[1].a_type != A_FLOAT ||
2917 ((rate = argv[1].a_w.w_float) <= 0))
2918 goto usage;
2919 argc -= 2; argv += 2;
2920 }
2921 else goto usage;
2922 }
2923 if (!argc || argv->a_type != A_SYMBOL)
2924 goto usage;
2925 filesym = argv->a_w.w_symbol;
2926
2927 /* check if format not specified and fill in */
2928 if (filetype < 0)
2929 {
2930 if (strlen(filesym->s_name) >= 5 &&
2931 (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".aif") ||
2932 !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".AIF")))
2933 filetype = FORMAT_AIFF;
2934 if (strlen(filesym->s_name) >= 6 &&
2935 (!strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".aiff") ||
2936 !strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".AIFF")))
2937 filetype = FORMAT_AIFF;
2938 if (strlen(filesym->s_name) >= 5 &&
2939 (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".snd") ||
2940 !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".SND")))
2941 filetype = FORMAT_NEXT;
2942 if (strlen(filesym->s_name) >= 4 &&
2943 (!strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".au") ||
2944 !strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".AU")))
2945 filetype = FORMAT_NEXT;
2946 if (filetype < 0)
2947 filetype = FORMAT_WAVE;
2948 }
2949 /* don't handle AIFF floating point samples */
2950 if (bytespersamp == 4)
2951 {
2952 if (filetype == FORMAT_AIFF)
2953 {
2954 pd_error(obj, "AIFF floating-point file format unavailable");
2955 goto usage;
2956 }
2957 }
2958 /* for WAVE force little endian; for nextstep use machine native */
2959 if (filetype == FORMAT_WAVE)
2960 {
2961 bigendian = 0;
2962 if (endianness == 1)
2963 pd_error(obj, "WAVE file forced to little endian");
2964 }
2965 else if (filetype == FORMAT_AIFF)
2966 {
2967 bigendian = 1;
2968 if (endianness == 0)
2969 pd_error(obj, "AIFF file forced to big endian");
2970 }
2971 else if (endianness == -1)
2972 {
2973 bigendian = garray_ambigendian();
2974 }
2975 else bigendian = endianness;
2976 swap = (bigendian != garray_ambigendian());
2977
2978 argc--; argv++;
2979
2980 *p_argc = argc;
2981 *p_argv = argv;
2982 *p_filesym = filesym;
2983 *p_filetype = filetype;
2984 *p_bytespersamp = bytespersamp;
2985 *p_swap = swap;
2986 *p_normalize = normalize;
2987 *p_onset = onset;
2988 *p_nframes = nframes;
2989 *p_bigendian = bigendian;
2990 *p_rate = rate;
2991 return (0);
2992usage:
2993 return (-1);
2994}
2995
2996static int create_soundfile(t_canvas *canvas, const char *filename,
2997 int filetype, int nframes, int bytespersamp,
2998 int bigendian, int nchannels, int swap, float samplerate)
2999{
3000 char filenamebuf[MAXPDSTRING], buf2[MAXPDSTRING];
3001 char headerbuf[WRITEHDRSIZE];
3002 t_wave *wavehdr = (t_wave *)headerbuf;
3003 t_nextstep *nexthdr = (t_nextstep *)headerbuf;
3004 t_aiff *aiffhdr = (t_aiff *)headerbuf;
3005 int fd, headersize = 0;
3006
3007 strncpy(filenamebuf, filename, MAXPDSTRING-10);
3008 filenamebuf[MAXPDSTRING-10] = 0;
3009
3010 if (filetype == FORMAT_NEXT)
3011 {
3012 if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".snd"))
3013 strcat(filenamebuf, ".snd");
3014 if (bigendian)
3015 strncpy(nexthdr->ns_fileid, ".snd", 4);
3016 else strncpy(nexthdr->ns_fileid, "dns.", 4);
3017 nexthdr->ns_onset = swap4(sizeof(*nexthdr), swap);
3018 nexthdr->ns_length = 0;
3019 nexthdr->ns_format = swap4((bytespersamp == 3 ? NS_FORMAT_LINEAR_24 :
3020 (bytespersamp == 4 ? NS_FORMAT_FLOAT : NS_FORMAT_LINEAR_16)), swap);
3021 nexthdr->ns_sr = swap4(samplerate, swap);
3022 nexthdr->ns_nchans = swap4(nchannels, swap);
3023 strcpy(nexthdr->ns_info, "Pd ");
3024 swapstring(nexthdr->ns_info, swap);
3025 headersize = sizeof(t_nextstep);
3026 }
3027 else if (filetype == FORMAT_AIFF)
3028 {
3029 long datasize = nframes * nchannels * bytespersamp;
3030 long longtmp;
3031 static unsigned char dogdoo[] =
3032 {0x40, 0x0e, 0xac, 0x44, 0, 0, 0, 0, 0, 0, 'S', 'S', 'N', 'D'};
3033 if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".aif") &&
3034 strcmp(filenamebuf + strlen(filenamebuf)-5, ".aiff"))
3035 strcat(filenamebuf, ".aif");
3036 strncpy(aiffhdr->a_fileid, "FORM", 4);
3037 aiffhdr->a_chunksize = swap4(datasize + sizeof(*aiffhdr) + 4, swap);
3038 strncpy(aiffhdr->a_aiffid, "AIFF", 4);
3039 strncpy(aiffhdr->a_fmtid, "COMM", 4);
3040 aiffhdr->a_fmtchunksize = swap4(18, swap);
3041 aiffhdr->a_nchannels = swap2(nchannels, swap);
3042 longtmp = swap4(nframes, swap);
3043 memcpy(&aiffhdr->a_nframeshi, &longtmp, 4);
3044 aiffhdr->a_bitspersamp = swap2(8 * bytespersamp, swap);
3045 memcpy(aiffhdr->a_samprate, dogdoo, sizeof(dogdoo));
3046 longtmp = swap4(datasize, swap);
3047 memcpy(aiffhdr->a_samprate + sizeof(dogdoo), &longtmp, 4);
3048 headersize = AIFFPLUS;
3049 }
3050 else /* WAVE format */
3051 {
3052 long datasize = nframes * nchannels * bytespersamp;
3053 if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".wav"))
3054 strcat(filenamebuf, ".wav");
3055 strncpy(wavehdr->w_fileid, "RIFF", 4);
3056 wavehdr->w_chunksize = swap4(datasize + sizeof(*wavehdr) - 8, swap);
3057 strncpy(wavehdr->w_waveid, "WAVE", 4);
3058 strncpy(wavehdr->w_fmtid, "fmt ", 4);
3059 wavehdr->w_fmtchunksize = swap4(16, swap);
3060 wavehdr->w_fmttag =
3061 swap2((bytespersamp == 4 ? WAV_FLOAT : WAV_INT), swap);
3062 wavehdr->w_nchannels = swap2(nchannels, swap);
3063 wavehdr->w_samplespersec = swap4(samplerate, swap);
3064 wavehdr->w_navgbytespersec =
3065 swap4((int)(samplerate * nchannels * bytespersamp), swap);
3066 wavehdr->w_nblockalign = swap2(nchannels * bytespersamp, swap);
3067 wavehdr->w_nbitspersample = swap2(8 * bytespersamp, swap);
3068 strncpy(wavehdr->w_datachunkid, "data", 4);
3069 wavehdr->w_datachunksize = swap4(datasize, swap);
3070 headersize = sizeof(t_wave);
3071 }
3072
3073 canvas_makefilename(canvas, filenamebuf, buf2, MAXPDSTRING);
3074 sys_bashfilename(buf2, buf2);
3075 if ((fd = open(buf2, BINCREATE, 0666)) < 0)
3076 return (-1);
3077
3078 if (write(fd, headerbuf, headersize) < headersize)
3079 {
3080 close (fd);
3081 return (-1);
3082 }
3083 return (fd);
3084}
3085
3086static void soundfile_finishwrite(void *obj, char *filename, int fd,
3087 int filetype, long nframes, long itemswritten, int bytesperframe, int swap)
3088{
3089 if (itemswritten < nframes)
3090 {
3091 if (nframes < 0x7fffffff)
3092 pd_error(obj, "soundfiler_write: %d out of %d bytes written",
3093 itemswritten, nframes);
3094 /* try to fix size fields in header */
3095 if (filetype == FORMAT_WAVE)
3096 {
3097 long datasize = itemswritten * bytesperframe, mofo;
3098
3099 if (lseek(fd,
3100 ((char *)(&((t_wave *)0)->w_chunksize)) - (char *)0,
3101 SEEK_SET) == 0)
3102 goto baddonewrite;
3103 mofo = swap4(datasize + sizeof(t_wave) - 8, swap);
3104 if (write(fd, (char *)(&mofo), 4) < 4)
3105 goto baddonewrite;
3106 if (lseek(fd,
3107 ((char *)(&((t_wave *)0)->w_datachunksize)) - (char *)0,
3108 SEEK_SET) == 0)
3109 goto baddonewrite;
3110 mofo = swap4(datasize, swap);
3111 if (write(fd, (char *)(&mofo), 4) < 4)
3112 goto baddonewrite;
3113 }
3114 if (filetype == FORMAT_AIFF)
3115 {
3116 long mofo;
3117 if (lseek(fd,
3118 ((char *)(&((t_aiff *)0)->a_nframeshi)) - (char *)0,
3119 SEEK_SET) == 0)
3120 goto baddonewrite;
3121 mofo = swap4(nframes, swap);
3122 if (write(fd, (char *)(&mofo), 4) < 4)
3123 goto baddonewrite;
3124 }
3125 if (filetype == FORMAT_NEXT)
3126 {
3127 /* do it the lazy way: just set the size field to 'unknown size'*/
3128 uint32 nextsize = 0xffffffff;
3129 if (lseek(fd, 8, SEEK_SET) == 0)
3130 {
3131 goto baddonewrite;
3132 }
3133 if (write(fd, &nextsize, 4) < 4)
3134 {
3135 goto baddonewrite;
3136 }
3137 }
3138 }
3139 return;
3140baddonewrite:
3141 post("%s: %s", filename, strerror(errno));
3142}
3143
3144static void soundfile_xferout(int nchannels, t_sample **vecs,
3145 unsigned char *buf, int nitems, long onset, int bytespersamp,
3146 int bigendian, float normalfactor)
3147{
3148 int i, j;
3149 unsigned char *sp, *sp2;
3150 t_sample *fp;
3151 int bytesperframe = bytespersamp * nchannels;
3152 long xx;
3153 for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp)
3154 {
3155 if (bytespersamp == 2)
3156 {
3157 float ff = normalfactor * 32768.;
3158 if (bigendian)
3159 {
3160 for (j = 0, sp2 = sp, fp = vecs[i] + onset;
3161 j < nitems; j++, sp2 += bytesperframe, fp++)
3162 {
3163 int xx = 32768. + (*fp * ff);
3164 xx -= 32768;
3165 if (xx < -32767)
3166 xx = -32767;
3167 if (xx > 32767)
3168 xx = 32767;
3169 sp2[0] = (xx >> 8);
3170 sp2[1] = xx;
3171 }
3172 }
3173 else
3174 {
3175 for (j = 0, sp2 = sp, fp=vecs[i] + onset;
3176 j < nitems; j++, sp2 += bytesperframe, fp++)
3177 {
3178 int xx = 32768. + (*fp * ff);
3179 xx -= 32768;
3180 if (xx < -32767)
3181 xx = -32767;
3182 if (xx > 32767)
3183 xx = 32767;
3184 sp2[1] = (xx >> 8);
3185 sp2[0] = xx;
3186 }
3187 }
3188 }
3189 else if (bytespersamp == 3)
3190 {
3191 float ff = normalfactor * 8388608.;
3192 if (bigendian)
3193 {
3194 for (j = 0, sp2 = sp, fp=vecs[i] + onset;
3195 j < nitems; j++, sp2 += bytesperframe, fp++)
3196 {
3197 int xx = 8388608. + (*fp * ff);
3198 xx -= 8388608;
3199 if (xx < -8388607)
3200 xx = -8388607;
3201 if (xx > 8388607)
3202 xx = 8388607;
3203 sp2[0] = (xx >> 16);
3204 sp2[1] = (xx >> 8);
3205 sp2[2] = xx;
3206 }
3207 }
3208 else
3209 {
3210 for (j = 0, sp2 = sp, fp=vecs[i] + onset;
3211 j < nitems; j++, sp2 += bytesperframe, fp++)
3212 {
3213 int xx = 8388608. + (*fp * ff);
3214 xx -= 8388608;
3215 if (xx < -8388607)
3216 xx = -8388607;
3217 if (xx > 8388607)
3218 xx = 8388607;
3219 sp2[2] = (xx >> 16);
3220 sp2[1] = (xx >> 8);
3221 sp2[0] = xx;
3222 }
3223 }
3224 }
3225 else if (bytespersamp == 4)
3226 {
3227 if (bigendian)
3228 {
3229 for (j = 0, sp2 = sp, fp=vecs[i] + onset;
3230 j < nitems; j++, sp2 += bytesperframe, fp++)
3231 {
3232 float f2 = *fp * normalfactor;
3233 xx = *(long *)&f2;
3234 sp2[0] = (xx >> 24); sp2[1] = (xx >> 16);
3235 sp2[2] = (xx >> 8); sp2[3] = xx;
3236 }
3237 }
3238 else
3239 {
3240 for (j = 0, sp2 = sp, fp=vecs[i] + onset;
3241 j < nitems; j++, sp2 += bytesperframe, fp++)
3242 {
3243 float f2 = *fp * normalfactor;
3244 xx = *(long *)&f2;
3245 sp2[3] = (xx >> 24); sp2[2] = (xx >> 16);
3246 sp2[1] = (xx >> 8); sp2[0] = xx;
3247 }
3248 }
3249 }
3250 }
3251}
3252
3253
3254/* ------- soundfiler - reads and writes soundfiles to/from "garrays" ---- */
3255#define DEFMAXSIZE 4000000 /* default maximum 16 MB per channel */
3256#define SAMPBUFSIZE 1024
3257
3258
3259static t_class *soundfiler_class;
3260
3261typedef struct _soundfiler
3262{
3263 t_object x_obj;
3264 t_canvas *x_canvas;
3265} t_soundfiler;
3266
3267static t_soundfiler *soundfiler_new(void)
3268{
3269 t_soundfiler *x = (t_soundfiler *)pd_new(soundfiler_class);
3270 x->x_canvas = canvas_getcurrent();
3271 outlet_new(&x->x_obj, &s_float);
3272 return (x);
3273}
3274
3275 /* soundfiler_read ...
3276
3277 usage: read [flags] filename table ...
3278 flags:
3279 -skip <frames> ... frames to skip in file
3280 -nframes <frames>
3281 -onset <frames> ... onset in table to read into (NOT DONE YET)
3282 -raw <headersize channels bytes endian>
3283 -resize
3284 -maxsize <max-size>
3285 */
3286
3287static void soundfiler_read(t_soundfiler *x, t_symbol *s,
3288 int argc, t_atom *argv)
3289{
3290 int headersize = -1, channels = 0, bytespersamp = 0, bigendian = 0,
3291 resize = 0, i, j;
3292 long skipframes = 0, nframes = 0, finalsize = 0, itemsleft,
3293 maxsize = DEFMAXSIZE, itemsread = 0, bytelimit = 0x7fffffff;
3294 int fd = -1;
3295 char endianness, *filename;
3296 t_garray *garrays[MAXSFCHANS];
3297 t_sample *vecs[MAXSFCHANS];
3298 char sampbuf[SAMPBUFSIZE];
3299 int bufframes, nitems;
3300 FILE *fp;
3301 while (argc > 0 && argv->a_type == A_SYMBOL &&
3302 *argv->a_w.w_symbol->s_name == '-')
3303 {
3304 char *flag = argv->a_w.w_symbol->s_name + 1;
3305 if (!strcmp(flag, "skip"))
3306 {
3307 if (argc < 2 || argv[1].a_type != A_FLOAT ||
3308 ((skipframes = argv[1].a_w.w_float) < 0))
3309 goto usage;
3310 argc -= 2; argv += 2;
3311 }
3312 else if (!strcmp(flag, "nframes"))
3313 {
3314 if (argc < 2 || argv[1].a_type != A_FLOAT ||
3315 ((nframes = argv[1].a_w.w_float) < 0))
3316 goto usage;
3317 argc -= 2; argv += 2;
3318 }
3319 else if (!strcmp(flag, "raw"))
3320 {
3321 if (argc < 5 ||
3322 argv[1].a_type != A_FLOAT ||
3323 ((headersize = argv[1].a_w.w_float) < 0) ||
3324 argv[2].a_type != A_FLOAT ||
3325 ((channels = argv[2].a_w.w_float) < 1) ||
3326 (channels > MAXSFCHANS) ||
3327 argv[3].a_type != A_FLOAT ||
3328 ((bytespersamp = argv[3].a_w.w_float) < 2) ||
3329 (bytespersamp > 4) ||
3330 argv[4].a_type != A_SYMBOL ||
3331 ((endianness = argv[4].a_w.w_symbol->s_name[0]) != 'b'
3332 && endianness != 'l' && endianness != 'n'))
3333 goto usage;
3334 if (endianness == 'b')
3335 bigendian = 1;
3336 else if (endianness == 'l')
3337 bigendian = 0;
3338 else
3339 bigendian = garray_ambigendian();
3340 argc -= 5; argv += 5;
3341 }
3342 else if (!strcmp(flag, "resize"))
3343 {
3344 resize = 1;
3345 argc -= 1; argv += 1;
3346 }
3347 else if (!strcmp(flag, "maxsize"))
3348 {
3349 if (argc < 2 || argv[1].a_type != A_FLOAT ||
3350 ((maxsize = argv[1].a_w.w_float) < 0))
3351 goto usage;
3352 resize = 1; /* maxsize implies resize. */
3353 argc -= 2; argv += 2;
3354 }
3355 else goto usage;
3356 }
3357 if (argc < 2 || argc > MAXSFCHANS + 1 || argv[0].a_type != A_SYMBOL)
3358 goto usage;
3359 filename = argv[0].a_w.w_symbol->s_name;
3360 argc--; argv++;
3361
3362 for (i = 0; i < argc; i++)
3363 {
3364 int vecsize;
3365 if (argv[i].a_type != A_SYMBOL)
3366 goto usage;
3367 if (!(garrays[i] =
3368 (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class)))
3369 {
3370 pd_error(x, "%s: no such table", argv[i].a_w.w_symbol->s_name);
3371 goto done;
3372 }
3373 else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i]))
3374 error("%s: bad template for tabwrite",
3375 argv[i].a_w.w_symbol->s_name);
3376 if (finalsize && finalsize != vecsize && !resize)
3377 {
3378 post("soundfiler_read: arrays have different lengths; resizing...");
3379 resize = 1;
3380 }
3381 finalsize = vecsize;
3382 }
3383 fd = open_soundfile(canvas_getdir(x->x_canvas)->s_name, filename,
3384 headersize, &bytespersamp, &bigendian, &channels, &bytelimit,
3385 skipframes);
3386
3387 if (fd < 0)
3388 {
3389 pd_error(x, "soundfiler_read: %s: %s", filename, (errno == EIO ?
3390 "unknown or bad header format" : strerror(errno)));
3391 goto done;
3392 }
3393
3394 if (resize)
3395 {
3396 /* figure out what to resize to */
3397 long poswas, eofis, framesinfile;
3398
3399 poswas = lseek(fd, 0, SEEK_CUR);
3400 eofis = lseek(fd, 0, SEEK_END);
3401 if (poswas < 0 || eofis < 0)
3402 {
3403 pd_error(x, "lseek failed");
3404 goto done;
3405 }
3406 lseek(fd, poswas, SEEK_SET);
3407 framesinfile = (eofis - poswas) / (channels * bytespersamp);
3408 if (framesinfile > maxsize)
3409 {
3410 pd_error(x, "soundfiler_read: truncated to %d elements", maxsize);
3411 framesinfile = maxsize;
3412 }
3413 if (framesinfile > bytelimit / (channels * bytespersamp))
3414 framesinfile = bytelimit / (channels * bytespersamp);
3415 finalsize = framesinfile;
3416 for (i = 0; i < argc; i++)
3417 {
3418 int vecsize;
3419
3420 garray_resize(garrays[i], finalsize);
3421 /* for sanity's sake let's clear the save-in-patch flag here */
3422 garray_setsaveit(garrays[i], 0);
3423 garray_getfloatarray(garrays[i], &vecsize, &vecs[i]);
3424 /* if the resize failed, garray_resize reported the error */
3425 if (vecsize != framesinfile)
3426 {
3427 pd_error(x, "resize failed");
3428 goto done;
3429 }
3430 }
3431 }
3432 if (!finalsize) finalsize = 0x7fffffff;
3433 if (finalsize > bytelimit / (channels * bytespersamp))
3434 finalsize = bytelimit / (channels * bytespersamp);
3435 fp = fdopen(fd, "rb");
3436 bufframes = SAMPBUFSIZE / (channels * bytespersamp);
3437
3438 for (itemsread = 0; itemsread < finalsize; )
3439 {
3440 int thisread = finalsize - itemsread;
3441 thisread = (thisread > bufframes ? bufframes : thisread);
3442 nitems = fread(sampbuf, channels * bytespersamp, thisread, fp);
3443 if (nitems <= 0) break;
3444 soundfile_xferin(channels, argc, vecs, itemsread,
3445 (unsigned char *)sampbuf, nitems, bytespersamp, bigendian);
3446 itemsread += nitems;
3447 }
3448 /* zero out remaining elements of vectors */
3449
3450 for (i = 0; i < argc; i++)
3451 {
3452 int nzero, vecsize;
3453 garray_getfloatarray(garrays[i], &vecsize, &vecs[i]);
3454 for (j = itemsread; j < vecsize; j++)
3455 vecs[i][j] = 0;
3456 }
3457 /* zero out vectors in excess of number of channels */
3458 for (i = channels; i < argc; i++)
3459 {
3460 int vecsize;
3461 t_sample *foo;
3462 garray_getfloatarray(garrays[i], &vecsize, &foo);
3463 for (j = 0; j < vecsize; j++)
3464 foo[j] = 0;
3465 }
3466 /* do all graphics updates */
3467 for (i = 0; i < argc; i++)
3468 garray_redraw(garrays[i]);
3469 fclose(fp);
3470 fd = -1;
3471 goto done;
3472usage:
3473 pd_error(x, "usage: read [flags] filename tablename...");
3474 post("flags: -skip <n> -nframes <n> -resize -maxsize <n> ...");
3475 post("-raw <headerbytes> <channels> <bytespersamp> <endian (b, l, or n)>.");
3476done:
3477 if (fd >= 0)
3478 close (fd);
3479 outlet_float(x->x_obj.ob_outlet, (float)itemsread);
3480}
3481
3482 /* this is broken out from soundfiler_write below so garray_write can
3483 call it too... not done yet though. */
3484
3485long soundfiler_dowrite(void *obj, t_canvas *canvas,
3486 int argc, t_atom *argv)
3487{
3488 int headersize, bytespersamp, bigendian,
3489 endianness, swap, filetype, normalize, i, j, nchannels;
3490 long onset, nframes, itemsleft,
3491 maxsize = DEFMAXSIZE, itemswritten = 0;
3492 t_garray *garrays[MAXSFCHANS];
3493 t_sample *vecs[MAXSFCHANS];
3494 char sampbuf[SAMPBUFSIZE];
3495 int bufframes, nitems;
3496 int fd = -1;
3497 float normfactor, biggest = 0, samplerate;
3498 t_symbol *filesym;
3499
3500 if (soundfiler_writeargparse(obj, &argc, &argv, &filesym, &filetype,
3501 &bytespersamp, &swap, &bigendian, &normalize, &onset, &nframes,
3502 &samplerate))
3503 goto usage;
3504 nchannels = argc;
3505 if (nchannels < 1 || nchannels > MAXSFCHANS)
3506 goto usage;
3507 if (samplerate < 0)
3508 samplerate = sys_getsr();
3509 for (i = 0; i < nchannels; i++)
3510 {
3511 int vecsize;
3512 if (argv[i].a_type != A_SYMBOL)
3513 goto usage;
3514 if (!(garrays[i] =
3515 (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class)))
3516 {
3517 pd_error(obj, "%s: no such table", argv[i].a_w.w_symbol->s_name);
3518 goto fail;
3519 }
3520 else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i]))
3521 error("%s: bad template for tabwrite",
3522 argv[i].a_w.w_symbol->s_name);
3523 if (nframes > vecsize - onset)
3524 nframes = vecsize - onset;
3525
3526 for (j = 0; j < vecsize; j++)
3527 {
3528 if (vecs[i][j] > biggest)
3529 biggest = vecs[i][j];
3530 else if (-vecs[i][j] > biggest)
3531 biggest = -vecs[i][j];
3532 }
3533 }
3534 if (nframes <= 0)
3535 {
3536 pd_error(obj, "soundfiler_write: no samples at onset %ld", onset);
3537 goto fail;
3538 }
3539
3540 if ((fd = create_soundfile(canvas, filesym->s_name, filetype,
3541 nframes, bytespersamp, bigendian, nchannels,
3542 swap, samplerate)) < 0)
3543 {
3544 post("%s: %s\n", filesym->s_name, strerror(errno));
3545 goto fail;
3546 }
3547 if (!normalize)
3548 {
3549 if ((bytespersamp != 4) && (biggest > 1))
3550 {
3551 post("%s: normalizing max amplitude %f to 1", filesym->s_name, biggest);
3552 normalize = 1;
3553 }
3554 else post("%s: biggest amplitude = %f", filesym->s_name, biggest);
3555 }
3556 if (normalize)
3557 normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1);
3558 else normfactor = 1;
3559
3560 bufframes = SAMPBUFSIZE / (nchannels * bytespersamp);
3561
3562 for (itemswritten = 0; itemswritten < nframes; )
3563 {
3564 int thiswrite = nframes - itemswritten, nitems, nbytes;
3565 thiswrite = (thiswrite > bufframes ? bufframes : thiswrite);
3566 soundfile_xferout(argc, vecs, (unsigned char *)sampbuf, thiswrite,
3567 onset, bytespersamp, bigendian, normfactor);
3568 nbytes = write(fd, sampbuf, nchannels * bytespersamp * thiswrite);
3569 if (nbytes < nchannels * bytespersamp * thiswrite)
3570 {
3571 post("%s: %s", filesym->s_name, strerror(errno));
3572 if (nbytes > 0)
3573 itemswritten += nbytes / (nchannels * bytespersamp);
3574 break;
3575 }
3576 itemswritten += thiswrite;
3577 onset += thiswrite;
3578 }
3579 if (fd >= 0)
3580 {
3581 soundfile_finishwrite(obj, filesym->s_name, fd,
3582 filetype, nframes, itemswritten, nchannels * bytespersamp, swap);
3583 close (fd);
3584 }
3585 return ((float)itemswritten);
3586usage:
3587 pd_error(obj, "usage: write [flags] filename tablename...");
3588 post("flags: -skip <n> -nframes <n> -bytes <n> -wave -aiff -nextstep ...");
3589 post("-big -little -normalize");
3590 post("(defaults to a 16-bit wave file).");
3591fail:
3592 if (fd >= 0)
3593 close (fd);
3594 return (0);
3595}
3596
3597static void soundfiler_write(t_soundfiler *x, t_symbol *s,
3598 int argc, t_atom *argv)
3599{
3600 long bozo = soundfiler_dowrite(x, x->x_canvas,
3601 argc, argv);
3602 outlet_float(x->x_obj.ob_outlet, (float)bozo);
3603}
3604
3605static void soundfiler_setup(void)
3606{
3607 soundfiler_class = class_new(gensym("soundfiler"), (t_newmethod)soundfiler_new,
3608 0, sizeof(t_soundfiler), 0, 0);
3609 class_addmethod(soundfiler_class, (t_method)soundfiler_read, gensym("read"),
3610 A_GIMME, 0);
3611 class_addmethod(soundfiler_class, (t_method)soundfiler_write,
3612 gensym("write"), A_GIMME, 0);
3613}
3614
3615
3616#ifndef FIXEDPOINT
3617/************************* readsf object ******************************/
3618
3619/* READSF uses the Posix threads package; for the moment we're Linux
3620only although this should be portable to the other platforms.
3621
3622Each instance of readsf~ owns a "child" thread for doing the UNIX (MSW?) file
3623reading. The parent thread signals the child each time:
3624 (1) a file wants opening or closing;
3625 (2) we've eaten another 1/16 of the shared buffer (so that the
3626 child thread should check if it's time to read some more.)
3627The child signals the parent whenever a read has completed. Signalling
3628is done by setting "conditions" and putting data in mutex-controlled common
3629areas.
3630*/
3631
3632#define MAXBYTESPERSAMPLE 4
3633#define MAXVECSIZE 128
3634
3635#define READSIZE 65536
3636#define WRITESIZE 65536
3637#define DEFBUFPERCHAN 262144
3638#define MINBUFSIZE (4 * READSIZE)
3639#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */
3640
3641#define REQUEST_NOTHING 0
3642#define REQUEST_OPEN 1
3643#define REQUEST_CLOSE 2
3644#define REQUEST_QUIT 3
3645#define REQUEST_BUSY 4
3646
3647#define STATE_IDLE 0
3648#define STATE_STARTUP 1
3649#define STATE_STREAM 2
3650
3651static t_class *readsf_class;
3652
3653typedef struct _readsf
3654{
3655 t_object x_obj;
3656 t_canvas *x_canvas;
3657 t_clock *x_clock;
3658 char *x_buf; /* soundfile buffer */
3659 int x_bufsize; /* buffer size in bytes */
3660 int x_noutlets; /* number of audio outlets */
3661 t_sample *(x_outvec[MAXSFCHANS]); /* audio vectors */
3662 int x_vecsize; /* vector size for transfers */
3663 t_outlet *x_bangout; /* bang-on-done outlet */
3664 int x_state; /* opened, running, or idle */
3665 float x_insamplerate; /* sample rate of input signal if known */
3666 /* parameters to communicate with subthread */
3667 int x_requestcode; /* pending request from parent to I/O thread */
3668 char *x_filename; /* file to open (string is permanently allocated) */
3669 int x_fileerror; /* slot for "errno" return */
3670 int x_skipheaderbytes; /* size of header we'll skip */
3671 int x_bytespersample; /* bytes per sample (2 or 3) */
3672 int x_bigendian; /* true if file is big-endian */
3673 int x_sfchannels; /* number of channels in soundfile */
3674 float x_samplerate; /* sample rate of soundfile */
3675 long x_onsetframes; /* number of sample frames to skip */
3676 long x_bytelimit; /* max number of data bytes to read */
3677 int x_fd; /* filedesc */
3678 int x_fifosize; /* buffer size appropriately rounded down */
3679 int x_fifohead; /* index of next byte to get from file */
3680 int x_fifotail; /* index of next byte the ugen will read */
3681 int x_eof; /* true if fifohead has stopped changing */
3682 int x_sigcountdown; /* counter for signalling child for more data */
3683 int x_sigperiod; /* number of ticks per signal */
3684 int x_filetype; /* writesf~ only; type of file to create */
3685 int x_itemswritten; /* writesf~ only; items writen */
3686 int x_swap; /* writesf~ only; true if byte swapping */
3687 float x_f; /* writesf~ only; scalar for signal inlet */
3688 pthread_mutex_t x_mutex;
3689 pthread_cond_t x_requestcondition;
3690 pthread_cond_t x_answercondition;
3691 pthread_t x_childthread;
3692} t_readsf;
3693
3694
3695/************** the child thread which performs file I/O ***********/
3696
3697#if 0
3698static void pute(char *s) /* debug routine */
3699{
3700 write(2, s, strlen(s));
3701}
3702#define DEBUG_SOUNDFILE
3703#endif
3704
3705#if 1
3706#define sfread_cond_wait pthread_cond_wait
3707#define sfread_cond_signal pthread_cond_signal
3708#else
3709#include <sys/time.h> /* debugging version... */
3710#include <sys/types.h>
3711static void readsf_fakewait(pthread_mutex_t *b)
3712{
3713 struct timeval timout;
3714 timout.tv_sec = 0;
3715 timout.tv_usec = 1000000;
3716 pthread_mutex_unlock(b);
3717 select(0, 0, 0, 0, &timout);
3718 pthread_mutex_lock(b);
3719}
3720
3721#define sfread_cond_wait(a,b) readsf_fakewait(b)
3722#define sfread_cond_signal(a)
3723#endif
3724
3725static void *readsf_child_main(void *zz)
3726{
3727 t_readsf *x = zz;
3728#ifdef DEBUG_SOUNDFILE
3729 pute("1\n");
3730#endif
3731 pthread_mutex_lock(&x->x_mutex);
3732 while (1)
3733 {
3734 int fd, fifohead;
3735 char *buf;
3736#ifdef DEBUG_SOUNDFILE
3737 pute("0\n");
3738#endif
3739 if (x->x_requestcode == REQUEST_NOTHING)
3740 {
3741#ifdef DEBUG_SOUNDFILE
3742 pute("wait 2\n");
3743#endif
3744 sfread_cond_signal(&x->x_answercondition);
3745 sfread_cond_wait(&x->x_requestcondition, &x->x_mutex);
3746#ifdef DEBUG_SOUNDFILE
3747 pute("3\n");
3748#endif
3749 }
3750 else if (x->x_requestcode == REQUEST_OPEN)
3751 {
3752 char boo[80];
3753 int sysrtn, wantbytes;
3754
3755 /* copy file stuff out of the data structure so we can
3756 relinquish the mutex while we're in open_soundfile(). */
3757 long onsetframes = x->x_onsetframes;
3758 long bytelimit = 0x7fffffff;
3759 int skipheaderbytes = x->x_skipheaderbytes;
3760 int bytespersample = x->x_bytespersample;
3761 int sfchannels = x->x_sfchannels;
3762 int bigendian = x->x_bigendian;
3763 char *filename = x->x_filename;
3764 char *dirname = canvas_getdir(x->x_canvas)->s_name;
3765 /* alter the request code so that an ensuing "open" will get
3766 noticed. */
3767#ifdef DEBUG_SOUNDFILE
3768 pute("4\n");
3769#endif
3770 x->x_requestcode = REQUEST_BUSY;
3771 x->x_fileerror = 0;
3772
3773 /* if there's already a file open, close it */
3774 if (x->x_fd >= 0)
3775 {
3776 fd = x->x_fd;
3777 pthread_mutex_unlock(&x->x_mutex);
3778 close (fd);
3779 pthread_mutex_lock(&x->x_mutex);
3780 x->x_fd = -1;
3781 if (x->x_requestcode != REQUEST_BUSY)
3782 goto lost;
3783 }
3784 /* open the soundfile with the mutex unlocked */
3785 pthread_mutex_unlock(&x->x_mutex);
3786 fd = open_soundfile(dirname, filename,
3787 skipheaderbytes, &bytespersample, &bigendian,
3788 &sfchannels, &bytelimit, onsetframes);
3789 pthread_mutex_lock(&x->x_mutex);
3790
3791#ifdef DEBUG_SOUNDFILE
3792 pute("5\n");
3793#endif
3794 /* copy back into the instance structure. */
3795 x->x_bytespersample = bytespersample;
3796 x->x_sfchannels = sfchannels;
3797 x->x_bigendian = bigendian;
3798 x->x_fd = fd;
3799 x->x_bytelimit = bytelimit;
3800 if (fd < 0)
3801 {
3802 x->x_fileerror = errno;
3803 x->x_eof = 1;
3804#ifdef DEBUG_SOUNDFILE
3805 pute("open failed\n");
3806 pute(filename);
3807 pute(dirname);
3808#endif
3809 goto lost;
3810 }
3811 /* check if another request has been made; if so, field it */
3812 if (x->x_requestcode != REQUEST_BUSY)
3813 goto lost;
3814#ifdef DEBUG_SOUNDFILE
3815 pute("6\n");
3816#endif
3817 x->x_fifohead = 0;
3818 /* set fifosize from bufsize. fifosize must be a
3819 multiple of the number of bytes eaten for each DSP
3820 tick. We pessimistically assume MAXVECSIZE samples
3821 per tick since that could change. There could be a
3822 problem here if the vector size increases while a
3823 soundfile is being played... */
3824 x->x_fifosize = x->x_bufsize - (x->x_bufsize %
3825 (x->x_bytespersample * x->x_sfchannels * MAXVECSIZE));
3826 /* arrange for the "request" condition to be signalled 16
3827 times per buffer */
3828#ifdef DEBUG_SOUNDFILE
3829 sprintf(boo, "fifosize %d\n",
3830 x->x_fifosize);
3831 pute(boo);
3832#endif
3833 x->x_sigcountdown = x->x_sigperiod =
3834 (x->x_fifosize /
3835 (16 * x->x_bytespersample * x->x_sfchannels *
3836 x->x_vecsize));
3837 /* in a loop, wait for the fifo to get hungry and feed it */
3838
3839 while (x->x_requestcode == REQUEST_BUSY)
3840 {
3841 int fifosize = x->x_fifosize;
3842#ifdef DEBUG_SOUNDFILE
3843 pute("77\n");
3844#endif
3845 if (x->x_eof)
3846 break;
3847 if (x->x_fifohead >= x->x_fifotail)
3848 {
3849 /* if the head is >= the tail, we can immediately read
3850 to the end of the fifo. Unless, that is, we would
3851 read all the way to the end of the buffer and the
3852 "tail" is zero; this would fill the buffer completely
3853 which isn't allowed because you can't tell a completely
3854 full buffer from an empty one. */
3855 if (x->x_fifotail || (fifosize - x->x_fifohead > READSIZE))
3856 {
3857 wantbytes = fifosize - x->x_fifohead;
3858 if (wantbytes > READSIZE)
3859 wantbytes = READSIZE;
3860 if (wantbytes > x->x_bytelimit)
3861 wantbytes = x->x_bytelimit;
3862#ifdef DEBUG_SOUNDFILE
3863 sprintf(boo, "head %d, tail %d, size %d\n",
3864 x->x_fifohead, x->x_fifotail, wantbytes);
3865 pute(boo);
3866#endif
3867 }
3868 else
3869 {
3870#ifdef DEBUG_SOUNDFILE
3871 pute("wait 7a ...\n");
3872#endif
3873 sfread_cond_signal(&x->x_answercondition);
3874#ifdef DEBUG_SOUNDFILE
3875 pute("signalled\n");
3876#endif
3877 sfread_cond_wait(&x->x_requestcondition,
3878 &x->x_mutex);
3879#ifdef DEBUG_SOUNDFILE
3880 pute("7a done\n");
3881#endif
3882 continue;
3883 }
3884 }
3885 else
3886 {
3887 /* otherwise check if there are at least READSIZE
3888 bytes to read. If not, wait and loop back. */
3889 wantbytes = x->x_fifotail - x->x_fifohead - 1;
3890 if (wantbytes < READSIZE)
3891 {
3892#ifdef DEBUG_SOUNDFILE
3893 pute("wait 7...\n");
3894#endif
3895 sfread_cond_signal(&x->x_answercondition);
3896 sfread_cond_wait(&x->x_requestcondition,
3897 &x->x_mutex);
3898#ifdef DEBUG_SOUNDFILE
3899 pute("7 done\n");
3900#endif
3901 continue;
3902 }
3903 else wantbytes = READSIZE;
3904 if (wantbytes > x->x_bytelimit)
3905 wantbytes = x->x_bytelimit;
3906 }
3907#ifdef DEBUG_SOUNDFILE
3908 pute("8\n");
3909#endif
3910 fd = x->x_fd;
3911 buf = x->x_buf;
3912 fifohead = x->x_fifohead;
3913 pthread_mutex_unlock(&x->x_mutex);
3914 sysrtn = read(fd, buf + fifohead, wantbytes);
3915 pthread_mutex_lock(&x->x_mutex);
3916 if (x->x_requestcode != REQUEST_BUSY)
3917 break;
3918 if (sysrtn < 0)
3919 {
3920#ifdef DEBUG_SOUNDFILE
3921 pute("fileerror\n");
3922#endif
3923 x->x_fileerror = errno;
3924 break;
3925 }
3926 else if (sysrtn == 0)
3927 {
3928 x->x_eof = 1;
3929 break;
3930 }
3931 else
3932 {
3933 x->x_fifohead += sysrtn;
3934 x->x_bytelimit -= sysrtn;
3935 if (x->x_bytelimit <= 0)
3936 {
3937 x->x_eof = 1;
3938 break;
3939 }
3940 if (x->x_fifohead == fifosize)
3941 x->x_fifohead = 0;
3942 }
3943#ifdef DEBUG_SOUNDFILE
3944 sprintf(boo, "after: head %d, tail %d\n",
3945 x->x_fifohead, x->x_fifotail);
3946 pute(boo);
3947#endif
3948 /* signal parent in case it's waiting for data */
3949 sfread_cond_signal(&x->x_answercondition);
3950 }
3951 lost:
3952
3953 if (x->x_requestcode == REQUEST_BUSY)
3954 x->x_requestcode = REQUEST_NOTHING;
3955 /* fell out of read loop: close file if necessary,
3956 set EOF and signal once more */
3957 if (x->x_fd >= 0)
3958 {
3959 fd = x->x_fd;
3960 pthread_mutex_unlock(&x->x_mutex);
3961 close (fd);
3962 pthread_mutex_lock(&x->x_mutex);
3963 x->x_fd = -1;
3964 }
3965 sfread_cond_signal(&x->x_answercondition);
3966
3967 }
3968 else if (x->x_requestcode == REQUEST_CLOSE)
3969 {
3970 if (x->x_fd >= 0)
3971 {
3972 fd = x->x_fd;
3973 pthread_mutex_unlock(&x->x_mutex);
3974 close (fd);
3975 pthread_mutex_lock(&x->x_mutex);
3976 x->x_fd = -1;
3977 }
3978 if (x->x_requestcode == REQUEST_CLOSE)
3979 x->x_requestcode = REQUEST_NOTHING;
3980 sfread_cond_signal(&x->x_answercondition);
3981 }
3982 else if (x->x_requestcode == REQUEST_QUIT)
3983 {
3984 if (x->x_fd >= 0)
3985 {
3986 fd = x->x_fd;
3987 pthread_mutex_unlock(&x->x_mutex);
3988 close (fd);
3989 pthread_mutex_lock(&x->x_mutex);
3990 x->x_fd = -1;
3991 }
3992 x->x_requestcode = REQUEST_NOTHING;
3993 sfread_cond_signal(&x->x_answercondition);
3994 break;
3995 }
3996 else
3997 {
3998#ifdef DEBUG_SOUNDFILE
3999 pute("13\n");
4000#endif
4001 }
4002 }
4003#ifdef DEBUG_SOUNDFILE
4004 pute("thread exit\n");
4005#endif
4006 pthread_mutex_unlock(&x->x_mutex);
4007 return (0);
4008}
4009
4010/******** the object proper runs in the calling (parent) thread ****/
4011
4012static void readsf_tick(t_readsf *x);
4013
4014static void *readsf_new(t_floatarg fnchannels, t_floatarg fbufsize)
4015{
4016 t_readsf *x;
4017 int nchannels = fnchannels, bufsize = fbufsize, i;
4018 char *buf;
4019
4020 if (nchannels < 1)
4021 nchannels = 1;
4022 else if (nchannels > MAXSFCHANS)
4023 nchannels = MAXSFCHANS;
4024 if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels;
4025 else if (bufsize < MINBUFSIZE)
4026 bufsize = MINBUFSIZE;
4027 else if (bufsize > MAXBUFSIZE)
4028 bufsize = MAXBUFSIZE;
4029 buf = getbytes(bufsize);
4030 if (!buf) return (0);
4031
4032 x = (t_readsf *)pd_new(readsf_class);
4033
4034 for (i = 0; i < nchannels; i++)
4035 outlet_new(&x->x_obj, gensym("signal"));
4036 x->x_noutlets = nchannels;
4037 x->x_bangout = outlet_new(&x->x_obj, &s_bang);
4038 pthread_mutex_init(&x->x_mutex, 0);
4039 pthread_cond_init(&x->x_requestcondition, 0);
4040 pthread_cond_init(&x->x_answercondition, 0);
4041 x->x_vecsize = MAXVECSIZE;
4042 x->x_state = STATE_IDLE;
4043 x->x_clock = clock_new(x, (t_method)readsf_tick);
4044 x->x_canvas = canvas_getcurrent();
4045 x->x_bytespersample = 2;
4046 x->x_sfchannels = 1;
4047 x->x_fd = -1;
4048 x->x_buf = buf;
4049 x->x_bufsize = bufsize;
4050 x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0;
4051 pthread_create(&x->x_childthread, 0, readsf_child_main, x);
4052 return (x);
4053}
4054
4055static void readsf_tick(t_readsf *x)
4056{
4057 outlet_bang(x->x_bangout);
4058}
4059
4060static t_int *readsf_perform(t_int *w)
4061{
4062 t_readsf *x = (t_readsf *)(w[1]);
4063 int vecsize = x->x_vecsize, noutlets = x->x_noutlets, i, j,
4064 bytespersample = x->x_bytespersample,
4065 bigendian = x->x_bigendian;
4066 float *fp;
4067 if (x->x_state == STATE_STREAM)
4068 {
4069 int wantbytes, nchannels, sfchannels = x->x_sfchannels;
4070 pthread_mutex_lock(&x->x_mutex);
4071 wantbytes = sfchannels * vecsize * bytespersample;
4072 while (
4073 !x->x_eof && x->x_fifohead >= x->x_fifotail &&
4074 x->x_fifohead < x->x_fifotail + wantbytes-1)
4075 {
4076#ifdef DEBUG_SOUNDFILE
4077 pute("wait...\n");
4078#endif
4079 sfread_cond_signal(&x->x_requestcondition);
4080 sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
4081#ifdef DEBUG_SOUNDFILE
4082 pute("done\n");
4083#endif
4084 }
4085 if (x->x_eof && x->x_fifohead >= x->x_fifotail &&
4086 x->x_fifohead < x->x_fifotail + wantbytes-1)
4087 {
4088 int xfersize;
4089 if (x->x_fileerror)
4090 {
4091 pd_error(x, "dsp: %s: %s", x->x_filename,
4092 (x->x_fileerror == EIO ?
4093 "unknown or bad header format" :
4094 strerror(x->x_fileerror)));
4095 }
4096 clock_delay(x->x_clock, 0);
4097 x->x_state = STATE_IDLE;
4098
4099 /* if there's a partial buffer left, copy it out. */
4100 xfersize = (x->x_fifohead - x->x_fifotail + 1) /
4101 (sfchannels * bytespersample);
4102 if (xfersize)
4103 {
4104 soundfile_xferin(sfchannels, noutlets, x->x_outvec, 0,
4105 (unsigned char *)(x->x_buf + x->x_fifotail), xfersize,
4106 bytespersample, bigendian);
4107 vecsize -= xfersize;
4108 }
4109 /* then zero out the (rest of the) output */
4110 for (i = 0; i < noutlets; i++)
4111 for (j = vecsize, fp = x->x_outvec[i] + xfersize; j--; )
4112 *fp++ = 0;
4113
4114 sfread_cond_signal(&x->x_requestcondition);
4115 pthread_mutex_unlock(&x->x_mutex);
4116 return (w+2);
4117 }
4118
4119 soundfile_xferin(sfchannels, noutlets, x->x_outvec, 0,
4120 (unsigned char *)(x->x_buf + x->x_fifotail), vecsize,
4121 bytespersample, bigendian);
4122
4123 x->x_fifotail += wantbytes;
4124 if (x->x_fifotail >= x->x_fifosize)
4125 x->x_fifotail = 0;
4126 if ((--x->x_sigcountdown) <= 0)
4127 {
4128 sfread_cond_signal(&x->x_requestcondition);
4129 x->x_sigcountdown = x->x_sigperiod;
4130 }
4131 pthread_mutex_unlock(&x->x_mutex);
4132 }
4133 else
4134 {
4135 idle:
4136 for (i = 0; i < noutlets; i++)
4137 for (j = vecsize, fp = x->x_outvec[i]; j--; )
4138 *fp++ = 0;
4139 }
4140 return (w+2);
4141}
4142
4143static void readsf_start(t_readsf *x)
4144{
4145 /* start making output. If we're in the "startup" state change
4146 to the "running" state. */
4147 if (x->x_state == STATE_STARTUP)
4148 x->x_state = STATE_STREAM;
4149 else pd_error(x, "readsf: start requested with no prior 'open'");
4150}
4151
4152static void readsf_stop(t_readsf *x)
4153{
4154 /* LATER rethink whether you need the mutex just to set a variable? */
4155 pthread_mutex_lock(&x->x_mutex);
4156 x->x_state = STATE_IDLE;
4157 x->x_requestcode = REQUEST_CLOSE;
4158 sfread_cond_signal(&x->x_requestcondition);
4159 pthread_mutex_unlock(&x->x_mutex);
4160}
4161
4162static void readsf_float(t_readsf *x, t_floatarg f)
4163{
4164 if (f != 0)
4165 readsf_start(x);
4166 else readsf_stop(x);
4167}
4168
4169 /* open method. Called as:
4170 open filename [skipframes headersize channels bytespersamp endianness]
4171 (if headersize is zero, header is taken to be automatically
4172 detected; thus, use the special "-1" to mean a truly headerless file.)
4173 */
4174
4175static void readsf_open(t_readsf *x, t_symbol *s, int argc, t_atom *argv)
4176{
4177 t_symbol *filesym = atom_getsymbolarg(0, argc, argv);
4178 t_float onsetframes = atom_getfloatarg(1, argc, argv);
4179 t_float headerbytes = atom_getfloatarg(2, argc, argv);
4180 t_float channels = atom_getfloatarg(3, argc, argv);
4181 t_float bytespersamp = atom_getfloatarg(4, argc, argv);
4182 t_symbol *endian = atom_getsymbolarg(5, argc, argv);
4183 if (!*filesym->s_name)
4184 return;
4185 pthread_mutex_lock(&x->x_mutex);
4186 x->x_requestcode = REQUEST_OPEN;
4187 x->x_filename = filesym->s_name;
4188 x->x_fifotail = 0;
4189 x->x_fifohead = 0;
4190 if (*endian->s_name == 'b')
4191 x->x_bigendian = 1;
4192 else if (*endian->s_name == 'l')
4193 x->x_bigendian = 0;
4194 else if (*endian->s_name)
4195 pd_error(x, "endianness neither 'b' nor 'l'");
4196 else x->x_bigendian = garray_ambigendian();
4197 x->x_onsetframes = (onsetframes > 0 ? onsetframes : 0);
4198 x->x_skipheaderbytes = (headerbytes > 0 ? headerbytes :
4199 (headerbytes == 0 ? -1 : 0));
4200 x->x_sfchannels = (channels >= 1 ? channels : 1);
4201 x->x_bytespersample = (bytespersamp > 2 ? bytespersamp : 2);
4202 x->x_eof = 0;
4203 x->x_fileerror = 0;
4204 x->x_state = STATE_STARTUP;
4205 sfread_cond_signal(&x->x_requestcondition);
4206 pthread_mutex_unlock(&x->x_mutex);
4207}
4208
4209static void readsf_dsp(t_readsf *x, t_signal **sp)
4210{
4211 int i, noutlets = x->x_noutlets;
4212 pthread_mutex_lock(&x->x_mutex);
4213 x->x_vecsize = sp[0]->s_n;
4214
4215 x->x_sigperiod = (x->x_fifosize /
4216 (x->x_bytespersample * x->x_sfchannels * x->x_vecsize));
4217 for (i = 0; i < noutlets; i++)
4218 x->x_outvec[i] = sp[i]->s_vec;
4219 pthread_mutex_unlock(&x->x_mutex);
4220 dsp_add(readsf_perform, 1, x);
4221}
4222
4223static void readsf_print(t_readsf *x)
4224{
4225 post("state %d", x->x_state);
4226 post("fifo head %d", x->x_fifohead);
4227 post("fifo tail %d", x->x_fifotail);
4228 post("fifo size %d", x->x_fifosize);
4229 post("fd %d", x->x_fd);
4230 post("eof %d", x->x_eof);
4231}
4232
4233static void readsf_free(t_readsf *x)
4234{
4235 /* request QUIT and wait for acknowledge */
4236 void *threadrtn;
4237 pthread_mutex_lock(&x->x_mutex);
4238 x->x_requestcode = REQUEST_QUIT;
4239 sfread_cond_signal(&x->x_requestcondition);
4240 while (x->x_requestcode != REQUEST_NOTHING)
4241 {
4242 sfread_cond_signal(&x->x_requestcondition);
4243 sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
4244 }
4245 pthread_mutex_unlock(&x->x_mutex);
4246 if (pthread_join(x->x_childthread, &threadrtn))
4247 error("readsf_free: join failed");
4248
4249 pthread_cond_destroy(&x->x_requestcondition);
4250 pthread_cond_destroy(&x->x_answercondition);
4251 pthread_mutex_destroy(&x->x_mutex);
4252 freebytes(x->x_buf, x->x_bufsize);
4253 clock_free(x->x_clock);
4254}
4255
4256static void readsf_setup(void)
4257{
4258 readsf_class = class_new(gensym("readsf~"), (t_newmethod)readsf_new,
4259 (t_method)readsf_free, sizeof(t_readsf), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
4260 class_addfloat(readsf_class, (t_method)readsf_float);
4261 class_addmethod(readsf_class, (t_method)readsf_start, gensym("start"), 0);
4262 class_addmethod(readsf_class, (t_method)readsf_stop, gensym("stop"), 0);
4263 class_addmethod(readsf_class, (t_method)readsf_dsp, gensym("dsp"), 0);
4264 class_addmethod(readsf_class, (t_method)readsf_open, gensym("open"),
4265 A_GIMME, 0);
4266 class_addmethod(readsf_class, (t_method)readsf_print, gensym("print"), 0);
4267}
4268
4269/******************************* writesf *******************/
4270
4271static t_class *writesf_class;
4272
4273#define t_writesf t_readsf /* just re-use the structure */
4274
4275/************** the child thread which performs file I/O ***********/
4276
4277static void *writesf_child_main(void *zz)
4278{
4279 t_writesf *x = zz;
4280#ifdef DEBUG_SOUNDFILE
4281 pute("1\n");
4282#endif
4283 pthread_mutex_lock(&x->x_mutex);
4284 while (1)
4285 {
4286#ifdef DEBUG_SOUNDFILE
4287 pute("0\n");
4288#endif
4289 if (x->x_requestcode == REQUEST_NOTHING)
4290 {
4291#ifdef DEBUG_SOUNDFILE
4292 pute("wait 2\n");
4293#endif
4294 sfread_cond_signal(&x->x_answercondition);
4295 sfread_cond_wait(&x->x_requestcondition, &x->x_mutex);
4296#ifdef DEBUG_SOUNDFILE
4297 pute("3\n");
4298#endif
4299 }
4300 else if (x->x_requestcode == REQUEST_OPEN)
4301 {
4302 char boo[80];
4303 int fd, sysrtn, writebytes;
4304
4305 /* copy file stuff out of the data structure so we can
4306 relinquish the mutex while we're in open_soundfile(). */
4307 long onsetframes = x->x_onsetframes;
4308 long bytelimit = 0x7fffffff;
4309 int skipheaderbytes = x->x_skipheaderbytes;
4310 int bytespersample = x->x_bytespersample;
4311 int sfchannels = x->x_sfchannels;
4312 int bigendian = x->x_bigendian;
4313 int filetype = x->x_filetype;
4314 char *filename = x->x_filename;
4315 t_canvas *canvas = x->x_canvas;
4316 float samplerate = x->x_samplerate;
4317
4318 /* alter the request code so that an ensuing "open" will get
4319 noticed. */
4320#ifdef DEBUG_SOUNDFILE
4321 pute("4\n");
4322#endif
4323 x->x_requestcode = REQUEST_BUSY;
4324 x->x_fileerror = 0;
4325
4326 /* if there's already a file open, close it */
4327 if (x->x_fd >= 0)
4328 {
4329 pthread_mutex_unlock(&x->x_mutex);
4330 close (x->x_fd);
4331 pthread_mutex_lock(&x->x_mutex);
4332 x->x_fd = -1;
4333 if (x->x_requestcode != REQUEST_BUSY)
4334 continue;
4335 }
4336 /* open the soundfile with the mutex unlocked */
4337 pthread_mutex_unlock(&x->x_mutex);
4338 fd = create_soundfile(canvas, filename, filetype, 0,
4339 bytespersample, bigendian, sfchannels,
4340 garray_ambigendian() != bigendian, samplerate);
4341 pthread_mutex_lock(&x->x_mutex);
4342#ifdef DEBUG_SOUNDFILE
4343 pute("5\n");
4344#endif
4345
4346 if (fd < 0)
4347 {
4348 x->x_fd = -1;
4349 x->x_eof = 1;
4350 x->x_fileerror = errno;
4351#ifdef DEBUG_SOUNDFILE
4352 pute("open failed\n");
4353 pute(filename);
4354#endif
4355 x->x_requestcode = REQUEST_NOTHING;
4356 continue;
4357 }
4358 /* check if another request has been made; if so, field it */
4359 if (x->x_requestcode != REQUEST_BUSY)
4360 continue;
4361#ifdef DEBUG_SOUNDFILE
4362 pute("6\n");
4363#endif
4364 x->x_fd = fd;
4365 x->x_fifotail = 0;
4366 x->x_itemswritten = 0;
4367 x->x_swap = garray_ambigendian() != bigendian;
4368 /* in a loop, wait for the fifo to have data and write it
4369 to disk */
4370 while (x->x_requestcode == REQUEST_BUSY ||
4371 (x->x_requestcode == REQUEST_CLOSE &&
4372 x->x_fifohead != x->x_fifotail))
4373 {
4374 int fifosize = x->x_fifosize, fifotail;
4375 char *buf = x->x_buf;
4376#ifdef DEBUG_SOUNDFILE
4377 pute("77\n");
4378#endif
4379
4380 /* if the head is < the tail, we can immediately write
4381 from tail to end of fifo to disk; otherwise we hold off
4382 writing until there are at least WRITESIZE bytes in the
4383 buffer */
4384 if (x->x_fifohead < x->x_fifotail ||
4385 x->x_fifohead >= x->x_fifotail + WRITESIZE
4386 || (x->x_requestcode == REQUEST_CLOSE &&
4387 x->x_fifohead != x->x_fifotail))
4388 {
4389 writebytes = (x->x_fifohead < x->x_fifotail ?
4390 fifosize : x->x_fifohead) - x->x_fifotail;
4391 if (writebytes > READSIZE)
4392 writebytes = READSIZE;
4393 }
4394 else
4395 {
4396#ifdef DEBUG_SOUNDFILE
4397 pute("wait 7a ...\n");
4398#endif
4399 sfread_cond_signal(&x->x_answercondition);
4400#ifdef DEBUG_SOUNDFILE
4401 pute("signalled\n");
4402#endif
4403 sfread_cond_wait(&x->x_requestcondition,
4404 &x->x_mutex);
4405#ifdef DEBUG_SOUNDFILE
4406 pute("7a done\n");
4407#endif
4408 continue;
4409 }
4410#ifdef DEBUG_SOUNDFILE
4411 pute("8\n");
4412#endif
4413 fifotail = x->x_fifotail;
4414 fd = x->x_fd;
4415 pthread_mutex_unlock(&x->x_mutex);
4416 sysrtn = write(fd, buf + fifotail, writebytes);
4417 pthread_mutex_lock(&x->x_mutex);
4418 if (x->x_requestcode != REQUEST_BUSY &&
4419 x->x_requestcode != REQUEST_CLOSE)
4420 break;
4421 if (sysrtn < writebytes)
4422 {
4423#ifdef DEBUG_SOUNDFILE
4424 pute("fileerror\n");
4425#endif
4426 x->x_fileerror = errno;
4427 break;
4428 }
4429 else
4430 {
4431 x->x_fifotail += sysrtn;
4432 if (x->x_fifotail == fifosize)
4433 x->x_fifotail = 0;
4434 }
4435 x->x_itemswritten +=
4436 sysrtn / (x->x_bytespersample * x->x_sfchannels);
4437 sprintf(boo, "after: head %d, tail %d\n",
4438 x->x_fifohead, x->x_fifotail);
4439#ifdef DEBUG_SOUNDFILE
4440 pute(boo);
4441#endif
4442 /* signal parent in case it's waiting for data */
4443 sfread_cond_signal(&x->x_answercondition);
4444 }
4445 }
4446 else if (x->x_requestcode == REQUEST_CLOSE ||
4447 x->x_requestcode == REQUEST_QUIT)
4448 {
4449 int quit = (x->x_requestcode == REQUEST_QUIT);
4450 if (x->x_fd >= 0)
4451 {
4452 int bytesperframe = x->x_bytespersample * x->x_sfchannels;
4453 int bigendian = x->x_bigendian;
4454 char *filename = x->x_filename;
4455 int fd = x->x_fd;
4456 int filetype = x->x_filetype;
4457 int itemswritten = x->x_itemswritten;
4458 int swap = x->x_swap;
4459 pthread_mutex_unlock(&x->x_mutex);
4460
4461 soundfile_finishwrite(x, filename, fd,
4462 filetype, 0x7fffffff, itemswritten,
4463 bytesperframe, swap);
4464 close (fd);
4465
4466 pthread_mutex_lock(&x->x_mutex);
4467 x->x_fd = -1;
4468 }
4469 x->x_requestcode = REQUEST_NOTHING;
4470 sfread_cond_signal(&x->x_answercondition);
4471 if (quit)
4472 break;
4473 }
4474 else
4475 {
4476#ifdef DEBUG_SOUNDFILE
4477 pute("13\n");
4478#endif
4479 }
4480 }
4481#ifdef DEBUG_SOUNDFILE
4482 pute("thread exit\n");
4483#endif
4484 pthread_mutex_unlock(&x->x_mutex);
4485 return (0);
4486}
4487
4488/******** the object proper runs in the calling (parent) thread ****/
4489
4490static void writesf_tick(t_writesf *x);
4491
4492static void *writesf_new(t_floatarg fnchannels, t_floatarg fbufsize)
4493{
4494 t_writesf *x;
4495 int nchannels = fnchannels, bufsize = fbufsize, i;
4496 char *buf;
4497
4498 if (nchannels < 1)
4499 nchannels = 1;
4500 else if (nchannels > MAXSFCHANS)
4501 nchannels = MAXSFCHANS;
4502 if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels;
4503 else if (bufsize < MINBUFSIZE)
4504 bufsize = MINBUFSIZE;
4505 else if (bufsize > MAXBUFSIZE)
4506 bufsize = MAXBUFSIZE;
4507 buf = getbytes(bufsize);
4508 if (!buf) return (0);
4509
4510 x = (t_writesf *)pd_new(writesf_class);
4511
4512 for (i = 1; i < nchannels; i++)
4513 inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
4514
4515 x->x_f = 0;
4516 x->x_sfchannels = nchannels;
4517 pthread_mutex_init(&x->x_mutex, 0);
4518 pthread_cond_init(&x->x_requestcondition, 0);
4519 pthread_cond_init(&x->x_answercondition, 0);
4520 x->x_vecsize = MAXVECSIZE;
4521 x->x_insamplerate = x->x_samplerate = 0;
4522 x->x_state = STATE_IDLE;
4523 x->x_clock = 0; /* no callback needed here */
4524 x->x_canvas = canvas_getcurrent();
4525 x->x_bytespersample = 2;
4526 x->x_fd = -1;
4527 x->x_buf = buf;
4528 x->x_bufsize = bufsize;
4529 x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0;
4530 pthread_create(&x->x_childthread, 0, writesf_child_main, x);
4531 return (x);
4532}
4533
4534static t_int *writesf_perform(t_int *w)
4535{
4536 t_writesf *x = (t_writesf *)(w[1]);
4537 int vecsize = x->x_vecsize, sfchannels = x->x_sfchannels, i, j,
4538 bytespersample = x->x_bytespersample,
4539 bigendian = x->x_bigendian;
4540 float *fp;
4541 if (x->x_state == STATE_STREAM)
4542 {
4543 int wantbytes;
4544 pthread_mutex_lock(&x->x_mutex);
4545 wantbytes = sfchannels * vecsize * bytespersample;
4546 while (x->x_fifotail > x->x_fifohead &&
4547 x->x_fifotail < x->x_fifohead + wantbytes + 1)
4548 {
4549#ifdef DEBUG_SOUNDFILE
4550 pute("wait...\n");
4551#endif
4552 sfread_cond_signal(&x->x_requestcondition);
4553 sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
4554#ifdef DEBUG_SOUNDFILE
4555 pute("done\n");
4556#endif
4557 }
4558
4559 soundfile_xferout(sfchannels, x->x_outvec,
4560 (unsigned char *)(x->x_buf + x->x_fifohead), vecsize, 0,
4561 bytespersample, bigendian, 1.);
4562
4563 x->x_fifohead += wantbytes;
4564 if (x->x_fifohead >= x->x_fifosize)
4565 x->x_fifohead = 0;
4566 if ((--x->x_sigcountdown) <= 0)
4567 {
4568#ifdef DEBUG_SOUNDFILE
4569 pute("signal 1\n");
4570#endif
4571 sfread_cond_signal(&x->x_requestcondition);
4572 x->x_sigcountdown = x->x_sigperiod;
4573 }
4574 pthread_mutex_unlock(&x->x_mutex);
4575 }
4576 return (w+2);
4577}
4578
4579static void writesf_start(t_writesf *x)
4580{
4581 /* start making output. If we're in the "startup" state change
4582 to the "running" state. */
4583 if (x->x_state == STATE_STARTUP)
4584 x->x_state = STATE_STREAM;
4585 else
4586 pd_error(x, "writesf: start requested with no prior 'open'");
4587}
4588
4589static void writesf_stop(t_writesf *x)
4590{
4591 /* LATER rethink whether you need the mutex just to set a Svariable? */
4592 pthread_mutex_lock(&x->x_mutex);
4593 x->x_state = STATE_IDLE;
4594 x->x_requestcode = REQUEST_CLOSE;
4595#ifdef DEBUG_SOUNDFILE
4596 pute("signal 2\n");
4597#endif
4598 sfread_cond_signal(&x->x_requestcondition);
4599 pthread_mutex_unlock(&x->x_mutex);
4600}
4601
4602
4603 /* open method. Called as: open [args] filename with args as in
4604 soundfiler_writeargparse().
4605 */
4606
4607static void writesf_open(t_writesf *x, t_symbol *s, int argc, t_atom *argv)
4608{
4609 t_symbol *filesym;
4610 int filetype, bytespersamp, swap, bigendian, normalize;
4611 long onset, nframes;
4612 float samplerate;
4613 if (soundfiler_writeargparse(x, &argc,
4614 &argv, &filesym, &filetype, &bytespersamp, &swap, &bigendian,
4615 &normalize, &onset, &nframes, &samplerate))
4616 {
4617 pd_error(x,
4618 "writesf~: usage: open [-bytes [234]] [-wave,-nextstep,-aiff] ...");
4619 post("... [-big,-little] [-rate ####] filename");
4620 }
4621 if (normalize || onset || (nframes != 0x7fffffff))
4622 pd_error(x, "normalize/onset/nframes argument to writesf~: ignored");
4623 if (argc)
4624 pd_error(x, "extra argument(s) to writesf~: ignored");
4625 pthread_mutex_lock(&x->x_mutex);
4626 x->x_bytespersample = bytespersamp;
4627 x->x_swap = swap;
4628 x->x_bigendian = bigendian;
4629 x->x_filename = filesym->s_name;
4630 x->x_filetype = filetype;
4631 x->x_itemswritten = 0;
4632 x->x_requestcode = REQUEST_OPEN;
4633 x->x_fifotail = 0;
4634 x->x_fifohead = 0;
4635 x->x_eof = 0;
4636 x->x_fileerror = 0;
4637 x->x_state = STATE_STARTUP;
4638 x->x_bytespersample = (bytespersamp > 2 ? bytespersamp : 2);
4639 if (samplerate > 0)
4640 x->x_samplerate = samplerate;
4641 else if (x->x_insamplerate > 0)
4642 x->x_samplerate = x->x_insamplerate;
4643 else x->x_samplerate = sys_getsr();
4644 /* set fifosize from bufsize. fifosize must be a
4645 multiple of the number of bytes eaten for each DSP
4646 tick. */
4647 x->x_fifosize = x->x_bufsize - (x->x_bufsize %
4648 (x->x_bytespersample * x->x_sfchannels * MAXVECSIZE));
4649 /* arrange for the "request" condition to be signalled 16
4650 times per buffer */
4651 x->x_sigcountdown = x->x_sigperiod =
4652 (x->x_fifosize /
4653 (16 * x->x_bytespersample * x->x_sfchannels *
4654 x->x_vecsize));
4655 sfread_cond_signal(&x->x_requestcondition);
4656 pthread_mutex_unlock(&x->x_mutex);
4657}
4658
4659static void writesf_dsp(t_writesf *x, t_signal **sp)
4660{
4661 int i, ninlets = x->x_sfchannels;
4662 pthread_mutex_lock(&x->x_mutex);
4663 x->x_vecsize = sp[0]->s_n;
4664
4665 x->x_sigperiod = (x->x_fifosize /
4666 (x->x_bytespersample * ninlets * x->x_vecsize));
4667 for (i = 0; i < ninlets; i++)
4668 x->x_outvec[i] = sp[i]->s_vec;
4669 x->x_insamplerate = sp[0]->s_sr;
4670 pthread_mutex_unlock(&x->x_mutex);
4671 dsp_add(writesf_perform, 1, x);
4672}
4673
4674static void writesf_print(t_writesf *x)
4675{
4676 post("state %d", x->x_state);
4677 post("fifo head %d", x->x_fifohead);
4678 post("fifo tail %d", x->x_fifotail);
4679 post("fifo size %d", x->x_fifosize);
4680 post("fd %d", x->x_fd);
4681 post("eof %d", x->x_eof);
4682}
4683
4684static void writesf_free(t_writesf *x)
4685{
4686 /* request QUIT and wait for acknowledge */
4687 void *threadrtn;
4688 pthread_mutex_lock(&x->x_mutex);
4689 x->x_requestcode = REQUEST_QUIT;
4690 /* post("stopping writesf thread..."); */
4691 sfread_cond_signal(&x->x_requestcondition);
4692 while (x->x_requestcode != REQUEST_NOTHING)
4693 {
4694 /* post("signalling..."); */
4695 sfread_cond_signal(&x->x_requestcondition);
4696 sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
4697 }
4698 pthread_mutex_unlock(&x->x_mutex);
4699 if (pthread_join(x->x_childthread, &threadrtn))
4700 error("writesf_free: join failed");
4701 /* post("... done."); */
4702
4703 pthread_cond_destroy(&x->x_requestcondition);
4704 pthread_cond_destroy(&x->x_answercondition);
4705 pthread_mutex_destroy(&x->x_mutex);
4706 freebytes(x->x_buf, x->x_bufsize);
4707}
4708
4709static void writesf_setup(void)
4710{
4711 writesf_class = class_new(gensym("writesf~"), (t_newmethod)writesf_new,
4712 (t_method)writesf_free, sizeof(t_writesf), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
4713 class_addmethod(writesf_class, (t_method)writesf_start, gensym("start"), 0);
4714 class_addmethod(writesf_class, (t_method)writesf_stop, gensym("stop"), 0);
4715 class_addmethod(writesf_class, (t_method)writesf_dsp, gensym("dsp"), 0);
4716 class_addmethod(writesf_class, (t_method)writesf_open, gensym("open"),
4717 A_GIMME, 0);
4718 class_addmethod(writesf_class, (t_method)writesf_print, gensym("print"), 0);
4719 CLASS_MAINSIGNALIN(writesf_class, t_writesf, x_f);
4720}
4721
4722#endif
4723
4724/* ------------------------ global setup routine ------------------------- */
4725
4726void d_soundfile_setup(void)
4727{
4728 soundfiler_setup();
4729#ifndef FIXEDPOINT
4730 readsf_setup();
4731 writesf_setup();
4732#endif
4733}
4734