aboutsummaryrefslogtreecommitdiff
path: root/threadlib/src
diff options
context:
space:
mode:
Diffstat (limited to 'threadlib/src')
-rwxr-xr-xthreadlib/src/Makefile68
-rwxr-xr-xthreadlib/src/Makefile_darwin69
-rwxr-xr-xthreadlib/src/Makefile_mingw68
-rwxr-xr-xthreadlib/src/callbacks.c134
-rwxr-xr-xthreadlib/src/detach.c240
-rwxr-xr-xthreadlib/src/fifo.c512
-rwxr-xr-xthreadlib/src/join.c161
-rwxr-xr-xthreadlib/src/sleep.c58
-rwxr-xr-xthreadlib/src/threadedsf.c1919
-rwxr-xr-xthreadlib/src/threadlib.c83
-rwxr-xr-xthreadlib/src/threadlib.h81
11 files changed, 3393 insertions, 0 deletions
diff --git a/threadlib/src/Makefile b/threadlib/src/Makefile
new file mode 100755
index 0000000..0579345
--- /dev/null
+++ b/threadlib/src/Makefile
@@ -0,0 +1,68 @@
+# -------------------------------------------------
+# adjust the next 2 pathes to your system:
+
+# this should point to the directory which contains
+# m_pd.h and g_canvas.h
+PDSCR=/home/holzi/pd-0.39-1test1/src
+
+# this is the pd directory, here the files will be
+# installed
+PDPATH=/usr/lib/pd
+
+# --------------------------------------------------
+
+TARGET=threadlib.pd_linux
+
+OBJ=fifo.o callbacks.o threadlib.o sleep.o detach.o \
+ join.o threadedsf.o
+
+CC = gcc
+LD = gcc
+INCLUDE=-I$(PDSCR) -I.
+LIB=-lc -lm
+CC_FLAGS = -DPD -DUNIX -c -fPIC \
+ -Wall -Wno-parentheses -Wno-switch -O3 \
+ -funroll-loops -fomit-frame-pointer -pthread
+LD_FLAGS = --export-dynamic -shared -o
+
+# --------------------------------------------------
+
+all: pd_linux
+
+pd_linux: $(TARGET)
+
+$(TARGET): $(OBJ)
+ $(LD) $(LD_FLAGS) $(TARGET) $(OBJ) $(LIB)
+ strip --strip-unneeded $(TARGET)
+ chmod 755 $(TARGET)
+
+threadlib.o: threadlib.h threadlib.c
+ $(CC) $(CC_FLAGS) $(INCLUDE) threadlib.c
+
+fifo.o: threadlib.o fifo.c
+ $(CC) $(CC_FLAGS) $(INCLUDE) fifo.c
+
+callbacks.o: fifo.o threadlib.o callbacks.c
+ $(CC) $(CC_FLAGS) $(INCLUDE) callbacks.c
+
+sleep.o: threadlib.o sleep.c
+ $(CC) $(CC_FLAGS) $(INCLUDE) sleep.c
+
+detach.o: threadlib.o fifo.o detach.c
+ $(CC) $(CC_FLAGS) $(INCLUDE) 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:
+ rm -f $(OBJ) $(TARGET)
+
+install:
+ @test -d $(PDPATH)/extra || mkdir -p $(PDPATH)/extra
+ install $(TARGET) $(PDPATH)/extra
+ install ../doc/*.pd $(PDPATH)/doc/5.reference
diff --git a/threadlib/src/Makefile_darwin b/threadlib/src/Makefile_darwin
new file mode 100755
index 0000000..a254ff0
--- /dev/null
+++ b/threadlib/src/Makefile_darwin
@@ -0,0 +1,69 @@
+# -------------------------------------------------
+# adjust the next 2 pathes to your system:
+
+# this should point to the directory which contains
+# m_pd.h and g_canvas.h
+PDSCR=/home/holzi/pd-0.39-1test1/src
+
+# this is the pd directory, here the files will be
+# installed
+PDPATH=/usr/lib/pd
+
+# --------------------------------------------------
+
+TARGET=threadlib.pd_linux
+
+OBJ=fifo.o callbacks.o threadlib.o sleep.o detach.o \
+ join.o threadedsf.o
+
+CC = gcc
+LD = gcc
+INCLUDE=-I$(PDSCR) -I.
+LIB=-lc -lm
+CC_FLAGS = -DPD -c \
+ -Wall -Wno-parentheses -Wno-switch -O3 \
+ -funroll-loops -fomit-frame-pointer -pthread
+LD_FLAGS = -bundle -bundle_loader $(PDPATH)/bin/pd \
+ --export-dynamic -o
+
+# --------------------------------------------------
+
+all: pd_linux
+
+pd_linux: $(TARGET)
+
+$(TARGET): $(OBJ)
+ $(LD) $(LD_FLAGS) $(TARGET) $(OBJ) $(LIB)
+ #strip --strip-unneeded $(TARGET)
+ chmod 755 $(TARGET)
+
+threadlib.o: threadlib.h threadlib.c
+ $(CC) $(CC_FLAGS) $(INCLUDE) threadlib.c
+
+fifo.o: threadlib.o fifo.c
+ $(CC) $(CC_FLAGS) $(INCLUDE) fifo.c
+
+callbacks.o: fifo.o threadlib.o callbacks.c
+ $(CC) $(CC_FLAGS) $(INCLUDE) callbacks.c
+
+sleep.o: threadlib.o sleep.c
+ $(CC) $(CC_FLAGS) $(INCLUDE) sleep.c
+
+detach.o: threadlib.o fifo.o detach.c
+ $(CC) $(CC_FLAGS) $(INCLUDE) 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:
+ rm -f $(OBJ) $(TARGET)
+
+install:
+ @test -d $(PDPATH)/extra || mkdir -p $(PDPATH)/extra
+ install $(TARGET) $(PDPATH)/extra
+ install ../doc/*.pd $(PDPATH)/doc/5.reference
diff --git a/threadlib/src/Makefile_mingw b/threadlib/src/Makefile_mingw
new file mode 100755
index 0000000..c110eec
--- /dev/null
+++ b/threadlib/src/Makefile_mingw
@@ -0,0 +1,68 @@
+# -------------------------------------------------
+# adjust the next 2 pathes to your system:
+
+# this should point to the directory which contains
+# m_pd.h and g_canvas.h
+PDSCR=c:/pd/src
+
+# this is the pd directory, here the files will be
+# installed
+PDPATH=c:/pd
+
+# --------------------------------------------------
+
+TARGET=threadlib.pd_linux
+
+OBJ=fifo.o callbacks.o threadlib.o sleep.o detach.o \
+ join.o threadedsf.o
+
+CC = gcc
+LD = gcc
+INCLUDE=-I$(PDSCR) -I.
+LIB=$(PD-PATH)/bin/pd.dll
+CC_FLAGS = -DPD -DWINDOWS -c -mms-bitfields \
+ -Wall -Wno-parentheses -Wno-switch -O3 \
+ -funroll-loops -fomit-frame-pointer -pthread
+LD_FLAGS = --export-dynamic -shared -o
+
+# --------------------------------------------------
+
+all: pd_linux
+
+pd_linux: $(TARGET)
+
+$(TARGET): $(OBJ)
+ $(LD) $(LD_FLAGS) $(TARGET) $(OBJ) $(LIB)
+ strip --strip-unneeded $(TARGET)
+ chmod 755 $(TARGET)
+
+threadlib.o: threadlib.h threadlib.c
+ $(CC) $(CC_FLAGS) $(INCLUDE) threadlib.c
+
+fifo.o: threadlib.o fifo.c
+ $(CC) $(CC_FLAGS) $(INCLUDE) fifo.c
+
+callbacks.o: fifo.o threadlib.o callbacks.c
+ $(CC) $(CC_FLAGS) $(INCLUDE) callbacks.c
+
+sleep.o: threadlib.o sleep.c
+ $(CC) $(CC_FLAGS) $(INCLUDE) sleep.c
+
+detach.o: threadlib.o fifo.o detach.c
+ $(CC) $(CC_FLAGS) $(INCLUDE) 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:
+ rm -f $(OBJ) $(TARGET)
+
+install:
+ @test -d $(PDPATH)/extra || mkdir -p $(PDPATH)/extra
+ install $(TARGET) $(PDPATH)/extra
+ install ../doc/*.pd $(PDPATH)/doc/5.reference
diff --git a/threadlib/src/callbacks.c b/threadlib/src/callbacks.c
new file mode 100755
index 0000000..1385992
--- /dev/null
+++ b/threadlib/src/callbacks.c
@@ -0,0 +1,134 @@
+/*
+*
+* callbacks.c
+* implementation of the callback FIFO
+*
+* this is the (modified) FIFO of the idle callbacks from pd_devel_0.38
+* (implemented in m_sched.c)
+*/
+
+#include "threadlib.h"
+
+// global callback fifo
+t_fifo * h_callback_fifo = NULL;
+
+// global clock callback to trigger
+// the callback fifo
+t_clock *h_callback_clock = NULL;
+
+/* linked list of callbacks
+ * callback will be freed after returning 0 */
+typedef struct _sched_callback
+{
+ struct _sched_callback* next; /* next callback in ringbuffer / in fifo */
+ t_int (*function) (t_int* argv);
+ t_int* argv;
+ t_int argc;
+} t_sched_callback;
+
+// forward declaration
+static void h_run_callbacks();
+
+void h_init_callbacks()
+{
+ h_callback_fifo = fifo_init();
+ h_callback_clock = clock_new(NULL, (t_method)h_run_callbacks);
+}
+
+void h_free_callbacks()
+{
+ clock_free(h_callback_clock);
+ fifo_destroy(h_callback_fifo);
+}
+
+void h_set_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));
+
+ new->function = callback;
+ new->argv = (t_int*) copybytes (argv, argc * sizeof (t_int));
+ new->argc = argc;
+ new->next = NULL;
+
+ fifo_put(h_callback_fifo, new);
+
+ // TODO find solution without lock
+ sys_lock();
+ clock_delay(h_callback_clock, 0);
+ sys_unlock();
+}
+
+static t_sched_callback *ringbuffer_head;
+
+void h_run_callbacks()
+{
+ t_sched_callback * new_callback;
+
+ sys_unlock();
+
+ /* append idle callback to ringbuffer */
+
+ while ( (new_callback = (t_sched_callback*) fifo_get(h_callback_fifo)) )
+ {
+ t_sched_callback * next;
+
+ /* set the next field to NULL ... it might be set in the fifo */
+ new_callback->next = NULL;
+ if (ringbuffer_head == NULL)
+ {
+ ringbuffer_head = new_callback;
+ }
+ else
+ {
+ next = ringbuffer_head;
+ while (next->next != 0)
+ next = next->next;
+ next->next = new_callback;
+ }
+ }
+
+ if (ringbuffer_head != NULL)
+ {
+ t_sched_callback * idle_callback = ringbuffer_head;
+ t_sched_callback * last = NULL;
+ t_sched_callback * next;
+
+ do
+ {
+ int status;
+
+ sys_lock();
+ status = (idle_callback->function)(idle_callback->argv);
+ sys_unlock();
+
+ switch (status)
+ {
+ /* callbacks returning 0 will be deleted */
+ case 0:
+ next = idle_callback->next;
+ freebytes (idle_callback->argv, idle_callback->argc);
+ freebytes ((void*)idle_callback, sizeof(t_sched_callback));
+
+ if (last == NULL)
+ ringbuffer_head = next;
+ else
+ last->next = next;
+
+ idle_callback = next;
+
+ /* callbacks returning 1 will be run again */
+ case 1:
+ break;
+
+ /* callbacks returning 2 will be run during the next idle callback */
+ case 2:
+ last = idle_callback;
+ idle_callback = idle_callback->next;
+ }
+ }
+ while (idle_callback != NULL);
+ }
+
+ sys_lock();
+}
diff --git a/threadlib/src/detach.c b/threadlib/src/detach.c
new file mode 100755
index 0000000..b92c7af
--- /dev/null
+++ b/threadlib/src/detach.c
@@ -0,0 +1,240 @@
+/*
+*
+* detach
+* Copyright (C) 2005 Georg Holzmann, <grh@mur.at>
+* Copyright (C) 2005 Tim Blechmann
+*
+* 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 "threadlib.h"
+
+static t_class *detach_class;
+
+typedef t_fifo fifo_t; /* for emacs syntax highlighting */
+
+typedef struct _detach
+{
+ t_object x_obj;
+
+ t_outlet * x_outlet;
+
+ pthread_t x_thread;
+ pthread_mutex_t x_mutex;
+ pthread_cond_t x_cond;
+
+ fifo_t * x_fifo;
+
+} detach_t;
+
+typedef struct _detach_content
+{
+ struct _detach_content_t * next;
+ enum { BANG,
+ POINTER,
+ FLOAT,
+ SYMBOL,
+ LIST,
+ ANYTHING,
+ CANCEL} type;
+ int argc;
+ t_atom * argv;
+ t_symbol * symbol;
+} detach_content_t;
+
+static void detach_thread(detach_t* x)
+{
+ detach_content_t * me;
+ while(1)
+ {
+ pthread_cond_wait(&x->x_cond, &x->x_mutex);
+
+ me = (detach_content_t*) fifo_get(x->x_fifo);
+
+ while (me != NULL)
+ {
+ /* run function */
+ switch (me->type)
+ {
+ case BANG:
+ outlet_bang(x->x_outlet);
+ break;
+ case FLOAT:
+ outlet_float(x->x_outlet, atom_getfloat(me->argv));
+ break;
+ case SYMBOL:
+ outlet_symbol(x->x_outlet, atom_getsymbol(me->argv));
+ break;
+ case LIST:
+ outlet_list(x->x_outlet, 0, me->argc, me->argv);
+ freebytes(me->argv, me->argc * sizeof(t_atom));
+ break;
+ case POINTER:
+ outlet_pointer(x->x_outlet, me->argv->a_w.w_gpointer);
+ break;
+ case ANYTHING:
+ outlet_anything(x->x_outlet, me->symbol, me->argc, me->argv);
+ freebytes(me->argv, me->argc * sizeof(t_atom));
+ break;
+ case CANCEL:
+ goto done;
+ }
+
+ /* free */
+ if (me->argc)
+ freebytes(me->argv, me->argc * sizeof (t_atom));
+ freebytes (me, sizeof(detach_content_t));
+ me = (detach_content_t*) fifo_get(x->x_fifo);
+ }
+ }
+
+ done: /* free the fifo and exit */
+
+ do
+ {
+ if (me->argc)
+ freebytes(me->argv, me->argc * sizeof (t_atom));
+ freebytes (me, sizeof(detach_content_t));
+ }
+ while ( (me = (detach_content_t*) fifo_get(x->x_fifo)) );
+
+ fifo_destroy(x->x_fifo);
+ pthread_mutex_destroy(&x->x_mutex);
+ pthread_cond_destroy(&x->x_cond);
+ return;
+}
+
+/* todo: take argument for thread priority */
+static detach_t * detach_new()
+{
+ detach_t *x = (detach_t*) pd_new(detach_class);
+ int status;
+
+ x->x_outlet = outlet_new(&x->x_obj, NULL);
+ x->x_fifo = fifo_init();
+
+ /* thread initialisation */
+ pthread_mutex_init (&x->x_mutex,NULL);
+ pthread_mutex_unlock (&x->x_mutex);
+ pthread_cond_init (&x->x_cond,NULL);
+
+ status = pthread_create(&x->x_thread, NULL,
+ (void*)detach_thread, x);
+
+ if (status == 0)
+ post("detaching thread");
+
+ return x;
+}
+
+static void detach_free(detach_t * x)
+{
+ detach_content_t * me = getbytes(sizeof(detach_content_t));
+
+ me->type = CANCEL;
+ me->argc = 0;
+ fifo_put(x->x_fifo, me);
+ pthread_cond_broadcast(&x->x_cond);
+}
+
+static void detach_bang(detach_t * x)
+{
+ detach_content_t * me = getbytes(sizeof(detach_content_t));
+
+ me->type = BANG;
+ me->argc = 0;
+ fifo_put(x->x_fifo, me);
+
+ pthread_cond_broadcast(&x->x_cond);
+}
+
+static void detach_float(detach_t * x, t_float f)
+{
+ detach_content_t * me = getbytes(sizeof(detach_content_t));
+
+ me->type = FLOAT;
+ me->argc = 1;
+ me->argv = getbytes(sizeof(t_atom));
+ SETFLOAT(me->argv, f);
+ fifo_put(x->x_fifo, me);
+
+ pthread_cond_broadcast(&x->x_cond);
+}
+
+static void detach_pointer(detach_t * x, t_gpointer* gp)
+{
+ detach_content_t * me = getbytes(sizeof(detach_content_t));
+
+ me->type = POINTER;
+ me->argc = 1;
+ me->argv = getbytes(sizeof(t_atom));
+ SETPOINTER(me->argv, gp);
+ fifo_put(x->x_fifo, me);
+
+ pthread_cond_broadcast(&x->x_cond);
+}
+
+static void detach_symbol(detach_t * x, t_symbol * s)
+{
+ detach_content_t * me = getbytes(sizeof(detach_content_t));
+
+ me->type = SYMBOL;
+ me->argc = 1;
+ me->argv = getbytes(sizeof(t_atom));
+ SETSYMBOL(me->argv, s);
+ fifo_put(x->x_fifo, me);
+
+ pthread_cond_broadcast(&x->x_cond);
+}
+
+static void detach_list(detach_t * x, t_symbol * s, int argc, t_atom* argv)
+{
+ detach_content_t * me = getbytes(sizeof(detach_content_t));
+
+ me->type = LIST;
+ me->argc = argc;
+ me->argv = copybytes(argv, argc * sizeof(t_atom));
+ fifo_put(x->x_fifo, me);
+
+ pthread_cond_broadcast(&x->x_cond);
+}
+
+static void detach_anything(detach_t * x, t_symbol * s, int argc, t_atom* argv)
+{
+ detach_content_t * me = getbytes(sizeof(detach_content_t));
+
+ me->type = ANYTHING;
+ me->argc = argc;
+ me->argv = copybytes(argv, argc * sizeof(t_atom));
+ me->symbol = s;
+ fifo_put(x->x_fifo, me);
+
+ pthread_cond_broadcast(&x->x_cond);
+}
+
+void detach_setup(void)
+{
+ detach_class = class_new(gensym("detach"), (t_newmethod)detach_new,
+ (t_method)detach_free, sizeof(detach_t),
+ CLASS_DEFAULT, 0);
+
+ class_addbang(detach_class, detach_bang);
+ class_addfloat(detach_class, detach_float);
+ class_addpointer(detach_class, detach_pointer);
+ class_addsymbol(detach_class, detach_symbol);
+ class_addlist(detach_class, detach_list);
+ class_addanything(detach_class, detach_anything);
+}
diff --git a/threadlib/src/fifo.c b/threadlib/src/fifo.c
new file mode 100755
index 0000000..daad265
--- /dev/null
+++ b/threadlib/src/fifo.c
@@ -0,0 +1,512 @@
+/*
+ * fifo.c
+ * this is the lockfree fifo implementation of pd_devel_0.39
+ *
+ * Copyright (c) 2004, Tim Blechmann
+ * supported by vibrez.net
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt" in this distribution. */
+
+
+#include "threadlib.h"
+#include "stddef.h"
+
+
+#ifndef THREADLIB_LOCKFREE
+
+/* we always have the implementation for posix systems with threadlocks */
+
+#include "errno.h"
+
+typedef struct _fifocell
+{
+ struct _fifocell* next;
+ void* data; /* pointer to our data */
+} t_fifocell;
+
+struct _fifo
+{
+ t_fifocell * head;
+ t_fifocell * tail;
+ pthread_mutex_t mutex;
+};
+
+
+t_fifo * fifo_init()
+{
+ t_fifo* ret = (t_fifo*) getbytes(sizeof (t_fifo));
+ t_fifocell * fifo_begin = (t_fifocell*) getbytes (sizeof (t_fifocell) );
+
+ fifo_begin->data = NULL;
+ fifo_begin->next = NULL;
+
+ ret->head = fifo_begin;
+ ret->tail = fifo_begin;
+
+ pthread_mutex_init(&ret->mutex, NULL);
+
+ pthread_mutex_unlock(&ret->mutex);
+
+ return ret;
+}
+
+void fifo_destroy(t_fifo* fifo)
+{
+ void * data;
+
+ do
+ {
+ data = fifo_get(fifo);
+ }
+ while (data != NULL);
+
+ pthread_mutex_lock(&fifo->mutex);
+ pthread_mutex_destroy(&fifo->mutex);
+
+ freebytes(fifo, sizeof(t_fifo));
+ return;
+}
+
+/* fifo_put and fifo_get are the only threadsafe functions!!! */
+void fifo_put(t_fifo* fifo, void* data)
+{
+ if (data != NULL)
+ {
+ t_fifocell * cell = (t_fifocell*) getbytes(sizeof(t_fifocell));
+
+ cell->data = data;
+ cell->next = NULL;
+
+ pthread_mutex_lock(&fifo->mutex);
+
+ fifo->tail->next = cell;
+ fifo->tail = cell;
+
+ pthread_mutex_unlock(&fifo->mutex);
+ }
+ return;
+}
+
+
+/* this fifo_get returns NULL if the fifo is empty
+ * or locked by another thread */
+void* fifo_get(t_fifo* fifo)
+{
+ t_fifocell * cell;
+ void* data;
+
+ if(pthread_mutex_trylock(&fifo->mutex) != EBUSY)
+ {
+ cell = fifo->head->next;
+
+ if (cell != NULL)
+ {
+ fifo->head->next = cell->next;
+ if(cell == fifo->tail)
+ fifo->tail = fifo->head;
+ data = cell->data;
+
+ freebytes (cell, sizeof(t_fifocell));
+ }
+ else
+ data = NULL;
+
+ pthread_mutex_unlock(&fifo->mutex);
+ }
+ else
+ data = NULL;
+ return data;
+}
+
+#else /* THREADLIB_LOCKFREE */
+
+/*
+ lockfree fifo adapted from the midishare: Copyright © Grame 1999
+ Grame Research Laboratory, 9, rue du Garet 69001 Lyon - France
+ grame@rd.grame.fr
+*/
+
+
+
+typedef struct _fifocell
+{
+ struct _fifocell* next;
+ void* data; /* pointer to our data */
+} t_fifocell;
+
+typedef struct _lifo
+{
+ unsigned long ic; /* operation counter */
+ t_fifocell* top; /* stack pointer */
+ unsigned long oc; /* operation counter */
+#ifdef __POWERPC__
+ long unused [5]; /* lifo size must be at least 32 bytes */
+ /* to avoid livelock in multiprocessor */
+#endif
+} t_lifo;
+
+struct _fifo
+{
+ t_lifo in;
+ t_lifo out;
+};
+
+/* platform dependent code */
+
+#ifdef __SMP__
+#define LOCK lock ;
+#else
+#define LOCK
+#endif
+
+#if defined(__GNUC__) && defined(__POWERPC__)
+
+static void* lifo_pop(t_lifo* lifo)
+{
+ register void * data;
+ register volatile long a, b;
+ register long c=0;
+ asm volatile (
+ "# LFPOP \n"
+ "0: \n"
+ " lwarx %4, %1, %2 \n" /* creates a reservation on lf */
+ " cmpwi %4, 0 \n" /* test if the lifo is empty */
+ " beq- 1f \n"
+ " lwz %5, 0(%4) \n" /* next cell in b */
+ " sync \n" /* synchronize instructions */
+ " stwcx. %5, %1, %2 \n" /* if the reservation is not altered */
+ /* modify lifo top */
+ " bne- 0b \n" /* otherwise: loop and try again */
+ "0: \n"
+ " lwarx %5, %1, %3 \n" /* creates a reservation on lf->count */
+ " addi %5, %5, -1 \n" /* dec count */
+ " sync \n" /* synchronize instructions */
+ " stwcx. %5, %1, %3 \n" /* conditionnal store */
+ " bne- 0b \n"
+ "1: \n"
+ " mr %0, %4 \n"
+ :"=r" (data), "=r" (c)
+ : "r" (&lifo->top), "r" (&lifo->oc), "r" (a), "r" (b), "1" (c)
+ : "r0" /* prevents using r0 because of the ambiguity of 'addi' coding: */
+ /* gcc version 2.95.3 20010315 (release - Linux-Mandrake 8.0 for PPC) */
+ /* compiles the instruction "addi 0, 0, n" as li 0, n */
+ );
+ return data;
+}
+
+static void* lifo_push(register t_lifo* lifo, register void* data)
+{
+ register volatile long t1;
+ register long t2=0;
+ asm volatile (
+ "# LFPUSH \n"
+ "0: \n"
+ " lwarx %0, %3, %1 \n"
+ " stw %0, 0(%2) \n"
+ " sync \n"
+ " stwcx. %2, %3, %1 \n"
+ " bne- 0b \n"
+ "0: \n"
+ " lwarx %0, %3, %4 \n"
+ " addi %0, %0, 1 \n"
+ " sync \n"
+ " stwcx. %0, %3, %4 \n"
+ " bne- 0b \n"
+ : "=r" (t1)
+ : "r" (&lifo->top), "r" (data), "r" (t2), "r" (&lifo->oc), "0" (t1)
+ : "r0" /* prevents using r0 because of the ambiguity of 'addi' coding: */
+ /* gcc version 2.95.3 20010315 (release - Linux-Mandrake 8.0 for PPC) */
+ /* compiles the instruction "addi 0, 0, n" as li 0, n */
+ );
+}
+
+#elif defined(__Macintosh__) || defined(__MacOSX__)
+
+static void* lifo_pop(t_lifo* lifo)
+{
+ register cell * data;
+ register long a, b;
+ asm {
+ addi lifo, lifo, 4
+ loop:
+ lwarx a, 0, lifo /* creates a reservation on lifo */
+ cmpwi a, 0 /* test if the lifo is empty */
+ beq- empty
+ lwz b, 0(a) /* next cell in b */
+ sync /* synchronize instructions */
+ stwcx. b, 0, lifo /* if the reservation is not altered */
+ /* modify lifo top */
+ bne- loop /* otherwise: loop and try again */
+
+ addi lifo, lifo, 4
+ dec:
+ lwarx b, 0, lifo /* creates a reservation on lifo->count */
+ addi b, b, -1 /* dec count */
+ sync /* synchronize instructions */
+ stwcx. b, 0, lifo /* conditionnal store */
+ bne- dec
+
+ empty:
+ mr data, a
+ }
+ return data;
+}
+
+static void lifo_push (register t_lifo * lifo, register void * data)
+{
+ register long tmp;
+ asm {
+ addi lifo, lifo, 4
+ loop:
+ lwarx tmp, 0, lifo /* creates a reservation on lifo */
+ stw tmp, 0(data) /* link the new cell to the lifo */
+ sync /* synchronize instructions */
+ stwcx. data, 0, lifo /* if the reservation is not altered */
+ /* modify lifo top */
+ bne- loop /* otherwise: loop and try again */
+
+ addi lifo, lifo, 4
+ inc:
+ lwarx tmp, 0, lifo /* creates a reservation on lifo->count */
+ addi tmp, tmp, 1 /* inc count */
+ sync /* synchronize instructions */
+ stwcx. tmp, 0, lifo /* conditionnal store */
+ bne- inc
+ }
+}
+
+
+
+#elif defined(__GNUC__) && (defined(_X86_) || defined(__i386__) || defined(__i586__) || defined(__i686__))
+
+static void* lifo_pop(t_lifo* lifo)
+{
+ void * data = 0;
+ __asm__ __volatile__ (
+ "# LFPOP \n\t"
+ "pushl %%ebx \n\t"
+ "pushl %%ecx \n\t"
+ "movl 4(%%esi), %%edx \n\t"
+ "movl (%%esi), %%eax \n\t"
+ "testl %%eax, %%eax \n\t"
+ "jz 20f \n"
+ "10:\t"
+ "movl (%%eax), %%ebx \n\t"
+ "movl %%edx, %%ecx \n\t"
+ "incl %%ecx \n\t"
+ LOCK "cmpxchg8b (%%esi) \n\t"
+ "jz 20f \n\t"
+ "testl %%eax, %%eax \n\t"
+ "jnz 10b \n"
+ "20:\t"
+ "popl %%ecx \n\t"
+ "popl %%ebx \n\t"
+ :"=a" (data)
+ :"S" (&lifo->top)
+ :"memory", "edx");
+ return data;
+}
+
+static void lifo_push(t_lifo * lifo, void * data)
+{
+ __asm__ __volatile__ (
+ "# LFPUSH \n\t"
+ "pushl %%ebx \n\t"
+ "pushl %%ecx \n\t"
+ "movl 0(%%esi), %%eax \n\t"
+ "movl 4(%%esi), %%edx \n"
+ "1:\t"
+ "movl %%eax, %%ebx \n\t"
+ "incl %%ebx \n\t"
+ "movl %%edx, (%%ecx) \n\t"
+ LOCK "cmpxchg8b (%%esi) \n\t"
+ "jnz 1b \n\t"
+ "popl %%ecx \n\t"
+ "popl %%ebx \n\t"
+ :/* no output */
+ :"S" (lifo), "c" (data)
+ :"memory", "eax", "edx");
+}
+
+#elif defined(__GNUC__) && defined(__x86_64__)
+
+/* this will not work for all revisions of the amd64 architecture ... */
+
+static void* lifo_pop(t_lifo* lifo)
+{
+ void * data = 0;
+ __asm__ __volatile__ (
+ "# LFPOP \n\t"
+ "push %%rbx \n\t"
+ "push %%rcx \n\t"
+ "mov 8(%%rdi), %%rdx \n\t"
+ "mov (%%rdi), %%rax \n\t"
+ "test %%rax, %%rax \n\t"
+ "jz 20f \n"
+ "10:\t"
+ "mov (%%rax), %%rbx \n\t"
+ "mov %%rdx, %%rcx \n\t"
+ "inc %%rcx \n\t"
+ LOCK "cmpxchg16b (%%rdi) \n\t"
+ "jz 20f \n\t"
+ "test %%rax, %%rax \n\t"
+ "jnz 10b \n"
+ "20:\t"
+ "pop %%rcx \n\t"
+ "pop %%rbx \n\t"
+ :"=a" (data)
+ :"D" (&lifo->top)
+ :"memory", "rdx");
+ return data;
+}
+
+static void lifo_push(t_lifo * lifo, void * data)
+{
+ __asm__ __volatile__ (
+ "# LFPUSH \n\t"
+ "push %%rbx \n\t"
+ "push %%rcx \n\t"
+ "mov 0(%%rdi), %%rax \n\t"
+ "mov 8(%%rdi), %%rdx \n"
+ "1:\t"
+ "mov %%rax, %%rbx \n\t"
+ "inc %%rbx \n\t"
+ "mov %%rdx, (%%rcx) \n\t"
+ LOCK "cmpxchg16b (%%rdi) \n\t"
+ "jnz 1b \n\t"
+ "pop %%rcx \n\t"
+ "pop %%rbx \n\t"
+ :/* no output */
+ :"D" (lifo), "c" (data)
+ :"memory", "rax", "rdx");
+}
+
+#elif defined(_WIN32) && defined(_MSC_VER)
+
+static void* lifo_pop(t_lifo* lifo)
+{
+ __asm
+ {
+ push ebx
+ push ecx
+ push edx
+ push esi
+ mov esi, lifo
+ add esi, 4
+ mov edx, dword ptr [esi+4]
+ mov eax, dword ptr [esi]
+ test eax, eax
+ jz _end
+ _loop:
+ mov ebx, dword ptr [eax]
+ mov ecx, edx
+ inc ecx
+ LOCK cmpxchg8b qword ptr [esi]
+ jz _end
+ test eax, eax
+ jnz _loop
+ _end:
+ pop esi
+ pop edx
+ pop ecx
+ pop ebx
+ }
+}
+
+static void lifo_push(t_lifo * lifo, void * data)
+{
+ __asm
+ {
+ push eax
+ push ebx
+ push ecx
+ push edx
+ push esi
+ mov esi, lifo
+ mov eax, dword ptr [esi]
+ mov ecx, data
+ mov edx, dword ptr 4[esi]
+ _loop:
+ mov ebx, eax
+ inc ebx
+ mov [ecx], edx
+ LOCK cmpxchg8b qword ptr [esi]
+ jnz _loop
+ pop esi
+ pop edx
+ pop ecx
+ pop ebx
+ pop eax
+ }
+}
+
+
+#else
+#error lockfree fifos not available on this platform
+#endif
+
+
+
+static void lifo_init(t_lifo* lifo)
+{
+ lifo->ic = 0;
+ lifo->top = NULL;
+ lifo->oc = 0;
+}
+
+t_fifo* fifo_init(void)
+{
+ t_fifo* ret = (t_fifo*) getbytes(sizeof(t_fifo));
+
+ lifo_init(&ret->in);
+ lifo_init(&ret->out);
+
+ return ret;
+}
+
+
+void fifo_destroy(t_fifo* fifo)
+{
+ void * data;
+ do
+ {
+ data = fifo_get(fifo);
+ }
+ while (data != NULL);
+
+ freebytes(fifo, sizeof(t_fifo));
+ return;
+}
+
+void fifo_put(t_fifo* fifo, void* data)
+{
+ lifo_push(&fifo->in, data);
+}
+
+void* fifo_get(t_fifo* fifo)
+{
+ void * data;
+ t_lifo *out = &fifo->out;
+
+ data = lifo_pop(out);
+
+ if (!data)
+ {
+ void * tmp;
+ t_lifo *in = &fifo->in;
+ data = lifo_pop(in);
+
+ if (data)
+ {
+ while((tmp = lifo_pop(in)))
+ {
+ lifo_push(out, data);
+ data = tmp;
+ }
+ }
+
+ }
+ return data;
+}
+
+#endif
diff --git a/threadlib/src/join.c b/threadlib/src/join.c
new file mode 100755
index 0000000..5e3f688
--- /dev/null
+++ b/threadlib/src/join.c
@@ -0,0 +1,161 @@
+/*
+*
+* join
+* Copyright (C) 2005 Georg Holzmann, <grh@mur.at>
+* Copyright (C) 2005 Tim Blechmann
+*
+* 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 "threadlib.h"
+#include <string.h>
+
+static t_class *join_class;
+
+typedef struct _join
+{
+ t_object x_obj;
+ t_outlet * x_outlet;
+} join_t;
+
+static join_t * join_new(void)
+{
+ join_t *x = (join_t*) pd_new(join_class);
+ x->x_outlet = outlet_new(&x->x_obj, NULL);
+ return x;
+}
+
+static t_int join_bang_callback(t_int * argv)
+{
+ outlet_bang((t_outlet*)argv[0]);
+ return 0;
+}
+
+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);
+}
+
+static t_int join_pointer_callback(t_int * argv)
+{
+ outlet_pointer((t_outlet*)argv[0], (t_gpointer*)argv[1]);
+ return 0;
+}
+
+static void join_pointer(join_t * x, t_gpointer * gp)
+{
+ t_int* argv = getbytes(2*sizeof(t_int*));
+ argv[0] = (t_int)x->x_outlet;
+ argv[1] = (t_int)gp;
+
+ h_set_callback(join_pointer_callback, argv, 2);
+}
+
+static t_int join_float_callback(t_int * argv)
+{
+ outlet_float((t_outlet*)argv[0], (t_float)argv[1]);
+ return 0;
+}
+
+static void join_float(join_t * x, t_float f)
+{
+ t_int* argv = getbytes(2*sizeof(t_int*));
+ argv[0] = (t_int)x->x_outlet;
+ argv[1] = (t_int)f;
+
+ h_set_callback(join_float_callback, argv, 2);
+}
+
+static t_int join_symbol_callback(t_int * argv)
+{
+ outlet_symbol((t_outlet*)argv[0], (t_symbol*)argv[1]);
+ return 0;
+}
+
+static void join_symbol(join_t * x, t_symbol * s)
+{
+ t_int* argv = getbytes(2*sizeof(t_int*));
+ argv[0] = (t_int)x->x_outlet;
+ argv[1] = (t_int)s;
+
+ h_set_callback(join_symbol_callback, argv, 2);
+}
+
+static t_int join_list_callback(t_int * argv)
+{
+ outlet_list((t_outlet*)argv[0], 0, (int)argv[1], (t_atom*)argv[2]);
+ freebytes ((t_atom*)argv[2], (int)argv[1] * sizeof(t_atom));
+ return 0;
+}
+
+static void join_list(join_t * x, t_symbol * s, int argc, t_atom* largv)
+{
+ t_int* argv = getbytes(3*sizeof(t_int*));
+ t_atom* copied_argv = copybytes(largv, argc * sizeof(t_atom));
+
+ argv[0] = (t_int)x->x_outlet;
+ argv[1] = (t_int)argc;
+ argv[2] = (t_int)copied_argv;
+
+ h_set_callback(join_list_callback, argv, 3);
+}
+
+static t_int join_anything_callback(t_int * argv)
+{
+ outlet_anything((t_outlet*)argv[0], &s_list,
+ (int)argv[1], (t_atom*)argv[2]);
+ freebytes ((t_atom*)argv[2], (int)argv[1] * sizeof(t_atom));
+ return 0;
+}
+
+static void join_anything(join_t * x, t_symbol * s, int argc, t_atom* largv)
+{
+ t_int* argv = getbytes(3*sizeof(t_int*));
+
+ // also copy selector symbol
+ int copied_argc = argc+1;
+ t_atom *copied_argv;
+ copied_argv = (t_atom*)getbytes(copied_argc * sizeof(t_atom));
+
+ if(copied_argc)
+ {
+ memcpy(copied_argv, s, sizeof(t_atom));
+ SETSYMBOL(copied_argv, s);
+ memcpy(copied_argv+1, largv, argc * sizeof(t_atom));
+ }
+
+ argv[0] = (t_int)x->x_outlet;
+ argv[1] = (t_int)copied_argc;
+ argv[2] = (t_int)copied_argv;
+
+ h_set_callback(join_anything_callback, argv, 3);
+}
+
+void join_setup(void)
+{
+ join_class = class_new(gensym("join"), (t_newmethod)join_new,
+ 0, sizeof(join_t), CLASS_DEFAULT, 0);
+
+ class_addbang(join_class, join_bang);
+ class_addfloat(join_class, join_float);
+ class_addpointer(join_class, join_pointer);
+ class_addsymbol(join_class, join_symbol);
+ class_addlist(join_class, join_list);
+ class_addanything(join_class, join_anything);
+}
diff --git a/threadlib/src/sleep.c b/threadlib/src/sleep.c
new file mode 100755
index 0000000..f800074
--- /dev/null
+++ b/threadlib/src/sleep.c
@@ -0,0 +1,58 @@
+/*
+*
+* sleep
+* like the c function sleep - blocks the system for a specific time
+* 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 "threadlib.h"
+#include <unistd.h>
+
+static t_class *sleep_class;
+
+typedef struct _sleep
+{
+ t_object x_obj;
+ t_outlet * x_outlet;
+} t_sleep;
+
+static void sleep_float(t_sleep * x, t_float f)
+{
+ int time = (int)(f<0?0:f);
+ sleep(time);
+ outlet_bang(x->x_outlet);
+}
+
+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;
+}
+
+void sleep_setup(void)
+{
+ sleep_class = class_new(gensym("sleep"),
+ (t_newmethod)sleep_new,
+ 0, sizeof(t_sleep),
+ CLASS_DEFAULT, 0);
+
+ class_addfloat(sleep_class, sleep_float);
+}
diff --git a/threadlib/src/threadedsf.c b/threadlib/src/threadedsf.c
new file mode 100755
index 0000000..c9d0b9f
--- /dev/null
+++ b/threadlib/src/threadedsf.c
@@ -0,0 +1,1919 @@
+/*
+*
+* threadedsf
+*
+* this is a little bit hacked version of the
+* threaded soundfiler of pd_devel_0.38 by Tim Blechmann
+*
+* (c) 2005, Georg Holzmann, <grh@mur.at>
+*/
+
+/* 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 <unistd.h>
+#include <fcntl.h>
+#endif
+#ifdef MSW
+#include <io.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#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 <fcntl.h>
+#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 <frames>
+ -skip <frames>
+ -bytes <bytes per sample>
+ -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 <sched.h>
+
+#ifdef _POSIX_MEMLOCK
+#include <sys/mman.h>
+#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> ... frames to skip in file
+ -nframes <frames>
+ -onset <frames> ... onset in table to read into (NOT DONE YET)
+ -raw <headersize channels bytes endian>
+ -resize
+ -maxsize <max-size>
+
+ 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 <n> -nframes <n> -resize -maxsize <n> ...");
+ post("-raw <headerbytes> <channels> <bytespersamp> <endian (b, l, or n)>.");
+
+ 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 <n> -nframes <n> -bytes <n> -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
new file mode 100755
index 0000000..da17100
--- /dev/null
+++ b/threadlib/src/threadlib.c
@@ -0,0 +1,83 @@
+/*
+*
+* threadlib
+* library for threaded patching in PureData
+* Copyright (C) 2005 Georg Holzmann <grh@mur.at>
+* heavily based on code by Tim Blechmann
+* (detach, join, pd_devel)
+*
+* 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 "threadlib.h"
+
+typedef struct threadlib
+{
+ t_object x_obj;
+} t_threadlib;
+
+t_class *threadlib_class;
+
+static void threadlib_help(void)
+{
+ post("\nthreadlib vers."VERSION", library for threaded patching\n"
+ "2005, by Georg Holzmann <grh@mur.at>\n"
+ "heavily based on pd_devel code by Tim Blechmann\n"
+
+ // help text:
+ "\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");
+}
+
+void *threadlib_new(void)
+{
+ t_threadlib *x = (t_threadlib *)pd_new(threadlib_class);
+ return (void *)x;
+}
+
+void sleep_setup();
+void detach_setup();
+void join_setup();
+void threadedsf_setup();
+void sys_start_sfthread();
+
+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"
+ "2005, by Georg Holzmann <grh@mur.at>\n"
+ "heavily based on pd_devel code by Tim Blechmann\n"
+ "WARNING: this is very experimental and may crash your patches !\n");
+
+ 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);
+}
diff --git a/threadlib/src/threadlib.h b/threadlib/src/threadlib.h
new file mode 100755
index 0000000..ead3a2f
--- /dev/null
+++ b/threadlib/src/threadlib.h
@@ -0,0 +1,81 @@
+/*
+*
+* threadlib
+* library for threaded patching in PureData
+* Copyright (C) 2005 Georg Holzmann <grh@mur.at>
+* heavily based on code by Tim Blechmann
+* (detach, join, pd_devel)
+*
+* 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 __PD_THREADLIB_H_
+#define __PD_THREADLIB_H_
+
+#include "m_pd.h"
+#include "pthread.h"
+
+
+// threadlib version string
+#define VERSION "0.1pre"
+
+// define it to use the lockfree fifo
+#define THREADLIB_LOCKFREE
+
+// for debuging
+//#define DEBUG
+
+
+/* --------- lockfree FIFO of pd devel ----------- */
+// (implemted in fifo.c)
+
+/* used data structures */
+EXTERN_STRUCT _fifo;
+#define t_fifo struct _fifo
+
+/* function prototypes */
+t_fifo * fifo_init(void);
+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*);
+
+
+/* --------- callback FIFO of pd devel ----------- */
+// (implemted in callbacks.c)
+
+/* NOTE: difference to pd_devel
+ * in pd_devel the callbacks are called in idle time
+ * (idle callbacks), because this is not possible
+ * in current pd, they are called here by the
+ * clock callbacks
+ */
+
+/* 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_