From 171485daa08e97782c05b3ca7490e6e42b827197 Mon Sep 17 00:00:00 2001 From: Georg Holzmann Date: Sun, 20 Nov 2005 21:29:12 +0000 Subject: version 0.1 svn path=/trunk/externals/grh/; revision=3989 --- threadlib/INSTALL | 7 +- threadlib/README | 29 +- threadlib/doc/help-threadedsf.pd | 54 -- threadlib/src/Makefile | 9 +- threadlib/src/Makefile_darwin | 13 +- threadlib/src/Makefile_mingw | 22 +- threadlib/src/Makefile_msvc | 72 ++ threadlib/src/callbacks.c | 2 +- threadlib/src/join.c | 12 +- threadlib/src/sleep.c | 9 +- threadlib/src/threadedsf.c | 1919 -------------------------------------- threadlib/src/threadlib.c | 15 +- threadlib/src/threadlib.h | 32 +- 13 files changed, 152 insertions(+), 2043 deletions(-) delete mode 100755 threadlib/doc/help-threadedsf.pd create mode 100755 threadlib/src/Makefile_msvc delete mode 100755 threadlib/src/threadedsf.c diff --git a/threadlib/INSTALL b/threadlib/INSTALL index 0aac333..35dd2f2 100755 --- a/threadlib/INSTALL +++ b/threadlib/INSTALL @@ -1,4 +1,9 @@ -threadlib installation +threadlib binary: + get them on http://grh.mur.at/software/threadlib.html + and follow the instructions there + + +threadlib compilation: 1) open the right makefile for your platform/compiler diff --git a/threadlib/README b/threadlib/README index 54850f1..e38f78b 100755 --- a/threadlib/README +++ b/threadlib/README @@ -1,23 +1,32 @@ threadlib -PD library for threaded patching +C and PD library for threaded patching and threaded PD externals heavily based on pd_devel code by Tim Blechmann (c) 2005, Georg Holzmann, http://grh.mur.at/software/threadlib.html ------------------------------------------------------------------------ -contents of threadlib: - detach run part of the patch in a helper thread - join synchronize messages to pd's main thread - sleep blocks the system for a specific time - threadedsf modified threaded soundfiler from pd_devel_0.38 +PD objects for threaded patching: +- detach run part of the patch in a helper thread +- join synchronize messages to pd's main thread +- sleep blocks the system for a specific time +- threadedsf modified threaded soundfiler from pd_devel_0.38 WARNING: these objects (especially detach/join) are very experimental and may crash your patches if you use them in the wrong way, because some externals/internals are not threadsafe! -REQUIREMENT: pd >= 0.39 +Features for PD external programmers: +- lockfree FIFO from pd_devel +- callback system like in pd_devel: + Instead of the idle callbacks, which are not really useable + in current main pd, it uses clock callbacks +- USAGE: you have to link your externals against threadlib + (see sndfiler as an example) +- ADVANTAGES: so it's possible to write threaded externals + for main and devel pd with the same source code + (using the lockfree FIFO and sys_callback) Many thanks to Tim Blechmann for his code and help! @@ -43,12 +52,6 @@ is also useful to place behind any threaded external calling the outlet functions from the helper thread, to make sure the following messages are being run in the main pd thread. -threadedsf: -This is the threaded soundfiler from pd devel 0.38 by Tim Blechmann, -adapted to work with main pd >= 0.39. -Instead of the idle callbacks, which are not really useable in current -main pd, it uses clock-callbacks (and also the lockfree FIFO of pd devel). - !!! WARNING: !!! detach/join provide the possibility of threaded patching. beware of the risks: diff --git a/threadlib/doc/help-threadedsf.pd b/threadlib/doc/help-threadedsf.pd deleted file mode 100755 index 7d6ea64..0000000 --- a/threadlib/doc/help-threadedsf.pd +++ /dev/null @@ -1,54 +0,0 @@ -#N canvas 728 33 465 834 10; -#X obj 29 23 cnv 15 404 54 empty empty empty 22 25 0 18 -1 -66577 0 -; -#X obj 31 25 cnv 15 400 50 empty empty threadlib 22 25 0 18 -228992 --66577 0; -#X text 314 35 help file of; -#X text 27 765 =%)!(%= threadlib \, by Georg Holzmann -\, 2005; -#X text 70 799 htttp://grh.mur.at/software/threadlib.html; -#X text 100 711 see also:; -#X text 55 782 heavily based on pd_devel code by Tim Blechmann; -#X obj 180 711 detach; -#X obj 237 711 join; -#X text 292 51 ::: threadedsf :::; -#X text 162 112 ::: THREADEDSF :::; -#X text 106 152 A threaded soundfiler for main pd.; -#X obj 274 191 soundfiler; -#X obj 278 711 sleep; -#X text 27 594 NOTE: this is the threaded soundfiler from pd devel -0.38 by Tim Blechmann \, adapted to work with main pd >= 0.39. Instead -of the idle callbacks \, which are not really useable in current main -pd \, it uses clock-callbacks (ans also the lockfree FIFO of pd devel). -; -#X obj 70 512 threadedsf; -#X obj 70 534 print THREADED_SF; -#X obj 70 489 r \$0-tsf; -#X text 101 191 USAGE: see help-file of; -#X text 148 207 (same usage); -#X obj 52 307 openpanel; -#X msg 52 329 read -resize \$1 array1; -#X obj 52 351 s \$0-tsf; -#X obj 52 287 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 --1; -#X obj 248 349 s \$0-tsf; -#X obj 248 285 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 --1; -#X msg 248 327 write -wave \$1 array1; -#X obj 248 305 savepanel; -#X obj 265 505 table array1; -#X text 48 255 the same as with soundfiler:; -#X text 73 286 <- read; -#X text 271 284 <- write; -#X obj 262 437 s \$0-tsf; -#X text 46 409 additional: threaded resize->; -#X msg 262 411 resize array1 4410; -#X connect 15 0 16 0; -#X connect 17 0 15 0; -#X connect 20 0 21 0; -#X connect 21 0 22 0; -#X connect 23 0 20 0; -#X connect 25 0 27 0; -#X connect 26 0 24 0; -#X connect 27 0 26 0; -#X connect 34 0 32 0; diff --git a/threadlib/src/Makefile b/threadlib/src/Makefile index 0579345..fd635c6 100755 --- a/threadlib/src/Makefile +++ b/threadlib/src/Makefile @@ -3,7 +3,7 @@ # this should point to the directory which contains # m_pd.h and g_canvas.h -PDSCR=/home/holzi/pd-0.39-1test1/src +PDSCR=/home/holzi/pd-0.39-1/src # this is the pd directory, here the files will be # installed @@ -13,8 +13,8 @@ PDPATH=/usr/lib/pd TARGET=threadlib.pd_linux -OBJ=fifo.o callbacks.o threadlib.o sleep.o detach.o \ - join.o threadedsf.o +OBJ=fifo.o callbacks.o threadlib.o \ + sleep.o detach.o join.o CC = gcc LD = gcc @@ -54,9 +54,6 @@ detach.o: threadlib.o fifo.o detach.c join.o: threadlib.o callbacks.o join.c $(CC) $(CC_FLAGS) $(INCLUDE) join.c -threadedsf.o: threadlib.o callbacks.o threadedsf.c - $(CC) $(CC_FLAGS) $(INCLUDE) threadedsf.c - # -------------------------------------------------- clean: diff --git a/threadlib/src/Makefile_darwin b/threadlib/src/Makefile_darwin index a254ff0..6c77444 100755 --- a/threadlib/src/Makefile_darwin +++ b/threadlib/src/Makefile_darwin @@ -11,10 +11,10 @@ PDPATH=/usr/lib/pd # -------------------------------------------------- -TARGET=threadlib.pd_linux +TARGET=threadlib.pd_darwin -OBJ=fifo.o callbacks.o threadlib.o sleep.o detach.o \ - join.o threadedsf.o +OBJ=fifo.o callbacks.o threadlib.o \ + sleep.o detach.o join.o CC = gcc LD = gcc @@ -28,9 +28,9 @@ LD_FLAGS = -bundle -bundle_loader $(PDPATH)/bin/pd \ # -------------------------------------------------- -all: pd_linux +all: pd_darwin -pd_linux: $(TARGET) +pd_darwin: $(TARGET) $(TARGET): $(OBJ) $(LD) $(LD_FLAGS) $(TARGET) $(OBJ) $(LIB) @@ -55,9 +55,6 @@ detach.o: threadlib.o fifo.o detach.c join.o: threadlib.o callbacks.o join.c $(CC) $(CC_FLAGS) $(INCLUDE) join.c -threadedsf.o: threadlib.o callbacks.o threadedsf.c - $(CC) $(CC_FLAGS) $(INCLUDE) threadedsf.c - # -------------------------------------------------- clean: diff --git a/threadlib/src/Makefile_mingw b/threadlib/src/Makefile_mingw index c110eec..21de955 100755 --- a/threadlib/src/Makefile_mingw +++ b/threadlib/src/Makefile_mingw @@ -11,25 +11,26 @@ PDPATH=c:/pd # -------------------------------------------------- -TARGET=threadlib.pd_linux +TARGET=threadlib.dll -OBJ=fifo.o callbacks.o threadlib.o sleep.o detach.o \ - join.o threadedsf.o +OBJ=fifo.o callbacks.o threadlib.o \ + sleep.o detach.o join.o CC = gcc LD = gcc -INCLUDE=-I$(PDSCR) -I. -LIB=$(PD-PATH)/bin/pd.dll -CC_FLAGS = -DPD -DWINDOWS -c -mms-bitfields \ +INCLUDE = -I$(PDSCR) -I. +LIB = $(PDPATH)/bin/pd.dll \ + c:/windows/system/pthreadGC.dll +CC_FLAGS = -DPD -DMSW -c -mms-bitfields \ -Wall -Wno-parentheses -Wno-switch -O3 \ - -funroll-loops -fomit-frame-pointer -pthread + -funroll-loops -fomit-frame-pointer LD_FLAGS = --export-dynamic -shared -o # -------------------------------------------------- -all: pd_linux +all: dll -pd_linux: $(TARGET) +dll: $(TARGET) $(TARGET): $(OBJ) $(LD) $(LD_FLAGS) $(TARGET) $(OBJ) $(LIB) @@ -54,9 +55,6 @@ detach.o: threadlib.o fifo.o detach.c join.o: threadlib.o callbacks.o join.c $(CC) $(CC_FLAGS) $(INCLUDE) join.c -threadedsf.o: threadlib.o callbacks.o threadedsf.c - $(CC) $(CC_FLAGS) $(INCLUDE) threadedsf.c - # -------------------------------------------------- clean: diff --git a/threadlib/src/Makefile_msvc b/threadlib/src/Makefile_msvc new file mode 100755 index 0000000..0f651d7 --- /dev/null +++ b/threadlib/src/Makefile_msvc @@ -0,0 +1,72 @@ +# ------------------------------------------------- +# adjust the next 3 pathes to your system: + +# this should point to the directory which contains +# m_pd.h and g_canvas.h +PD_SCR = C:\pd\src + +# this is the pd directory, here the files will be +# installed +PD_PATH = C:\pd + +# path of the microsoft compiler +VIS_CPP_PATH = "C:\Programme\Microsoft Visual Studio\Vc98" + +# -------------------------------------------------- + +TARGET=threadlib.dll + +OBJ=fifo.obj callbacks.obj threadlib.obj \ + sleep.obj detach.obj join.obj threadedsf.obj + +CC = cl.exe +LD = link.exe +INCLUDE = /I. /I$(PD_SCR) /I$(VIS_CPP_PATH)\include +CC_FLAGS = /W3 /WX /DMSW /DPD /nologo /D_WINDOWS +LD_FLAGS = /nologo /dll /export:threadlib_setup + +#LDIR = $(VIS_CPP_PATH)\lib +LIB = /NODEFAULTLIB:libc /NODEFAULTLIB:oldnames \ + /NODEFAULTLIB:kernel /NODEFAULTLIB:uuid \ + $(VIS_CPP_PATH)\lib\libc.lib \ + $(VIS_CPP_PATH)\lib\oldnames.lib \ + $(VIS_CPP_PATH)\lib\kernel32.lib \ + $(PD_PATH)\bin\pd.lib \ + $(PD_PATH)\bin\pthreadVC.lib + +# -------------------------------------------------- + +all: dll + +dll: $(TARGET) + +$(TARGET): $(OBJ) + $(LD) $(LD_FLAGS) /out:$(TARGET) $(OBJ) $(LIB) + + +threadlib.obj: threadlib.h threadlib.c + $(CC) $(CC_FLAGS) $(INCLUDE) /c threadlib.c + +fifo.obj: threadlib.obj fifo.c + $(CC) $(CC_FLAGS) $(INCLUDE) /c fifo.c + +callbacks.obj: fifo.obj threadlib.obj callbacks.c + $(CC) $(CC_FLAGS) $(INCLUDE) /c callbacks.c + +sleep.obj: threadlib.obj sleep.c + $(CC) $(CC_FLAGS) $(INCLUDE) /c sleep.c + +detach.obj: threadlib.obj fifo.obj detach.c + $(CC) $(CC_FLAGS) $(INCLUDE) /c detach.c + +join.obj: threadlib.obj callbacks.obj join.c + $(CC) $(CC_FLAGS) $(INCLUDE) /c join.c + +# -------------------------------------------------- + +clean: + del $(OBJ) $(TARGET) *.lib *.exp + +install: + copy $(TARGET) $(PD_PATH)\extra + copy help\*.pd $(PD_PATH)\doc\5.reference diff --git a/threadlib/src/callbacks.c b/threadlib/src/callbacks.c index 1385992..7bf4a19 100755 --- a/threadlib/src/callbacks.c +++ b/threadlib/src/callbacks.c @@ -41,7 +41,7 @@ void h_free_callbacks() fifo_destroy(h_callback_fifo); } -void h_set_callback(t_int (*callback) (t_int* argv), t_int* argv, t_int argc) +void sys_callback(t_int (*callback) (t_int* argv), t_int* argv, t_int argc) { t_sched_callback* new = (t_sched_callback*) getbytes (sizeof(t_sched_callback)); diff --git a/threadlib/src/join.c b/threadlib/src/join.c index 5e3f688..65c8092 100755 --- a/threadlib/src/join.c +++ b/threadlib/src/join.c @@ -49,7 +49,7 @@ static void join_bang(join_t * x) t_int* argv = getbytes(sizeof(t_int*)); argv[0] = (t_int)x->x_outlet; - h_set_callback(join_bang_callback, argv, 1); + sys_callback(join_bang_callback, argv, 1); } static t_int join_pointer_callback(t_int * argv) @@ -64,7 +64,7 @@ static void join_pointer(join_t * x, t_gpointer * gp) argv[0] = (t_int)x->x_outlet; argv[1] = (t_int)gp; - h_set_callback(join_pointer_callback, argv, 2); + sys_callback(join_pointer_callback, argv, 2); } static t_int join_float_callback(t_int * argv) @@ -79,7 +79,7 @@ static void join_float(join_t * x, t_float f) argv[0] = (t_int)x->x_outlet; argv[1] = (t_int)f; - h_set_callback(join_float_callback, argv, 2); + sys_callback(join_float_callback, argv, 2); } static t_int join_symbol_callback(t_int * argv) @@ -94,7 +94,7 @@ static void join_symbol(join_t * x, t_symbol * s) argv[0] = (t_int)x->x_outlet; argv[1] = (t_int)s; - h_set_callback(join_symbol_callback, argv, 2); + sys_callback(join_symbol_callback, argv, 2); } static t_int join_list_callback(t_int * argv) @@ -113,7 +113,7 @@ static void join_list(join_t * x, t_symbol * s, int argc, t_atom* largv) argv[1] = (t_int)argc; argv[2] = (t_int)copied_argv; - h_set_callback(join_list_callback, argv, 3); + sys_callback(join_list_callback, argv, 3); } static t_int join_anything_callback(t_int * argv) @@ -144,7 +144,7 @@ static void join_anything(join_t * x, t_symbol * s, int argc, t_atom* largv) argv[1] = (t_int)copied_argc; argv[2] = (t_int)copied_argv; - h_set_callback(join_anything_callback, argv, 3); + sys_callback(join_anything_callback, argv, 3); } void join_setup(void) diff --git a/threadlib/src/sleep.c b/threadlib/src/sleep.c index f800074..9f9eaab 100755 --- a/threadlib/src/sleep.c +++ b/threadlib/src/sleep.c @@ -21,7 +21,14 @@ */ #include "threadlib.h" + +// include for sleep +#ifdef MSW +#include +#define sleep(t) Sleep(1000*(t)) +#else #include +#endif static t_class *sleep_class; @@ -41,9 +48,7 @@ static void sleep_float(t_sleep * x, t_float f) static void *sleep_new(void) { t_sleep *x = (t_sleep *)pd_new(sleep_class); - x->x_outlet = outlet_new(&x->x_obj,&s_float); - return (void *)x; } diff --git a/threadlib/src/threadedsf.c b/threadlib/src/threadedsf.c deleted file mode 100755 index c9d0b9f..0000000 --- a/threadlib/src/threadedsf.c +++ /dev/null @@ -1,1919 +0,0 @@ -/* -* -* threadedsf -* -* this is a little bit hacked version of the -* threaded soundfiler of pd_devel_0.38 by Tim Blechmann -* -* (c) 2005, Georg Holzmann, -*/ - -/* Copyright (c) 1997-1999 Miller Puckette. -* For information on usage and redistribution, and for a DISCLAIMER OF ALL -* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ - -/* this file contains, first, a collection of soundfile access routines, a -sort of soundfile library. Second, the "soundfiler" object is defined which -uses the routines to read or write soundfiles, synchronously, from garrays. -These operations are not to be done in "real time" as they may have to wait -for disk accesses (even the write routine.) Finally, the realtime objects -readsf~ and writesf~ are defined which confine disk operations to a separate -thread so that they can be used in real time. The readsf~ and writesf~ -objects use Posix-like threads. */ - -/* threaded soundfiler by Tim Blechmann */ - - -#include "threadlib.h" - -#ifndef MSW -#include -#include -#endif -#ifdef MSW -#include -#endif -#include -#include -#include - -#include "g_canvas.h" - -#define MAXSFCHANS 64 - - -// forward declarations - -struct _garray -{ - t_gobj x_gobj; - t_scalar *x_scalar; /* scalar "containing" the array */ - t_glist *x_glist; /* containing glist */ - t_symbol *x_name; /* unexpanded name (possibly with leading '$') */ - t_symbol *x_realname; /* expanded name (symbol we're bound to) */ - char x_usedindsp; /* true if some DSP routine is using this */ - char x_saveit; /* true if we should save this with parent */ - char x_listviewing; /* true if list view window is open */ -}; - -t_array *garray_getarray(t_garray *x); -int garray_ambigendian(void); - - - -/***************** soundfile header structures ************************/ - -typedef unsigned short uint16; -typedef unsigned int uint32; /* long isn't 32-bit on amd64 */ - -#define FORMAT_WAVE 0 -#define FORMAT_AIFF 1 -#define FORMAT_NEXT 2 - -/* the NeXTStep sound header structure; can be big or little endian */ - -typedef struct _nextstep -{ - char ns_fileid[4]; /* magic number '.snd' if file is big-endian */ - uint32 ns_onset; /* byte offset of first sample */ - uint32 ns_length; /* length of sound in bytes */ - uint32 ns_format; /* format; see below */ - uint32 ns_sr; /* sample rate */ - uint32 ns_nchans; /* number of channels */ - char ns_info[4]; /* comment */ -} t_nextstep; - -#define NS_FORMAT_LINEAR_16 3 -#define NS_FORMAT_LINEAR_24 4 -#define NS_FORMAT_FLOAT 6 -#define SCALE (1./(1024. * 1024. * 1024. * 2.)) - -/* the WAVE header. All Wave files are little endian. We assume - the "fmt" chunk comes first which is usually the case but perhaps not - always; same for AIFF and the "COMM" chunk. */ - -typedef struct _wave -{ - char w_fileid[4]; /* chunk id 'RIFF' */ - uint32 w_chunksize; /* chunk size */ - char w_waveid[4]; /* wave chunk id 'WAVE' */ - char w_fmtid[4]; /* format chunk id 'fmt ' */ - uint32 w_fmtchunksize; /* format chunk size */ - uint16 w_fmttag; /* format tag (WAV_INT etc) */ - uint16 w_nchannels; /* number of channels */ - uint32 w_samplespersec; /* sample rate in hz */ - uint32 w_navgbytespersec; /* average bytes per second */ - uint16 w_nblockalign; /* number of bytes per frame */ - uint16 w_nbitspersample; /* number of bits in a sample */ - char w_datachunkid[4]; /* data chunk id 'data' */ - uint32 w_datachunksize; /* length of data chunk */ -} t_wave; - -typedef struct _fmt /* format chunk */ -{ - uint16 f_fmttag; /* format tag, 1 for PCM */ - uint16 f_nchannels; /* number of channels */ - uint32 f_samplespersec; /* sample rate in hz */ - uint32 f_navgbytespersec; /* average bytes per second */ - uint16 f_nblockalign; /* number of bytes per frame */ - uint16 f_nbitspersample; /* number of bits in a sample */ -} t_fmt; - -typedef struct _wavechunk /* ... and the last two items */ -{ - char wc_id[4]; /* data chunk id, e.g., 'data' or 'fmt ' */ - uint32 wc_size; /* length of data chunk */ -} t_wavechunk; - -#define WAV_INT 1 -#define WAV_FLOAT 3 - -/* the AIFF header. I'm assuming AIFC is compatible but don't really know - that. */ - -typedef struct _datachunk -{ - char dc_id[4]; /* data chunk id 'SSND' */ - uint32 dc_size; /* length of data chunk */ -} t_datachunk; - -typedef struct _comm -{ - uint16 c_nchannels; /* number of channels */ - uint16 c_nframeshi; /* # of sample frames (hi) */ - uint16 c_nframeslo; /* # of sample frames (lo) */ - uint16 c_bitspersamp; /* bits per sample */ - unsigned char c_samprate[10]; /* sample rate, 80-bit float! */ -} t_comm; - - /* this version is more convenient for writing them out: */ -typedef struct _aiff -{ - char a_fileid[4]; /* chunk id 'FORM' */ - uint32 a_chunksize; /* chunk size */ - char a_aiffid[4]; /* aiff chunk id 'AIFF' */ - char a_fmtid[4]; /* format chunk id 'COMM' */ - uint32 a_fmtchunksize; /* format chunk size, 18 */ - uint16 a_nchannels; /* number of channels */ - uint16 a_nframeshi; /* # of sample frames (hi) */ - uint16 a_nframeslo; /* # of sample frames (lo) */ - uint16 a_bitspersamp; /* bits per sample */ - unsigned char a_samprate[10]; /* sample rate, 80-bit float! */ -} t_aiff; - -#define AIFFHDRSIZE 38 /* probably not what sizeof() gives */ - - -#define AIFFPLUS (AIFFHDRSIZE + 8) /* header size including first chunk hdr */ - -#define WHDR1 sizeof(t_nextstep) -#define WHDR2 (sizeof(t_wave) > WHDR1 ? sizeof (t_wave) : WHDR1) -#define WRITEHDRSIZE (AIFFPLUS > WHDR2 ? AIFFPLUS : WHDR2) - -#define READHDRSIZE (16 > WHDR2 + 2 ? 16 : WHDR2 + 2) - -#define OBUFSIZE MAXPDSTRING /* assume MAXPDSTRING is bigger than headers */ - -#ifdef MSW -#include -#define BINCREATE _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY -#else -#define BINCREATE O_WRONLY | O_CREAT | O_TRUNC -#endif - -/* this routine returns 1 if the high order byte comes at the lower -address on our architecture (big-endianness.). It's 1 for Motorola, -0 for Intel: */ - -extern int garray_ambigendian(void); - -/* byte swappers */ - -static uint32 swap4(uint32 n, int doit) -{ - if (doit) - return (((n & 0xff) << 24) | ((n & 0xff00) << 8) | - ((n & 0xff0000) >> 8) | ((n & 0xff000000) >> 24)); - else return (n); -} - -static uint16 swap2(uint32 n, int doit) -{ - if (doit) - return (((n & 0xff) << 8) | ((n & 0xff00) >> 8)); - else return (n); -} - -static void swapstring(char *foo, int doit) -{ - if (doit) - { - char a = foo[0], b = foo[1], c = foo[2], d = foo[3]; - foo[0] = d; foo[1] = c; foo[2] = b; foo[3] = a; - } -} - -/******************** soundfile access routines **********************/ - -/* This routine opens a file, looks for either a nextstep or "wave" header, -* seeks to end of it, and fills in bytes per sample and number of channels. -* Only 2- and 3-byte fixed-point samples and 4-byte floating point samples -* are supported. If "headersize" is nonzero, the -* caller should supply the number of channels, endinanness, and bytes per -* sample; the header is ignored. Otherwise, the routine tries to read the -* header and fill in the properties. -*/ - -int open_soundfile(const char *dirname, const char *filename, int headersize, - int *p_bytespersamp, int *p_bigendian, int *p_nchannels, long *p_bytelimit, - long skipframes) -{ - char buf[OBUFSIZE], *bufptr; - int fd, format, nchannels, bigendian, bytespersamp, swap, sysrtn; - long bytelimit = 0x7fffffff; - errno = 0; - fd = open_via_path(dirname, filename, - "", buf, &bufptr, MAXPDSTRING, 1); - if (fd < 0) - return (-1); - if (headersize >= 0) /* header detection overridden */ - { - bigendian = *p_bigendian; - nchannels = *p_nchannels; - bytespersamp = *p_bytespersamp; - bytelimit = *p_bytelimit; - } - else - { - int bytesread = read(fd, buf, READHDRSIZE); - int format; - if (bytesread < 4) - goto badheader; - if (!strncmp(buf, ".snd", 4)) - format = FORMAT_NEXT, bigendian = 1; - else if (!strncmp(buf, "dns.", 4)) - format = FORMAT_NEXT, bigendian = 0; - else if (!strncmp(buf, "RIFF", 4)) - { - if (bytesread < 12 || strncmp(buf + 8, "WAVE", 4)) - goto badheader; - format = FORMAT_WAVE, bigendian = 0; - } - else if (!strncmp(buf, "FORM", 4)) - { - if (bytesread < 12 || strncmp(buf + 8, "AIFF", 4)) - goto badheader; - format = FORMAT_AIFF, bigendian = 1; - } - else - goto badheader; - swap = (bigendian != garray_ambigendian()); - if (format == FORMAT_NEXT) /* nextstep header */ - { - uint32 param; - if (bytesread < (int)sizeof(t_nextstep)) - goto badheader; - nchannels = swap4(((t_nextstep *)buf)->ns_nchans, swap); - format = swap4(((t_nextstep *)buf)->ns_format, swap); - headersize = swap4(((t_nextstep *)buf)->ns_onset, swap); - if (format == NS_FORMAT_LINEAR_16) - bytespersamp = 2; - else if (format == NS_FORMAT_LINEAR_24) - bytespersamp = 3; - else if (format == NS_FORMAT_FLOAT) - bytespersamp = 4; - else goto badheader; - bytelimit = 0x7fffffff; - } - else if (format == FORMAT_WAVE) /* wave header */ - { - /* This is awful. You have to skip over chunks, - except that if one happens to be a "fmt" chunk, you want to - find out the format from that one. The case where the - "fmt" chunk comes after the audio isn't handled. */ - headersize = 12; - if (bytesread < 20) - goto badheader; - /* First we guess a number of channels, etc., in case there's - no "fmt" chunk to follow. */ - nchannels = 1; - bytespersamp = 2; - /* copy the first chunk header to beginnning of buffer. */ - memcpy(buf, buf + headersize, sizeof(t_wavechunk)); - /* post("chunk %c %c %c %c", - ((t_wavechunk *)buf)->wc_id[0], - ((t_wavechunk *)buf)->wc_id[1], - ((t_wavechunk *)buf)->wc_id[2], - ((t_wavechunk *)buf)->wc_id[3]); */ - /* read chunks in loop until we get to the data chunk */ - while (strncmp(((t_wavechunk *)buf)->wc_id, "data", 4)) - { - long chunksize = swap4(((t_wavechunk *)buf)->wc_size, - swap), seekto = headersize + chunksize + 8, seekout; - - if (!strncmp(((t_wavechunk *)buf)->wc_id, "fmt ", 4)) - { - long commblockonset = headersize + 8; - seekout = lseek(fd, commblockonset, SEEK_SET); - if (seekout != commblockonset) - goto badheader; - if (read(fd, buf, sizeof(t_fmt)) < (int) sizeof(t_fmt)) - goto badheader; - nchannels = swap2(((t_fmt *)buf)->f_nchannels, swap); - format = swap2(((t_fmt *)buf)->f_nbitspersample, swap); - if (format == 16) - bytespersamp = 2; - else if (format == 24) - bytespersamp = 3; - else if (format == 32) - bytespersamp = 4; - else goto badheader; - } - seekout = lseek(fd, seekto, SEEK_SET); - if (seekout != seekto) - goto badheader; - if (read(fd, buf, sizeof(t_wavechunk)) < - (int) sizeof(t_wavechunk)) - goto badheader; - /* post("new chunk %c %c %c %c at %d", - ((t_wavechunk *)buf)->wc_id[0], - ((t_wavechunk *)buf)->wc_id[1], - ((t_wavechunk *)buf)->wc_id[2], - ((t_wavechunk *)buf)->wc_id[3], seekto); */ - headersize = seekto; - } - bytelimit = swap4(((t_wavechunk *)buf)->wc_size, swap); - headersize += 8; - } - else - { - /* AIFF. same as WAVE; actually predates it. Disgusting. */ - headersize = 12; - if (bytesread < 20) - goto badheader; - /* First we guess a number of channels, etc., in case there's - no COMM block to follow. */ - nchannels = 1; - bytespersamp = 2; - /* copy the first chunk header to beginnning of buffer. */ - memcpy(buf, buf + headersize, sizeof(t_datachunk)); - /* read chunks in loop until we get to the data chunk */ - while (strncmp(((t_datachunk *)buf)->dc_id, "SSND", 4)) - { - long chunksize = swap4(((t_datachunk *)buf)->dc_size, - swap), seekto = headersize + chunksize + 8, seekout; - /* post("chunk %c %c %c %c seek %d", - ((t_datachunk *)buf)->dc_id[0], - ((t_datachunk *)buf)->dc_id[1], - ((t_datachunk *)buf)->dc_id[2], - ((t_datachunk *)buf)->dc_id[3], seekto); */ - if (!strncmp(((t_datachunk *)buf)->dc_id, "COMM", 4)) - { - long commblockonset = headersize + 8; - seekout = lseek(fd, commblockonset, SEEK_SET); - if (seekout != commblockonset) - goto badheader; - if (read(fd, buf, sizeof(t_comm)) < - (int) sizeof(t_comm)) - goto badheader; - nchannels = swap2(((t_comm *)buf)->c_nchannels, swap); - format = swap2(((t_comm *)buf)->c_bitspersamp, swap); - if (format == 16) - bytespersamp = 2; - else if (format == 24) - bytespersamp = 3; - else goto badheader; - } - seekout = lseek(fd, seekto, SEEK_SET); - if (seekout != seekto) - goto badheader; - if (read(fd, buf, sizeof(t_datachunk)) < - (int) sizeof(t_datachunk)) - goto badheader; - headersize = seekto; - } - bytelimit = swap4(((t_datachunk *)buf)->dc_size, swap); - headersize += 8; - } - } - /* seek past header and any sample frames to skip */ - sysrtn = lseek(fd, nchannels * bytespersamp * skipframes + headersize, 0); - if (sysrtn != nchannels * bytespersamp * skipframes + headersize) - return (-1); - bytelimit -= nchannels * bytespersamp * skipframes; - if (bytelimit < 0) - bytelimit = 0; - /* copy sample format back to caller */ - *p_bigendian = bigendian; - *p_nchannels = nchannels; - *p_bytespersamp = bytespersamp; - *p_bytelimit = bytelimit; - return (fd); -badheader: - /* the header wasn't recognized. We're threadable here so let's not - print out the error... */ - errno = EIO; - return (-1); -} - -static void soundfile_xferin(int sfchannels, int nvecs, float **vecs, - long itemsread, unsigned char *buf, int nitems, int bytespersamp, - int bigendian) -{ - int i, j; - unsigned char *sp, *sp2; - float *fp; - int nchannels = (sfchannels < nvecs ? sfchannels : nvecs); - int bytesperframe = bytespersamp * sfchannels; - for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp) - { - if (bytespersamp == 2) - { - if (bigendian) - { - for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; - j < nitems; j++, sp2 += bytesperframe, fp++) - *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16)); - } - else - { - for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; - j < nitems; j++, sp2 += bytesperframe, fp++) - *fp = SCALE * ((sp2[1] << 24) | (sp2[0] << 16)); - } - } - else if (bytespersamp == 3) - { - if (bigendian) - { - for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; - j < nitems; j++, sp2 += bytesperframe, fp++) - *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16) - | (sp2[2] << 8)); - } - else - { - for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; - j < nitems; j++, sp2 += bytesperframe, fp++) - *fp = SCALE * ((sp2[2] << 24) | (sp2[1] << 16) - | (sp2[0] << 8)); - } - } - else if (bytespersamp == 4) - { - if (bigendian) - { - for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; - j < nitems; j++, sp2 += bytesperframe, fp++) - *(long *)fp = ((sp2[0] << 24) | (sp2[1] << 16) - | (sp2[2] << 8) | sp2[3]); - } - else - { - for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; - j < nitems; j++, sp2 += bytesperframe, fp++) - *(long *)fp = ((sp2[3] << 24) | (sp2[2] << 16) - | (sp2[1] << 8) | sp2[0]); - } - } - } - /* zero out other outputs */ - for (i = sfchannels; i < nvecs; i++) - for (j = nitems, fp = vecs[i]; j--; ) - *fp++ = 0; - -} - - /* soundfiler_write ... - - usage: write [flags] filename table ... - flags: - -nframes - -skip - -bytes - -normalize - -nextstep - -wave - -big - -little - */ - - /* the routine which actually does the work should LATER also be called - from garray_write16. */ - - - /* Parse arguments for writing. The "obj" argument is only for flagging - errors. For streaming to a file the "normalize", "onset" and "nframes" - arguments shouldn't be set but the calling routine flags this. */ - -static int soundfiler_writeargparse(void *obj, int *p_argc, t_atom **p_argv, - t_symbol **p_filesym, - int *p_filetype, int *p_bytespersamp, int *p_swap, int *p_bigendian, - int *p_normalize, long *p_onset, long *p_nframes, float *p_rate) -{ - int argc = *p_argc; - t_atom *argv = *p_argv; - int bytespersamp = 2, bigendian = 0, - endianness = -1, swap, filetype = -1, normalize = 0; - long onset = 0, nframes = 0x7fffffff; - t_symbol *filesym; - float rate = -1; - - while (argc > 0 && argv->a_type == A_SYMBOL && - *argv->a_w.w_symbol->s_name == '-') - { - char *flag = argv->a_w.w_symbol->s_name + 1; - if (!strcmp(flag, "skip")) - { - if (argc < 2 || argv[1].a_type != A_FLOAT || - ((onset = argv[1].a_w.w_float) < 0)) - goto usage; - argc -= 2; argv += 2; - } - else if (!strcmp(flag, "nframes")) - { - if (argc < 2 || argv[1].a_type != A_FLOAT || - ((nframes = argv[1].a_w.w_float) < 0)) - goto usage; - argc -= 2; argv += 2; - } - else if (!strcmp(flag, "bytes")) - { - if (argc < 2 || argv[1].a_type != A_FLOAT || - ((bytespersamp = argv[1].a_w.w_float) < 2) || - bytespersamp > 4) - goto usage; - argc -= 2; argv += 2; - } - else if (!strcmp(flag, "normalize")) - { - normalize = 1; - argc -= 1; argv += 1; - } - else if (!strcmp(flag, "wave")) - { - filetype = FORMAT_WAVE; - argc -= 1; argv += 1; - } - else if (!strcmp(flag, "nextstep")) - { - filetype = FORMAT_NEXT; - argc -= 1; argv += 1; - } - else if (!strcmp(flag, "aiff")) - { - filetype = FORMAT_AIFF; - argc -= 1; argv += 1; - } - else if (!strcmp(flag, "big")) - { - endianness = 1; - argc -= 1; argv += 1; - } - else if (!strcmp(flag, "little")) - { - endianness = 0; - argc -= 1; argv += 1; - } - else if (!strcmp(flag, "r") || !strcmp(flag, "rate")) - { - if (argc < 2 || argv[1].a_type != A_FLOAT || - ((rate = argv[1].a_w.w_float) <= 0)) - goto usage; - argc -= 2; argv += 2; - } - else goto usage; - } - if (!argc || argv->a_type != A_SYMBOL) - goto usage; - filesym = argv->a_w.w_symbol; - - /* check if format not specified and fill in */ - if (filetype < 0) - { - if (strlen(filesym->s_name) >= 5 && - (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".aif") || - !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".AIF"))) - filetype = FORMAT_AIFF; - if (strlen(filesym->s_name) >= 6 && - (!strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".aiff") || - !strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".AIFF"))) - filetype = FORMAT_AIFF; - if (strlen(filesym->s_name) >= 5 && - (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".snd") || - !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".SND"))) - filetype = FORMAT_NEXT; - if (strlen(filesym->s_name) >= 4 && - (!strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".au") || - !strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".AU"))) - filetype = FORMAT_NEXT; - if (filetype < 0) - filetype = FORMAT_WAVE; - } - /* don't handle AIFF floating point samples */ - if (bytespersamp == 4) - { - if (filetype == FORMAT_AIFF) - { - pd_error(obj, "AIFF floating-point file format unavailable"); - goto usage; - } - } - /* for WAVE force little endian; for nextstep use machine native */ - if (filetype == FORMAT_WAVE) - { - bigendian = 0; - if (endianness == 1) - pd_error(obj, "WAVE file forced to little endian"); - } - else if (filetype == FORMAT_AIFF) - { - bigendian = 1; - if (endianness == 0) - pd_error(obj, "AIFF file forced to big endian"); - } - else if (endianness == -1) - { - bigendian = garray_ambigendian(); - } - else bigendian = endianness; - swap = (bigendian != garray_ambigendian()); - - argc--; argv++; - - *p_argc = argc; - *p_argv = argv; - *p_filesym = filesym; - *p_filetype = filetype; - *p_bytespersamp = bytespersamp; - *p_swap = swap; - *p_normalize = normalize; - *p_onset = onset; - *p_nframes = nframes; - *p_bigendian = bigendian; - *p_rate = rate; - return (0); -usage: - return (-1); -} - -static int create_soundfile(t_canvas *canvas, const char *filename, - int filetype, int nframes, int bytespersamp, - int bigendian, int nchannels, int swap, float samplerate) -{ - char filenamebuf[MAXPDSTRING], buf2[MAXPDSTRING]; - char headerbuf[WRITEHDRSIZE]; - t_wave *wavehdr = (t_wave *)headerbuf; - t_nextstep *nexthdr = (t_nextstep *)headerbuf; - t_aiff *aiffhdr = (t_aiff *)headerbuf; - int fd, headersize = 0; - - strncpy(filenamebuf, filename, MAXPDSTRING-10); - filenamebuf[MAXPDSTRING-10] = 0; - - if (filetype == FORMAT_NEXT) - { - if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".snd")) - strcat(filenamebuf, ".snd"); - if (bigendian) - strncpy(nexthdr->ns_fileid, ".snd", 4); - else strncpy(nexthdr->ns_fileid, "dns.", 4); - nexthdr->ns_onset = swap4(sizeof(*nexthdr), swap); - nexthdr->ns_length = 0; - nexthdr->ns_format = swap4((bytespersamp == 3 ? NS_FORMAT_LINEAR_24 : - (bytespersamp == 4 ? NS_FORMAT_FLOAT : NS_FORMAT_LINEAR_16)), swap); - nexthdr->ns_sr = swap4(samplerate, swap); - nexthdr->ns_nchans = swap4(nchannels, swap); - strcpy(nexthdr->ns_info, "Pd "); - swapstring(nexthdr->ns_info, swap); - headersize = sizeof(t_nextstep); - } - else if (filetype == FORMAT_AIFF) - { - long datasize = nframes * nchannels * bytespersamp; - long longtmp; - static unsigned char dogdoo[] = - {0x40, 0x0e, 0xac, 0x44, 0, 0, 0, 0, 0, 0, 'S', 'S', 'N', 'D'}; - if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".aif") && - strcmp(filenamebuf + strlen(filenamebuf)-5, ".aiff")) - strcat(filenamebuf, ".aif"); - strncpy(aiffhdr->a_fileid, "FORM", 4); - aiffhdr->a_chunksize = swap4(datasize + sizeof(*aiffhdr) + 4, swap); - strncpy(aiffhdr->a_aiffid, "AIFF", 4); - strncpy(aiffhdr->a_fmtid, "COMM", 4); - aiffhdr->a_fmtchunksize = swap4(18, swap); - aiffhdr->a_nchannels = swap2(nchannels, swap); - longtmp = swap4(nframes, swap); - memcpy(&aiffhdr->a_nframeshi, &longtmp, 4); - aiffhdr->a_bitspersamp = swap2(8 * bytespersamp, swap); - memcpy(aiffhdr->a_samprate, dogdoo, sizeof(dogdoo)); - longtmp = swap4(datasize, swap); - memcpy(aiffhdr->a_samprate + sizeof(dogdoo), &longtmp, 4); - headersize = AIFFPLUS; - } - else /* WAVE format */ - { - long datasize = nframes * nchannels * bytespersamp; - if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".wav")) - strcat(filenamebuf, ".wav"); - strncpy(wavehdr->w_fileid, "RIFF", 4); - wavehdr->w_chunksize = swap4(datasize + sizeof(*wavehdr) - 8, swap); - strncpy(wavehdr->w_waveid, "WAVE", 4); - strncpy(wavehdr->w_fmtid, "fmt ", 4); - wavehdr->w_fmtchunksize = swap4(16, swap); - wavehdr->w_fmttag = - swap2((bytespersamp == 4 ? WAV_FLOAT : WAV_INT), swap); - wavehdr->w_nchannels = swap2(nchannels, swap); - wavehdr->w_samplespersec = swap4(samplerate, swap); - wavehdr->w_navgbytespersec = - swap4((int)(samplerate * nchannels * bytespersamp), swap); - wavehdr->w_nblockalign = swap2(nchannels * bytespersamp, swap); - wavehdr->w_nbitspersample = swap2(8 * bytespersamp, swap); - strncpy(wavehdr->w_datachunkid, "data", 4); - wavehdr->w_datachunksize = swap4(datasize, swap); - headersize = sizeof(t_wave); - } - - canvas_makefilename(canvas, filenamebuf, buf2, MAXPDSTRING); - sys_bashfilename(buf2, buf2); - if ((fd = open(buf2, BINCREATE, 0666)) < 0) - return (-1); - - if (write(fd, headerbuf, headersize) < headersize) - { - close (fd); - return (-1); - } - return (fd); -} - -static void soundfile_finishwrite(void *obj, char *filename, int fd, - int filetype, long nframes, long itemswritten, int bytesperframe, int swap) -{ - if (itemswritten < nframes) - { - if (nframes < 0x7fffffff) - pd_error(obj, "soundfiler_write: %d out of %d bytes written", - itemswritten, nframes); - /* try to fix size fields in header */ - if (filetype == FORMAT_WAVE) - { - long datasize = itemswritten * bytesperframe, mofo; - - if (lseek(fd, - ((char *)(&((t_wave *)0)->w_chunksize)) - (char *)0, - SEEK_SET) == 0) - goto baddonewrite; - mofo = swap4(datasize + sizeof(t_wave) - 8, swap); - if (write(fd, (char *)(&mofo), 4) < 4) - goto baddonewrite; - if (lseek(fd, - ((char *)(&((t_wave *)0)->w_datachunksize)) - (char *)0, - SEEK_SET) == 0) - goto baddonewrite; - mofo = swap4(datasize, swap); - if (write(fd, (char *)(&mofo), 4) < 4) - goto baddonewrite; - } - if (filetype == FORMAT_AIFF) - { - long mofo; - if (lseek(fd, - ((char *)(&((t_aiff *)0)->a_nframeshi)) - (char *)0, - SEEK_SET) == 0) - goto baddonewrite; - mofo = swap4(nframes, swap); - if (write(fd, (char *)(&mofo), 4) < 4) - goto baddonewrite; - } - if (filetype == FORMAT_NEXT) - { - /* do it the lazy way: just set the size field to 'unknown size'*/ - uint32 nextsize = 0xffffffff; - if (lseek(fd, 8, SEEK_SET) == 0) - { - goto baddonewrite; - } - if (write(fd, &nextsize, 4) < 4) - { - goto baddonewrite; - } - } - } - return; -baddonewrite: - post("%s: %s", filename, strerror(errno)); -} - -static void soundfile_xferout(int nchannels, float **vecs, - unsigned char *buf, int nitems, long onset, int bytespersamp, - int bigendian, float normalfactor) -{ - int i, j; - unsigned char *sp, *sp2; - float *fp; - int bytesperframe = bytespersamp * nchannels; - long xx; - for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp) - { - if (bytespersamp == 2) - { - float ff = normalfactor * 32768.; - if (bigendian) - { - for (j = 0, sp2 = sp, fp = vecs[i] + onset; - j < nitems; j++, sp2 += bytesperframe, fp++) - { - int xx = 32768. + (*fp * ff); - xx -= 32768; - if (xx < -32767) - xx = -32767; - if (xx > 32767) - xx = 32767; - sp2[0] = (xx >> 8); - sp2[1] = xx; - } - } - else - { - for (j = 0, sp2 = sp, fp=vecs[i] + onset; - j < nitems; j++, sp2 += bytesperframe, fp++) - { - int xx = 32768. + (*fp * ff); - xx -= 32768; - if (xx < -32767) - xx = -32767; - if (xx > 32767) - xx = 32767; - sp2[1] = (xx >> 8); - sp2[0] = xx; - } - } - } - else if (bytespersamp == 3) - { - float ff = normalfactor * 8388608.; - if (bigendian) - { - for (j = 0, sp2 = sp, fp=vecs[i] + onset; - j < nitems; j++, sp2 += bytesperframe, fp++) - { - int xx = 8388608. + (*fp * ff); - xx -= 8388608; - if (xx < -8388607) - xx = -8388607; - if (xx > 8388607) - xx = 8388607; - sp2[0] = (xx >> 16); - sp2[1] = (xx >> 8); - sp2[2] = xx; - } - } - else - { - for (j = 0, sp2 = sp, fp=vecs[i] + onset; - j < nitems; j++, sp2 += bytesperframe, fp++) - { - int xx = 8388608. + (*fp * ff); - xx -= 8388608; - if (xx < -8388607) - xx = -8388607; - if (xx > 8388607) - xx = 8388607; - sp2[2] = (xx >> 16); - sp2[1] = (xx >> 8); - sp2[0] = xx; - } - } - } - else if (bytespersamp == 4) - { - if (bigendian) - { - for (j = 0, sp2 = sp, fp=vecs[i] + onset; - j < nitems; j++, sp2 += bytesperframe, fp++) - { - float f2 = *fp * normalfactor; - xx = *(long *)&f2; - sp2[0] = (xx >> 24); sp2[1] = (xx >> 16); - sp2[2] = (xx >> 8); sp2[3] = xx; - } - } - else - { - for (j = 0, sp2 = sp, fp=vecs[i] + onset; - j < nitems; j++, sp2 += bytesperframe, fp++) - { - float f2 = *fp * normalfactor; - xx = *(long *)&f2; - sp2[3] = (xx >> 24); sp2[2] = (xx >> 16); - sp2[1] = (xx >> 8); sp2[0] = xx; - } - } - } - } -} - - - -/* ------- threadedsf - reads and writes soundfiles to/from "garrays" ---- */ - -// in pd's soundfiler maxsize is 4000000 -// threaded we should be able to hande a bit more -#define DEFMAXSIZE 40000000 -#define SAMPBUFSIZE 1024 - - -static t_class *threadedsf_class; - -typedef struct _threadedsf -{ - t_object x_obj; - t_canvas *x_canvas; -} t_threadedsf; - -#include - -#ifdef _POSIX_MEMLOCK -#include -#endif /* _POSIX_MEMLOCK */ - -#ifdef DEBUG -#define SFDEBUG -#endif - -static pthread_t sf_thread_id; /* id of soundfiler thread */ -//static pthread_attr_t sf_attr; - -typedef struct _sfprocess -{ - void (* process) (t_threadedsf *,t_symbol *, - int, t_atom *); /* function to call */ - t_threadedsf * x; /* soundfiler */ - int argc; - t_atom * argv; - struct _sfprocess * next; /* next object in queue */ - pthread_mutex_t mutex; -} t_sfprocess; - -/* this is the queue for all threadedsf objects */ -typedef struct _sfqueue -{ - t_sfprocess * begin; - t_sfprocess * end; - pthread_mutex_t mutex; - pthread_cond_t cond; -} t_sfqueue; - -static t_sfqueue * threadedsf_queue; - - -/* we fill the queue */ -void threadedsf_queue_add(void (* process) (t_threadedsf *,t_symbol *, - int,t_atom *), void * x, - int argc, t_atom * argv) -{ - /* preparing entry */ - t_sfprocess * last_entry = (t_sfprocess*)getbytes(sizeof(t_sfprocess)); - -#ifdef SFDEBUG - post("adding process to queue"); -#endif - - pthread_mutex_init(&(last_entry->mutex), NULL); - pthread_mutex_lock(&(last_entry->mutex)); - last_entry->process = process; - last_entry->x = x; - last_entry->argc = argc; - last_entry->argv = copybytes (argv, argc * sizeof(t_atom)); - last_entry->next = NULL; - pthread_mutex_unlock(&(last_entry->mutex)); - - /* add new entry to queue */ - pthread_mutex_lock(&(threadedsf_queue->mutex)); - - if (threadedsf_queue->begin==NULL) - { - threadedsf_queue->begin=last_entry; - threadedsf_queue->end=last_entry; - } - else - { - pthread_mutex_lock(&(threadedsf_queue->end->mutex)); - threadedsf_queue->end->next=last_entry; - pthread_mutex_unlock(&(threadedsf_queue->end->mutex)); - threadedsf_queue->end=last_entry; - } - - - if ( threadedsf_queue->begin == threadedsf_queue->end ) - { -#ifdef DEBUG - post("signaling"); -#endif - pthread_mutex_unlock(&(threadedsf_queue->mutex)); - - /* and signal the helper thread */ - pthread_cond_signal(&(threadedsf_queue->cond)); - } - else - { -#ifdef DEBUG - post("not signaling"); -#endif - pthread_mutex_unlock(&(threadedsf_queue->mutex)); - } - return; -} - -/* global threadedsf thread ... sleeping until signaled */ -void threadedsf_thread(void) -{ - t_sfprocess * me; - t_sfprocess * next; - -#ifdef DEBUG - post("threadedsf_thread ID: %d", pthread_self()); -#endif - while (1) - { -#ifdef DEBUG - post("Soundfiler sleeping"); -#endif - pthread_cond_wait(&threadedsf_queue->cond, &threadedsf_queue->mutex); -#ifdef DEBUG - post("Soundfiler awake"); -#endif - - /* work on the queue */ - while (threadedsf_queue->begin!=NULL) - { - post("threadedsf: locked queue"); - /* locking process */ - pthread_mutex_lock(&(threadedsf_queue->begin->mutex)); - - me = threadedsf_queue->begin; - - pthread_mutex_unlock(&(me->mutex)); - pthread_mutex_unlock(&(threadedsf_queue->mutex)); -#ifdef DEBUG - post("threadedsf: mutex unlocked, running process"); -#endif - - /* running the specific function */ - me->process(me->x, NULL, me->argc, me->argv); -#ifdef DEBUG - post("threadedsf: process done, locking mutex"); -#endif - - pthread_mutex_lock(&(threadedsf_queue->mutex)); - pthread_mutex_lock(&(me->mutex)); - - /* freeing the argument vector */ - freebytes(me->argv, sizeof(t_atom) * me->argc); - - /* the process struct */ - next=me->next; - threadedsf_queue->begin=next; - freebytes(me, sizeof(t_sfprocess)); - }; - threadedsf_queue->end=NULL; - } -} - -extern int sys_hipriority; /* real-time flag, true if priority boosted */ - -/* create soundfiler thread */ -void sys_start_sfthread(void) -{ - pthread_attr_t sf_attr; - struct sched_param sf_param; - - int status; - - //initialize queue - threadedsf_queue = getbytes (sizeof(t_sfqueue)); - - pthread_mutex_init (&threadedsf_queue->mutex,NULL); - pthread_cond_init (&threadedsf_queue->cond,NULL); - - threadedsf_queue->begin=threadedsf_queue->end=NULL; -/* pthread_mutex_unlock(&(threadedsf_queue->mutex)); */ - - // initialize thread - pthread_attr_init(&sf_attr); - -//#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING - sf_param.sched_priority=sched_get_priority_min(SCHED_OTHER); - pthread_attr_setschedparam(&sf_attr,&sf_param); -/* pthread_attr_setinheritsched(&sf_attr,PTHREAD_EXPLICIT_SCHED); */ - -#ifdef UNIX - if (sys_hipriority == 1 && getuid() == 0) - { - sf_param.sched_priority=sched_get_priority_min(SCHED_RR); - pthread_attr_setschedpolicy(&sf_attr,SCHED_RR); - } - else - { -/* pthread_attr_setschedpolicy(&sf_attr,SCHED_OTHER); */ -/* sf_param.sched_priority=sched_get_priority_min(SCHED_OTHER); */ - } -#endif /* UNIX */ - -//#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ - - //start thread - status = pthread_create(&sf_thread_id, &sf_attr, - (void *) threadedsf_thread,NULL); - if (status != 0) - error("Couldn't create threadedsf thread: %d",status); -#ifdef DEBUG - else - post("global threadedsf thread launched, priority: %d", - sf_param.sched_priority); -#endif -} - -static void threadedsf_t_write(t_threadedsf *x, t_symbol *s, - int argc, t_atom *argv); - -static void threadedsf_t_write_addq(t_threadedsf *x, t_symbol *s, - int argc, t_atom *argv) -{ - threadedsf_queue_add(threadedsf_t_write,(void *)x,argc, argv); -} - -static void threadedsf_t_read(t_threadedsf *x, t_symbol *s, - int argc, t_atom *argv); - -static void threadedsf_t_read_addq(t_threadedsf *x, t_symbol *s, - int argc, t_atom *argv) -{ - threadedsf_queue_add(threadedsf_t_read,(void *)x,argc, argv); -} - - - /* threadedsf_read ... - - usage: read [flags] filename table ... - flags: - -skip ... frames to skip in file - -nframes - -onset ... onset in table to read into (NOT DONE YET) - -raw - -resize - -maxsize - - TB: adapted for threaded use - */ - -/* callback prototypes */ -static t_int threadedsf_read_update_garray(t_int * w); -static t_int threadedsf_read_update_graphics(t_int * w); -static t_int threadedsf_read_output(t_int * w); - - -static void threadedsf_t_read(t_threadedsf *x, t_symbol *s, - int argc, t_atom *argv) -{ - int headersize = -1, channels = 0, bytespersamp = 0, bigendian = 0, - resize = 0, i, j; - long skipframes = 0, - nframes = 0, - finalsize = 0, /* new size */ - maxsize = DEFMAXSIZE, - itemsread = 0, - bytelimit = 0x7fffffff; - int fd = -1; - char endianness, *filename; - t_garray *garrays[MAXSFCHANS]; - t_float *vecs[MAXSFCHANS]; /* the old array */ - t_float *nvecs[MAXSFCHANS]; /* the new array */ - int vecsize[MAXSFCHANS]; /* the old array size */ - char sampbuf[SAMPBUFSIZE]; - int bufframes, nitems; - FILE *fp; - pthread_cond_t resume_after_callback = PTHREAD_COND_INITIALIZER; - pthread_mutex_t resume_after_callback_mutex = PTHREAD_MUTEX_INITIALIZER; /* dummy */ - t_int* outargs; - - while (argc > 0 && argv->a_type == A_SYMBOL && - *argv->a_w.w_symbol->s_name == '-') - { - char *flag = argv->a_w.w_symbol->s_name + 1; - if (!strcmp(flag, "skip")) - { - if (argc < 2 || argv[1].a_type != A_FLOAT || - ((skipframes = argv[1].a_w.w_float) < 0)) - goto usage; - argc -= 2; argv += 2; - } - else if (!strcmp(flag, "nframes")) - { - if (argc < 2 || argv[1].a_type != A_FLOAT || - ((nframes = argv[1].a_w.w_float) < 0)) - goto usage; - argc -= 2; argv += 2; - } - else if (!strcmp(flag, "raw")) - { - if (argc < 5 || - argv[1].a_type != A_FLOAT || - ((headersize = argv[1].a_w.w_float) < 0) || - argv[2].a_type != A_FLOAT || - ((channels = argv[2].a_w.w_float) < 1) || - (channels > MAXSFCHANS) || - argv[3].a_type != A_FLOAT || - ((bytespersamp = argv[3].a_w.w_float) < 2) || - (bytespersamp > 4) || - argv[4].a_type != A_SYMBOL || - ((endianness = argv[4].a_w.w_symbol->s_name[0]) != 'b' - && endianness != 'l' && endianness != 'n')) - goto usage; - if (endianness == 'b') - bigendian = 1; - else if (endianness == 'l') - bigendian = 0; - else - bigendian = garray_ambigendian(); - argc -= 5; argv += 5; - } - else if (!strcmp(flag, "resize")) - { - resize = 1; - argc -= 1; argv += 1; - } - else if (!strcmp(flag, "maxsize")) - { - if (argc < 2 || argv[1].a_type != A_FLOAT || - ((maxsize = argv[1].a_w.w_float) < 0)) - goto usage; - resize = 1; /* maxsize implies resize. */ - argc -= 2; argv += 2; - } - else goto usage; - } - if (argc < 2 || argc > MAXSFCHANS + 1 || argv[0].a_type != A_SYMBOL) - goto usage; - filename = argv[0].a_w.w_symbol->s_name; - argc--; argv++; - - for (i = 0; i < argc; i++) - { - if (argv[i].a_type != A_SYMBOL) - goto usage; - if (!(garrays[i] = - (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class))) - { - pd_error(x, "%s: no such table", argv[i].a_w.w_symbol->s_name); - goto done; - } - else if (!garray_getfloatarray(garrays[i], &vecsize[i], &vecs[i])) - error("%s: bad template for tabwrite", - argv[i].a_w.w_symbol->s_name); - if (finalsize && finalsize != vecsize[i] && !resize) - { - post("threadedsf_read: arrays have different lengths; resizing..."); - resize = 1; - } - finalsize = vecsize[i]; - } - fd = open_soundfile(canvas_getdir(x->x_canvas)->s_name, filename, - headersize, &bytespersamp, &bigendian, &channels, &bytelimit, - skipframes); - - if (fd < 0) - { - pd_error(x, "threadedsf_read: %s: %s", filename, (errno == EIO ? - "unknown or bad header format" : strerror(errno))); - goto done; - } - - if (resize) - { - /* figure out what to resize to */ - long poswas, eofis, framesinfile; - - poswas = lseek(fd, 0, SEEK_CUR); - eofis = lseek(fd, 0, SEEK_END); - if (poswas < 0 || eofis < 0) - { - pd_error(x, "lseek failed"); - goto done; - } - lseek(fd, poswas, SEEK_SET); - framesinfile = (eofis - poswas) / (channels * bytespersamp); - if (framesinfile > maxsize) - { - pd_error(x, "threadedsf_read: truncated to %d elements", maxsize); - framesinfile = maxsize; - } - if (framesinfile > bytelimit / (channels * bytespersamp)) - framesinfile = bytelimit / (channels * bytespersamp); - finalsize = framesinfile; - - } - if (!finalsize) finalsize = 0x7fffffff; - if (finalsize > bytelimit / (channels * bytespersamp)) - finalsize = bytelimit / (channels * bytespersamp); - fp = fdopen(fd, "rb"); - bufframes = SAMPBUFSIZE / (channels * bytespersamp); - -#ifdef SFDEBUG - post("buffers: %d", argc); - post("channels: %d", channels); -#endif - -#ifdef _POSIX_MEMLOCK - munlockall(); -#endif - - /* allocate memory for new array */ - if (resize) - for (i = 0; i < argc; i++) - { -#ifdef SFDEBUG - post("allocating buffer %d",i); -#endif - nvecs[i]=getbytes (finalsize * sizeof(t_float)); - /* if we are out of memory, free it again and quit */ - if (nvecs[i]==0) - { - pd_error(x, "resize failed"); - for (j=0; j!=i;++j) - /* if the resizing fails, we'll have to free all arrays again */ - freebytes (nvecs[i],finalsize * sizeof(t_float)); - goto done; - } - /* zero samples */ - if(i > channels) - { - memset(nvecs[i],0,vecsize[i] * sizeof(t_float)); - } - } - else - for (i = 0; i < argc; i++) - { - nvecs[i] = getbytes (vecsize[i] * sizeof(t_float)); - /* if we are out of memory, free it again and quit */ - if (nvecs[i]==0) - { - pd_error(x, "resize failed"); - for (j=0; j!=i;++j) - /* if the resizing fails, we'll have to free all arrays again */ - freebytes (nvecs[i],vecsize[i] * sizeof(t_float)); - goto done; - } - /* zero samples */ - if(i > channels) - memset(nvecs[i],0,vecsize[i] * sizeof(t_float)); - } - -#ifdef SFDEBUG - post("transfer soundfile"); -#endif - - for (itemsread = 0; itemsread < finalsize; ) - { - int thisread = finalsize - itemsread; - thisread = (thisread > bufframes ? bufframes : thisread); - nitems = fread(sampbuf, channels * bytespersamp, thisread, fp); - if (nitems <= 0) break; - soundfile_xferin(channels, argc, nvecs, itemsread, - (unsigned char *)sampbuf, nitems, bytespersamp, bigendian); - itemsread += nitems; - } - -#ifdef SFDEBUG - post("zeroing remaining elements"); -#endif - /* zero out remaining elements of vectors */ - for (i = 0; i < argc; i++) - { - for (j = itemsread; j < finalsize; j++) - nvecs[i][j] = 0; - } - - /* set idle callback to switch pointers */ -#ifdef SFDEBUG - post("locked"); -#endif - for (i = 0; i < argc; i++) - { - t_int* w = (t_int*)getbytes(4*sizeof(t_int)); - - w[0] = (t_int)(garrays[i]); - w[1] = (t_int)nvecs[i]; - w[2] = (t_int)finalsize; - w[3] = (t_int)(&resume_after_callback); - - h_set_callback(&threadedsf_read_update_garray, w, 4); - - pthread_cond_wait(&resume_after_callback, &resume_after_callback_mutex); - } - -#ifdef SFDEBUG - post("unlocked, doing graphics updates"); -#endif - - /* do all graphics updates - * run this in the main thread via callback */ - for (i = 0; i < argc; i++) - { - t_int* w = (t_int*)getbytes(2*sizeof(t_int)); - - w[0] = (t_int)(garrays[i]); - w[1] = (t_int)finalsize; - - h_set_callback(&threadedsf_read_update_graphics, w, 2); - } - - /* free the old arrays */ - for (i = 0; i < argc; i++) - freebytes(vecs[i], vecsize[i] * sizeof(t_float)); - - fclose(fp); - fd = -1; - goto done; - - usage: - pd_error(x, "usage: read [flags] filename tablename..."); - post("flags: -skip -nframes -resize -maxsize ..."); - post("-raw ."); - - done: - if (fd >= 0) - close (fd); - -#ifdef _POSIX_MEMLOCK - mlockall(MCL_FUTURE); -#endif - - outargs = (t_int*)getbytes(2*sizeof(t_int)); - outargs[0] = (t_int)x->x_obj.ob_outlet; - outargs[1] = (t_int)itemsread; - h_set_callback(&threadedsf_read_output, outargs, 2); -} - -/* idle callback for threadsafe synchronisation */ -static t_int threadedsf_read_update_garray(t_int * w) -{ - t_garray* garray = (t_garray*)w[0]; - t_array *data = garray_getarray(garray); - t_int nvec = w[1]; - t_int finalsize = w[2]; - pthread_cond_t * conditional = (pthread_cond_t*) w[3]; - -#ifdef SFDEBUG - post("lock array %p", garray); -#endif - - // no garray_locks in current pd - //garray_lock(garray); - - data->a_vec = (char *) nvec; - data->a_n = finalsize; - if (garray->x_usedindsp) canvas_update_dsp(); - -#ifdef SFDEBUG - post("unlock array %p", garray); -#endif - - // no garray_locks in current pd - //garray_unlock(garray); - - /* signal helper thread */ - pthread_cond_broadcast(conditional); - - return 0; -} - -static t_int threadedsf_read_update_graphics(t_int * w) -{ - t_garray* garray = (t_garray*) w[0]; - t_glist *gl; - int n = w[1]; - /* if this is the only array in the graph, - reset the graph's coordinates */ - -#ifdef SFDEBUG - post("redraw array %p", garray); -#endif - - gl = garray->x_glist; - if (gl->gl_list == &garray->x_gobj && !garray->x_gobj.g_next) - { - vmess(&gl->gl_pd, gensym("bounds"), "ffff", - 0., gl->gl_y1, (double)(n > 1 ? n-1 : 1), gl->gl_y2); - /* close any dialogs that might have the wrong info now... */ - gfxstub_deleteforkey(gl); - } - else - garray_redraw(garray); - - return 0; -} - - -static t_int threadedsf_read_output(t_int * w) -{ - t_outlet* outlet = (t_outlet*) w[0]; - float itemsread = (float) w[1]; - -#ifdef SFDEBUG - post("bang %p", outlet); -#endif - - outlet_float (outlet, itemsread); - - return 0; -} - - - - - /* this is broken out from threadedsf_write below so garray_write can - call it too... not done yet though. */ - -long threadedsf_t_dowrite(void *obj, t_canvas *canvas, - int argc, t_atom *argv) -{ - int bytespersamp, bigendian, - swap, filetype, normalize, i, j, nchannels; - long onset, nframes, itemswritten = 0; - t_garray *garrays[MAXSFCHANS]; - t_float *vecs[MAXSFCHANS]; - char sampbuf[SAMPBUFSIZE]; - int bufframes; - int fd = -1; - float normfactor, biggest = 0, samplerate; - t_symbol *filesym; - - if (soundfiler_writeargparse(obj, &argc, &argv, &filesym, &filetype, - &bytespersamp, &swap, &bigendian, &normalize, &onset, &nframes, - &samplerate)) - goto usage; - nchannels = argc; - if (nchannels < 1 || nchannels > MAXSFCHANS) - goto usage; - if (samplerate < 0) - samplerate = sys_getsr(); - for (i = 0; i < nchannels; i++) - { - int vecsize; - if (argv[i].a_type != A_SYMBOL) - goto usage; - if (!(garrays[i] = - (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class))) - { - pd_error(obj, "%s: no such table", argv[i].a_w.w_symbol->s_name); - goto fail; - } - else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i])) - error("%s: bad template for tabwrite", - argv[i].a_w.w_symbol->s_name); - if (nframes > vecsize - onset) - nframes = vecsize - onset; - - for (j = 0; j < vecsize; j++) - { - if (vecs[i][j] > biggest) - biggest = vecs[i][j]; - else if (-vecs[i][j] > biggest) - biggest = -vecs[i][j]; - } - } - if (nframes <= 0) - { - pd_error(obj, "threadedsf_write: no samples at onset %ld", onset); - goto fail; - } - - if ((fd = create_soundfile(canvas, filesym->s_name, filetype, - nframes, bytespersamp, bigendian, nchannels, - swap, samplerate)) < 0) - { - post("%s: %s\n", filesym->s_name, strerror(errno)); - goto fail; - } - if (!normalize) - { - if ((bytespersamp != 4) && (biggest > 1)) - { - post("%s: normalizing max amplitude %f to 1", filesym->s_name, biggest); - normalize = 1; - } - else post("%s: biggest amplitude = %f", filesym->s_name, biggest); - } - if (normalize) - normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1); - else normfactor = 1; - - bufframes = SAMPBUFSIZE / (nchannels * bytespersamp); - - for (itemswritten = 0; itemswritten < nframes; ) - { - int thiswrite = nframes - itemswritten, nbytes; - thiswrite = (thiswrite > bufframes ? bufframes : thiswrite); - soundfile_xferout(argc, vecs, (unsigned char *)sampbuf, thiswrite, - onset, bytespersamp, bigendian, normfactor); - nbytes = write(fd, sampbuf, nchannels * bytespersamp * thiswrite); - if (nbytes < nchannels * bytespersamp * thiswrite) - { - post("%s: %s", filesym->s_name, strerror(errno)); - if (nbytes > 0) - itemswritten += nbytes / (nchannels * bytespersamp); - break; - } - itemswritten += thiswrite; - onset += thiswrite; - } - if (fd >= 0) - { - soundfile_finishwrite(obj, filesym->s_name, fd, - filetype, nframes, itemswritten, nchannels * bytespersamp, swap); - close (fd); - } - return ((float)itemswritten); -usage: - pd_error(obj, "usage: write [flags] filename tablename..."); - post("flags: -skip -nframes -bytes -wave -aiff -nextstep ..."); - post("-big -little -normalize"); - post("(defaults to a 16-bit wave file)."); -fail: - if (fd >= 0) - close (fd); - return (0); -} - -static void threadedsf_t_write(t_threadedsf *x, t_symbol *s, - int argc, t_atom *argv) -{ - long bozo = threadedsf_t_dowrite(x, x->x_canvas, - argc, argv); - sys_lock(); - outlet_float(x->x_obj.ob_outlet, (float)bozo); - sys_lock(); -} - -static void threadedsf_t_resize(t_threadedsf *x, t_symbol *s, - int argc, t_atom *argv); - -static void threadedsf_t_resize_addq(t_threadedsf *x, t_symbol *s, - int argc, t_atom *argv) -{ - threadedsf_queue_add(threadedsf_t_resize,(void *)x,argc, argv); -} - - - /* TB: threadedsf_t_resize ... - usage: resize table size - adapted from garray_resize - */ -static void threadedsf_t_resize(t_threadedsf *y, t_symbol *s, - int argc, t_atom *argv) -{ - int was, elemsize; /* array contains was elements of size elemsize */ - t_float * vec; /* old array */ - t_glist *gl; - int n; /* resize of n elements */ - char *nvec; /* new array */ - - t_garray * x = (t_garray *)pd_findbyclass(argv[0].a_w.w_symbol, garray_class); - t_array *data = garray_getarray(x); // TODO muss der im lock sein ? - - if (!(x)) - { - pd_error(y, "%s: no such table", argv[0].a_w.w_symbol->s_name); - goto usage; - } - - vec = (t_float*) data->a_vec; - was = data->a_n; - - if ((argv+1)->a_type == A_FLOAT) - { - n = (int) (argv+1)->a_w.w_float; - } - else - { - goto usage; - } - - if (n == was) - { - return; - } - - if (n < 1) n = 1; - elemsize = template_findbyname(data->a_templatesym)->t_n * sizeof(t_word); - -#ifdef _POSIX_MEMLOCK - munlockall(); -#endif - if (was > n) - { - nvec = (char*)copybytes(data->a_vec, was * elemsize); - } - else - { - nvec = getbytes(n * elemsize); - memcpy (nvec, data->a_vec, was * elemsize); - - /* LATER should check t_resizebytes result */ - memset(nvec + was*elemsize, - 0, (n - was) * elemsize); - } - if (!nvec) - { - pd_error(x, "array resize failed: out of memory"); -#ifdef _POSIX_MEMLOCK - mlockall(MCL_FUTURE); -#endif - return; - } - - - /* TB: we'll have to be sure that no one is accessing the array */ - sys_lock(); - - // no garray_locks in current pd -//#ifdef GARRAY_THREAD_LOCK - //garray_lock(x); -//#endif - - data->a_vec = nvec; - data->a_n = n; - - // no garray_locks in current pd -//#ifdef GARRAY_THREAD_LOCK - //garray_unlock(x); -//#endif - - if (x->x_usedindsp) canvas_update_dsp(); - sys_unlock(); - - - /* if this is the only array in the graph, - reset the graph's coordinates */ - gl = x->x_glist; - if (gl->gl_list == &x->x_gobj && !x->x_gobj.g_next) - { - vmess(&gl->gl_pd, gensym("bounds"), "ffff", - 0., gl->gl_y1, (double)(n > 1 ? n-1 : 1), gl->gl_y2); - /* close any dialogs that might have the wrong info now... */ - gfxstub_deleteforkey(gl); - } - else garray_redraw(x); - - freebytes (vec, was * elemsize); -#ifdef _POSIX_MEMLOCK - mlockall(MCL_FUTURE); -#endif - sys_lock(); - outlet_float(y->x_obj.ob_outlet, (float)atom_getintarg(1,argc,argv)); - sys_unlock(); - return; - - usage: - pd_error(x, "usage: resize tablename size"); -} - - -//static void threadedsf_t_const(t_threadedsf *x, t_symbol *s, -// int argc, t_atom *argv); -// -//static void threadedsf_t_const_addq(t_threadedsf *x, t_symbol *s, -// int argc, t_atom *argv) -//{ -// threadedsf_queue_add(threadedsf_t_const,(void *)x,argc, argv); -//} - - -/* TB: threadedsf_t_const ... - usage: const table value -*/ -//static void threadedsf_t_const(t_threadedsf *y, t_symbol *s, -// int argc, t_atom *argv) -//{ -// int size, elemsize; /* array contains was elements of size elemsize */ -// t_float * vec; /* old array */ -// t_glist *gl; -// int val; /* value */ -// char *nvec; /* new array */ -// int i; -// -// t_garray * x = (t_garray *)pd_findbyclass(argv[0].a_w.w_symbol, garray_class); -// t_array *data = garray_getarray(x); // TODO muss der im lock sein ? -// -// if (!(x)) -// { -// pd_error(y, "%s: no such table", argv[0].a_w.w_symbol->s_name); -// goto usage; -// } -// -// -// -// vec = (t_float*) data->a_vec; -// size = data->a_n; -// -// if ((argv+1)->a_type == A_FLOAT) -// { -// val = (int) (argv+1)->a_w.w_float; -// } -// else -// { -// goto usage; -// } -// -//#ifdef SFDEBUG -// post("array size: %d; new value: %d",size,val); -//#endif -// -// elemsize = template_findbyname(data->a_templatesym)->t_n * sizeof(t_word); -// -// -// /* allocating memory */ -//#ifdef _POSIX_MEMLOCK -// munlockall(); -//#endif -// nvec = getbytes(size * elemsize); -// -// if (!nvec) -// { -// pd_error(x, "array resize failed: out of memory"); -//#ifdef _POSIX_MEMLOCK -// mlockall(MCL_FUTURE); -//#endif -// return; -// } -// -// /* setting array */ -// for (i=0; i!=size; ++i) -// { -// nvec[i]=val; -// } -// -// /* TB: we'll have to be sure that no one is accessing the array */ -// sys_lock(); -//#ifdef GARRAY_THREAD_LOCK -// garray_lock(x); -//#endif -// data->a_vec = nvec; -//#ifdef GARRAY_THREAD_LOCK -// garray_unlock(x); -//#endif -// if (x->x_usedindsp) canvas_update_dsp(); -// sys_unlock(); -// -// -// /* if this is the only array in the graph, -// reset the graph's coordinates */ -// gl = x->x_glist; -// if (gl->gl_list == &x->x_gobj && !x->x_gobj.g_next) -// { -// vmess(&gl->gl_pd, gensym("bounds"), "ffff", -// 0., gl->gl_y1, (double)(size > 1 ? size-1 : 1), gl->gl_y2); -// /* close any dialogs that might have the wrong info now... */ -// gfxstub_deleteforkey(gl); -// } -// else garray_redraw(x); -// -// freebytes (vec, size * elemsize); -//#ifdef _POSIX_MEMLOCK -// mlockall(MCL_FUTURE); -//#endif -// sys_lock(); -// outlet_float(y->x_obj.ob_outlet, size); -// sys_unlock(); -// return; -// -// usage: -// pd_error(x, "usage: const tablename value"); -//} - - -static t_threadedsf *threadedsf_new(void) -{ - t_threadedsf *x = (t_threadedsf *)pd_new(threadedsf_class); - x->x_canvas = canvas_getcurrent(); - outlet_new(&x->x_obj, &s_float); - return (x); -} - - -void threadedsf_setup(void) -{ - threadedsf_class = class_new(gensym("threadedsf"), (t_newmethod)threadedsf_new, - 0, sizeof(t_threadedsf), 0, 0); - - class_addmethod(threadedsf_class, (t_method)threadedsf_t_read_addq, - gensym("read"), A_GIMME, 0); - class_addmethod(threadedsf_class, (t_method)threadedsf_t_write_addq, - gensym("write"), A_GIMME, 0); - class_addmethod(threadedsf_class, (t_method)threadedsf_t_resize_addq, - gensym("resize"), A_GIMME, 0); -// class_addmethod(threadedsf_class, (t_method)threadedsf_t_const_addq, -// gensym("const"), A_GIMME, 0); -} diff --git a/threadlib/src/threadlib.c b/threadlib/src/threadlib.c index da17100..6cdf1af 100755 --- a/threadlib/src/threadlib.c +++ b/threadlib/src/threadlib.c @@ -33,7 +33,7 @@ t_class *threadlib_class; static void threadlib_help(void) { - post("\nthreadlib vers."VERSION", library for threaded patching\n" + post("\nthreadlib vers."VERSION", library for threaded patching and externals\n" "2005, by Georg Holzmann \n" "heavily based on pd_devel code by Tim Blechmann\n" @@ -41,7 +41,6 @@ static void threadlib_help(void) "\tdetach run part of the patch in a helper thread\n" "\tjoin synchronize messages to pd's main thread\n" "\tsleep block system for specific time\n" - "\tthreadedsf threaded soundfiler from pd_devel_0.38\n" "WARNING: this is very experimental and can crash your patches !\n"); } @@ -55,24 +54,18 @@ void *threadlib_new(void) void sleep_setup(); void detach_setup(); void join_setup(); -void threadedsf_setup(); -void sys_start_sfthread(); -void threadlib_setup(void) +void threadlib_setup(void) { // call all the setup functions: sleep_setup(); detach_setup(); join_setup(); - threadedsf_setup(); // init callback system h_init_callbacks(); - - // start global soundfiler helper thread - sys_start_sfthread(); - post("\nthreadlib vers."VERSION", library for threaded patching\n" + post("\nthreadlib vers."VERSION", library for threaded patching and externals\n" "2005, by Georg Holzmann \n" "heavily based on pd_devel code by Tim Blechmann\n" "WARNING: this is very experimental and may crash your patches !\n"); @@ -80,4 +73,6 @@ void threadlib_setup(void) threadlib_class = class_new(gensym("threadlib"), threadlib_new, 0, sizeof(t_threadlib), 0, 0); class_addmethod(threadlib_class, (t_method)threadlib_help, gensym("help"), 0); + + return 0; } diff --git a/threadlib/src/threadlib.h b/threadlib/src/threadlib.h index 80a7449..b94fc93 100755 --- a/threadlib/src/threadlib.h +++ b/threadlib/src/threadlib.h @@ -28,6 +28,12 @@ #include "m_pd.h" #include "pthread.h" +#ifdef MSW +#define THREADLIB_EXTERN __declspec(dllexport) extern +#else +#define THREADLIB_EXTERN extern +#endif /* MSW */ + // threadlib version string #define VERSION "0.1" @@ -38,6 +44,8 @@ // for debuging //#define DEBUG +// setup function +THREADLIB_EXTERN void threadlib_setup(void); /* --------- lockfree FIFO of pd devel ----------- */ // (implemted in fifo.c) @@ -47,12 +55,12 @@ EXTERN_STRUCT _fifo; #define t_fifo struct _fifo /* function prototypes */ -t_fifo * fifo_init(void); -void fifo_destroy(t_fifo*); +THREADLIB_EXTERN t_fifo * fifo_init(void); +THREADLIB_EXTERN void fifo_destroy(t_fifo*); /* fifo_put() and fifo_get are the only threadsafe functions!!! */ -void fifo_put(t_fifo*, void*); -void* fifo_get(t_fifo*); +THREADLIB_EXTERN void fifo_put(t_fifo*, void*); +THREADLIB_EXTERN void* fifo_get(t_fifo*); /* --------- callback FIFO of pd devel ----------- */ @@ -65,17 +73,19 @@ void* fifo_get(t_fifo*); * clock callbacks */ +/* register a new callback in FIFO */ +/* tb: to be called at idle time */ +/* Holzmann: idle callbacks of current PD are not reliable, so + it will be called by the clock-callbacks for now */ +THREADLIB_EXTERN void sys_callback(t_int (*callback) (t_int* argv), + t_int* argv, t_int argc); + +/* private: */ + /* set up callback fifo and start clock callback */ void h_init_callbacks(); /* free fifo and clock callback */ void h_free_callbacks(); -/* tb: to be called at idle time */ -/* Holzmann: idle callbacks of current PD are not reliable, so - it will be called by the clock-callbacks for now */ -/* register a new callback in FIFO */ -void h_set_callback(t_int (*callback) (t_int* argv), t_int* argv, t_int argc); - - #endif // __PD_THREADLIB_H_ -- cgit v1.2.1