diff options
Diffstat (limited to 'sndfiler/src')
-rwxr-xr-x | sndfiler/src/Makefile | 98 | ||||
-rwxr-xr-x | sndfiler/src/Makefile.pd_main | 122 | ||||
-rw-r--r-- | sndfiler/src/file_input.c | 253 | ||||
-rw-r--r-- | sndfiler/src/file_input.h | 66 | ||||
-rw-r--r-- | sndfiler/src/sndfiler.c | 498 | ||||
-rw-r--r-- | sndfiler/src/sndfiler.h | 87 |
6 files changed, 1124 insertions, 0 deletions
diff --git a/sndfiler/src/Makefile b/sndfiler/src/Makefile new file mode 100755 index 0000000..3bcd658 --- /dev/null +++ b/sndfiler/src/Makefile @@ -0,0 +1,98 @@ + +NAME=sndfiler +CSYM=sndfiler + +OBJ=sndfiler.o file_input.o + +current: pd_linux + +# ----------------------- NT ----------------------- + +pd_nt: $(NAME).dll + +.SUFFIXES: .dll + +PDNTCFLAGS = /W3 /WX /DNT /DPD /nologo +VC="C:\Program Files\Microsoft Visual Studio\Vc98" + +PDNTINCLUDE = /I. /I..\..\src /I$(VC)\include + +PDNTLDIR = $(VC)\lib +PDNTLIB = $(PDNTLDIR)\libc.lib \ + $(PDNTLDIR)\oldnames.lib \ + $(PDNTLDIR)\kernel32.lib \ + ..\..\bin\pd.lib + +.c.dll: + cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $*.c + link /dll /export:$(CSYM)_setup $*.obj $(PDNTLIB) + +# ----------------------- IRIX 5.x ----------------------- + +pd_irix5: $(NAME).pd_irix5 + +.SUFFIXES: .pd_irix5 + +SGICFLAGS5 = -o32 -DPD -DUNIX -DIRIX -O2 + +SGIINCLUDE = -I../../src + +.c.pd_irix5: + $(CC) $(SGICFLAGS5) $(SGIINCLUDE) -o $*.o -c $*.c + ld -elf -shared -rdata_shared -o $*.pd_irix5 $*.o + rm $*.o + +# ----------------------- IRIX 6.x ----------------------- + +pd_irix6: $(NAME).pd_irix6 + +.SUFFIXES: .pd_irix6 + +SGICFLAGS6 = -n32 -DPD -DUNIX -DIRIX -DN32 -woff 1080,1064,1185 \ + -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \ + -Ofast=ip32 + +.c.pd_irix6: + $(CC) $(SGICFLAGS6) $(SGIINCLUDE) -o $*.o -c $*.c + ld -n32 -IPA -shared -rdata_shared -o $*.pd_irix6 $*.o + rm $*.o + +# ----------------------- LINUX i386 ----------------------- + +pd_linux: $(NAME).pd_linux + +.SUFFIXES: .pd_linux + +LINUXCFLAGS = -DPD -O3 -fPIC -funroll-loops -fomit-frame-pointer \ + -Wall -W -Wshadow -Wstrict-prototypes -Werror \ + -Wno-unused -Wno-parentheses -Wno-switch + +LINUXINCLUDE = -I../../../../pd/src + +LSTRIP = strip --strip-unneeded -R .note -R .comment + +.c.pd_linux: + cc $(LINUXCFLAGS) $(LINUXINCLUDE) -o sndfiler.o -c sndfiler.c + cc $(LINUXCFLAGS) $(LINUXINCLUDE) -o file_input.o -c file_input.c + cc -Wl,--export-dynamic --shared -o $*.pd_linux $(OBJ) -lm -lsndfile -lvorbisfile +# $(LSTRIP) $*.pd_linux + rm -f $*.o + +# ----------------------- Mac OSX ----------------------- + +pd_darwin: $(NAME).pd_darwin + +.SUFFIXES: .pd_darwin + +DARWINCFLAGS = -DPD -O2 -Wall -W -Wshadow -Wstrict-prototypes \ + -Wno-unused -Wno-parentheses -Wno-switch + +.c.pd_darwin: + $(CC) $(DARWINCFLAGS) $(LINUXINCLUDE) -o $*.o -c $*.c + $(CC) -bundle -undefined suppress -flat_namespace -o $*.pd_darwin $*.o + rm -f $*.o + +# ---------------------------------------------------------- + +clean: + rm -f *.o *.pd_darwin *.pd_linux *.dll so_locations diff --git a/sndfiler/src/Makefile.pd_main b/sndfiler/src/Makefile.pd_main new file mode 100755 index 0000000..29bde80 --- /dev/null +++ b/sndfiler/src/Makefile.pd_main @@ -0,0 +1,122 @@ +# ----------------------------------------------------------
+
+# adjust the next pathes to your system:
+
+# this should point to the directory which contains
+# m_pd.h and g_canvas.h
+PD_SCR = /usr/local/src/pd/src
+#PD_SCR = c:/pd/src
+
+# this is the pd directory, usually /usr/lib/pd
+# or c:/pd etc.
+PD_PATH = /usr/lib/pd
+#PD_PATH = c:/pd
+
+# path of sndfile.h from libsndfile, usually it's in
+# /usr/include and so detected automatically
+SNDFILE_SRC = /usr/include
+
+# the directory, where libsndfile is located
+# (in linux it' normally not necessary, in windows it's
+# normally in c:/windwos/system or so)
+#SNDFILE_PATH = c:/windows/system
+
+# path to threadlib.h
+THREADLIB_SRC = /home/holzi/pd-cvs/externals/grh/threadlib/src
+#THREADLIB_SRC = c:/Georg/pd-cvs/externals/grh/threadlib/src
+
+# path to threadlib.pd_linux/dll/pd_darwin
+# (usually path/to/pd/extra)
+THREADLIB_PATH = $(PD_PATH)/extra
+
+# ----------------------------------------------------------
+
+NAME=sndfiler
+
+CC = gcc
+LD = gcc
+INCLUDE= -I. -I$(PD_SCR) -I$(SNDFILE_SRC) -I$(THREADLIB_SRC)
+CC_FLAGS = -DPD -DUSE_PD_MAIN -O3 -funroll-loops \
+ -Wall -W -Wshadow -Wno-parentheses -Wno-switch \
+ -Wno-unused -fomit-frame-pointer
+LD_FLAGS = --export-dynamic -shared -o
+
+OBJ=sndfiler.o file_input.o
+
+current:
+ @echo ----------------------------
+ @echo USAGE:
+ @echo linux: make pd_linux
+ @echo windows: make pd_win
+ @echo darwin: make pd_darwin
+ @echo ----------------------------
+
+# ----------------------- LINUX i386 -----------------------
+
+pd_linux: $(NAME).pd_linux
+
+.SUFFIXES: .pd_linux
+
+CC_UNIX = -DUNIX -fPIC -pthread
+LIB_UNIX = -lc -lm -lsndfile -lvorbisfile $(THREADLIB_PATH)/threadlib.pd_linux
+
+.c.pd_linux:
+ $(CC) $(CC_UNIX) $(CC_FLAGS) $(INCLUDE) -o sndfiler.o -c sndfiler.c
+ $(CC) $(CC_UNIX) $(CC_FLAGS) $(INCLUDE) -o file_input.o -c file_input.c
+ $(LD) $(LD_FLAGS) $*.pd_linux $(OBJ) $(LIB_UNIX)
+ strip --strip-unneeded $*.pd_linux
+ chmod 755 $*.pd_linux
+ @test -d ../bin || mkdir -p ../bin
+ cp $*.pd_linux ../bin
+ rm -f $*.o
+
+# ------------------------ WIN MinGW -----------------------
+
+pd_win: $(NAME).dll
+
+.SUFFIXES: .dll
+
+CC_WIN = -DMSW -mms-bitfields
+LIB_WIN = $(PD_PATH)/bin/pd.dll \
+ $(SNDFILE_PATH)/pthreadGC.dll \
+ $(SNDFILE_PATH)/libsndfile.dll \
+ $(THREADLIB_PATH)/threadlib.dll
+
+.c.dll:
+ $(CC) $(CC_WIN) $(CC_FLAGS) $(INCLUDE) -o $*.o -c $*.c
+ $(LD) $(LD_FLAGS) $*.dll $*.o $(LIB_WIN)
+ strip --strip-unneeded $*.dll
+ chmod 755 $*.dll
+ @test -d ../bin || mkdir -p ../bin
+ cp $*.dll ../bin
+ rm -f $*.o
+
+# ----------------------- Mac OSX -----------------------
+
+pd_darwin: $(NAME).pd_darwin
+
+.SUFFIXES: .pd_darwin
+
+CC_DARWIN = -pthread
+LD_DARWIN = -bundle -undefined suppress -flat_namespace \
+ -bundle_loader $(PD_PATH)/bin/pd --export-dynamic \
+ -L/sw/lib -L/opt/local/lib -lsndfile -lvorbisfile \
+ $(THREADLIB_PATH)/threadlib.pd_darwin
+
+.c.pd_darwin:
+ $(CC) $(CC_UNIX) $(CC_FLAGS) $(INCLUDE) -o sndfiler.o -c sndfiler.c
+ $(CC) $(CC_UNIX) $(CC_FLAGS) $(INCLUDE) -o file_input.o -c file_input.c
+ $(LD) $(LD_DARWIN) -o $*.pd_darwin $*.o $(LIB)
+ chmod 755 $*.pd_darwin
+ @test -d ../bin || mkdir -p ../bin
+ cp $*.pd_darwin ../bin
+ rm -f $*.o
+
+# ----------------------------------------------------------
+
+clean:
+ rm -f *.o *.pd_darwin *.pd_linux *.dll
+
+install:
+ install ../bin/$(NAME).* $(PD_PATH)/extra
+ install ../doc/*.pd $(PD_PATH)/doc/5.reference
diff --git a/sndfiler/src/file_input.c b/sndfiler/src/file_input.c new file mode 100644 index 0000000..79ad27e --- /dev/null +++ b/sndfiler/src/file_input.c @@ -0,0 +1,253 @@ +/* + * threaded soundfiler for PD + * Copyright (C) 2005, Georg Holzmann <grh@mur.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "sndfiler.h" +#include "file_input.h" + +int check_fileformat(t_symbol* file) +{ + FILE *fp = NULL; + OggVorbis_File vorbisfile; + + // partially opens a vorbis file to test for Vorbis-ness + if( !(fp = fopen(file->s_name, "r")) ) + return -1; + + if( ov_test(fp, &vorbisfile, NULL, 0) < 0 ) + { + fclose(fp); + return USE_LIBSNDFILE; + } + + ov_clear(&vorbisfile); + return USE_LIBVORBISFILE; +} + +int read_libsndfile(t_float** helper_arrays, int channel_count, int seek, + int resize, int array_size, t_symbol* file) +{ + int arraysize = array_size; + int writesize = 0, i=0, j=0; + SNDFILE* sndfile; + SF_INFO info; + + sndfile = sf_open(file->s_name, SFM_READ, &info); + + if(!sndfile) + return -1; + + int pos = 0; + int maxchannels = (channel_count < info.channels) ? + channel_count : info.channels; + + t_float * item = alloca(maxchannels * sizeof(t_float)); + + // negative seek: offset from the end of the file + if(seek<0) + { + if(CHECK_SEEK(seek+info.frames, info.frames)) + pos = sf_seek(sndfile, seek, SEEK_END); + else pos = -1; + } + if(seek>0) + { + if(CHECK_SEEK(seek, info.frames)) + pos = sf_seek(sndfile, seek, SEEK_SET); + else pos = -1; + } + if(pos == -1) + { + sf_close(sndfile); + post("invalid seek in soundfile"); + return -1; + } + + if(resize) + { + writesize = (info.frames-pos); + arraysize = writesize; + } + else + writesize = (arraysize>(info.frames-pos)) ? + info.frames-pos : arraysize; + +#if (_POSIX_MEMLOCK - 0) >= 200112L + munlockall(); +#endif + + for (i = 0; i != channel_count; ++i) + { + helper_arrays[i] = getalignedbytes(arraysize * sizeof(t_float)); + } + + for (i = 0; i != writesize; ++i) + { + sf_read_float(sndfile, item, info.channels); + + for (j = 0; j != info.channels; ++j) + { + if (j < channel_count) + { + helper_arrays[j][i] = item[j]; + } + } + } + + // fill remaining elements with zero + if(!resize && (arraysize > (info.frames-pos))) + { + for (i = writesize; i != arraysize; ++i) + { + for (j = 0; j != info.channels; ++j) + { + if (j < channel_count) + { + helper_arrays[j][i] = 0; + } + } + } + } + +#if (_POSIX_MEMLOCK - 0) >= 200112L + mlockall(MCL_FUTURE); +#endif + + sf_close(sndfile); + return arraysize; +} + +int read_libvorbisfile(t_float** helper_arrays, int channel_count, int seek, + int resize, int array_size, t_symbol* file) +{ + int arraysize = array_size; + int writesize = 0, i=0, j=0; + int pos=0, maxchannels=0, frames=0, frames_read=0; + int current_section=0, finished=0; + float **buftmp = NULL; + FILE *fp = NULL; + OggVorbis_File vorbisfile; + vorbis_info *info; + + if( !(fp = fopen(file->s_name, "r")) ) + return -1; + + if( ov_open(fp, &vorbisfile, NULL, 0) < 0 ) + { + fclose(fp); + return -1; + } + + info = ov_info(&vorbisfile, -1); + frames = ov_pcm_total(&vorbisfile, -1); + if( !info || frames==OV_EINVAL ) + { + ov_clear(&vorbisfile); + post("failed to get info about vorbis file"); + return -1; + } + + maxchannels = (channel_count < info->channels) ? + channel_count : info->channels; + + // negative seek: offset from the end of the file + if(seek<0) + { + if(CHECK_SEEK(frames+seek, frames)) + { + int ret = ov_pcm_seek(&vorbisfile, frames+seek); + if(ret!=0) + pos =-1; + else + pos = frames+seek; + } + else pos = -1; + } + if(seek>0) + { + if(CHECK_SEEK(seek, frames)) + { + int ret = ov_pcm_seek(&vorbisfile, seek); + if(ret!=0) + pos =-1; + else + pos = seek; + } + else pos = -1; + } + if(pos == -1) + { + ov_clear(&vorbisfile); + post("invalid seek in vorbis file"); + return -1; + } + + if(resize) + { + writesize = (frames-pos); + arraysize = writesize; + } + else + writesize = (arraysize>(frames-pos)) ? + frames-pos : arraysize; + +#if (_POSIX_MEMLOCK - 0) >= 200112L + munlockall(); +#endif + + for (i = 0; i != channel_count; ++i) + { + helper_arrays[i] = getalignedbytes(arraysize * sizeof(t_float)); + } + + for (i = 0; i != writesize; ++i) + { + int ret = ov_read_float(&vorbisfile, &buftmp, 1, + ¤t_section); + if(ret!=1) + post("wrong return type while ogg decoding!"); + + for (j = 0; j != channel_count; ++j) + { + helper_arrays[j][i] = buftmp[j][0]; + } + } + + // fill remaining elements with zero + if(!resize && (arraysize > (frames-pos))) + { + for (i = writesize; i != arraysize; ++i) + { + for (j = 0; j != info->channels; ++j) + { + if (j < channel_count) + { + helper_arrays[j][i] = 0; + } + } + } + } + +#if (_POSIX_MEMLOCK - 0) >= 200112L + mlockall(MCL_FUTURE); +#endif + + ov_clear(&vorbisfile); + return arraysize; +} diff --git a/sndfiler/src/file_input.h b/sndfiler/src/file_input.h new file mode 100644 index 0000000..e5b5232 --- /dev/null +++ b/sndfiler/src/file_input.h @@ -0,0 +1,66 @@ +/* + * threaded soundfiler for PD + * Copyright (C) 2005, Georg Holzmann <grh@mur.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _FILE_INPUT__ +#define _FILE_INPUT__ + +#define USE_LIBSNDFILE 0 +#define USE_LIBVORBISFILE 1 + +//! returns 1 if s is in [0,c) +#define CHECK_SEEK(s, c) (s<0 ? 0 : (s>=c ? 0 : 1)) + +/*! + * checks which library to use + * + * @param file filename + * @return USE_LIBSNDFILE or USE_LIBVORBISFILE, -1 if there was an error + */ +int check_fileformat(t_symbol* file); + +/*! + * read audio data with libsndfile + * + * @param helper_arrays (unallocated) pointer to the data + * @param channel_count nr of channels + * @param seek frames to seek in file + * @param resize 1 if array should be resized + * @param array_size size of the array in samples + * @param file filename + * @return new arraysiize, -1 if there was a failure + */ +int read_libsndfile(t_float** helper_arrays, int channel_count, int seek, + int resize, int array_size, t_symbol* file); + +/*! + * read audio data with libvorbisfile + * + * @param helper_arrays (unallocated) pointer to the data + * @param channel_count nr of channels + * @param seek frames to seek in file + * @param resize 1 if array should be resized + * @param array_size size of the array in samples + * @param file filename + * @return new arraysiize, -1 if there was a failure + */ +int read_libvorbisfile(t_float** helper_arrays, int channel_count, int seek, + int resize, int array_size, t_symbol* file); + +#endif //_FILE_INPUT__ diff --git a/sndfiler/src/sndfiler.c b/sndfiler/src/sndfiler.c new file mode 100644 index 0000000..4cd14d9 --- /dev/null +++ b/sndfiler/src/sndfiler.c @@ -0,0 +1,498 @@ +/* + * + * threaded soundfiler for pd + * Copyright (C) 2005, Tim Blechmann + * (C) 2005, Georg Holzmann <grh@mur.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "sndfiler.h" +#include "file_input.h" + + +/************ forward declarations **************/ + +#ifdef UNIX +/* real-time flag, true if priority boosted */ +extern int sys_hipriority; +#endif + +#ifdef USE_PD_MAIN +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 */ +}; +#endif + +/* get a garray's "array" structure. */ +t_array *h_garray_getarray(t_garray *x) +{ + int zonset, ztype; + t_symbol *zarraytype; + t_scalar *sc = x->x_scalar; + t_symbol *templatesym = sc->sc_template; + t_template *_template = template_findbyname(templatesym); + if (!_template) + { + error("array: couldn't find template %s", templatesym->s_name); + return (0); + } + if (!template_find_field(_template, gensym("z"), + &zonset, &ztype, &zarraytype)) + { + error("array: template %s has no 'z' field", templatesym->s_name); + return (0); + } + if (ztype != DT_ARRAY) + { + error("array: template %s, 'z' field is not an array", + templatesym->s_name); + return (0); + } + return (sc->sc_vec[zonset].w_array); +} + + +/************ sndfiler **************/ + +static t_class *sndfiler_class; + +typedef struct _sndfiler +{ + t_object x_obj; + t_canvas *x_canvas; +} t_sndfiler; + +typedef struct _sfprocess +{ + void* padding; + /* callback function */ + void (* process) (t_sndfiler *, int, t_atom *); + t_sndfiler * x; /* soundfiler */ + int argc; + t_atom * argv; +} t_sfprocess; + +/* this is the queue for all soundfiler objects */ +typedef struct _sfqueue +{ + t_fifo* x_jobs; + SEM_T sem; +} t_sfqueue; + +typedef struct _syncdata +{ + t_garray** arrays; + t_float** helper_arrays; + int argc; + t_int frames; +} t_syncdata; + +static t_sfqueue sndfiler_queue; +static pthread_t sf_thread_id; /* id of soundfiler thread */ + +static t_sndfiler *sndfiler_new(void) +{ + t_sndfiler *x = (t_sndfiler *)pd_new(sndfiler_class); + x->x_canvas = canvas_getcurrent(); + outlet_new(&x->x_obj, &s_float); + return (x); +} + +/* global soundfiler thread ... sleeping until signaled */ +static void sndfiler_thread(void) +{ + for(;;) + { + t_sfprocess * me; + SEM_WAIT(sndfiler_queue.sem); + + while (me = (t_sfprocess *)fifo_get(sndfiler_queue.x_jobs)) + { + (me->process)(me->x, me->argc, me->argv); + + /* freeing the argument vector */ + freebytes(me->argv, sizeof(t_atom) * me->argc); + freebytes(me, sizeof(t_sfprocess)); + } + } +} + +static void sndfiler_start_thread(void) +{ + pthread_attr_t sf_attr; + struct sched_param sf_param; + int status; + + //initialize queue + sndfiler_queue.x_jobs = fifo_init(); + + status = SEM_INIT(sndfiler_queue.sem); + if(!status) + error("Couldn't create sndfiler semaphore: %i",status); + + // initialize thread + pthread_attr_init(&sf_attr); + + sf_param.sched_priority=sched_get_priority_min(SCHED_OTHER); + pthread_attr_setschedparam(&sf_attr,&sf_param); + + /* 1mb of stack should be enough */ + pthread_attr_setstacksize(&sf_attr,1048576); + +#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); + } +#endif /* UNIX */ + + //start thread + status = pthread_create(&sf_thread_id, &sf_attr, + (void *) sndfiler_thread,NULL); + + if (status != 0) + error("Couldn't create sndfiler thread: %d",status); + else + post("Global sndfiler thread launched, priority: %d", + sf_param.sched_priority); +} + +static void sndfiler_read_cb(t_sndfiler * x, int argc, t_atom* argv); + +/* syntax: + * read soundfile array0..n + * if the soundfile has less channels than arrays are given, these arrays are + * set to zero + * if there are too little arrays given, only the first n channels will be used + * */ +static void sndfiler_read(t_sndfiler * x, t_symbol *s, int argc, t_atom* argv) +{ + t_sfprocess * process = getbytes(sizeof(t_sfprocess)); + + process->process = &sndfiler_read_cb; + process->x = x; + process->argc = argc; + process->argv = (t_atom*) copybytes(argv, sizeof(t_atom) * argc); + + fifo_put(sndfiler_queue.x_jobs, process); + + SEM_SIGNAL(sndfiler_queue.sem); +} + +static t_int sndfiler_synchonize(t_int * w); + +static void sndfiler_read_cb(t_sndfiler * x, int argc, t_atom* argv) +{ + int i, j, lib; + int channel_count; + t_float** helper_arrays; + int resize = 0; + int seek = 0, arraysize = 0; + + t_symbol* file; + t_garray ** arrays; + + // parse flags + 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, "resize")) + { + resize = 1; + argc -= 1; argv += 1; + } + else if (!strcmp(flag, "skip")) + { + if (argc < 2 || argv[1].a_type != A_FLOAT) + goto usage; + else + seek = argv[1].a_w.w_float; + argc -= 2; argv += 2; + } + else goto usage; + } + + if (argc < 2) + goto usage; + + file = atom_getsymbolarg(0, argc, argv); + + channel_count = argc - 1; + helper_arrays = getbytes(channel_count * sizeof(t_float*)); + + arrays = getbytes(channel_count * sizeof(t_garray*)); + for (i = 0; i != channel_count; ++i) + { + t_float *dummy; + int size; + t_garray *array; + + if(!(array = (t_garray *)pd_findbyclass( + atom_getsymbolarg(i+1, argc, argv), garray_class))) + { + pd_error(x, "%s: no such array", atom_getsymbolarg(i+1, + argc, argv)->s_name); + return; + } + + if(garray_getfloatarray(array, &size, &dummy)) + arrays[i] = array; + else + { + pd_error(x, "%s: bad template for sndfiler", atom_getsymbolarg(i+1, + argc, argv)->s_name); + return; + } + + // in multichannel mode: check if arrays have different length + if (arraysize && arraysize != size && !resize) + { + post("sndfiler: arrays have different lengths, resizing to last one ..."); + } + arraysize = size; + } + + lib = check_fileformat(file); + if(lib == USE_LIBSNDFILE) + arraysize = read_libsndfile(helper_arrays, channel_count, seek, + resize, arraysize, file); + else if(lib == USE_LIBVORBISFILE) + arraysize = read_libvorbisfile(helper_arrays, channel_count, seek, + resize, arraysize, file); + else + { + pd_error(x, "Error opening file"); + return; + } + + if(arraysize > 0) + { + t_int ** syncdata = getbytes(sizeof(t_int*) * 5); + + syncdata[0] = (t_int*)arrays; + syncdata[1] = (t_int*)helper_arrays; + syncdata[2] = (t_int*)channel_count; + syncdata[3] = (t_int*)arraysize; + syncdata[4] = (t_int*)x; + + sys_callback(sndfiler_synchonize, (t_int*)syncdata, 5); + return; + } + else + { + pd_error(x, "Error opening file"); + return; + } + + usage: + pd_error(x, "usage: read [flags] filename array1 array2 ..."); + post("flags: -skip <n> -resize "); +} + +static t_int sndfiler_synchonize(t_int * w) +{ + int i; + t_garray** arrays = (t_garray**) w[0]; + t_float** helper_arrays = (t_float**) w[1]; + t_int channel_count = (t_int)w[2]; + t_int frames = (t_int)w[3]; + t_sndfiler* x = (t_sndfiler*)w[4]; + + for (i = 0; i != channel_count; ++i) + { + t_garray * garray = arrays[i]; + t_array * array = h_garray_getarray(garray); + t_glist * gl = garray->x_glist;; + + freealignedbytes(array->a_vec, array->a_n); + array->a_vec = (char*)helper_arrays[i]; + array->a_n = frames; + + if (gl->gl_list == &garray->x_gobj && !garray->x_gobj.g_next) + { + vmess(&gl->gl_pd, gensym("bounds"), "ffff", 0., gl->gl_y1, + (double)(frames > 1 ? frames-1 : 1), gl->gl_y2); + + /* close any dialogs that might have the wrong info now... */ + gfxstub_deleteforkey(gl); + } + else + garray_redraw(garray); + } + + free(arrays); + free(helper_arrays); + + canvas_update_dsp(); + + outlet_float(x->x_obj.ob_outlet, frames); + + return 0; +} + +static void sndfiler_t_resize(t_sndfiler * y, int argc, t_atom* argv); + +/* syntax: + * resize table size + * */ +static void sndfiler_resize(t_sndfiler * x, t_symbol *s, int argc, t_atom* argv) +{ + t_sfprocess * process = getbytes(sizeof(t_sfprocess)); + + process->process = &sndfiler_t_resize; + process->x = x; + process->argc = argc; + process->argv = (t_atom*) copybytes(argv, sizeof(t_atom) * argc); + + fifo_put(sndfiler_queue.x_jobs, process); + + SEM_SIGNAL(sndfiler_queue.sem); +} + +static void sndfiler_t_resize(t_sndfiler *y, 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 = h_garray_getarray(x); + + 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); + +#if (_POSIX_MEMLOCK - 0) >= 200112L + 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"); + +#if (_POSIX_MEMLOCK - 0) >= 200112L + mlockall(MCL_FUTURE); +#endif + + return; + } + + /* 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; + data->a_n = n; + +#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); + +#if (_POSIX_MEMLOCK - 0) >= 200112L + 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"); +} + +#ifdef _MSC_VER +__declspec(dllexport) +#endif +void sndfiler_setup(void) +{ + sndfiler_class = class_new(gensym("sndfiler"), + (t_newmethod)sndfiler_new, 0, + sizeof(t_sndfiler), 0, 0); + + class_addmethod(sndfiler_class, (t_method)sndfiler_read, + gensym("read"), A_GIMME, 0); + class_addmethod(sndfiler_class, (t_method)sndfiler_resize, + gensym("resize"), A_GIMME, 0); + +#ifdef USE_PD_MAIN + // needed to start thread callback system + threadlib_setup(); +#endif + + // starts helper thread + sndfiler_start_thread(); +} diff --git a/sndfiler/src/sndfiler.h b/sndfiler/src/sndfiler.h new file mode 100644 index 0000000..f5fbd2c --- /dev/null +++ b/sndfiler/src/sndfiler.h @@ -0,0 +1,87 @@ +/* + * + * threaded soundfiler for pd + * Copyright (C) 2005, Tim Blechmann + * (C) 2005, Georg Holzmann <grh@mur.at> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _SND_FILER__ +#define _SND_FILER__ + + +/* to be compatible with main pd */ +#ifdef USE_PD_MAIN + +#define getalignedbytes(a) getbytes(a) +#define freealignedbytes(a,b) freebytes(a,b) +#include "threadlib.h" + +#else /* now for pd_devel */ + +#include "m_pd.h" +#include "m_fifo.h" + +#include "pthread.h" + +#endif /* USE_PD_MAIN */ + + +#include "g_canvas.h" +#include "sndfile.h" +#include "vorbis/codec.h" +#include "vorbis/vorbisfile.h" + +#include "stdlib.h" +#include <stdio.h> +#include "sched.h" /* for thread priority */ +#include <string.h> +#include "semaphore.h" + +#ifdef MSW +#include <io.h> +#include <fcntl.h> +#endif + +/* for alloca */ +#ifdef MSW +#include <malloc.h> +#else +#include "alloca.h" +#endif + +#if (_POSIX_MEMLOCK - 0) >= 200112L +#include <sys/mman.h> +#endif /* _POSIX_MEMLOCK */ + +#ifdef __APPLE__ +#include <mach/mach.h> +#include <mach/task.h> +#include <mach/semaphore.h> +#define SEM_T semaphore_t +#define SEM_INIT(s) (semaphore_create(mach_task_self(),&s,SYNC_POLICY_FIFO,0) == 0) +#define SEM_SIGNAL(s) semaphore_signal(s) +#define SEM_WAIT(s) semaphore_wait(s) +#else +#define SEM_T sem_t +#define SEM_INIT(s) (sem_init(&s,0,0) == 0) +#define SEM_SIGNAL(s) sem_post(&s) +#define SEM_WAIT(s) sem_wait(&s) +#endif + + +#endif // _SND_FILER__ |