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-24 21:28:16 +0000
committerPeter D'Hoye <peter.dhoye@gmail.com>2009-05-24 21:28:16 +0000
commit526b5580dabbfed7cfe5439dc3a90ec727f563c2 (patch)
tree22b1af92348785daad16714ee5e2b633017e0e48 /apps/plugins/pdbox/PDa/src/d_soundfile.c
parent4f2dfcc01b260d946044ef2b6af5fe36cb772c8d (diff)
downloadrockbox-526b5580dabbfed7cfe5439dc3a90ec727f563c2.tar.gz
rockbox-526b5580dabbfed7cfe5439dc3a90ec727f563c2.zip
Cut the files in half and it might work better (note to self: check your tree is really clean before patching)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21070 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.c2367
1 files changed, 0 insertions, 2367 deletions
diff --git a/apps/plugins/pdbox/PDa/src/d_soundfile.c b/apps/plugins/pdbox/PDa/src/d_soundfile.c
index 872a44a923..4b89e93fd4 100644
--- a/apps/plugins/pdbox/PDa/src/d_soundfile.c
+++ b/apps/plugins/pdbox/PDa/src/d_soundfile.c
@@ -2365,2370 +2365,3 @@ void d_soundfile_setup(void)
2365#endif 2365#endif
2366} 2366}
2367 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