aboutsummaryrefslogtreecommitdiff
path: root/threadlib
diff options
context:
space:
mode:
Diffstat (limited to 'threadlib')
-rwxr-xr-xthreadlib/GnuGPL.txt290
-rwxr-xr-xthreadlib/INSTALL16
-rwxr-xr-xthreadlib/README65
-rwxr-xr-xthreadlib/doc/help-detach.pd105
-rwxr-xr-xthreadlib/doc/help-join.pd106
-rwxr-xr-xthreadlib/doc/help-sleep.pd88
-rwxr-xr-xthreadlib/doc/help-threadedsf.pd54
-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
18 files changed, 4117 insertions, 0 deletions
diff --git a/threadlib/GnuGPL.txt b/threadlib/GnuGPL.txt
new file mode 100755
index 0000000..fa0bef4
--- /dev/null
+++ b/threadlib/GnuGPL.txt
@@ -0,0 +1,290 @@
+GNU GENERAL PUBLIC LICENSE
+
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom
+to share and change it. By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This General
+Public License applies to most of the Free Software Foundation's
+software and to any other program whose authors commit to using it.
+(Some other Free Software Foundation software is covered by the
+GNU Library General Public License instead.) You can apply it to your
+programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new free
+programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone
+to deny you these rights or to ask you to surrender the rights. These
+restrictions translate to certain responsibilities for you if you distribute
+copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis
+or for a fee, you must give the recipients all the rights that you have. You
+must make sure that they, too, receive or can get the source code. And
+you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on,
+we want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software patents.
+We wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program
+proprietary. To prevent this, we have made it clear that any patent must
+be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+TERMS AND CONDITIONS FOR
+COPYING, DISTRIBUTION AND
+MODIFICATION
+
+0. This License applies to any program or other work which contains a
+notice placed by the copyright holder saying it may be distributed under
+the terms of this General Public License. The "Program", below, refers
+to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it, either
+verbatim or with modifications and/or translated into another language.
+(Hereinafter, translation is included without limitation in the term
+"modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of running
+the Program is not restricted, and the output from the Program is
+covered only if its contents constitute a work based on the Program
+(independent of having been made by running the Program). Whether
+that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the notices
+that refer to this License and to the absence of any warranty; and give
+any other recipients of the Program a copy of this License along with the
+Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide a
+ warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but does
+ not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program, and
+can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based on
+the Program, the distribution of the whole must be on the terms of this
+License, whose permissions for other licensees extend to the entire
+whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your
+rights to work written entirely by you; rather, the intent is to exercise the
+right to control the distribution of derivative or collective works based
+on the Program.
+
+In addition, mere aggregation of another work not based on the
+Program with the Program (or with a work based on the Program) on a
+volume of a storage or distribution medium does not bring the other
+work under the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding
+ machine-readable source code, which must be distributed under
+ the terms of Sections 1 and 2 above on a medium customarily
+ used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your cost
+ of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a
+ medium customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with
+ such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to control
+compilation and installation of the executable. However, as a special
+exception, the source code distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access to
+copy from a designated place, then offering equivalent access to copy
+the source code from the same place counts as distribution of the source
+code, even though third parties are not compelled to copy the source
+along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt otherwise
+to copy, modify, sublicense or distribute the Program is void, and will
+automatically terminate your rights under this License. However, parties
+who have received copies, or rights, from you under this License will not
+have their licenses terminated so long as such parties remain in full
+compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and all
+its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these terms
+and conditions. You may not impose any further restrictions on the
+recipients' exercise of the rights granted herein. You are not responsible
+for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot distribute
+so as to satisfy simultaneously your obligations under this License and
+any other pertinent obligations, then as a consequence you may not
+distribute the Program at all. For example, if a patent license would not
+permit royalty-free redistribution of the Program by all those who
+receive copies directly or indirectly through you, then the only way you
+could satisfy both it and this License would be to refrain entirely from
+distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents
+or other property right claims or to contest validity of any such claims;
+this section has the sole purpose of protecting the integrity of the free
+software distribution system, which is implemented by public license
+practices. Many people have made generous contributions to the wide
+range of software distributed through that system in reliance on
+consistent application of that system; it is up to the author/donor to
+decide if he or she is willing to distribute software through any other
+system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be
+a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain
+countries either by patents or by copyrighted interfaces, the original
+copyright holder who places the Program under this License may add an
+explicit geographical distribution limitation excluding those countries, so
+that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new
+versions of the General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may differ in
+detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number
+of this License, you may choose any version ever published by the Free
+Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we
+sometimes make exceptions for this. Our decision will be guided by the
+two goals of preserving the free status of all derivatives of our free
+software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF
+CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM,
+TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT
+WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE
+PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND,
+EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
+PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD
+THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE
+COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW
+OR AGREED TO IN WRITING WILL ANY COPYRIGHT
+HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED
+ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING
+ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT
+LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
+WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR
+OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
diff --git a/threadlib/INSTALL b/threadlib/INSTALL
new file mode 100755
index 0000000..0aac333
--- /dev/null
+++ b/threadlib/INSTALL
@@ -0,0 +1,16 @@
+threadlib installation
+
+1) open the right makefile for your platform/compiler
+
+2) adapt the makefile for your needs:
+ PDPATH should point to your pd installation (usually /usr/local/lib/pd or /usr/lib/pd)
+ PDSCR should point to the PD source diretory
+
+3) type "make" to compile
+
+4) type "make install" to install help patches and binary
+(you have to be root probably)
+
+
+in case of problems:
+grh@mur.at \ No newline at end of file
diff --git a/threadlib/README b/threadlib/README
new file mode 100755
index 0000000..54850f1
--- /dev/null
+++ b/threadlib/README
@@ -0,0 +1,65 @@
+threadlib
+PD library for threaded patching
+heavily based on pd_devel code by Tim Blechmann
+(c) 2005, Georg Holzmann, <grh@mur.at>
+http://grh.mur.at/software/threadlib.html
+
+------------------------------------------------------------------------
+
+contents of threadlib:
+ detach run part of the patch in a helper thread
+ join synchronize messages to pd's main thread
+ sleep blocks the system for a specific time
+ threadedsf modified threaded soundfiler from pd_devel_0.38
+
+WARNING:
+these objects (especially detach/join) are very experimental and may
+crash your patches if you use them in the wrong way, because some
+externals/internals are not threadsafe!
+
+REQUIREMENT: pd >= 0.39
+
+Many thanks to Tim Blechmann for his code and help!
+
+------------------------------------------------------------------------
+
+detach:
+Detach is working on control objects only, creates a helper thread
+and runs all functions of the following objects in this helper thread.
+If a message from that thread reaches a join object a callback for
+running the outlet function will be placed in the scheduler and run in
+the next scheduler loop - so you can synchronize the message with pd's
+main thread again.
+Detach is useful if you have a control operation that would be too
+CPU-intensive and would disturb dsp.
+
+join:
+The message to the inlet will be sent to the outlet during the
+next clock callback of the scheduler. It doesn't matter, which thread
+the message was coming from. Messages from the main pd thread will be
+rescheduled.
+Join can be used everywhere, where a message has to be rescheduled, this
+is also useful to place behind any threaded external calling the outlet
+functions from the helper thread, to make sure the following messages
+are being run in the main pd thread.
+
+threadedsf:
+This is the threaded soundfiler from pd devel 0.38 by Tim Blechmann,
+adapted to work with main pd >= 0.39.
+Instead of the idle callbacks, which are not really useable in current
+main pd, it uses clock-callbacks (and also the lockfree FIFO of pd devel).
+
+!!! WARNING: !!!
+detach/join provide the possibility of threaded patching.
+beware of the risks:
+- not every pd object is thread-safe (if unsure, look at the source
+code or ask at the pd-list or me)
+- pd is not completely thread-safe itself
+- everything that is triggered by the detached message will be
+detached. don't mix threaded and non-threaded message paths unless you
+know what you are doing. use the join external to synchronize with the
+main pd thread!!!!
+- if detach/join crashes pd during a performance, don't complain ...
+- both detach and join have an overhead ... so only use them, if you
+really need threaded patching, e.g. have a subpatch that has to run for
+quite some time and would interrupt audio or something else
diff --git a/threadlib/doc/help-detach.pd b/threadlib/doc/help-detach.pd
new file mode 100755
index 0000000..03895aa
--- /dev/null
+++ b/threadlib/doc/help-detach.pd
@@ -0,0 +1,105 @@
+#N canvas 658 0 480 847 10;
+#X obj 29 23 cnv 15 404 54 empty empty empty 22 25 0 18 -1 -66577 0
+;
+#X obj 31 25 cnv 15 400 50 empty empty threadlib 22 25 0 18 -228992
+-66577 0;
+#X text 322 34 help file of;
+#X text 316 50 ::: detach :::;
+#X text 39 783 =%)!(%= threadlib \, by Georg Holzmann <grh@mur.at>
+\, 2005;
+#X text 82 817 htttp://grh.mur.at/software/threadlib.html;
+#X text 29 150 Detach is working on control objects only \, creates
+a helper thread and runs all functions of the following object in this
+helper thread.;
+#X text 27 194 If a message form that thread reaches a join object
+a callback for running the outlet function will be placed in the scheduler
+and run in the next scheduler loop - so you can synchronize the message
+with pd's main thread again.;
+#X text 173 110 ::: DETACH :::;
+#X text 28 251 Detach is useful \, if you have a control operation
+that would be too CPU-intensive and would disturb dsp.;
+#X obj 223 717 sleep;
+#X obj 178 717 join;
+#X obj 273 717 threadedsf;
+#X text 98 717 see also:;
+#X text 163 324 see the examples:;
+#X text 154 407 !!!!! WARNING !!!!!;
+#X text 30 436 detach/join provide the possibility of threaded patching
+\, beware of the risks:;
+#X text 30 466 - not every pd object is thread-safe (if unsure \, look
+at the source \, ask the pd-list or me);
+#X text 31 497 - pd is not completely thread-safe itself;
+#X text 31 516 - everything that is triggered by the detached message
+will be detached - so don't mix threaded and non-threaded message paths
+unless you know what you are doing. use join to synchronize with the
+main pd thread !;
+#X text 31 575 - if detach/join crashes pd during a performance \,
+don't complain ...;
+#X text 31 607 - both detach and join have an overhead ... so only
+use them if you really need threaded patching \, e.g. have a subpatch
+that has to run for quite some time and would interrupt audio or something
+else;
+#X text 67 800 heavily based on pd_devel code by Tim Blechmann;
+#N canvas 421 0 803 576 detach_join_examples 0;
+#X msg 117 105 5;
+#X obj 117 133 sleep;
+#X text 37 39 1) this will block the system for 5 sec:;
+#X obj 117 161 print EX1_DONE;
+#X text 554 112 (instead of sleep you could;
+#X msg 499 73 5;
+#X obj 499 123 sleep;
+#X text 396 39 2) to avoid this you can run it in a helper thread:
+;
+#X obj 499 97 detach;
+#X obj 499 184 print EX2_DONE;
+#X text 560 127 of course use an other cpu;
+#X text 559 143 intensive object which could;
+#X text 561 158 block the main thread);
+#X text 34 252 3) be aware that each object connected to;
+#X text 55 267 detach will run in the helper thread:;
+#X msg 127 305 5;
+#X obj 127 366 sleep;
+#X obj 127 330 detach;
+#X obj 170 397 print EX3_DONE_1;
+#X msg 127 396 5;
+#X obj 127 433 sleep;
+#X obj 127 514 print EX3_DONE_2;
+#X text 179 436 <- also in helper thread;
+#X text 164 457 (and anything else connected;
+#X text 171 472 to objects which are connected;
+#X text 172 487 to detach !);
+#X text 392 251 4) to synchronize these objects with pd main thread
+;
+#X text 413 268 again you have to use the join object:;
+#X text 58 56 (so you will get e.g. an 5 sec;
+#X text 61 71 audio drop out !);
+#X msg 503 303 5;
+#X obj 503 364 sleep;
+#X obj 503 328 detach;
+#X msg 503 394 5;
+#X obj 546 395 print EX4_DONE_1;
+#X text 579 368 <- in helper thread;
+#X obj 503 431 join;
+#X obj 503 463 sleep;
+#X obj 503 518 print EX4_DONE_2;
+#X text 551 464 <- in main thread again;
+#X text 551 481 (and will so block the system);
+#X connect 0 0 1 0;
+#X connect 1 0 3 0;
+#X connect 5 0 8 0;
+#X connect 6 0 9 0;
+#X connect 8 0 6 0;
+#X connect 15 0 17 0;
+#X connect 16 0 18 0;
+#X connect 16 0 19 0;
+#X connect 17 0 16 0;
+#X connect 19 0 20 0;
+#X connect 20 0 21 0;
+#X connect 30 0 32 0;
+#X connect 31 0 33 0;
+#X connect 31 0 34 0;
+#X connect 32 0 31 0;
+#X connect 33 0 36 0;
+#X connect 36 0 37 0;
+#X connect 37 0 38 0;
+#X restore 141 343 pd detach_join_examples;
diff --git a/threadlib/doc/help-join.pd b/threadlib/doc/help-join.pd
new file mode 100755
index 0000000..44fe459
--- /dev/null
+++ b/threadlib/doc/help-join.pd
@@ -0,0 +1,106 @@
+#N canvas 280 37 480 847 10;
+#X obj 29 23 cnv 15 404 54 empty empty empty 22 25 0 18 -1 -66577 0
+;
+#X obj 31 25 cnv 15 400 50 empty empty threadlib 22 25 0 18 -228992
+-66577 0;
+#X text 322 34 help file of;
+#X text 39 783 =%)!(%= threadlib \, by Georg Holzmann <grh@mur.at>
+\, 2005;
+#X text 82 817 htttp://grh.mur.at/software/threadlib.html;
+#X obj 232 717 sleep;
+#X obj 281 717 threadedsf;
+#X text 95 717 see also:;
+#X text 163 324 see the examples:;
+#X text 154 407 !!!!! WARNING !!!!!;
+#X text 30 436 detach/join provide the possibility of threaded patching
+\, beware of the risks:;
+#X text 30 466 - not every pd object is thread-safe (if unsure \, look
+at the source \, ask the pd-list or me);
+#X text 31 497 - pd is not completely thread-safe itself;
+#X text 31 516 - everything that is triggered by the detached message
+will be detached - so don't mix threaded and non-threaded message paths
+unless you know what you are doing. use join to synchronize with the
+main pd thread !;
+#X text 31 575 - if detach/join crashes pd during a performance \,
+don't complain ...;
+#X text 31 607 - both detach and join have an overhead ... so only
+use them if you really need threaded patching \, e.g. have a subpatch
+that has to run for quite some time and would interrupt audio or something
+else;
+#X text 67 800 heavily based on pd_devel code by Tim Blechmann;
+#N canvas 421 0 803 576 detach_join_examples 0;
+#X msg 117 105 5;
+#X obj 117 133 sleep;
+#X text 37 39 1) this will block the system for 5 sec:;
+#X obj 117 161 print EX1_DONE;
+#X text 554 112 (instead of sleep you could;
+#X msg 499 73 5;
+#X obj 499 123 sleep;
+#X text 396 39 2) to avoid this you can run it in a helper thread:
+;
+#X obj 499 97 detach;
+#X obj 499 184 print EX2_DONE;
+#X text 560 127 of course use an other cpu;
+#X text 559 143 intensive object which could;
+#X text 561 158 block the main thread);
+#X text 34 252 3) be aware that each object connected to;
+#X text 55 267 detach will run in the helper thread:;
+#X msg 127 305 5;
+#X obj 127 366 sleep;
+#X obj 127 330 detach;
+#X obj 170 397 print EX3_DONE_1;
+#X msg 127 396 5;
+#X obj 127 433 sleep;
+#X obj 127 514 print EX3_DONE_2;
+#X text 179 436 <- also in helper thread;
+#X text 164 457 (and anything else connected;
+#X text 171 472 to objects which are connected;
+#X text 172 487 to detach !);
+#X text 392 251 4) to synchronize these objects with pd main thread
+;
+#X text 413 268 again you have to use the join object:;
+#X text 58 56 (so you will get e.g. an 5 sec;
+#X text 61 71 audio drop out !);
+#X msg 503 303 5;
+#X obj 503 364 sleep;
+#X obj 503 328 detach;
+#X msg 503 394 5;
+#X obj 546 395 print EX4_DONE_1;
+#X text 579 368 <- in helper thread;
+#X obj 503 431 join;
+#X obj 503 463 sleep;
+#X obj 503 518 print EX4_DONE_2;
+#X text 551 464 <- in main thread again;
+#X text 551 481 (and will so block the system);
+#X connect 0 0 1 0;
+#X connect 1 0 3 0;
+#X connect 5 0 8 0;
+#X connect 6 0 9 0;
+#X connect 8 0 6 0;
+#X connect 15 0 17 0;
+#X connect 16 0 18 0;
+#X connect 16 0 19 0;
+#X connect 17 0 16 0;
+#X connect 19 0 20 0;
+#X connect 20 0 21 0;
+#X connect 30 0 32 0;
+#X connect 31 0 33 0;
+#X connect 31 0 34 0;
+#X connect 32 0 31 0;
+#X connect 33 0 36 0;
+#X connect 36 0 37 0;
+#X connect 37 0 38 0;
+#X restore 141 343 pd detach_join_examples;
+#X text 321 50 ::: join :::;
+#X text 176 109 ::: JOIN :::;
+#X obj 175 717 detach;
+#X text 31 150 Join synchronizes messages from all threads to the pd
+main thread. The message to the inlet will be sent to the outlet during
+the next clock callback of the scheduler. It doesn't matter \, which
+thread the message was coming from. Messages from the main pd thread
+will be rescheduled.;
+#X text 30 217 Join can be used everywhere \, where a message has to
+be rescheduled \, this is also useful to place behind any threaded
+external calling the outlet function from a helper thread - so you
+can be sure \, that the following messages are being run in the main
+pd thread.;
diff --git a/threadlib/doc/help-sleep.pd b/threadlib/doc/help-sleep.pd
new file mode 100755
index 0000000..b9c172d
--- /dev/null
+++ b/threadlib/doc/help-sleep.pd
@@ -0,0 +1,88 @@
+#N canvas 280 37 465 589 10;
+#X obj 29 23 cnv 15 404 54 empty empty empty 22 25 0 18 -1 -66577 0
+;
+#X obj 31 25 cnv 15 400 50 empty empty threadlib 22 25 0 18 -228992
+-66577 0;
+#X text 322 34 help file of;
+#X text 27 518 =%)!(%= threadlib \, by Georg Holzmann <grh@mur.at>
+\, 2005;
+#X text 70 552 htttp://grh.mur.at/software/threadlib.html;
+#X obj 261 452 threadedsf;
+#X text 83 452 see also:;
+#X text 55 535 heavily based on pd_devel code by Tim Blechmann;
+#N canvas 421 0 803 576 detach_join_examples 0;
+#X msg 117 105 5;
+#X obj 117 133 sleep;
+#X text 37 39 1) this will block the system for 5 sec:;
+#X obj 117 161 print EX1_DONE;
+#X text 554 112 (instead of sleep you could;
+#X msg 499 73 5;
+#X obj 499 123 sleep;
+#X text 396 39 2) to avoid this you can run it in a helper thread:
+;
+#X obj 499 97 detach;
+#X obj 499 184 print EX2_DONE;
+#X text 560 127 of course use an other cpu;
+#X text 559 143 intensive object which could;
+#X text 561 158 block the main thread);
+#X text 34 252 3) be aware that each object connected to;
+#X text 55 267 detach will run in the helper thread:;
+#X msg 127 305 5;
+#X obj 127 366 sleep;
+#X obj 127 330 detach;
+#X obj 170 397 print EX3_DONE_1;
+#X msg 127 396 5;
+#X obj 127 433 sleep;
+#X obj 127 514 print EX3_DONE_2;
+#X text 179 436 <- also in helper thread;
+#X text 164 457 (and anything else connected;
+#X text 171 472 to objects which are connected;
+#X text 172 487 to detach !);
+#X text 392 251 4) to synchronize these objects with pd main thread
+;
+#X text 413 268 again you have to use the join object:;
+#X text 58 56 (so you will get e.g. an 5 sec;
+#X text 61 71 audio drop out !);
+#X msg 503 303 5;
+#X obj 503 364 sleep;
+#X obj 503 328 detach;
+#X msg 503 394 5;
+#X obj 546 395 print EX4_DONE_1;
+#X text 579 368 <- in helper thread;
+#X obj 503 431 join;
+#X obj 503 463 sleep;
+#X obj 503 518 print EX4_DONE_2;
+#X text 551 464 <- in main thread again;
+#X text 551 481 (and will so block the system);
+#X connect 0 0 1 0;
+#X connect 1 0 3 0;
+#X connect 5 0 8 0;
+#X connect 6 0 9 0;
+#X connect 8 0 6 0;
+#X connect 15 0 17 0;
+#X connect 16 0 18 0;
+#X connect 16 0 19 0;
+#X connect 17 0 16 0;
+#X connect 19 0 20 0;
+#X connect 20 0 21 0;
+#X connect 30 0 32 0;
+#X connect 31 0 33 0;
+#X connect 31 0 34 0;
+#X connect 32 0 31 0;
+#X connect 33 0 36 0;
+#X connect 36 0 37 0;
+#X connect 37 0 38 0;
+#X restore 135 372 pd detach_join_examples;
+#X obj 163 452 detach;
+#X text 319 50 ::: sleep :::;
+#X text 176 109 ::: SLEEP :::;
+#X text 31 149 Sleep simply uses the c-function sleep() and blocks
+the system for a specific time.;
+#X obj 122 256 sleep;
+#X msg 122 226 5;
+#X text 159 227 <- sleep time in seconds !;
+#X obj 122 287 print SLEEP_OVER;
+#X text 154 353 see more examples:;
+#X obj 220 452 join;
+#X connect 13 0 16 0;
+#X connect 14 0 13 0;
diff --git a/threadlib/doc/help-threadedsf.pd b/threadlib/doc/help-threadedsf.pd
new file mode 100755
index 0000000..7d6ea64
--- /dev/null
+++ b/threadlib/doc/help-threadedsf.pd
@@ -0,0 +1,54 @@
+#N canvas 728 33 465 834 10;
+#X obj 29 23 cnv 15 404 54 empty empty empty 22 25 0 18 -1 -66577 0
+;
+#X obj 31 25 cnv 15 400 50 empty empty threadlib 22 25 0 18 -228992
+-66577 0;
+#X text 314 35 help file of;
+#X text 27 765 =%)!(%= threadlib \, by Georg Holzmann <grh@mur.at>
+\, 2005;
+#X text 70 799 htttp://grh.mur.at/software/threadlib.html;
+#X text 100 711 see also:;
+#X text 55 782 heavily based on pd_devel code by Tim Blechmann;
+#X obj 180 711 detach;
+#X obj 237 711 join;
+#X text 292 51 ::: threadedsf :::;
+#X text 162 112 ::: THREADEDSF :::;
+#X text 106 152 A threaded soundfiler for main pd.;
+#X obj 274 191 soundfiler;
+#X obj 278 711 sleep;
+#X text 27 594 NOTE: this is the threaded soundfiler from pd devel
+0.38 by Tim Blechmann \, adapted to work with main pd >= 0.39. Instead
+of the idle callbacks \, which are not really useable in current main
+pd \, it uses clock-callbacks (ans also the lockfree FIFO of pd devel).
+;
+#X obj 70 512 threadedsf;
+#X obj 70 534 print THREADED_SF;
+#X obj 70 489 r \$0-tsf;
+#X text 101 191 USAGE: see help-file of;
+#X text 148 207 (same usage);
+#X obj 52 307 openpanel;
+#X msg 52 329 read -resize \$1 array1;
+#X obj 52 351 s \$0-tsf;
+#X obj 52 287 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 248 349 s \$0-tsf;
+#X obj 248 285 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X msg 248 327 write -wave \$1 array1;
+#X obj 248 305 savepanel;
+#X obj 265 505 table array1;
+#X text 48 255 the same as with soundfiler:;
+#X text 73 286 <- read;
+#X text 271 284 <- write;
+#X obj 262 437 s \$0-tsf;
+#X text 46 409 additional: threaded resize->;
+#X msg 262 411 resize array1 4410;
+#X connect 15 0 16 0;
+#X connect 17 0 15 0;
+#X connect 20 0 21 0;
+#X connect 21 0 22 0;
+#X connect 23 0 20 0;
+#X connect 25 0 27 0;
+#X connect 26 0 24 0;
+#X connect 27 0 26 0;
+#X connect 34 0 32 0;
diff --git a/threadlib/src/Makefile b/threadlib/src/Makefile
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_