Update of /cvsroot/pure-data/externals/tb/sndfiler/src In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19959/src
Added Files: Makefile Makefile.pd_main sndfiler.c Log Message: also works with pd_main now - additional threaded resize method
--- NEW FILE: sndfiler.c --- /* * * threaded soundfiler based on libsndfile * 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. */
/* 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"
/* forward declaration */ 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 */ };
#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 "stdlib.h" #include "sched.h" /* for thread priority */ #include <string.h>
/* for alloca */ #ifdef MSW #include <malloc.h> #else #include "alloca.h" #endif
#if (_POSIX_MEMLOCK - 0) >= 200112L #include <sys/mman.h> #endif /* _POSIX_MEMLOCK */
/************ forward declarations **************/
#ifdef UNIX /* real-time flag, true if priority boosted */ extern int sys_hipriority; #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;
pthread_mutex_t mutex; pthread_cond_t cond; } 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) { while (1) { t_sfprocess * me; pthread_cond_wait(&sndfiler_queue.cond, &sndfiler_queue.mutex);
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(); pthread_mutex_init (&sndfiler_queue.mutex,NULL); pthread_cond_init (&sndfiler_queue.cond,NULL);
// initialize thread pthread_attr_init(&sf_attr);
sf_param.sched_priority=sched_get_priority_min(SCHED_OTHER); pthread_attr_setschedparam(&sf_attr,&sf_param); #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);
pthread_cond_signal(&sndfiler_queue.cond); }
static t_int sndfiler_synchonize(t_int * w);
static void sndfiler_read_cb(t_sndfiler * x, int argc, t_atom* argv) { int i, j; int channel_count; t_float** helper_arrays;
t_symbol* file; t_garray ** arrays;
SNDFILE* sndfile; SF_INFO info;
if (argc < 2) { pd_error(x, "usage: read soundfile array1 array2 ..."); return; }
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_garray * array = (t_garray *)pd_findbyclass(atom_getsymbolarg(i+1, argc, argv), garray_class);
if (array) arrays[i] = array; else { pd_error(x, "%s: no such array", atom_getsymbolarg(i+1, argc, argv)->s_name); return; } }
post("sndfiler: loading file ...");
sndfile = sf_open(file->s_name, SFM_READ, &info);
if (sndfile) { int maxchannels = (channel_count < info.channels) ? channel_count : info.channels;
t_float * item = alloca(maxchannels * sizeof(t_float));
t_int ** syncdata = getbytes(sizeof(t_int*) * 5);
#if (_POSIX_MEMLOCK - 0) >= 200112L munlockall(); #endif
for (i = 0; i != channel_count; ++i) { helper_arrays[i] = getalignedbytes(info.frames * sizeof(t_float)); }
for (i = 0; i != info.frames; ++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]; } } }
#if (_POSIX_MEMLOCK - 0) >= 200112L mlockall(MCL_FUTURE); #endif
sf_close(sndfile);
syncdata[0] = (t_int*)arrays; syncdata[1] = (t_int*)helper_arrays; syncdata[2] = (t_int*)channel_count; syncdata[3] = (t_int*)(long)info.frames; syncdata[4] = (t_int*)x;
sys_callback(sndfiler_synchonize, (t_int*)syncdata, 5); } else pd_error(x, "Error opening file"); }
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);
pthread_cond_signal(&sndfiler_queue.cond); }
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"); }
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(); }
--- NEW FILE: Makefile.pd_main --- # ----------------------------------------------------------
# adjust the next pathes to your system:
# this should point to the directory which contains # m_pd.h and g_canvas.h PD_SCR = /home/holzi/pd-0.39-1/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/Georg/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
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 $(THREADLIB_PATH)/threadlib.pd_linux
.c.pd_linux: $(CC) $(CC_UNIX) $(CC_FLAGS) $(INCLUDE) -o $*.o -c $*.c $(LD) $(LD_FLAGS) $*.pd_linux $*.o $(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
.c.pd_darwin: $(CC) $(CC_FLAGS) $(CC_DARWIN) $(INCLUDE) -o $*.o -c $*.c $(LD) $(LD_DARWIN) $*.pd_linux $*.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
--- NEW FILE: Makefile ---
NAME=sndfiler CSYM=sndfiler
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/home/tim/pd/devel_0_39/src
LSTRIP = strip --strip-unneeded -R .note -R .comment
.c.pd_linux: cc $(LINUXCFLAGS) $(LINUXINCLUDE) -o $*.o -c $*.c cc -Wl,-export_dynamic --shared -o $*.pd_linux $*.o -lm -lsndfile # $(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