A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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 ROCKBOX
15#include "plugin.h"
16#include "../../pdbox.h"
17#else /* ROCKBOX */
18#ifdef UNIX
19#include <unistd.h>
20#include <fcntl.h>
21#endif
22#include <pthread.h>
23#ifdef MSW
24#include <io.h>
25#endif
26#include <stdio.h>
27#include <string.h>
28#include <errno.h>
29#endif /* ROCKBOX */
30
31#include "m_pd.h"
32
33#define MAXSFCHANS 64
34
35/***************** soundfile header structures ************************/
36
37typedef unsigned short uint16;
38typedef unsigned long uint32;
39
40#define FORMAT_WAVE 0
41#define FORMAT_AIFF 1
42#define FORMAT_NEXT 2
43
44/* the NeXTStep sound header structure; can be big or little endian */
45
46typedef struct _nextstep
47{
48 char ns_fileid[4]; /* magic number '.snd' if file is big-endian */
49 uint32 ns_onset; /* byte offset of first sample */
50 uint32 ns_length; /* length of sound in bytes */
51 uint32 ns_format; /* format; see below */
52 uint32 ns_sr; /* sample rate */
53 uint32 ns_nchans; /* number of channels */
54 char ns_info[4]; /* comment */
55} t_nextstep;
56
57#define NS_FORMAT_LINEAR_16 3
58#define NS_FORMAT_LINEAR_24 4
59#define NS_FORMAT_FLOAT 6
60#define SCALE (1./(1024. * 1024. * 1024. * 2.))
61
62/* the WAVE header. All Wave files are little endian. We assume
63 the "fmt" chunk comes first which is usually the case but perhaps not
64 always; same for AIFF and the "COMM" chunk. */
65
66typedef unsigned word;
67typedef unsigned long dword;
68
69typedef struct _wave
70{
71 char w_fileid[4]; /* chunk id 'RIFF' */
72 uint32 w_chunksize; /* chunk size */
73 char w_waveid[4]; /* wave chunk id 'WAVE' */
74 char w_fmtid[4]; /* format chunk id 'fmt ' */
75 uint32 w_fmtchunksize; /* format chunk size */
76 uint16 w_fmttag; /* format tag (WAV_INT etc) */
77 uint16 w_nchannels; /* number of channels */
78 uint32 w_samplespersec; /* sample rate in hz */
79 uint32 w_navgbytespersec; /* average bytes per second */
80 uint16 w_nblockalign; /* number of bytes per frame */
81 uint16 w_nbitspersample; /* number of bits in a sample */
82 char w_datachunkid[4]; /* data chunk id 'data' */
83 uint32 w_datachunksize; /* length of data chunk */
84} t_wave;
85
86typedef struct _fmt /* format chunk */
87{
88 uint16 f_fmttag; /* format tag, 1 for PCM */
89 uint16 f_nchannels; /* number of channels */
90 uint32 f_samplespersec; /* sample rate in hz */
91 uint32 f_navgbytespersec; /* average bytes per second */
92 uint16 f_nblockalign; /* number of bytes per frame */
93 uint16 f_nbitspersample; /* number of bits in a sample */
94} t_fmt;
95
96typedef struct _wavechunk /* ... and the last two items */
97{
98 char wc_id[4]; /* data chunk id, e.g., 'data' or 'fmt ' */
99 uint32 wc_size; /* length of data chunk */
100} t_wavechunk;
101
102#define WAV_INT 1
103#define WAV_FLOAT 3
104
105/* the AIFF header. I'm assuming AIFC is compatible but don't really know
106 that. */
107
108typedef struct _datachunk
109{
110 char dc_id[4]; /* data chunk id 'SSND' */
111 uint32 dc_size; /* length of data chunk */
112} t_datachunk;
113
114typedef struct _comm
115{
116 uint16 c_nchannels; /* number of channels */
117 uint16 c_nframeshi; /* # of sample frames (hi) */
118 uint16 c_nframeslo; /* # of sample frames (lo) */
119 uint16 c_bitspersamp; /* bits per sample */
120 unsigned char c_samprate[10]; /* sample rate, 80-bit float! */
121} t_comm;
122
123 /* this version is more convenient for writing them out: */
124typedef struct _aiff
125{
126 char a_fileid[4]; /* chunk id 'FORM' */
127 uint32 a_chunksize; /* chunk size */
128 char a_aiffid[4]; /* aiff chunk id 'AIFF' */
129 char a_fmtid[4]; /* format chunk id 'COMM' */
130 uint32 a_fmtchunksize; /* format chunk size, 18 */
131 uint16 a_nchannels; /* number of channels */
132 uint16 a_nframeshi; /* # of sample frames (hi) */
133 uint16 a_nframeslo; /* # of sample frames (lo) */
134 uint16 a_bitspersamp; /* bits per sample */
135 unsigned char a_samprate[10]; /* sample rate, 80-bit float! */
136} t_aiff;
137
138#define AIFFHDRSIZE 38 /* probably not what sizeof() gives */
139
140
141#define AIFFPLUS (AIFFHDRSIZE + 8) /* header size including first chunk hdr */
142
143#define WHDR1 sizeof(t_nextstep)
144#define WHDR2 (sizeof(t_wave) > WHDR1 ? sizeof (t_wave) : WHDR1)
145#define WRITEHDRSIZE (AIFFPLUS > WHDR2 ? AIFFPLUS : WHDR2)
146
147#define READHDRSIZE (16 > WHDR2 + 2 ? 16 : WHDR2 + 2)
148
149#define OBUFSIZE MAXPDSTRING /* assume MAXPDSTRING is bigger than headers */
150
151#ifdef MSW
152#include <fcntl.h>
153#define BINCREATE _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY
154#else
155#define BINCREATE O_WRONLY | O_CREAT | O_TRUNC
156#endif
157
158/* this routine returns 1 if the high order byte comes at the lower
159address on our architecture (big-endianness.). It's 1 for Motorola,
1600 for Intel: */
161
162extern int garray_ambigendian(void);
163
164/* byte swappers */
165
166static uint32 swap4(uint32 n, int doit)
167{
168 if (doit)
169 return (((n & 0xff) << 24) | ((n & 0xff00) << 8) |
170 ((n & 0xff0000) >> 8) | ((n & 0xff000000) >> 24));
171 else return (n);
172}
173
174static uint16 swap2(uint32 n, int doit)
175{
176 if (doit)
177 return (((n & 0xff) << 8) | ((n & 0xff00) >> 8));
178 else return (n);
179}
180
181static void swapstring(char *foo, int doit)
182{
183 if (doit)
184 {
185 char a = foo[0], b = foo[1], c = foo[2], d = foo[3];
186 foo[0] = d; foo[1] = c; foo[2] = b; foo[3] = a;
187 }
188}
189
190/******************** soundfile access routines **********************/
191
192/* This routine opens a file, looks for either a nextstep or "wave" header,
193* seeks to end of it, and fills in bytes per sample and number of channels.
194* Only 2- and 3-byte fixed-point samples and 4-byte floating point samples
195* are supported. If "headersize" is nonzero, the
196* caller should supply the number of channels, endinanness, and bytes per
197* sample; the header is ignored. Otherwise, the routine tries to read the
198* header and fill in the properties.
199*/
200
201int open_soundfile(const char *dirname, const char *filename, int headersize,
202 int *p_bytespersamp, int *p_bigendian, int *p_nchannels, long *p_bytelimit,
203 long skipframes)
204{
205 char buf[OBUFSIZE], *bufptr;
206#ifdef ROCKBOX
207 int fd, nchannels, bigendian, bytespersamp, swap, sysrtn;
208#else
209 int fd, format, nchannels, bigendian, bytespersamp, swap, sysrtn;
210#endif
211 long bytelimit = 0x7fffffff;
212#ifndef ROCKBOX
213 errno = 0;
214#endif
215 fd = open_via_path(dirname, filename,
216 "", buf, &bufptr, MAXPDSTRING, 1);
217 if (fd < 0)
218 return (-1);
219 if (headersize >= 0) /* header detection overridden */
220 {
221 bigendian = *p_bigendian;
222 nchannels = *p_nchannels;
223 bytespersamp = *p_bytespersamp;
224 bytelimit = *p_bytelimit;
225 }
226 else
227 {
228 int bytesread = read(fd, buf, READHDRSIZE);
229 int format;
230 if (bytesread < 4)
231 goto badheader;
232 if (!strncmp(buf, ".snd", 4))
233 format = FORMAT_NEXT, bigendian = 1;
234 else if (!strncmp(buf, "dns.", 4))
235 format = FORMAT_NEXT, bigendian = 0;
236 else if (!strncmp(buf, "RIFF", 4))
237 {
238 if (bytesread < 12 || strncmp(buf + 8, "WAVE", 4))
239 goto badheader;
240 format = FORMAT_WAVE, bigendian = 0;
241 }
242 else if (!strncmp(buf, "FORM", 4))
243 {
244 if (bytesread < 12 || strncmp(buf + 8, "AIFF", 4))
245 goto badheader;
246 format = FORMAT_AIFF, bigendian = 1;
247 }
248 else
249 goto badheader;
250 swap = (bigendian != garray_ambigendian());
251 if (format == FORMAT_NEXT) /* nextstep header */
252 {
253#ifndef ROCKBOX
254 uint32 param;
255#endif
256 if (bytesread < (int)sizeof(t_nextstep))
257 goto badheader;
258 nchannels = swap4(((t_nextstep *)buf)->ns_nchans, swap);
259 format = swap4(((t_nextstep *)buf)->ns_format, swap);
260 headersize = swap4(((t_nextstep *)buf)->ns_onset, swap);
261 if (format == NS_FORMAT_LINEAR_16)
262 bytespersamp = 2;
263 else if (format == NS_FORMAT_LINEAR_24)
264 bytespersamp = 3;
265 else if (format == NS_FORMAT_FLOAT)
266 bytespersamp = 4;
267 else goto badheader;
268 bytelimit = 0x7fffffff;
269 }
270 else if (format == FORMAT_WAVE) /* wave header */
271 {
272 /* This is awful. You have to skip over chunks,
273 except that if one happens to be a "fmt" chunk, you want to
274 find out the format from that one. The case where the
275 "fmt" chunk comes after the audio isn't handled. */
276 headersize = 12;
277 if (bytesread < 20)
278 goto badheader;
279 /* First we guess a number of channels, etc., in case there's
280 no "fmt" chunk to follow. */
281 nchannels = 1;
282 bytespersamp = 2;
283 /* copy the first chunk header to beginnning of buffer. */
284 memmove(buf, buf + headersize, sizeof(t_wavechunk));
285 /* post("chunk %c %c %c %c",
286 ((t_wavechunk *)buf)->wc_id[0],
287 ((t_wavechunk *)buf)->wc_id[1],
288 ((t_wavechunk *)buf)->wc_id[2],
289 ((t_wavechunk *)buf)->wc_id[3]); */
290 /* read chunks in loop until we get to the data chunk */
291 while (strncmp(((t_wavechunk *)buf)->wc_id, "data", 4))
292 {
293 long chunksize = swap4(((t_wavechunk *)buf)->wc_size,
294 swap), seekto = headersize + chunksize + 8, seekout;
295
296 if (!strncmp(((t_wavechunk *)buf)->wc_id, "fmt ", 4))
297 {
298 long commblockonset = headersize + 8;
299 seekout = lseek(fd, commblockonset, SEEK_SET);
300 if (seekout != commblockonset)
301 goto badheader;
302 if (read(fd, buf, sizeof(t_fmt)) < (int) sizeof(t_fmt))
303 goto badheader;
304 nchannels = swap2(((t_fmt *)buf)->f_nchannels, swap);
305 format = swap2(((t_fmt *)buf)->f_nbitspersample, swap);
306 if (format == 16)
307 bytespersamp = 2;
308 else if (format == 24)
309 bytespersamp = 3;
310 else if (format == 32)
311 bytespersamp = 4;
312 else goto badheader;
313 }
314 seekout = lseek(fd, seekto, SEEK_SET);
315 if (seekout != seekto)
316 goto badheader;
317 if (read(fd, buf, sizeof(t_wavechunk)) <
318 (int) sizeof(t_wavechunk))
319 goto badheader;
320 /* post("new chunk %c %c %c %c at %d",
321 ((t_wavechunk *)buf)->wc_id[0],
322 ((t_wavechunk *)buf)->wc_id[1],
323 ((t_wavechunk *)buf)->wc_id[2],
324 ((t_wavechunk *)buf)->wc_id[3], seekto); */
325 headersize = seekto;
326 }
327 bytelimit = swap4(((t_wavechunk *)buf)->wc_size, swap);
328 headersize += 8;
329 }
330 else
331 {
332 /* AIFF. same as WAVE; actually predates it. Disgusting. */
333 headersize = 12;
334 if (bytesread < 20)
335 goto badheader;
336 /* First we guess a number of channels, etc., in case there's
337 no COMM block to follow. */
338 nchannels = 1;
339 bytespersamp = 2;
340 /* copy the first chunk header to beginnning of buffer. */
341 memmove(buf, buf + headersize, sizeof(t_datachunk));
342 /* read chunks in loop until we get to the data chunk */
343 while (strncmp(((t_datachunk *)buf)->dc_id, "SSND", 4))
344 {
345 long chunksize = swap4(((t_datachunk *)buf)->dc_size,
346 swap), seekto = headersize + chunksize + 8, seekout;
347 /* post("chunk %c %c %c %c seek %d",
348 ((t_datachunk *)buf)->dc_id[0],
349 ((t_datachunk *)buf)->dc_id[1],
350 ((t_datachunk *)buf)->dc_id[2],
351 ((t_datachunk *)buf)->dc_id[3], seekto); */
352 if (!strncmp(((t_datachunk *)buf)->dc_id, "COMM", 4))
353 {
354 long commblockonset = headersize + 8;
355 seekout = lseek(fd, commblockonset, SEEK_SET);
356 if (seekout != commblockonset)
357 goto badheader;
358 if (read(fd, buf, sizeof(t_comm)) <
359 (int) sizeof(t_comm))
360 goto badheader;
361 nchannels = swap2(((t_comm *)buf)->c_nchannels, swap);
362 format = swap2(((t_comm *)buf)->c_bitspersamp, swap);
363 if (format == 16)
364 bytespersamp = 2;
365 else if (format == 24)
366 bytespersamp = 3;
367 else goto badheader;
368 }
369 seekout = lseek(fd, seekto, SEEK_SET);
370 if (seekout != seekto)
371 goto badheader;
372 if (read(fd, buf, sizeof(t_datachunk)) <
373 (int) sizeof(t_datachunk))
374 goto badheader;
375 headersize = seekto;
376 }
377 bytelimit = swap4(((t_datachunk *)buf)->dc_size, swap);
378 headersize += 8;
379 }
380 }
381 /* seek past header and any sample frames to skip */
382 sysrtn = lseek(fd, nchannels * bytespersamp * skipframes + headersize, 0);
383 if (sysrtn != nchannels * bytespersamp * skipframes + headersize)
384 return (-1);
385 bytelimit -= nchannels * bytespersamp * skipframes;
386 if (bytelimit < 0)
387 bytelimit = 0;
388 /* copy sample format back to caller */
389 *p_bigendian = bigendian;
390 *p_nchannels = nchannels;
391 *p_bytespersamp = bytespersamp;
392 *p_bytelimit = bytelimit;
393 return (fd);
394badheader:
395 /* the header wasn't recognized. We're threadable here so let's not
396 print out the error... */
397#ifndef ROCKBOX
398 errno = EIO;
399#endif
400 return (-1);
401}
402
403static void soundfile_xferin(int sfchannels, int nvecs, t_sample **vecs,
404 long itemsread, unsigned char *buf, int nitems, int bytespersamp,
405 int bigendian)
406{
407 int i, j;
408 unsigned char *sp, *sp2;
409 t_sample *fp;
410 int nchannels = (sfchannels < nvecs ? sfchannels : nvecs);
411 int bytesperframe = bytespersamp * sfchannels;
412 for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp)
413 {
414 if (bytespersamp == 2)
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 }
422 else
423 {
424 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
425 j < nitems; j++, sp2 += bytesperframe, fp++)
426#ifdef ROCKBOX_BIG_ENDIAN
427 {
428 short xx = (sp2[1] << 8) | sp2[0];
429 *fp = xx << (fix1-16);
430 }
431#else
432 *fp = ((short*)sp2)[0]<<(fix1-16);
433#endif
434 }
435 }
436 else if (bytespersamp == 3)
437 {
438 if (bigendian)
439 {
440 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
441 j < nitems; j++, sp2 += bytesperframe, fp++)
442 *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16)
443 | (sp2[2] << 8));
444 }
445 else
446 {
447 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
448 j < nitems; j++, sp2 += bytesperframe, fp++)
449 *fp = SCALE * ((sp2[2] << 24) | (sp2[1] << 16)
450 | (sp2[0] << 8));
451 }
452 }
453 else if (bytespersamp == 4)
454 {
455 if (bigendian)
456 {
457 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
458 j < nitems; j++, sp2 += bytesperframe, fp++)
459 *(long *)fp = ((sp2[0] << 24) | (sp2[1] << 16)
460 | (sp2[2] << 8) | sp2[3]);
461 }
462 else
463 {
464 for (j = 0, sp2 = sp, fp=vecs[i] + itemsread;
465 j < nitems; j++, sp2 += bytesperframe, fp++)
466 *(long *)fp = ((sp2[3] << 24) | (sp2[2] << 16)
467 | (sp2[1] << 8) | sp2[0]);
468 }
469 }
470 }
471 /* zero out other outputs */
472 for (i = sfchannels; i < nvecs; i++)
473 for (j = nitems, fp = vecs[i]; j--; )
474 *fp++ = 0;
475
476}
477
478 /* soundfiler_write ...
479
480 usage: write [flags] filename table ...
481 flags:
482 -nframes <frames>
483 -skip <frames>
484 -bytes <bytes per sample>
485 -normalize
486 -nextstep
487 -wave
488 -big
489 -little
490 */
491
492 /* the routine which actually does the work should LATER also be called
493 from garray_write16. */
494
495
496 /* Parse arguments for writing. The "obj" argument is only for flagging
497 errors. For streaming to a file the "normalize", "onset" and "nframes"
498 arguments shouldn't be set but the calling routine flags this. */
499
500static int soundfiler_writeargparse(void *obj, int *p_argc, t_atom **p_argv,
501 t_symbol **p_filesym,
502 int *p_filetype, int *p_bytespersamp, int *p_swap, int *p_bigendian,
503 int *p_normalize, long *p_onset, long *p_nframes, float *p_rate)
504{
505 int argc = *p_argc;
506 t_atom *argv = *p_argv;
507 int bytespersamp = 2, bigendian = 0,
508 endianness = -1, swap, filetype = -1, normalize = 0;
509 long onset = 0, nframes = 0x7fffffff;
510 t_symbol *filesym;
511 float rate = -1;
512
513 while (argc > 0 && argv->a_type == A_SYMBOL &&
514 *argv->a_w.w_symbol->s_name == '-')
515 {
516 char *flag = argv->a_w.w_symbol->s_name + 1;
517 if (!strcmp(flag, "skip"))
518 {
519 if (argc < 2 || argv[1].a_type != A_FLOAT ||
520 ((onset = argv[1].a_w.w_float) < 0))
521 goto usage;
522 argc -= 2; argv += 2;
523 }
524 else if (!strcmp(flag, "nframes"))
525 {
526 if (argc < 2 || argv[1].a_type != A_FLOAT ||
527 ((nframes = argv[1].a_w.w_float) < 0))
528 goto usage;
529 argc -= 2; argv += 2;
530 }
531 else if (!strcmp(flag, "bytes"))
532 {
533 if (argc < 2 || argv[1].a_type != A_FLOAT ||
534 ((bytespersamp = argv[1].a_w.w_float) < 2) ||
535 bytespersamp > 4)
536 goto usage;
537 argc -= 2; argv += 2;
538 }
539 else if (!strcmp(flag, "normalize"))
540 {
541 normalize = 1;
542 argc -= 1; argv += 1;
543 }
544 else if (!strcmp(flag, "wave"))
545 {
546 filetype = FORMAT_WAVE;
547 argc -= 1; argv += 1;
548 }
549 else if (!strcmp(flag, "nextstep"))
550 {
551 filetype = FORMAT_NEXT;
552 argc -= 1; argv += 1;
553 }
554 else if (!strcmp(flag, "aiff"))
555 {
556 filetype = FORMAT_AIFF;
557 argc -= 1; argv += 1;
558 }
559 else if (!strcmp(flag, "big"))
560 {
561 endianness = 1;
562 argc -= 1; argv += 1;
563 }
564 else if (!strcmp(flag, "little"))
565 {
566 endianness = 0;
567 argc -= 1; argv += 1;
568 }
569 else if (!strcmp(flag, "r") || !strcmp(flag, "rate"))
570 {
571 if (argc < 2 || argv[1].a_type != A_FLOAT ||
572 ((rate = argv[1].a_w.w_float) <= 0))
573 goto usage;
574 argc -= 2; argv += 2;
575 }
576 else goto usage;
577 }
578 if (!argc || argv->a_type != A_SYMBOL)
579 goto usage;
580 filesym = argv->a_w.w_symbol;
581
582 /* check if format not specified and fill in */
583 if (filetype < 0)
584 {
585 if (strlen(filesym->s_name) >= 5 &&
586 (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".aif") ||
587 !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".AIF")))
588 filetype = FORMAT_AIFF;
589 if (strlen(filesym->s_name) >= 6 &&
590 (!strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".aiff") ||
591 !strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".AIFF")))
592 filetype = FORMAT_AIFF;
593 if (strlen(filesym->s_name) >= 5 &&
594 (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".snd") ||
595 !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".SND")))
596 filetype = FORMAT_NEXT;
597 if (strlen(filesym->s_name) >= 4 &&
598 (!strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".au") ||
599 !strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".AU")))
600 filetype = FORMAT_NEXT;
601 if (filetype < 0)
602 filetype = FORMAT_WAVE;
603 }
604 /* don't handle AIFF floating point samples */
605 if (bytespersamp == 4)
606 {
607 if (filetype == FORMAT_AIFF)
608 {
609 pd_error(obj, "AIFF floating-point file format unavailable");
610 goto usage;
611 }
612 }
613 /* for WAVE force little endian; for nextstep use machine native */
614 if (filetype == FORMAT_WAVE)
615 {
616 bigendian = 0;
617 if (endianness == 1)
618 pd_error(obj, "WAVE file forced to little endian");
619 }
620 else if (filetype == FORMAT_AIFF)
621 {
622 bigendian = 1;
623 if (endianness == 0)
624 pd_error(obj, "AIFF file forced to big endian");
625 }
626 else if (endianness == -1)
627 {
628 bigendian = garray_ambigendian();
629 }
630 else bigendian = endianness;
631 swap = (bigendian != garray_ambigendian());
632
633 argc--; argv++;
634
635 *p_argc = argc;
636 *p_argv = argv;
637 *p_filesym = filesym;
638 *p_filetype = filetype;
639 *p_bytespersamp = bytespersamp;
640 *p_swap = swap;
641 *p_normalize = normalize;
642 *p_onset = onset;
643 *p_nframes = nframes;
644 *p_bigendian = bigendian;
645 *p_rate = rate;
646 return (0);
647usage:
648 return (-1);
649}
650
651static int create_soundfile(t_canvas *canvas, const char *filename,
652 int filetype, int nframes, int bytespersamp,
653 int bigendian, int nchannels, int swap, float samplerate)
654{
655 char filenamebuf[MAXPDSTRING], buf2[MAXPDSTRING];
656 char headerbuf[WRITEHDRSIZE];
657 t_wave *wavehdr = (t_wave *)headerbuf;
658 t_nextstep *nexthdr = (t_nextstep *)headerbuf;
659 t_aiff *aiffhdr = (t_aiff *)headerbuf;
660 int fd, headersize = 0;
661
662 strncpy(filenamebuf, filename, MAXPDSTRING-10);
663 filenamebuf[MAXPDSTRING-10] = 0;
664
665 if (filetype == FORMAT_NEXT)
666 {
667 if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".snd"))
668 strcat(filenamebuf, ".snd");
669 if (bigendian)
670 strncpy(nexthdr->ns_fileid, ".snd", 4);
671 else strncpy(nexthdr->ns_fileid, "dns.", 4);
672 nexthdr->ns_onset = swap4(sizeof(*nexthdr), swap);
673 nexthdr->ns_length = 0;
674 nexthdr->ns_format = swap4((bytespersamp == 3 ? NS_FORMAT_LINEAR_24 :
675 (bytespersamp == 4 ? NS_FORMAT_FLOAT : NS_FORMAT_LINEAR_16)), swap);
676 nexthdr->ns_sr = swap4(samplerate, swap);
677 nexthdr->ns_nchans = swap4(nchannels, swap);
678 strcpy(nexthdr->ns_info, "Pd ");
679 swapstring(nexthdr->ns_info, swap);
680 headersize = sizeof(t_nextstep);
681 }
682 else if (filetype == FORMAT_AIFF)
683 {
684 long datasize = nframes * nchannels * bytespersamp;
685 long longtmp;
686 t_datachunk *aiffdc = (t_datachunk *)(headerbuf + sizeof(t_aiff));
687 static unsigned char AIFF_splrate[] = {0x40, 0x0e, 0xac, 0x44, 0, 0, 0, 0, 0, 0};
688 static unsigned char datachunk_ID[] = {'S', 'S', 'N', 'D'};
689 if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".aif") &&
690 strcmp(filenamebuf + strlen(filenamebuf)-5, ".aiff"))
691 strcat(filenamebuf, ".aif");
692 strncpy(aiffhdr->a_fileid, "FORM", 4);
693 aiffhdr->a_chunksize = swap4(datasize + sizeof(*aiffhdr) + 4, swap);
694 strncpy(aiffhdr->a_aiffid, "AIFF", 4);
695 strncpy(aiffhdr->a_fmtid, "COMM", 4);
696 aiffhdr->a_fmtchunksize = swap4(18, swap);
697 aiffhdr->a_nchannels = swap2(nchannels, swap);
698 longtmp = swap4(nframes, swap);
699 memcpy(&aiffhdr->a_nframeshi, &longtmp, 4);
700 aiffhdr->a_bitspersamp = swap2(8 * bytespersamp, swap);
701 memcpy(aiffhdr->a_samprate, AIFF_splrate, sizeof(AIFF_splrate));
702 memcpy(aiffdc->dc_id, datachunk_ID, sizeof(datachunk_ID));
703 longtmp = swap4(datasize, swap);
704#if __GNUC__ == 9 || __GNUC__ == 10 // False positive with GCC9 && GCC10
705#pragma GCC diagnostic push
706#pragma GCC diagnostic ignored "-Wstringop-overflow"
707#pragma GCC diagnostic ignored "-Warray-bounds"
708#endif
709 memcpy(&aiffdc->dc_size, &longtmp, 4);
710#if __GNUC__ == 9
711#pragma GCC diagnostic pop
712#endif
713 headersize = AIFFPLUS;
714 }
715 else /* WAVE format */
716 {
717 long datasize = nframes * nchannels * bytespersamp;
718 if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".wav"))
719 strcat(filenamebuf, ".wav");
720 strncpy(wavehdr->w_fileid, "RIFF", 4);
721 wavehdr->w_chunksize = swap4(datasize + sizeof(*wavehdr) - 8, swap);
722 strncpy(wavehdr->w_waveid, "WAVE", 4);
723 strncpy(wavehdr->w_fmtid, "fmt ", 4);
724 wavehdr->w_fmtchunksize = swap4(16, swap);
725 wavehdr->w_fmttag =
726 swap2((bytespersamp == 4 ? WAV_FLOAT : WAV_INT), swap);
727 wavehdr->w_nchannels = swap2(nchannels, swap);
728 wavehdr->w_samplespersec = swap4(samplerate, swap);
729 wavehdr->w_navgbytespersec =
730 swap4((int)(samplerate * nchannels * bytespersamp), swap);
731 wavehdr->w_nblockalign = swap2(nchannels * bytespersamp, swap);
732 wavehdr->w_nbitspersample = swap2(8 * bytespersamp, swap);
733 strncpy(wavehdr->w_datachunkid, "data", 4);
734 wavehdr->w_datachunksize = swap4(datasize, swap);
735 headersize = sizeof(t_wave);
736 }
737
738 canvas_makefilename(canvas, filenamebuf, buf2, MAXPDSTRING);
739 sys_bashfilename(buf2, buf2);
740 if ((fd = open(buf2, BINCREATE, 0666)) < 0)
741 return (-1);
742
743 if (write(fd, headerbuf, headersize) < headersize)
744 {
745 close (fd);
746 return (-1);
747 }
748 return (fd);
749}
750
751static void soundfile_finishwrite(void *obj, char *filename, int fd,
752 int filetype, long nframes, long itemswritten, int bytesperframe, int swap)
753{
754 if (itemswritten < nframes)
755 {
756 if (nframes < 0x7fffffff)
757 pd_error(obj, "soundfiler_write: %d out of %d bytes written",
758 itemswritten, nframes);
759 /* try to fix size fields in header */
760 if (filetype == FORMAT_WAVE)
761 {
762 long datasize = itemswritten * bytesperframe, mofo;
763
764 if (lseek(fd,
765 ((char *)(&((t_wave *)0)->w_chunksize)) - (char *)0,
766 SEEK_SET) == 0)
767 goto baddonewrite;
768 mofo = swap4(datasize + sizeof(t_wave) - 8, swap);
769 if (write(fd, (char *)(&mofo), 4) < 4)
770 goto baddonewrite;
771 if (lseek(fd,
772 ((char *)(&((t_wave *)0)->w_datachunksize)) - (char *)0,
773 SEEK_SET) == 0)
774 goto baddonewrite;
775 mofo = swap4(datasize, swap);
776 if (write(fd, (char *)(&mofo), 4) < 4)
777 goto baddonewrite;
778 }
779 if (filetype == FORMAT_AIFF)
780 {
781 long mofo;
782 if (lseek(fd,
783 ((char *)(&((t_aiff *)0)->a_nframeshi)) - (char *)0,
784 SEEK_SET) == 0)
785 goto baddonewrite;
786 mofo = swap4(nframes, swap);
787 if (write(fd, (char *)(&mofo), 4) < 4)
788 goto baddonewrite;
789 }
790 if (filetype == FORMAT_NEXT)
791 {
792 /* do it the lazy way: just set the size field to 'unknown size'*/
793 uint32 nextsize = 0xffffffff;
794 if (lseek(fd, 8, SEEK_SET) == 0)
795 {
796 goto baddonewrite;
797 }
798 if (write(fd, &nextsize, 4) < 4)
799 {
800 goto baddonewrite;
801 }
802 }
803 }
804 return;
805baddonewrite:
806#ifdef ROCKBOX
807 post("%s: error", filename);
808#else
809 post("%s: %s", filename, strerror(errno));
810#endif
811}
812
813static void soundfile_xferout(int nchannels, t_sample **vecs,
814 unsigned char *buf, int nitems, long onset, int bytespersamp,
815 int bigendian, float normalfactor)
816{
817 int i, j;
818 unsigned char *sp, *sp2;
819 t_sample *fp;
820 int bytesperframe = bytespersamp * nchannels;
821 long xx;
822 for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp)
823 {
824 if (bytespersamp == 2)
825 {
826 float ff = normalfactor * 32768.;
827 if (bigendian)
828 {
829 for (j = 0, sp2 = sp, fp = vecs[i] + onset;
830 j < nitems; j++, sp2 += bytesperframe, fp++)
831 {
832 int xx = 32768. + (*fp * ff);
833 xx -= 32768;
834 if (xx < -32767)
835 xx = -32767;
836 if (xx > 32767)
837 xx = 32767;
838 sp2[0] = (xx >> 8);
839 sp2[1] = xx;
840 }
841 }
842 else
843 {
844 for (j = 0, sp2 = sp, fp=vecs[i] + onset;
845 j < nitems; j++, sp2 += bytesperframe, fp++)
846 {
847 int xx = 32768. + (*fp * ff);
848 xx -= 32768;
849 if (xx < -32767)
850 xx = -32767;
851 if (xx > 32767)
852 xx = 32767;
853 sp2[1] = (xx >> 8);
854 sp2[0] = xx;
855 }
856 }
857 }
858 else if (bytespersamp == 3)
859 {
860 float ff = normalfactor * 8388608.;
861 if (bigendian)
862 {
863 for (j = 0, sp2 = sp, fp=vecs[i] + onset;
864 j < nitems; j++, sp2 += bytesperframe, fp++)
865 {
866 int xx = 8388608. + (*fp * ff);
867 xx -= 8388608;
868 if (xx < -8388607)
869 xx = -8388607;
870 if (xx > 8388607)
871 xx = 8388607;
872 sp2[0] = (xx >> 16);
873 sp2[1] = (xx >> 8);
874 sp2[2] = xx;
875 }
876 }
877 else
878 {
879 for (j = 0, sp2 = sp, fp=vecs[i] + onset;
880 j < nitems; j++, sp2 += bytesperframe, fp++)
881 {
882 int xx = 8388608. + (*fp * ff);
883 xx -= 8388608;
884 if (xx < -8388607)
885 xx = -8388607;
886 if (xx > 8388607)
887 xx = 8388607;
888 sp2[2] = (xx >> 16);
889 sp2[1] = (xx >> 8);
890 sp2[0] = xx;
891 }
892 }
893 }
894 else if (bytespersamp == 4)
895 {
896 if (bigendian)
897 {
898 for (j = 0, sp2 = sp, fp=vecs[i] + onset;
899 j < nitems; j++, sp2 += bytesperframe, fp++)
900 {
901#ifdef ROCKBOX
902 union f2i f2i;
903 f2i.f = *fp * normalfactor;
904 xx = f2i.i;
905#else /* ROCKBOX */
906 float f2 = *fp * normalfactor;
907 xx = *(long *)&f2;
908#endif /* ROCKBOX */
909 sp2[0] = (xx >> 24); sp2[1] = (xx >> 16);
910 sp2[2] = (xx >> 8); sp2[3] = xx;
911 }
912 }
913 else
914 {
915 for (j = 0, sp2 = sp, fp=vecs[i] + onset;
916 j < nitems; j++, sp2 += bytesperframe, fp++)
917 {
918#ifdef ROCKBOX
919 union f2i f2i;
920 f2i.f = *fp * normalfactor;
921 xx = f2i.i;
922#else /* ROCKBOX */
923 float f2 = *fp * normalfactor;
924 xx = *(long *)&f2;
925#endif /* ROCKBOX */
926 sp2[3] = (xx >> 24); sp2[2] = (xx >> 16);
927 sp2[1] = (xx >> 8); sp2[0] = xx;
928 }
929 }
930 }
931 }
932}
933
934
935/* ------- soundfiler - reads and writes soundfiles to/from "garrays" ---- */
936#define DEFMAXSIZE 4000000 /* default maximum 16 MB per channel */
937#define SAMPBUFSIZE 1024
938
939
940static t_class *soundfiler_class;
941
942typedef struct _soundfiler
943{
944 t_object x_obj;
945 t_canvas *x_canvas;
946} t_soundfiler;
947
948static t_soundfiler *soundfiler_new(void)
949{
950 t_soundfiler *x = (t_soundfiler *)pd_new(soundfiler_class);
951 x->x_canvas = canvas_getcurrent();
952 outlet_new(&x->x_obj, &s_float);
953 return (x);
954}
955
956 /* soundfiler_read ...
957
958 usage: read [flags] filename table ...
959 flags:
960 -skip <frames> ... frames to skip in file
961 -nframes <frames>
962 -onset <frames> ... onset in table to read into (NOT DONE YET)
963 -raw <headersize channels bytes endian>
964 -resize
965 -maxsize <max-size>
966 */
967
968static void soundfiler_read(t_soundfiler *x, t_symbol *s,
969 int argc, t_atom *argv)
970{
971#ifdef ROCKBOX
972 (void) s;
973#endif
974 int headersize = -1, channels = 0, bytespersamp = 0, bigendian = 0,
975 resize = 0, i, j;
976#ifdef ROCKBOX
977 long skipframes = 0, nframes = 0, finalsize = 0,
978#else
979 long skipframes = 0, nframes = 0, finalsize = 0, itemsleft,
980#endif
981 maxsize = DEFMAXSIZE, itemsread = 0, bytelimit = 0x7fffffff;
982 int fd = -1;
983 char endianness, *filename;
984 t_garray *garrays[MAXSFCHANS];
985 t_sample *vecs[MAXSFCHANS];
986 char sampbuf[SAMPBUFSIZE];
987 int bufframes, nitems;
988#ifdef ROCKBOX
989 int fp;
990#else
991 FILE *fp;
992#endif
993 while (argc > 0 && argv->a_type == A_SYMBOL &&
994 *argv->a_w.w_symbol->s_name == '-')
995 {
996 char *flag = argv->a_w.w_symbol->s_name + 1;
997 if (!strcmp(flag, "skip"))
998 {
999 if (argc < 2 || argv[1].a_type != A_FLOAT ||
1000 ((skipframes = argv[1].a_w.w_float) < 0))
1001 goto usage;
1002 argc -= 2; argv += 2;
1003 }
1004 else if (!strcmp(flag, "nframes"))
1005 {
1006 if (argc < 2 || argv[1].a_type != A_FLOAT ||
1007 ((nframes = argv[1].a_w.w_float) < 0))
1008 goto usage;
1009 argc -= 2; argv += 2;
1010 }
1011 else if (!strcmp(flag, "raw"))
1012 {
1013 if (argc < 5 ||
1014 argv[1].a_type != A_FLOAT ||
1015 ((headersize = argv[1].a_w.w_float) < 0) ||
1016 argv[2].a_type != A_FLOAT ||
1017 ((channels = argv[2].a_w.w_float) < 1) ||
1018 (channels > MAXSFCHANS) ||
1019 argv[3].a_type != A_FLOAT ||
1020 ((bytespersamp = argv[3].a_w.w_float) < 2) ||
1021 (bytespersamp > 4) ||
1022 argv[4].a_type != A_SYMBOL ||
1023 ((endianness = argv[4].a_w.w_symbol->s_name[0]) != 'b'
1024 && endianness != 'l' && endianness != 'n'))
1025 goto usage;
1026 if (endianness == 'b')
1027 bigendian = 1;
1028 else if (endianness == 'l')
1029 bigendian = 0;
1030 else
1031 bigendian = garray_ambigendian();
1032 argc -= 5; argv += 5;
1033 }
1034 else if (!strcmp(flag, "resize"))
1035 {
1036 resize = 1;
1037 argc -= 1; argv += 1;
1038 }
1039 else if (!strcmp(flag, "maxsize"))
1040 {
1041 if (argc < 2 || argv[1].a_type != A_FLOAT ||
1042 ((maxsize = argv[1].a_w.w_float) < 0))
1043 goto usage;
1044 resize = 1; /* maxsize implies resize. */
1045 argc -= 2; argv += 2;
1046 }
1047 else goto usage;
1048 }
1049 if (argc < 2 || argc > MAXSFCHANS + 1 || argv[0].a_type != A_SYMBOL)
1050 goto usage;
1051 filename = argv[0].a_w.w_symbol->s_name;
1052 argc--; argv++;
1053
1054 for (i = 0; i < argc; i++)
1055 {
1056 int vecsize;
1057 if (argv[i].a_type != A_SYMBOL)
1058 goto usage;
1059 if (!(garrays[i] =
1060 (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class)))
1061 {
1062 pd_error(x, "%s: no such table", argv[i].a_w.w_symbol->s_name);
1063 goto done;
1064 }
1065 else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i]))
1066 error("%s: bad template for tabwrite",
1067 argv[i].a_w.w_symbol->s_name);
1068 if (finalsize && finalsize != vecsize && !resize)
1069 {
1070 post("soundfiler_read: arrays have different lengths; resizing...");
1071 resize = 1;
1072 }
1073 finalsize = vecsize;
1074 }
1075 fd = open_soundfile(canvas_getdir(x->x_canvas)->s_name, filename,
1076 headersize, &bytespersamp, &bigendian, &channels, &bytelimit,
1077 skipframes);
1078
1079 if (fd < 0)
1080 {
1081#ifdef ROCKBOX
1082 pd_error(x, "soundfiler_read: %s: %s",
1083 filename,
1084 "unknown or bad header format");
1085#else
1086 pd_error(x, "soundfiler_read: %s: %s", filename, (errno == EIO ?
1087 "unknown or bad header format" : strerror(errno)));
1088#endif
1089 goto done;
1090 }
1091
1092 if (resize)
1093 {
1094 /* figure out what to resize to */
1095 long poswas, eofis, framesinfile;
1096
1097 poswas = lseek(fd, 0, SEEK_CUR);
1098 eofis = lseek(fd, 0, SEEK_END);
1099 if (poswas < 0 || eofis < 0)
1100 {
1101 pd_error(x, "lseek failed");
1102 goto done;
1103 }
1104 lseek(fd, poswas, SEEK_SET);
1105 framesinfile = (eofis - poswas) / (channels * bytespersamp);
1106 if (framesinfile > maxsize)
1107 {
1108 pd_error(x, "soundfiler_read: truncated to %d elements", maxsize);
1109 framesinfile = maxsize;
1110 }
1111 if (framesinfile > bytelimit / (channels * bytespersamp))
1112 framesinfile = bytelimit / (channels * bytespersamp);
1113 finalsize = framesinfile;
1114 for (i = 0; i < argc; i++)
1115 {
1116 int vecsize;
1117
1118 garray_resize(garrays[i], finalsize);
1119 /* for sanity's sake let's clear the save-in-patch flag here */
1120 garray_setsaveit(garrays[i], 0);
1121 garray_getfloatarray(garrays[i], &vecsize, &vecs[i]);
1122 /* if the resize failed, garray_resize reported the error */
1123 if (vecsize != framesinfile)
1124 {
1125 pd_error(x, "resize failed");
1126 goto done;
1127 }
1128 }
1129 }
1130 if (!finalsize) finalsize = 0x7fffffff;
1131 if (finalsize > bytelimit / (channels * bytespersamp))
1132 finalsize = bytelimit / (channels * bytespersamp);
1133#ifdef ROCKBOX
1134 fp = fd;
1135#else
1136 fp = fdopen(fd, "rb");
1137#endif
1138 bufframes = SAMPBUFSIZE / (channels * bytespersamp);
1139
1140 for (itemsread = 0; itemsread < finalsize; )
1141 {
1142 int thisread = finalsize - itemsread;
1143 thisread = (thisread > bufframes ? bufframes : thisread);
1144#ifdef ROCKBOX
1145 nitems = read(fp, sampbuf, thisread * bytespersamp * channels) / (bytespersamp * channels);
1146#else
1147 nitems = fread(sampbuf, channels * bytespersamp, thisread, fp);
1148#endif
1149 if (nitems <= 0) break;
1150 soundfile_xferin(channels, argc, vecs, itemsread,
1151 (unsigned char *)sampbuf, nitems, bytespersamp, bigendian);
1152 itemsread += nitems;
1153 }
1154 /* zero out remaining elements of vectors */
1155
1156 for (i = 0; i < argc; i++)
1157 {
1158#ifdef ROCKBOX
1159 int vecsize;
1160#else
1161 int nzero, vecsize;
1162#endif
1163 garray_getfloatarray(garrays[i], &vecsize, &vecs[i]);
1164 for (j = itemsread; j < vecsize; j++)
1165 vecs[i][j] = 0;
1166 }
1167 /* zero out vectors in excess of number of channels */
1168 for (i = channels; i < argc; i++)
1169 {
1170 int vecsize;
1171 t_sample *foo;
1172 garray_getfloatarray(garrays[i], &vecsize, &foo);
1173 for (j = 0; j < vecsize; j++)
1174 foo[j] = 0;
1175 }
1176 /* do all graphics updates */
1177 for (i = 0; i < argc; i++)
1178 garray_redraw(garrays[i]);
1179#ifdef ROCKBOX
1180 close(fp);
1181#else
1182 fclose(fp);
1183#endif
1184 fd = -1;
1185 goto done;
1186usage:
1187 pd_error(x, "usage: read [flags] filename tablename...");
1188 post("flags: -skip <n> -nframes <n> -resize -maxsize <n> ...");
1189 post("-raw <headerbytes> <channels> <bytespersamp> <endian (b, l, or n)>.");
1190done:
1191 if (fd >= 0)
1192 close (fd);
1193 outlet_float(x->x_obj.ob_outlet, (float)itemsread);
1194}
1195
1196 /* this is broken out from soundfiler_write below so garray_write can
1197 call it too... not done yet though. */
1198
1199long soundfiler_dowrite(void *obj, t_canvas *canvas,
1200 int argc, t_atom *argv)
1201{
1202#ifdef ROCKBOX
1203 int bytespersamp, bigendian,
1204 swap, filetype, normalize, i, j, nchannels;
1205 long onset, nframes,
1206 itemswritten = 0;
1207#else
1208 int headersize, bytespersamp, bigendian,
1209 endianness, swap, filetype, normalize, i, j, nchannels;
1210 long onset, nframes, itemsleft,
1211 maxsize = DEFMAXSIZE, itemswritten = 0;
1212#endif
1213 t_garray *garrays[MAXSFCHANS];
1214 t_sample *vecs[MAXSFCHANS];
1215 char sampbuf[SAMPBUFSIZE];
1216#ifdef ROCKBOX
1217 int bufframes;
1218#else
1219 int bufframes, nitems;
1220#endif
1221 int fd = -1;
1222 float normfactor, biggest = 0, samplerate;
1223 t_symbol *filesym;
1224
1225 if (soundfiler_writeargparse(obj, &argc, &argv, &filesym, &filetype,
1226 &bytespersamp, &swap, &bigendian, &normalize, &onset, &nframes,
1227 &samplerate))
1228 goto usage;
1229 nchannels = argc;
1230 if (nchannels < 1 || nchannels > MAXSFCHANS)
1231 goto usage;
1232 if (samplerate < 0)
1233 samplerate = sys_getsr();
1234 for (i = 0; i < nchannels; i++)
1235 {
1236 int vecsize;
1237 if (argv[i].a_type != A_SYMBOL)
1238 goto usage;
1239 if (!(garrays[i] =
1240 (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class)))
1241 {
1242 pd_error(obj, "%s: no such table", argv[i].a_w.w_symbol->s_name);
1243 goto fail;
1244 }
1245 else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i]))
1246 error("%s: bad template for tabwrite",
1247 argv[i].a_w.w_symbol->s_name);
1248 if (nframes > vecsize - onset)
1249 nframes = vecsize - onset;
1250
1251 for (j = 0; j < vecsize; j++)
1252 {
1253 if (vecs[i][j] > biggest)
1254 biggest = vecs[i][j];
1255 else if (-vecs[i][j] > biggest)
1256 biggest = -vecs[i][j];
1257 }
1258 }
1259 if (nframes <= 0)
1260 {
1261 pd_error(obj, "soundfiler_write: no samples at onset %ld", onset);
1262 goto fail;
1263 }
1264
1265 if ((fd = create_soundfile(canvas, filesym->s_name, filetype,
1266 nframes, bytespersamp, bigendian, nchannels,
1267 swap, samplerate)) < 0)
1268 {
1269#ifdef ROCKBOX
1270 post("%s: %s\n", filesym->s_name, "error");
1271#else
1272 post("%s: %s\n", filesym->s_name, strerror(errno));
1273#endif
1274 goto fail;
1275 }
1276 if (!normalize)
1277 {
1278 if ((bytespersamp != 4) && (biggest > 1))
1279 {
1280 post("%s: normalizing max amplitude %f to 1", filesym->s_name, biggest);
1281 normalize = 1;
1282 }
1283 else post("%s: biggest amplitude = %f", filesym->s_name, biggest);
1284 }
1285 if (normalize)
1286 normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1);
1287 else normfactor = 1;
1288
1289 bufframes = SAMPBUFSIZE / (nchannels * bytespersamp);
1290
1291 for (itemswritten = 0; itemswritten < nframes; )
1292 {
1293#ifdef ROCKBOX
1294 int thiswrite = nframes - itemswritten, nbytes;
1295#else
1296 int thiswrite = nframes - itemswritten, nitems, nbytes;
1297#endif
1298 thiswrite = (thiswrite > bufframes ? bufframes : thiswrite);
1299 soundfile_xferout(argc, vecs, (unsigned char *)sampbuf, thiswrite,
1300 onset, bytespersamp, bigendian, normfactor);
1301 nbytes = write(fd, sampbuf, nchannels * bytespersamp * thiswrite);
1302 if (nbytes < nchannels * bytespersamp * thiswrite)
1303 {
1304#ifdef ROCKBOX
1305 post("%s: %s", filesym->s_name, "error");
1306#else
1307 post("%s: %s", filesym->s_name, strerror(errno));
1308#endif
1309 if (nbytes > 0)
1310 itemswritten += nbytes / (nchannels * bytespersamp);
1311 break;
1312 }
1313 itemswritten += thiswrite;
1314 onset += thiswrite;
1315 }
1316 if (fd >= 0)
1317 {
1318 soundfile_finishwrite(obj, filesym->s_name, fd,
1319 filetype, nframes, itemswritten, nchannels * bytespersamp, swap);
1320 close (fd);
1321 }
1322 return ((float)itemswritten);
1323usage:
1324 pd_error(obj, "usage: write [flags] filename tablename...");
1325 post("flags: -skip <n> -nframes <n> -bytes <n> -wave -aiff -nextstep ...");
1326 post("-big -little -normalize");
1327 post("(defaults to a 16-bit wave file).");
1328fail:
1329 if (fd >= 0)
1330 close (fd);
1331 return (0);
1332}
1333
1334static void soundfiler_write(t_soundfiler *x, t_symbol *s,
1335 int argc, t_atom *argv)
1336{
1337#ifdef ROCKBOX
1338 (void) s;
1339#endif
1340 long bozo = soundfiler_dowrite(x, x->x_canvas,
1341 argc, argv);
1342 outlet_float(x->x_obj.ob_outlet, (float)bozo);
1343}
1344
1345static void soundfiler_setup(void)
1346{
1347 soundfiler_class = class_new(gensym("soundfiler"), (t_newmethod)soundfiler_new,
1348 0, sizeof(t_soundfiler), 0, 0);
1349 class_addmethod(soundfiler_class, (t_method)soundfiler_read, gensym("read"),
1350 A_GIMME, 0);
1351 class_addmethod(soundfiler_class, (t_method)soundfiler_write,
1352 gensym("write"), A_GIMME, 0);
1353}
1354
1355
1356#ifndef FIXEDPOINT
1357/************************* readsf object ******************************/
1358
1359/* READSF uses the Posix threads package; for the moment we're Linux
1360only although this should be portable to the other platforms.
1361
1362Each instance of readsf~ owns a "child" thread for doing the UNIX (MSW?) file
1363reading. The parent thread signals the child each time:
1364 (1) a file wants opening or closing;
1365 (2) we've eaten another 1/16 of the shared buffer (so that the
1366 child thread should check if it's time to read some more.)
1367The child signals the parent whenever a read has completed. Signalling
1368is done by setting "conditions" and putting data in mutex-controlled common
1369areas.
1370*/
1371
1372#define MAXBYTESPERSAMPLE 4
1373#define MAXVECSIZE 128
1374
1375#define READSIZE 65536
1376#define WRITESIZE 65536
1377#define DEFBUFPERCHAN 262144
1378#define MINBUFSIZE (4 * READSIZE)
1379#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */
1380
1381#define REQUEST_NOTHING 0
1382#define REQUEST_OPEN 1
1383#define REQUEST_CLOSE 2
1384#define REQUEST_QUIT 3
1385#define REQUEST_BUSY 4
1386
1387#define STATE_IDLE 0
1388#define STATE_STARTUP 1
1389#define STATE_STREAM 2
1390
1391static t_class *readsf_class;
1392
1393typedef struct _readsf
1394{
1395 t_object x_obj;
1396 t_canvas *x_canvas;
1397 t_clock *x_clock;
1398 char *x_buf; /* soundfile buffer */
1399 int x_bufsize; /* buffer size in bytes */
1400 int x_noutlets; /* number of audio outlets */
1401 t_sample *(x_outvec[MAXSFCHANS]); /* audio vectors */
1402 int x_vecsize; /* vector size for transfers */
1403 t_outlet *x_bangout; /* bang-on-done outlet */
1404 int x_state; /* opened, running, or idle */
1405 float x_insamplerate; /* sample rate of input signal if known */
1406 /* parameters to communicate with subthread */
1407 int x_requestcode; /* pending request from parent to I/O thread */
1408 char *x_filename; /* file to open (string is permanently allocated) */
1409 int x_fileerror; /* slot for "errno" return */
1410 int x_skipheaderbytes; /* size of header we'll skip */
1411 int x_bytespersample; /* bytes per sample (2 or 3) */
1412 int x_bigendian; /* true if file is big-endian */
1413 int x_sfchannels; /* number of channels in soundfile */
1414 float x_samplerate; /* sample rate of soundfile */
1415 long x_onsetframes; /* number of sample frames to skip */
1416 long x_bytelimit; /* max number of data bytes to read */
1417 int x_fd; /* filedesc */
1418 int x_fifosize; /* buffer size appropriately rounded down */
1419 int x_fifohead; /* index of next byte to get from file */
1420 int x_fifotail; /* index of next byte the ugen will read */
1421 int x_eof; /* true if fifohead has stopped changing */
1422 int x_sigcountdown; /* counter for signalling child for more data */
1423 int x_sigperiod; /* number of ticks per signal */
1424 int x_filetype; /* writesf~ only; type of file to create */
1425 int x_itemswritten; /* writesf~ only; items writen */
1426 int x_swap; /* writesf~ only; true if byte swapping */
1427 float x_f; /* writesf~ only; scalar for signal inlet */
1428 pthread_mutex_t x_mutex;
1429 pthread_cond_t x_requestcondition;
1430 pthread_cond_t x_answercondition;
1431 pthread_t x_childthread;
1432} t_readsf;
1433
1434
1435/************** the child thread which performs file I/O ***********/
1436
1437#if 0
1438static void pute(char *s) /* debug routine */
1439{
1440 write(2, s, strlen(s));
1441}
1442#define DEBUG_SOUNDFILE
1443#endif
1444
1445#if 1
1446#define sfread_cond_wait pthread_cond_wait
1447#define sfread_cond_signal pthread_cond_signal
1448#else
1449#include <sys/time.h> /* debugging version... */
1450#include <sys/types.h>
1451static void readsf_fakewait(pthread_mutex_t *b)
1452{
1453 struct timeval timout;
1454 timout.tv_sec = 0;
1455 timout.tv_usec = 1000000;
1456 pthread_mutex_unlock(b);
1457 select(0, 0, 0, 0, &timout);
1458 pthread_mutex_lock(b);
1459}
1460
1461#define sfread_cond_wait(a,b) readsf_fakewait(b)
1462#define sfread_cond_signal(a)
1463#endif
1464
1465static void *readsf_child_main(void *zz)
1466{
1467 t_readsf *x = zz;
1468#ifdef DEBUG_SOUNDFILE
1469 pute("1\n");
1470#endif
1471 pthread_mutex_lock(&x->x_mutex);
1472 while (1)
1473 {
1474 int fd, fifohead;
1475 char *buf;
1476#ifdef DEBUG_SOUNDFILE
1477 pute("0\n");
1478#endif
1479 if (x->x_requestcode == REQUEST_NOTHING)
1480 {
1481#ifdef DEBUG_SOUNDFILE
1482 pute("wait 2\n");
1483#endif
1484 sfread_cond_signal(&x->x_answercondition);
1485 sfread_cond_wait(&x->x_requestcondition, &x->x_mutex);
1486#ifdef DEBUG_SOUNDFILE
1487 pute("3\n");
1488#endif
1489 }
1490 else if (x->x_requestcode == REQUEST_OPEN)
1491 {
1492 char boo[80];
1493 int sysrtn, wantbytes;
1494
1495 /* copy file stuff out of the data structure so we can
1496 relinquish the mutex while we're in open_soundfile(). */
1497 long onsetframes = x->x_onsetframes;
1498 long bytelimit = 0x7fffffff;
1499 int skipheaderbytes = x->x_skipheaderbytes;
1500 int bytespersample = x->x_bytespersample;
1501 int sfchannels = x->x_sfchannels;
1502 int bigendian = x->x_bigendian;
1503 char *filename = x->x_filename;
1504 char *dirname = canvas_getdir(x->x_canvas)->s_name;
1505 /* alter the request code so that an ensuing "open" will get
1506 noticed. */
1507#ifdef DEBUG_SOUNDFILE
1508 pute("4\n");
1509#endif
1510 x->x_requestcode = REQUEST_BUSY;
1511 x->x_fileerror = 0;
1512
1513 /* if there's already a file open, close it */
1514 if (x->x_fd >= 0)
1515 {
1516 fd = x->x_fd;
1517 pthread_mutex_unlock(&x->x_mutex);
1518 close (fd);
1519 pthread_mutex_lock(&x->x_mutex);
1520 x->x_fd = -1;
1521 if (x->x_requestcode != REQUEST_BUSY)
1522 goto lost;
1523 }
1524 /* open the soundfile with the mutex unlocked */
1525 pthread_mutex_unlock(&x->x_mutex);
1526 fd = open_soundfile(dirname, filename,
1527 skipheaderbytes, &bytespersample, &bigendian,
1528 &sfchannels, &bytelimit, onsetframes);
1529 pthread_mutex_lock(&x->x_mutex);
1530
1531#ifdef DEBUG_SOUNDFILE
1532 pute("5\n");
1533#endif
1534 /* copy back into the instance structure. */
1535 x->x_bytespersample = bytespersample;
1536 x->x_sfchannels = sfchannels;
1537 x->x_bigendian = bigendian;
1538 x->x_fd = fd;
1539 x->x_bytelimit = bytelimit;
1540 if (fd < 0)
1541 {
1542 x->x_fileerror = errno;
1543 x->x_eof = 1;
1544#ifdef DEBUG_SOUNDFILE
1545 pute("open failed\n");
1546 pute(filename);
1547 pute(dirname);
1548#endif
1549 goto lost;
1550 }
1551 /* check if another request has been made; if so, field it */
1552 if (x->x_requestcode != REQUEST_BUSY)
1553 goto lost;
1554#ifdef DEBUG_SOUNDFILE
1555 pute("6\n");
1556#endif
1557 x->x_fifohead = 0;
1558 /* set fifosize from bufsize. fifosize must be a
1559 multiple of the number of bytes eaten for each DSP
1560 tick. We pessimistically assume MAXVECSIZE samples
1561 per tick since that could change. There could be a
1562 problem here if the vector size increases while a
1563 soundfile is being played... */
1564 x->x_fifosize = x->x_bufsize - (x->x_bufsize %
1565 (x->x_bytespersample * x->x_sfchannels * MAXVECSIZE));
1566 /* arrange for the "request" condition to be signalled 16
1567 times per buffer */
1568#ifdef DEBUG_SOUNDFILE
1569 sprintf(boo, "fifosize %d\n",
1570 x->x_fifosize);
1571 pute(boo);
1572#endif
1573 x->x_sigcountdown = x->x_sigperiod =
1574 (x->x_fifosize /
1575 (16 * x->x_bytespersample * x->x_sfchannels *
1576 x->x_vecsize));
1577 /* in a loop, wait for the fifo to get hungry and feed it */
1578
1579 while (x->x_requestcode == REQUEST_BUSY)
1580 {
1581 int fifosize = x->x_fifosize;
1582#ifdef DEBUG_SOUNDFILE
1583 pute("77\n");
1584#endif
1585 if (x->x_eof)
1586 break;
1587 if (x->x_fifohead >= x->x_fifotail)
1588 {
1589 /* if the head is >= the tail, we can immediately read
1590 to the end of the fifo. Unless, that is, we would
1591 read all the way to the end of the buffer and the
1592 "tail" is zero; this would fill the buffer completely
1593 which isn't allowed because you can't tell a completely
1594 full buffer from an empty one. */
1595 if (x->x_fifotail || (fifosize - x->x_fifohead > READSIZE))
1596 {
1597 wantbytes = fifosize - x->x_fifohead;
1598 if (wantbytes > READSIZE)
1599 wantbytes = READSIZE;
1600 if (wantbytes > x->x_bytelimit)
1601 wantbytes = x->x_bytelimit;
1602#ifdef DEBUG_SOUNDFILE
1603 sprintf(boo, "head %d, tail %d, size %d\n",
1604 x->x_fifohead, x->x_fifotail, wantbytes);
1605 pute(boo);
1606#endif
1607 }
1608 else
1609 {
1610#ifdef DEBUG_SOUNDFILE
1611 pute("wait 7a ...\n");
1612#endif
1613 sfread_cond_signal(&x->x_answercondition);
1614#ifdef DEBUG_SOUNDFILE
1615 pute("signalled\n");
1616#endif
1617 sfread_cond_wait(&x->x_requestcondition,
1618 &x->x_mutex);
1619#ifdef DEBUG_SOUNDFILE
1620 pute("7a done\n");
1621#endif
1622 continue;
1623 }
1624 }
1625 else
1626 {
1627 /* otherwise check if there are at least READSIZE
1628 bytes to read. If not, wait and loop back. */
1629 wantbytes = x->x_fifotail - x->x_fifohead - 1;
1630 if (wantbytes < READSIZE)
1631 {
1632#ifdef DEBUG_SOUNDFILE
1633 pute("wait 7...\n");
1634#endif
1635 sfread_cond_signal(&x->x_answercondition);
1636 sfread_cond_wait(&x->x_requestcondition,
1637 &x->x_mutex);
1638#ifdef DEBUG_SOUNDFILE
1639 pute("7 done\n");
1640#endif
1641 continue;
1642 }
1643 else wantbytes = READSIZE;
1644 if (wantbytes > x->x_bytelimit)
1645 wantbytes = x->x_bytelimit;
1646 }
1647#ifdef DEBUG_SOUNDFILE
1648 pute("8\n");
1649#endif
1650 fd = x->x_fd;
1651 buf = x->x_buf;
1652 fifohead = x->x_fifohead;
1653 pthread_mutex_unlock(&x->x_mutex);
1654 sysrtn = read(fd, buf + fifohead, wantbytes);
1655 pthread_mutex_lock(&x->x_mutex);
1656 if (x->x_requestcode != REQUEST_BUSY)
1657 break;
1658 if (sysrtn < 0)
1659 {
1660#ifdef DEBUG_SOUNDFILE
1661 pute("fileerror\n");
1662#endif
1663 x->x_fileerror = errno;
1664 break;
1665 }
1666 else if (sysrtn == 0)
1667 {
1668 x->x_eof = 1;
1669 break;
1670 }
1671 else
1672 {
1673 x->x_fifohead += sysrtn;
1674 x->x_bytelimit -= sysrtn;
1675 if (x->x_bytelimit <= 0)
1676 {
1677 x->x_eof = 1;
1678 break;
1679 }
1680 if (x->x_fifohead == fifosize)
1681 x->x_fifohead = 0;
1682 }
1683#ifdef DEBUG_SOUNDFILE
1684 sprintf(boo, "after: head %d, tail %d\n",
1685 x->x_fifohead, x->x_fifotail);
1686 pute(boo);
1687#endif
1688 /* signal parent in case it's waiting for data */
1689 sfread_cond_signal(&x->x_answercondition);
1690 }
1691 lost:
1692
1693 if (x->x_requestcode == REQUEST_BUSY)
1694 x->x_requestcode = REQUEST_NOTHING;
1695 /* fell out of read loop: close file if necessary,
1696 set EOF and signal once more */
1697 if (x->x_fd >= 0)
1698 {
1699 fd = x->x_fd;
1700 pthread_mutex_unlock(&x->x_mutex);
1701 close (fd);
1702 pthread_mutex_lock(&x->x_mutex);
1703 x->x_fd = -1;
1704 }
1705 sfread_cond_signal(&x->x_answercondition);
1706
1707 }
1708 else if (x->x_requestcode == REQUEST_CLOSE)
1709 {
1710 if (x->x_fd >= 0)
1711 {
1712 fd = x->x_fd;
1713 pthread_mutex_unlock(&x->x_mutex);
1714 close (fd);
1715 pthread_mutex_lock(&x->x_mutex);
1716 x->x_fd = -1;
1717 }
1718 if (x->x_requestcode == REQUEST_CLOSE)
1719 x->x_requestcode = REQUEST_NOTHING;
1720 sfread_cond_signal(&x->x_answercondition);
1721 }
1722 else if (x->x_requestcode == REQUEST_QUIT)
1723 {
1724 if (x->x_fd >= 0)
1725 {
1726 fd = x->x_fd;
1727 pthread_mutex_unlock(&x->x_mutex);
1728 close (fd);
1729 pthread_mutex_lock(&x->x_mutex);
1730 x->x_fd = -1;
1731 }
1732 x->x_requestcode = REQUEST_NOTHING;
1733 sfread_cond_signal(&x->x_answercondition);
1734 break;
1735 }
1736 else
1737 {
1738#ifdef DEBUG_SOUNDFILE
1739 pute("13\n");
1740#endif
1741 }
1742 }
1743#ifdef DEBUG_SOUNDFILE
1744 pute("thread exit\n");
1745#endif
1746 pthread_mutex_unlock(&x->x_mutex);
1747 return (0);
1748}
1749
1750/******** the object proper runs in the calling (parent) thread ****/
1751
1752static void readsf_tick(t_readsf *x);
1753
1754static void *readsf_new(t_floatarg fnchannels, t_floatarg fbufsize)
1755{
1756 t_readsf *x;
1757 int nchannels = fnchannels, bufsize = fbufsize, i;
1758 char *buf;
1759
1760 if (nchannels < 1)
1761 nchannels = 1;
1762 else if (nchannels > MAXSFCHANS)
1763 nchannels = MAXSFCHANS;
1764 if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels;
1765 else if (bufsize < MINBUFSIZE)
1766 bufsize = MINBUFSIZE;
1767 else if (bufsize > MAXBUFSIZE)
1768 bufsize = MAXBUFSIZE;
1769 buf = getbytes(bufsize);
1770 if (!buf) return (0);
1771
1772 x = (t_readsf *)pd_new(readsf_class);
1773
1774 for (i = 0; i < nchannels; i++)
1775 outlet_new(&x->x_obj, gensym("signal"));
1776 x->x_noutlets = nchannels;
1777 x->x_bangout = outlet_new(&x->x_obj, &s_bang);
1778 pthread_mutex_init(&x->x_mutex, 0);
1779 pthread_cond_init(&x->x_requestcondition, 0);
1780 pthread_cond_init(&x->x_answercondition, 0);
1781 x->x_vecsize = MAXVECSIZE;
1782 x->x_state = STATE_IDLE;
1783 x->x_clock = clock_new(x, (t_method)readsf_tick);
1784 x->x_canvas = canvas_getcurrent();
1785 x->x_bytespersample = 2;
1786 x->x_sfchannels = 1;
1787 x->x_fd = -1;
1788 x->x_buf = buf;
1789 x->x_bufsize = bufsize;
1790 x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0;
1791 pthread_create(&x->x_childthread, 0, readsf_child_main, x);
1792 return (x);
1793}
1794
1795static void readsf_tick(t_readsf *x)
1796{
1797 outlet_bang(x->x_bangout);
1798}
1799
1800static t_int *readsf_perform(t_int *w)
1801{
1802 t_readsf *x = (t_readsf *)(w[1]);
1803 int vecsize = x->x_vecsize, noutlets = x->x_noutlets, i, j,
1804 bytespersample = x->x_bytespersample,
1805 bigendian = x->x_bigendian;
1806 float *fp;
1807 if (x->x_state == STATE_STREAM)
1808 {
1809 int wantbytes, nchannels, sfchannels = x->x_sfchannels;
1810 pthread_mutex_lock(&x->x_mutex);
1811 wantbytes = sfchannels * vecsize * bytespersample;
1812 while (
1813 !x->x_eof && x->x_fifohead >= x->x_fifotail &&
1814 x->x_fifohead < x->x_fifotail + wantbytes-1)
1815 {
1816#ifdef DEBUG_SOUNDFILE
1817 pute("wait...\n");
1818#endif
1819 sfread_cond_signal(&x->x_requestcondition);
1820 sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
1821#ifdef DEBUG_SOUNDFILE
1822 pute("done\n");
1823#endif
1824 }
1825 if (x->x_eof && x->x_fifohead >= x->x_fifotail &&
1826 x->x_fifohead < x->x_fifotail + wantbytes-1)
1827 {
1828 int xfersize;
1829 if (x->x_fileerror)
1830 {
1831 pd_error(x, "dsp: %s: %s", x->x_filename,
1832 (x->x_fileerror == EIO ?
1833 "unknown or bad header format" :
1834 strerror(x->x_fileerror)));
1835 }
1836 clock_delay(x->x_clock, 0);
1837 x->x_state = STATE_IDLE;
1838
1839 /* if there's a partial buffer left, copy it out. */
1840 xfersize = (x->x_fifohead - x->x_fifotail + 1) /
1841 (sfchannels * bytespersample);
1842 if (xfersize)
1843 {
1844 soundfile_xferin(sfchannels, noutlets, x->x_outvec, 0,
1845 (unsigned char *)(x->x_buf + x->x_fifotail), xfersize,
1846 bytespersample, bigendian);
1847 vecsize -= xfersize;
1848 }
1849 /* then zero out the (rest of the) output */
1850 for (i = 0; i < noutlets; i++)
1851 for (j = vecsize, fp = x->x_outvec[i] + xfersize; j--; )
1852 *fp++ = 0;
1853
1854 sfread_cond_signal(&x->x_requestcondition);
1855 pthread_mutex_unlock(&x->x_mutex);
1856 return (w+2);
1857 }
1858
1859 soundfile_xferin(sfchannels, noutlets, x->x_outvec, 0,
1860 (unsigned char *)(x->x_buf + x->x_fifotail), vecsize,
1861 bytespersample, bigendian);
1862
1863 x->x_fifotail += wantbytes;
1864 if (x->x_fifotail >= x->x_fifosize)
1865 x->x_fifotail = 0;
1866 if ((--x->x_sigcountdown) <= 0)
1867 {
1868 sfread_cond_signal(&x->x_requestcondition);
1869 x->x_sigcountdown = x->x_sigperiod;
1870 }
1871 pthread_mutex_unlock(&x->x_mutex);
1872 }
1873 else
1874 {
1875 idle:
1876 for (i = 0; i < noutlets; i++)
1877 for (j = vecsize, fp = x->x_outvec[i]; j--; )
1878 *fp++ = 0;
1879 }
1880 return (w+2);
1881}
1882
1883static void readsf_start(t_readsf *x)
1884{
1885 /* start making output. If we're in the "startup" state change
1886 to the "running" state. */
1887 if (x->x_state == STATE_STARTUP)
1888 x->x_state = STATE_STREAM;
1889 else pd_error(x, "readsf: start requested with no prior 'open'");
1890}
1891
1892static void readsf_stop(t_readsf *x)
1893{
1894 /* LATER rethink whether you need the mutex just to set a variable? */
1895 pthread_mutex_lock(&x->x_mutex);
1896 x->x_state = STATE_IDLE;
1897 x->x_requestcode = REQUEST_CLOSE;
1898 sfread_cond_signal(&x->x_requestcondition);
1899 pthread_mutex_unlock(&x->x_mutex);
1900}
1901
1902static void readsf_float(t_readsf *x, t_floatarg f)
1903{
1904 if (f != 0)
1905 readsf_start(x);
1906 else readsf_stop(x);
1907}
1908
1909 /* open method. Called as:
1910 open filename [skipframes headersize channels bytespersamp endianness]
1911 (if headersize is zero, header is taken to be automatically
1912 detected; thus, use the special "-1" to mean a truly headerless file.)
1913 */
1914
1915static void readsf_open(t_readsf *x, t_symbol *s, int argc, t_atom *argv)
1916{
1917 t_symbol *filesym = atom_getsymbolarg(0, argc, argv);
1918 t_float onsetframes = atom_getfloatarg(1, argc, argv);
1919 t_float headerbytes = atom_getfloatarg(2, argc, argv);
1920 t_float channels = atom_getfloatarg(3, argc, argv);
1921 t_float bytespersamp = atom_getfloatarg(4, argc, argv);
1922 t_symbol *endian = atom_getsymbolarg(5, argc, argv);
1923 if (!*filesym->s_name)
1924 return;
1925 pthread_mutex_lock(&x->x_mutex);
1926 x->x_requestcode = REQUEST_OPEN;
1927 x->x_filename = filesym->s_name;
1928 x->x_fifotail = 0;
1929 x->x_fifohead = 0;
1930 if (*endian->s_name == 'b')
1931 x->x_bigendian = 1;
1932 else if (*endian->s_name == 'l')
1933 x->x_bigendian = 0;
1934 else if (*endian->s_name)
1935 pd_error(x, "endianness neither 'b' nor 'l'");
1936 else x->x_bigendian = garray_ambigendian();
1937 x->x_onsetframes = (onsetframes > 0 ? onsetframes : 0);
1938 x->x_skipheaderbytes = (headerbytes > 0 ? headerbytes :
1939 (headerbytes == 0 ? -1 : 0));
1940 x->x_sfchannels = (channels >= 1 ? channels : 1);
1941 x->x_bytespersample = (bytespersamp > 2 ? bytespersamp : 2);
1942 x->x_eof = 0;
1943 x->x_fileerror = 0;
1944 x->x_state = STATE_STARTUP;
1945 sfread_cond_signal(&x->x_requestcondition);
1946 pthread_mutex_unlock(&x->x_mutex);
1947}
1948
1949static void readsf_dsp(t_readsf *x, t_signal **sp)
1950{
1951 int i, noutlets = x->x_noutlets;
1952 pthread_mutex_lock(&x->x_mutex);
1953 x->x_vecsize = sp[0]->s_n;
1954
1955 x->x_sigperiod = (x->x_fifosize /
1956 (x->x_bytespersample * x->x_sfchannels * x->x_vecsize));
1957 for (i = 0; i < noutlets; i++)
1958 x->x_outvec[i] = sp[i]->s_vec;
1959 pthread_mutex_unlock(&x->x_mutex);
1960 dsp_add(readsf_perform, 1, x);
1961}
1962
1963static void readsf_print(t_readsf *x)
1964{
1965 post("state %d", x->x_state);
1966 post("fifo head %d", x->x_fifohead);
1967 post("fifo tail %d", x->x_fifotail);
1968 post("fifo size %d", x->x_fifosize);
1969 post("fd %d", x->x_fd);
1970 post("eof %d", x->x_eof);
1971}
1972
1973static void readsf_free(t_readsf *x)
1974{
1975 /* request QUIT and wait for acknowledge */
1976 void *threadrtn;
1977 pthread_mutex_lock(&x->x_mutex);
1978 x->x_requestcode = REQUEST_QUIT;
1979 sfread_cond_signal(&x->x_requestcondition);
1980 while (x->x_requestcode != REQUEST_NOTHING)
1981 {
1982 sfread_cond_signal(&x->x_requestcondition);
1983 sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
1984 }
1985 pthread_mutex_unlock(&x->x_mutex);
1986 if (pthread_join(x->x_childthread, &threadrtn))
1987 error("readsf_free: join failed");
1988
1989 pthread_cond_destroy(&x->x_requestcondition);
1990 pthread_cond_destroy(&x->x_answercondition);
1991 pthread_mutex_destroy(&x->x_mutex);
1992 freebytes(x->x_buf, x->x_bufsize);
1993 clock_free(x->x_clock);
1994}
1995
1996static void readsf_setup(void)
1997{
1998 readsf_class = class_new(gensym("readsf~"), (t_newmethod)readsf_new,
1999 (t_method)readsf_free, sizeof(t_readsf), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
2000 class_addfloat(readsf_class, (t_method)readsf_float);
2001 class_addmethod(readsf_class, (t_method)readsf_start, gensym("start"), 0);
2002 class_addmethod(readsf_class, (t_method)readsf_stop, gensym("stop"), 0);
2003 class_addmethod(readsf_class, (t_method)readsf_dsp, gensym("dsp"), 0);
2004 class_addmethod(readsf_class, (t_method)readsf_open, gensym("open"),
2005 A_GIMME, 0);
2006 class_addmethod(readsf_class, (t_method)readsf_print, gensym("print"), 0);
2007}
2008
2009/******************************* writesf *******************/
2010
2011static t_class *writesf_class;
2012
2013#define t_writesf t_readsf /* just re-use the structure */
2014
2015/************** the child thread which performs file I/O ***********/
2016
2017static void *writesf_child_main(void *zz)
2018{
2019 t_writesf *x = zz;
2020#ifdef DEBUG_SOUNDFILE
2021 pute("1\n");
2022#endif
2023 pthread_mutex_lock(&x->x_mutex);
2024 while (1)
2025 {
2026#ifdef DEBUG_SOUNDFILE
2027 pute("0\n");
2028#endif
2029 if (x->x_requestcode == REQUEST_NOTHING)
2030 {
2031#ifdef DEBUG_SOUNDFILE
2032 pute("wait 2\n");
2033#endif
2034 sfread_cond_signal(&x->x_answercondition);
2035 sfread_cond_wait(&x->x_requestcondition, &x->x_mutex);
2036#ifdef DEBUG_SOUNDFILE
2037 pute("3\n");
2038#endif
2039 }
2040 else if (x->x_requestcode == REQUEST_OPEN)
2041 {
2042 char boo[80];
2043 int fd, sysrtn, writebytes;
2044
2045 /* copy file stuff out of the data structure so we can
2046 relinquish the mutex while we're in open_soundfile(). */
2047 long onsetframes = x->x_onsetframes;
2048 long bytelimit = 0x7fffffff;
2049 int skipheaderbytes = x->x_skipheaderbytes;
2050 int bytespersample = x->x_bytespersample;
2051 int sfchannels = x->x_sfchannels;
2052 int bigendian = x->x_bigendian;
2053 int filetype = x->x_filetype;
2054 char *filename = x->x_filename;
2055 t_canvas *canvas = x->x_canvas;
2056 float samplerate = x->x_samplerate;
2057
2058 /* alter the request code so that an ensuing "open" will get
2059 noticed. */
2060#ifdef DEBUG_SOUNDFILE
2061 pute("4\n");
2062#endif
2063 x->x_requestcode = REQUEST_BUSY;
2064 x->x_fileerror = 0;
2065
2066 /* if there's already a file open, close it */
2067 if (x->x_fd >= 0)
2068 {
2069 pthread_mutex_unlock(&x->x_mutex);
2070 close (x->x_fd);
2071 pthread_mutex_lock(&x->x_mutex);
2072 x->x_fd = -1;
2073 if (x->x_requestcode != REQUEST_BUSY)
2074 continue;
2075 }
2076 /* open the soundfile with the mutex unlocked */
2077 pthread_mutex_unlock(&x->x_mutex);
2078 fd = create_soundfile(canvas, filename, filetype, 0,
2079 bytespersample, bigendian, sfchannels,
2080 garray_ambigendian() != bigendian, samplerate);
2081 pthread_mutex_lock(&x->x_mutex);
2082#ifdef DEBUG_SOUNDFILE
2083 pute("5\n");
2084#endif
2085
2086 if (fd < 0)
2087 {
2088 x->x_fd = -1;
2089 x->x_eof = 1;
2090 x->x_fileerror = errno;
2091#ifdef DEBUG_SOUNDFILE
2092 pute("open failed\n");
2093 pute(filename);
2094#endif
2095 x->x_requestcode = REQUEST_NOTHING;
2096 continue;
2097 }
2098 /* check if another request has been made; if so, field it */
2099 if (x->x_requestcode != REQUEST_BUSY)
2100 continue;
2101#ifdef DEBUG_SOUNDFILE
2102 pute("6\n");
2103#endif
2104 x->x_fd = fd;
2105 x->x_fifotail = 0;
2106 x->x_itemswritten = 0;
2107 x->x_swap = garray_ambigendian() != bigendian;
2108 /* in a loop, wait for the fifo to have data and write it
2109 to disk */
2110 while (x->x_requestcode == REQUEST_BUSY ||
2111 (x->x_requestcode == REQUEST_CLOSE &&
2112 x->x_fifohead != x->x_fifotail))
2113 {
2114 int fifosize = x->x_fifosize, fifotail;
2115 char *buf = x->x_buf;
2116#ifdef DEBUG_SOUNDFILE
2117 pute("77\n");
2118#endif
2119
2120 /* if the head is < the tail, we can immediately write
2121 from tail to end of fifo to disk; otherwise we hold off
2122 writing until there are at least WRITESIZE bytes in the
2123 buffer */
2124 if (x->x_fifohead < x->x_fifotail ||
2125 x->x_fifohead >= x->x_fifotail + WRITESIZE
2126 || (x->x_requestcode == REQUEST_CLOSE &&
2127 x->x_fifohead != x->x_fifotail))
2128 {
2129 writebytes = (x->x_fifohead < x->x_fifotail ?
2130 fifosize : x->x_fifohead) - x->x_fifotail;
2131 if (writebytes > READSIZE)
2132 writebytes = READSIZE;
2133 }
2134 else
2135 {
2136#ifdef DEBUG_SOUNDFILE
2137 pute("wait 7a ...\n");
2138#endif
2139 sfread_cond_signal(&x->x_answercondition);
2140#ifdef DEBUG_SOUNDFILE
2141 pute("signalled\n");
2142#endif
2143 sfread_cond_wait(&x->x_requestcondition,
2144 &x->x_mutex);
2145#ifdef DEBUG_SOUNDFILE
2146 pute("7a done\n");
2147#endif
2148 continue;
2149 }
2150#ifdef DEBUG_SOUNDFILE
2151 pute("8\n");
2152#endif
2153 fifotail = x->x_fifotail;
2154 fd = x->x_fd;
2155 pthread_mutex_unlock(&x->x_mutex);
2156 sysrtn = write(fd, buf + fifotail, writebytes);
2157 pthread_mutex_lock(&x->x_mutex);
2158 if (x->x_requestcode != REQUEST_BUSY &&
2159 x->x_requestcode != REQUEST_CLOSE)
2160 break;
2161 if (sysrtn < writebytes)
2162 {
2163#ifdef DEBUG_SOUNDFILE
2164 pute("fileerror\n");
2165#endif
2166 x->x_fileerror = errno;
2167 break;
2168 }
2169 else
2170 {
2171 x->x_fifotail += sysrtn;
2172 if (x->x_fifotail == fifosize)
2173 x->x_fifotail = 0;
2174 }
2175 x->x_itemswritten +=
2176 sysrtn / (x->x_bytespersample * x->x_sfchannels);
2177 sprintf(boo, "after: head %d, tail %d\n",
2178 x->x_fifohead, x->x_fifotail);
2179#ifdef DEBUG_SOUNDFILE
2180 pute(boo);
2181#endif
2182 /* signal parent in case it's waiting for data */
2183 sfread_cond_signal(&x->x_answercondition);
2184 }
2185 }
2186 else if (x->x_requestcode == REQUEST_CLOSE ||
2187 x->x_requestcode == REQUEST_QUIT)
2188 {
2189 int quit = (x->x_requestcode == REQUEST_QUIT);
2190 if (x->x_fd >= 0)
2191 {
2192 int bytesperframe = x->x_bytespersample * x->x_sfchannels;
2193 int bigendian = x->x_bigendian;
2194 char *filename = x->x_filename;
2195 int fd = x->x_fd;
2196 int filetype = x->x_filetype;
2197 int itemswritten = x->x_itemswritten;
2198 int swap = x->x_swap;
2199 pthread_mutex_unlock(&x->x_mutex);
2200
2201 soundfile_finishwrite(x, filename, fd,
2202 filetype, 0x7fffffff, itemswritten,
2203 bytesperframe, swap);
2204 close (fd);
2205
2206 pthread_mutex_lock(&x->x_mutex);
2207 x->x_fd = -1;
2208 }
2209 x->x_requestcode = REQUEST_NOTHING;
2210 sfread_cond_signal(&x->x_answercondition);
2211 if (quit)
2212 break;
2213 }
2214 else
2215 {
2216#ifdef DEBUG_SOUNDFILE
2217 pute("13\n");
2218#endif
2219 }
2220 }
2221#ifdef DEBUG_SOUNDFILE
2222 pute("thread exit\n");
2223#endif
2224 pthread_mutex_unlock(&x->x_mutex);
2225 return (0);
2226}
2227
2228/******** the object proper runs in the calling (parent) thread ****/
2229
2230static void writesf_tick(t_writesf *x);
2231
2232static void *writesf_new(t_floatarg fnchannels, t_floatarg fbufsize)
2233{
2234 t_writesf *x;
2235 int nchannels = fnchannels, bufsize = fbufsize, i;
2236 char *buf;
2237
2238 if (nchannels < 1)
2239 nchannels = 1;
2240 else if (nchannels > MAXSFCHANS)
2241 nchannels = MAXSFCHANS;
2242 if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels;
2243 else if (bufsize < MINBUFSIZE)
2244 bufsize = MINBUFSIZE;
2245 else if (bufsize > MAXBUFSIZE)
2246 bufsize = MAXBUFSIZE;
2247 buf = getbytes(bufsize);
2248 if (!buf) return (0);
2249
2250 x = (t_writesf *)pd_new(writesf_class);
2251
2252 for (i = 1; i < nchannels; i++)
2253 inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
2254
2255 x->x_f = 0;
2256 x->x_sfchannels = nchannels;
2257 pthread_mutex_init(&x->x_mutex, 0);
2258 pthread_cond_init(&x->x_requestcondition, 0);
2259 pthread_cond_init(&x->x_answercondition, 0);
2260 x->x_vecsize = MAXVECSIZE;
2261 x->x_insamplerate = x->x_samplerate = 0;
2262 x->x_state = STATE_IDLE;
2263 x->x_clock = 0; /* no callback needed here */
2264 x->x_canvas = canvas_getcurrent();
2265 x->x_bytespersample = 2;
2266 x->x_fd = -1;
2267 x->x_buf = buf;
2268 x->x_bufsize = bufsize;
2269 x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0;
2270 pthread_create(&x->x_childthread, 0, writesf_child_main, x);
2271 return (x);
2272}
2273
2274static t_int *writesf_perform(t_int *w)
2275{
2276 t_writesf *x = (t_writesf *)(w[1]);
2277 int vecsize = x->x_vecsize, sfchannels = x->x_sfchannels, i, j,
2278 bytespersample = x->x_bytespersample,
2279 bigendian = x->x_bigendian;
2280 float *fp;
2281 if (x->x_state == STATE_STREAM)
2282 {
2283 int wantbytes;
2284 pthread_mutex_lock(&x->x_mutex);
2285 wantbytes = sfchannels * vecsize * bytespersample;
2286 while (x->x_fifotail > x->x_fifohead &&
2287 x->x_fifotail < x->x_fifohead + wantbytes + 1)
2288 {
2289#ifdef DEBUG_SOUNDFILE
2290 pute("wait...\n");
2291#endif
2292 sfread_cond_signal(&x->x_requestcondition);
2293 sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
2294#ifdef DEBUG_SOUNDFILE
2295 pute("done\n");
2296#endif
2297 }
2298
2299 soundfile_xferout(sfchannels, x->x_outvec,
2300 (unsigned char *)(x->x_buf + x->x_fifohead), vecsize, 0,
2301 bytespersample, bigendian, 1.);
2302
2303 x->x_fifohead += wantbytes;
2304 if (x->x_fifohead >= x->x_fifosize)
2305 x->x_fifohead = 0;
2306 if ((--x->x_sigcountdown) <= 0)
2307 {
2308#ifdef DEBUG_SOUNDFILE
2309 pute("signal 1\n");
2310#endif
2311 sfread_cond_signal(&x->x_requestcondition);
2312 x->x_sigcountdown = x->x_sigperiod;
2313 }
2314 pthread_mutex_unlock(&x->x_mutex);
2315 }
2316 return (w+2);
2317}
2318
2319static void writesf_start(t_writesf *x)
2320{
2321 /* start making output. If we're in the "startup" state change
2322 to the "running" state. */
2323 if (x->x_state == STATE_STARTUP)
2324 x->x_state = STATE_STREAM;
2325 else
2326 pd_error(x, "writesf: start requested with no prior 'open'");
2327}
2328
2329static void writesf_stop(t_writesf *x)
2330{
2331 /* LATER rethink whether you need the mutex just to set a Svariable? */
2332 pthread_mutex_lock(&x->x_mutex);
2333 x->x_state = STATE_IDLE;
2334 x->x_requestcode = REQUEST_CLOSE;
2335#ifdef DEBUG_SOUNDFILE
2336 pute("signal 2\n");
2337#endif
2338 sfread_cond_signal(&x->x_requestcondition);
2339 pthread_mutex_unlock(&x->x_mutex);
2340}
2341
2342
2343 /* open method. Called as: open [args] filename with args as in
2344 soundfiler_writeargparse().
2345 */
2346
2347static void writesf_open(t_writesf *x, t_symbol *s, int argc, t_atom *argv)
2348{
2349 t_symbol *filesym;
2350 int filetype, bytespersamp, swap, bigendian, normalize;
2351 long onset, nframes;
2352 float samplerate;
2353 if (soundfiler_writeargparse(x, &argc,
2354 &argv, &filesym, &filetype, &bytespersamp, &swap, &bigendian,
2355 &normalize, &onset, &nframes, &samplerate))
2356 {
2357 pd_error(x,
2358 "writesf~: usage: open [-bytes [234]] [-wave,-nextstep,-aiff] ...");
2359 post("... [-big,-little] [-rate ####] filename");
2360 }
2361 if (normalize || onset || (nframes != 0x7fffffff))
2362 pd_error(x, "normalize/onset/nframes argument to writesf~: ignored");
2363 if (argc)
2364 pd_error(x, "extra argument(s) to writesf~: ignored");
2365 pthread_mutex_lock(&x->x_mutex);
2366 x->x_bytespersample = bytespersamp;
2367 x->x_swap = swap;
2368 x->x_bigendian = bigendian;
2369 x->x_filename = filesym->s_name;
2370 x->x_filetype = filetype;
2371 x->x_itemswritten = 0;
2372 x->x_requestcode = REQUEST_OPEN;
2373 x->x_fifotail = 0;
2374 x->x_fifohead = 0;
2375 x->x_eof = 0;
2376 x->x_fileerror = 0;
2377 x->x_state = STATE_STARTUP;
2378 x->x_bytespersample = (bytespersamp > 2 ? bytespersamp : 2);
2379 if (samplerate > 0)
2380 x->x_samplerate = samplerate;
2381 else if (x->x_insamplerate > 0)
2382 x->x_samplerate = x->x_insamplerate;
2383 else x->x_samplerate = sys_getsr();
2384 /* set fifosize from bufsize. fifosize must be a
2385 multiple of the number of bytes eaten for each DSP
2386 tick. */
2387 x->x_fifosize = x->x_bufsize - (x->x_bufsize %
2388 (x->x_bytespersample * x->x_sfchannels * MAXVECSIZE));
2389 /* arrange for the "request" condition to be signalled 16
2390 times per buffer */
2391 x->x_sigcountdown = x->x_sigperiod =
2392 (x->x_fifosize /
2393 (16 * x->x_bytespersample * x->x_sfchannels *
2394 x->x_vecsize));
2395 sfread_cond_signal(&x->x_requestcondition);
2396 pthread_mutex_unlock(&x->x_mutex);
2397}
2398
2399static void writesf_dsp(t_writesf *x, t_signal **sp)
2400{
2401 int i, ninlets = x->x_sfchannels;
2402 pthread_mutex_lock(&x->x_mutex);
2403 x->x_vecsize = sp[0]->s_n;
2404
2405 x->x_sigperiod = (x->x_fifosize /
2406 (x->x_bytespersample * ninlets * x->x_vecsize));
2407 for (i = 0; i < ninlets; i++)
2408 x->x_outvec[i] = sp[i]->s_vec;
2409 x->x_insamplerate = sp[0]->s_sr;
2410 pthread_mutex_unlock(&x->x_mutex);
2411 dsp_add(writesf_perform, 1, x);
2412}
2413
2414static void writesf_print(t_writesf *x)
2415{
2416 post("state %d", x->x_state);
2417 post("fifo head %d", x->x_fifohead);
2418 post("fifo tail %d", x->x_fifotail);
2419 post("fifo size %d", x->x_fifosize);
2420 post("fd %d", x->x_fd);
2421 post("eof %d", x->x_eof);
2422}
2423
2424static void writesf_free(t_writesf *x)
2425{
2426 /* request QUIT and wait for acknowledge */
2427 void *threadrtn;
2428 pthread_mutex_lock(&x->x_mutex);
2429 x->x_requestcode = REQUEST_QUIT;
2430 /* post("stopping writesf thread..."); */
2431 sfread_cond_signal(&x->x_requestcondition);
2432 while (x->x_requestcode != REQUEST_NOTHING)
2433 {
2434 /* post("signalling..."); */
2435 sfread_cond_signal(&x->x_requestcondition);
2436 sfread_cond_wait(&x->x_answercondition, &x->x_mutex);
2437 }
2438 pthread_mutex_unlock(&x->x_mutex);
2439 if (pthread_join(x->x_childthread, &threadrtn))
2440 error("writesf_free: join failed");
2441 /* post("... done."); */
2442
2443 pthread_cond_destroy(&x->x_requestcondition);
2444 pthread_cond_destroy(&x->x_answercondition);
2445 pthread_mutex_destroy(&x->x_mutex);
2446 freebytes(x->x_buf, x->x_bufsize);
2447}
2448
2449static void writesf_setup(void)
2450{
2451 writesf_class = class_new(gensym("writesf~"), (t_newmethod)writesf_new,
2452 (t_method)writesf_free, sizeof(t_writesf), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
2453 class_addmethod(writesf_class, (t_method)writesf_start, gensym("start"), 0);
2454 class_addmethod(writesf_class, (t_method)writesf_stop, gensym("stop"), 0);
2455 class_addmethod(writesf_class, (t_method)writesf_dsp, gensym("dsp"), 0);
2456 class_addmethod(writesf_class, (t_method)writesf_open, gensym("open"),
2457 A_GIMME, 0);
2458 class_addmethod(writesf_class, (t_method)writesf_print, gensym("print"), 0);
2459 CLASS_MAINSIGNALIN(writesf_class, t_writesf, x_f);
2460}
2461
2462#endif
2463
2464/* ------------------------ global setup routine ------------------------- */
2465
2466void d_soundfile_setup(void)
2467{
2468 soundfiler_setup();
2469#ifndef FIXEDPOINT
2470 readsf_setup();
2471 writesf_setup();
2472#endif
2473}
2474