aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--DMXUSBPro/DMXUSBPro-help.pd24
-rw-r--r--DMXUSBPro/DMXUSBPro.pd24
-rw-r--r--MIDIvice/LICENSE.txt290
-rw-r--r--MIDIvice/README.txt92
-rw-r--r--MIDIvice/doc/motormix.pd193
-rw-r--r--MIDIvice/src/MIDIvice.c46
-rw-r--r--MIDIvice/src/MIDIvice.h44
-rw-r--r--MIDIvice/src/Make.config.in31
-rw-r--r--MIDIvice/src/Makefile86
-rw-r--r--MIDIvice/src/configure.ac273
-rw-r--r--MIDIvice/src/motormix.c868
-rw-r--r--MIDIvice/src/motormix_buttons.txt374
-rw-r--r--README.txt8
-rw-r--r--aka.wiiremote/COPYRIGHT.txt13
-rw-r--r--aka.wiiremote/HISTORY.txt54
-rw-r--r--aka.wiiremote/Makefile33
-rw-r--r--aka.wiiremote/README.txt6
-rw-r--r--aka.wiiremote/TODO7
-rw-r--r--aka.wiiremote/aka.wiiremote.c646
-rw-r--r--aka.wiiremote/akawiiremote-help.pd82
-rw-r--r--aka.wiiremote/wiiremote.c1100
-rw-r--r--aka.wiiremote/wiiremote.h177
-rw-r--r--dmx512/FAQ.txt2
-rw-r--r--dmx512/GnuGPL.txt340
-rw-r--r--dmx512/README.dmx4linux.txt79
-rw-r--r--dmx512/README.txt48
-rw-r--r--dmx512/dmx4linux2.6.1.patch196
-rw-r--r--dmx512/help/dmxout-help.pd36
-rw-r--r--dmx512/src/Make.config.in31
-rw-r--r--dmx512/src/Make.version1
-rw-r--r--dmx512/src/Makefile76
-rw-r--r--dmx512/src/configure.ac177
-rw-r--r--dmx512/src/dmx4pd.h41
-rw-r--r--dmx512/src/dmxin.c139
-rw-r--r--dmx512/src/dmxout.c299
-rw-r--r--dmx512/src/dmxout_b.c240
-rw-r--r--itrax2/GnuGPL.txt340
-rw-r--r--itrax2/LICENSE.txt21
-rw-r--r--itrax2/itrax2.c128
-rw-r--r--itrax2/makefile54
-rw-r--r--itrax2/makefile_win38
-rw-r--r--lanbox/lanbox-help.pd183
-rw-r--r--lanbox/lanbox.pd61
-rw-r--r--memPIO/README.txt40
-rw-r--r--memPIO/memPIO-help.pd52
-rw-r--r--memPIO/memPIO.cpp192
-rw-r--r--memPIO/memPIO.dsp67
-rw-r--r--memPIO/memPIO.dsw29
-rwxr-xr-xmultio/Makefile72
-rw-r--r--multio/multio-help.pd95
-rw-r--r--multio/multio.c614
-rw-r--r--multio/multio.libs1
-rw-r--r--sixaxis/Makefile242
-rw-r--r--sixaxis/sixaxis-help.pd127
-rw-r--r--sixaxis/sixaxis.c412
-rw-r--r--uDMX/README2
-rw-r--r--uDMX/makefile105
-rw-r--r--uDMX/uDMX-help.pd12
-rwxr-xr-xuDMX/uDMX.c275
-rw-r--r--uDMX/uDMX_cmds.h31
-rw-r--r--w32mote/VC2008/wiimote.sln17
-rw-r--r--w32mote/VC2008/wiimote.vcproj379
-rw-r--r--w32mote/WiiYourself/History.txt281
-rw-r--r--w32mote/WiiYourself/License.txt42
-rw-r--r--w32mote/WiiYourself/ReadMe.txt202
-rw-r--r--w32mote/WiiYourself/wiimote.cpp2806
-rw-r--r--w32mote/WiiYourself/wiimote.h495
-rw-r--r--w32mote/WiiYourself/wiimote_common.h109
-rw-r--r--w32mote/WiiYourself/wiimote_state.h379
-rw-r--r--w32mote/wiimote-test.pd48
-rw-r--r--w32mote/wiimote4pd.cpp525
-rw-r--r--xbee/Makefile244
-rw-r--r--xbee/max2pd.h19
-rw-r--r--xbee/xbee.c340
-rw-r--r--xbee/xbee.h261
-rw-r--r--xbee/xbee_internal.h248
-rw-r--r--xbee/xbee_io.c268
-rw-r--r--xbee/xbee_io.h34
-rw-r--r--xbee/xbee_protocol.h292
-rw-r--r--xbee/xbee_test-help.pd2
-rw-r--r--xbee/xbee_test.c457
81 files changed, 16817 insertions, 0 deletions
diff --git a/DMXUSBPro/DMXUSBPro-help.pd b/DMXUSBPro/DMXUSBPro-help.pd
new file mode 100644
index 0000000..0b5e022
--- /dev/null
+++ b/DMXUSBPro/DMXUSBPro-help.pd
@@ -0,0 +1,24 @@
+#N canvas 1083 45 723 488 10;
+#X obj -186 358 DMXUSBPro;
+#X msg -185 176 devices;
+#X msg -146 198 devicename /dev/ttyUSB0;
+#X text -200 77 tags: hardware;
+#X text -200 100 contact/bugs/comments: mrtoftrash@gmail.com;
+#X text -201 43 description: DMXUSBPro is an abstraction for the control
+of the DMX USB Pro by Enttec.;
+#X msg -40 310 132 543 237 174;
+#X text 67 308 Sending a list will output that DMX value to the channel
+matching the position of the element in the list. So the first element
+is mapped to channel 1 \, the second to channel 2 and son on.;
+#X msg -102 233 close;
+#X msg -81 254 open 1;
+#X text -63 232 Close Serial port;
+#X text -34 253 Open serial port by number;
+#X text 6 197 open the serial port by name;
+#X text -129 173 print available devices (wait for it...);
+#X text -191 131 For now \, only DMX output is supported.;
+#X connect 1 0 0 0;
+#X connect 2 0 0 0;
+#X connect 6 0 0 0;
+#X connect 8 0 0 0;
+#X connect 9 0 0 0;
diff --git a/DMXUSBPro/DMXUSBPro.pd b/DMXUSBPro/DMXUSBPro.pd
new file mode 100644
index 0000000..5a3776b
--- /dev/null
+++ b/DMXUSBPro/DMXUSBPro.pd
@@ -0,0 +1,24 @@
+#N canvas 230 295 640 380 10;
+#X obj 33 12 inlet;
+#X obj 37 39 route list;
+#X obj 38 128 t a a;
+#X obj 65 151 zexy/length;
+#X obj 38 207 list prepend;
+#X obj 38 237 list append 231;
+#X obj 37 98 list prepend 0;
+#X msg 65 175 126 6 \$1 0;
+#X obj 266 295 comport;
+#X obj 420 189 loadbang;
+#X msg 420 217 baud 115200;
+#X connect 0 0 1 0;
+#X connect 1 0 6 0;
+#X connect 1 1 8 0;
+#X connect 2 0 4 0;
+#X connect 2 1 3 0;
+#X connect 3 0 7 0;
+#X connect 4 0 5 0;
+#X connect 5 0 8 0;
+#X connect 6 0 2 0;
+#X connect 7 0 4 1;
+#X connect 9 0 10 0;
+#X connect 10 0 8 0;
diff --git a/MIDIvice/LICENSE.txt b/MIDIvice/LICENSE.txt
new file mode 100644
index 0000000..fa0bef4
--- /dev/null
+++ b/MIDIvice/LICENSE.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/MIDIvice/README.txt b/MIDIvice/README.txt
new file mode 100644
index 0000000..a57ec6e
--- /dev/null
+++ b/MIDIvice/README.txt
@@ -0,0 +1,92 @@
+the MIDIvice library
+====================
+
+VERSION:
+0.1
+
+--------------------
+
+GENERAL::
+the MIDIvice library is a collection of externals for miller.s.puckette's realtime-computermusic-environment called "puredata" (or abbreviated "pd")
+this MIDIvice-library will be of no use, if you don't have a running version of pd on your system.
+check out for http://puredata.info to learn more about pd and how to get it
+
+LICENSE::
+the MIDIvice library is published under the Gnu General Public License that is included (LICENSE.txt).
+some parts of the code are taken directly from the pd source-code, they, of course, fall under the license pd is published under.
+
+AUTHORS::
+this software is copyleft 2002-2008 by IOhannes m zmoelnig <zmoelnig@iem.at>, Institute of Electronic Music and Acoustics, University of Music and Dramatic Arts, Graz, Austria
+
+--------------------
+
+PURPOSE::
+MIDIvice attempts to make handling of complex(!!!) MIDI-devices easier under Pd. It's like hiding all the SysEx-crap.
+Such devices include
+ - mixing-consoles
+ - controllable patch-bays
+ - Synthesizers (for doing SampleDumps etc.)
+
+Such devices do NOT include:
+- Synthesizers (for playing purposes; there is enough support under pd, i think)
+- hardware, that gives your PC the possibility of doing MIDI (so really, i am not going to write another device-driver for your USBMIDI thing)
+
+SUPPORTED DEVICES::
+- MotorMix(tm) by cm-labs(r) - http://www.cmlabs.net
+ 8-channel motorfader-box with lots of buttons, LCDisplay and pan-pots.
+ the MotorMix-specification were supplied by cm-labs (Many thanks !!).
+ You can now download it from ftp://ftp.iem.at/pub/pd/Externals/MIDIvice/motormix.pdf
+
+ objects:
+ MotorMix - ping and reset the MotorMix
+ motormix_rotary - get movements of the rotaries
+ motormix_encoder - get movement and push-state of the special "encoder"-rotary
+ motormix_faderIn - get movements of the faders
+ motormix_faderOut - move the motorfaders
+ motormix_button - get button press/releases
+ motormix_LED - switch on/off the button-LEDs (ot let them blink)
+ motormix_LCDtext - display some text on the MotorMix-LCDisplay
+ motormix_LCDgraph - display some simple graphics on the MotorMix-LCDisplay
+ motormix_7seg - display something on MotorMix's 7segment dispay
+
+--------------------
+
+TODO::
+support for FriendChip digital patchbays
+support for TCelectronics M-5000
+...
+feel free to send me your wish-list (probably with MIDI-specifications)
+
+BUGS:
+none known (right now)
+motormix_button/LED could be more intuitive...
+
+--------------------
+
+INSTALLING::
+linux :
+change to directory source
+adapt the makefile to match your system (where is pd installed ?)
+"make clean"
+"make"
+"make install"
+this will install the MIDIvice library into <mypdpath>/pd/extra
+documentation will be installed to <mypdpath>/pd/doc/5.reference/MIDIvice
+alternatively you can try "make everything"
+
+win32 :
+i haven't had time to compile and test the MIDIvice-library under Windos yet.
+Good luck !
+
+darwin :
+TODO
+
+irix :
+though i have physical access to both SGI's O2s and indys, i haven't tried to compile the MIDIvice library there.
+Good luck !
+
+--------------------
+
+RUNNING::
+add the "MIDIvice" library to your startup-path
+see doc/ for more information
diff --git a/MIDIvice/doc/motormix.pd b/MIDIvice/doc/motormix.pd
new file mode 100644
index 0000000..2663f0a
--- /dev/null
+++ b/MIDIvice/doc/motormix.pd
@@ -0,0 +1,193 @@
+#N canvas 171 107 566 570 10;
+#X msg 77 131 bang;
+#X msg 121 131 reset;
+#X msg 178 131 help;
+#N canvas 269 178 589 510 switches 0;
+#X obj 83 99 motormix_button;
+#X floatatom 83 122 5 0 0;
+#X floatatom 183 122 5 0 0;
+#X text 182 144 press/release;
+#X text 81 148 button#;
+#X obj 83 457 motormix_LED;
+#X msg 181 299 1;
+#X msg 181 321 0;
+#X msg 181 343 -1;
+#X floatatom 83 321 5 0 128;
+#X obj 132 379 t b f;
+#X text 93 301 button#;
+#X text 215 299 on;
+#X text 212 320 off;
+#X text 217 344 blink;
+#X obj 83 215 spigot;
+#X obj 120 198 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 0
+1;
+#X floatatom 162 429 5 0 0;
+#X text 16 13 get button-states (pressed/released) and illuminated
+the button-LEDs.;
+#X text 337 89 get state;
+#X text 316 409 illuminate LED;
+#X connect 0 0 1 0;
+#X connect 0 1 2 0;
+#X connect 1 0 15 0;
+#X connect 6 0 10 0;
+#X connect 7 0 10 0;
+#X connect 8 0 10 0;
+#X connect 9 0 5 0;
+#X connect 10 0 5 0;
+#X connect 10 1 17 0;
+#X connect 15 0 9 0;
+#X connect 16 0 15 1;
+#X connect 17 0 5 1;
+#X restore 79 386 pd switches;
+#N canvas 221 144 600 463 faders 0;
+#X obj 312 58 motormix_faderIn;
+#X floatatom 312 81 5 0 0;
+#X floatatom 365 81 5 0 0;
+#X floatatom 419 80 5 0 0;
+#X floatatom 107 80 5 0 0;
+#X floatatom 228 80 5 0 0;
+#X obj 107 58 motormix_faderIn 1;
+#X text 311 102 value;
+#X text 361 103 touched;
+#X text 423 103 fader#;
+#X text 107 100 value;
+#X text 223 99 touched;
+#X obj 309 348 motormix_faderOut;
+#X obj 97 348 motormix_faderOut 4;
+#X floatatom 309 326 5 0 128;
+#X floatatom 97 326 5 0 128;
+#X floatatom 423 326 5 1 8;
+#X text 306 309 value;
+#X text 422 309 fader#;
+#X text 93 310 value;
+#X text 36 18 get fader-values:;
+#X text 33 278 move the motor-faders:;
+#X connect 0 0 1 0;
+#X connect 0 1 2 0;
+#X connect 0 2 3 0;
+#X connect 6 0 4 0;
+#X connect 6 1 5 0;
+#X connect 14 0 12 0;
+#X connect 15 0 13 0;
+#X connect 16 0 12 1;
+#X restore 77 307 pd faders;
+#N canvas 417 60 470 311 rotaries 0;
+#X obj 290 73 motormix_rotary;
+#X floatatom 290 96 5 0 0;
+#X floatatom 390 96 5 0 0;
+#X floatatom 36 87 5 0 0;
+#X obj 36 64 motormix_rotary 4;
+#X text 39 110 increment;
+#X text 284 121 increment;
+#X text 396 122 rotary#;
+#X obj 68 233 motormix_encoder;
+#X floatatom 68 257 5 0 0;
+#X floatatom 175 258 5 0 0;
+#X text 178 281 push;
+#X text 68 280 increment;
+#X connect 0 0 1 0;
+#X connect 0 1 2 0;
+#X connect 4 0 3 0;
+#X connect 8 0 9 0;
+#X connect 8 1 10 0;
+#X restore 77 342 pd rotaries;
+#N canvas 179 9 750 744 LCDisplay 0;
+#X obj 72 391 motormix_LCDtext;
+#X msg 72 92 MotorMix (tm) is now supported by pd !!!;
+#X msg 107 126 clear;
+#X msg 108 149 clear 1;
+#X msg 109 172 clear 2;
+#X text 174 127 clear LCD-display;
+#X text 172 149 clear 1st row;
+#X text 174 174 clear 2nd row;
+#X msg 121 218 (l) forum::fuer::umlaeute:2002;
+#X obj 121 241 t a b;
+#X msg 179 291 0;
+#X msg 151 264 45;
+#X msg 198 324 10 20;
+#X text 243 325 start position and string-length;
+#X floatatom 207 605 5 0 0;
+#X floatatom 79 541 5 0 128;
+#X floatatom 143 576 5 0 8;
+#X obj 79 627 motormix_LCDgraph 3;
+#X text 208 589 type;
+#X text 132 559 channel#;
+#X text 80 523 value;
+#X text 272 534 LCDgraph-types:;
+#X text 395 580 03__single vertical line;
+#X text 394 533 00__left justified horizontal bar graph;
+#X text 395 549 01__centered horizontal bar graph;
+#X text 394 564 02__right justified horizontal bar graph;
+#X text 395 595 04__left justified double vertical line;
+#X text 396 611 05__centered spreading bar;
+#X text 395 626 06__ascending bar graph;
+#X text 396 642 07__descending bar graph;
+#X text 184 11 write some text to the MotorMix-display;
+#X floatatom 151 326 5 0 0;
+#X symbolatom 48 66 10 0 0;
+#X text 139 66 some text;
+#X text 206 292 start position (0..79);
+#X text 112 457 put some graphics on the second-line of the LCDisplay
+(like pannings \, gain-reductions...);
+#X connect 1 0 0 0;
+#X connect 2 0 0 0;
+#X connect 3 0 0 0;
+#X connect 4 0 0 0;
+#X connect 8 0 9 0;
+#X connect 9 0 0 0;
+#X connect 9 1 11 0;
+#X connect 10 0 31 0;
+#X connect 11 0 31 0;
+#X connect 12 0 0 1;
+#X connect 14 0 17 2;
+#X connect 15 0 17 0;
+#X connect 16 0 17 1;
+#X connect 31 0 0 1;
+#X connect 32 0 0 0;
+#X restore 87 458 pd LCDisplay;
+#N canvas 146 108 501 430 7segment-display 0;
+#X obj 71 370 motormix_7seg;
+#X obj 99 337 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 0 1
+;
+#X obj 157 338 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 0
+1;
+#X text 206 335 toggle points on/off;
+#X text 184 11 write something to the 7-segment-display;
+#X floatatom 71 59 5 0 0;
+#X obj 138 142 % 10;
+#X obj 107 142 / 10;
+#X obj 107 163 pack;
+#X obj 107 121 t f f;
+#X floatatom 107 99 5 0 99;
+#X symbolatom 128 255 10 0 0;
+#X text 119 60 write " %d";
+#X text 147 164 set 1st and 2nd char;
+#X symbolatom 128 230 10 0 0;
+#X text 215 230 set 1st char;
+#X text 215 256 set 2nd char;
+#X connect 1 0 0 1;
+#X connect 2 0 0 3;
+#X connect 5 0 0 0;
+#X connect 6 0 8 1;
+#X connect 7 0 8 0;
+#X connect 8 0 0 0;
+#X connect 9 0 7 0;
+#X connect 9 1 6 0;
+#X connect 10 0 9 0;
+#X connect 11 0 0 2;
+#X connect 14 0 0 0;
+#X restore 86 484 pd 7segment-display;
+#X text 183 306 get/set the motorfaders;
+#X text 184 343 get the state of the rotaries ("pan");
+#X text 184 384 get button-presses an illuminate their LEDs;
+#X text 257 457 write something to the LCDisplay;
+#X text 256 484 write something to the 7segment-display;
+#X text 161 28 MotorMix (tm) by CM-labs;
+#X obj 77 225 print MotorMix;
+#X msg 77 201 OK;
+#X obj 77 171 motormix;
+#X connect 0 0 16 0;
+#X connect 1 0 16 0;
+#X connect 2 0 16 0;
+#X connect 15 0 14 0;
+#X connect 16 0 15 0;
diff --git a/MIDIvice/src/MIDIvice.c b/MIDIvice/src/MIDIvice.c
new file mode 100644
index 0000000..0acdb1d
--- /dev/null
+++ b/MIDIvice/src/MIDIvice.c
@@ -0,0 +1,46 @@
+/* (c) copyleft 2002-2008 IOhannes m zmölnig
+ * forum::für::umläute
+ * Institute of Electronic Music and Acoustics (IEM)
+ * University of Music and Dramatic Arts Graz (KUG)
+ */
+
+/*
+ * MIDIvice - accessing complex MIDI devices
+ */
+
+#include "MIDIvice.h"
+void motormix_setup();
+
+typedef struct MIDIvice
+{
+ t_object t_ob;
+} t_MIDIvice;
+
+t_class *MIDIvice_class;
+
+static void MIDIvice_help(void)
+{
+ post("\nMIDIvice "VERSION);
+ post("supported devices:");
+ post("\tmotormix\t\tMotorMix(tm) by cm-labs(r)"
+ "\n");
+}
+
+void *MIDIvice_new(void)
+{
+ t_MIDIvice *x = (t_MIDIvice *)pd_new(MIDIvice_class);
+ return (void *)x;
+}
+
+
+void MIDIvice_setup(void)
+{
+ motormix_setup();
+
+ /* ************************************** */
+ post("\n\tMIDIvice "VERSION);
+ post("\tcopyleft forum::für::umläute @ IEM/KUG 2002-2008");
+
+ MIDIvice_class = class_new(gensym("MIDIvice"), MIDIvice_new, 0, sizeof(t_MIDIvice), 0, 0);
+ class_addmethod(MIDIvice_class, MIDIvice_help, gensym("help"), 0);
+}
diff --git a/MIDIvice/src/MIDIvice.h b/MIDIvice/src/MIDIvice.h
new file mode 100644
index 0000000..b3f3e93
--- /dev/null
+++ b/MIDIvice/src/MIDIvice.h
@@ -0,0 +1,44 @@
+/* ********************************************** */
+/* the MIDIvice external */
+/* externals for controlling MIDI-devices */
+/* ********************************************** */
+/* forum::für::umläute */
+/* ********************************************** */
+
+/* (c) copyleft 2002-2008 IOhannes m zmölnig
+ * forum::für::umläute
+ * Institute of Electronic Music and Acoustics (IEM)
+ * University of Music and Dramatic Arts Graz (KUG)
+ */
+
+/* the MIDIvice external is a runtime-library for miller s. puckette's realtime-computermusic-software "pure data"
+ * therefore you NEED "pure data" to make any use of the MIDIvice external
+ * (except if you want to use the code for other things)
+ * download "pure data" at
+
+ http://iem.kug.ac.at/pd
+ ftp://ftp.iem.at/pub/pd
+
+ *
+ * if you are looking for the latest release of the MIDIvice-external you should have another look at
+
+ ftp://iem.kug.ac.at/pd/Externals/MIDIvice
+
+ *
+ * MIDIvice is published under the GNU GeneralPublicLicense, that must be shipped with MIDIvice.
+ * if you are using Debian GNU/linux, the GNU-GPL can be found under /usr/share/common-licenses/GPL
+ * if you still haven't found a copy of the GNU-GPL, have a look at http://www.gnu.org
+ *
+ * "pure data" has it's own license, that comes shipped with "pure data".
+ *
+ * there are ABSOLUTELY NO WARRANTIES for anything
+ */
+
+#ifndef INCLUDE_MIDIVICE_H__
+#define INCLUDE_MIDIVICE_H__
+
+#include "m_pd.h"
+
+#define VERSION "0.1"
+
+#endif
diff --git a/MIDIvice/src/Make.config.in b/MIDIvice/src/Make.config.in
new file mode 100644
index 0000000..4bf19b8
--- /dev/null
+++ b/MIDIvice/src/Make.config.in
@@ -0,0 +1,31 @@
+LIBNAME=@LIBNAME@
+
+# when build as a library this holds a pre-processor define
+# (e.g. "-DBUILD_LIBRARY")
+# when build as single externals this is empty
+BUILDLIBRARY =@BUILDLIBRARY@
+
+PREFIX =@prefix@@PDLIBDIR@
+
+INSTALL_BIN=$(DESTDIR)$(PREFIX)/extra/$(LIBNAME)
+INSTALL_DOC=$(DESTDIR)$(PREFIX)/@REFERENCEPATH@$(LIBNAME)
+
+EXT = @EXT@
+DEFS = @DFLAGS@
+IFLAGS = -I. @INCLUDES@ $(INCLUDES)
+MAKEDEP_FLAGS = @MAKEDEP_FLAGS@
+
+CC = @CC@
+LD = @LD@
+STRIP = @STRIP@
+STRIPFLAGS= @STRIPFLAGS@
+
+AFLAGS =
+LFLAGS = @LFLAGS@
+WFLAGS =
+
+TARNAME = $(LIBNAME).tgz
+
+CONFIGUREFLAGS = @CONFIGUREFLAGS@
+CFLAGS = $(DEFS) $(IFLAGS) $(BUILDLIBRARY) -DPD $(WFLAGS) @CFLAGS@
+LIBS = @LIBS@
diff --git a/MIDIvice/src/Makefile b/MIDIvice/src/Makefile
new file mode 100644
index 0000000..4601f4c
--- /dev/null
+++ b/MIDIvice/src/Makefile
@@ -0,0 +1,86 @@
+default: all
+
+.PHONEY: default all everything dist \
+ clean realclean distclean \
+ install install-bin install-doc install-abs \
+ tests
+
+HELPERSOURCES=MIDIvice.c
+OBJECTSOURCES=$(sort $(filter-out $(HELPERSOURCES), $(filter %.c, $(wildcard *.c))))
+SOURCES=$(OBJECTSOURCES) $(HELPERSOURCES)
+
+
+configure: configure.ac
+ autoconf
+
+-include $(SOURCES:.c=.d)
+
+Make.config: Make.config.in configure
+ ./configure $(CONFIGUREFLAGS)
+
+-include Make.config
+
+
+## 2nd only generate depend-files when we have Make.config included
+## and thus MAKEDEP_FLAGS defined
+ifdef MAKEDEP_FLAGS
+## dependencies: as proposed by the GNU-make documentation
+## see http://www.gnu.org/software/make/manual/html_node/make_47.html#SEC51
+%.d: %.c
+ @set -e; rm -f $@; \
+ $(CPP) $(MAKEDEP_FLAGS) $(CFLAGS) $< > $@.$$$$; \
+ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
+ rm -f $@.$$$$
+endif
+
+.SUFFIXES: .$(EXT)
+
+TARGETS = $(SOURCES:.c=.o)
+
+OBJECTS = $(OBJECTSOURCES:.c=.$(EXT))
+
+
+## if $(BUILDLIBRARY) is defined, we build everything as a single library
+## else we build separate externals
+ifneq "$(BUILDLIBRARY)" ""
+all: $(LIBNAME)
+ cp $(LIBNAME).$(EXT) ..
+else
+all: $(OBJECTS)
+endif
+
+$(OBJECTS): %.$(EXT) : %.o
+ $(LD) $(LFLAGS) -o $@ $*.o $(LIBS)
+ $(STRIP) $(STRIPFLAGS) $@
+
+
+$(LIBNAME): $(TARGETS)
+ $(LD) $(LFLAGS) -o $@.$(EXT) *.o $(LIBS)
+ $(STRIP) $(STRIPFLAGS) $(LIBNAME).$(EXT)
+
+$(TARGETS): %.o : %.c
+ $(CC) $(CFLAGS) -c -o $@ $*.c
+
+clean:
+ -rm -f *.$(EXT) *.o
+
+realclean: clean
+ -rm -f *~ _* config.*
+ -rm -f *.d *.d.*
+
+distclean: realclean
+ -rm -f Make.config
+ -rm -f *.exp *.lib *.ncb *.opt *.plg
+ -rm -rf autom4te.cache/
+
+install: install-bin install-doc
+
+install-bin:
+ -install -d $(INSTALL_BIN)
+ -install -m 644 *.$(EXT) $(INSTALL_BIN)
+
+install-doc:
+ -install -d $(INSTALL_DOC)
+ -install -m 644 *-help.pd $(INSTALL_DOC)
+
+everything: clean all install distclean
diff --git a/MIDIvice/src/configure.ac b/MIDIvice/src/configure.ac
new file mode 100644
index 0000000..7fe2f21
--- /dev/null
+++ b/MIDIvice/src/configure.ac
@@ -0,0 +1,273 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(MIDIvice.c)
+
+LIBNAME=MIDIvice
+
+
+dnl Checks for programs.
+AC_PROG_CC
+
+AC_SUBST(STK)
+AC_SUBST(DFLAGS)
+AC_SUBST(LFLAGS)
+AC_SUBST(EXT)
+AC_SUBST(LD)
+AC_SUBST(STRIP)
+AC_SUBST(STRIPFLAGS)
+AC_SUBST(LIBRARY_VERSION)
+AC_SUBST(REFERENCEPATH)
+AC_SUBST(PDLIBDIR)
+AC_SUBST(INCLUDES)
+AC_SUBST(LIBNAME)
+AC_SUBST(CONFIGUREFLAGS)
+AC_SUBST(BUILDLIBRARY)
+
+## store the flags passed to us
+## is there no way to get the flags without quotes?
+#CONFIGUREFLAGS=${ac_configure_args}
+## and is this solution portable? time will show....
+CONFIGUREFLAGS=$(echo ${ac_configure_args} | sed "s/'//g")
+
+
+AC_ARG_WITH(pdversion, [ --with-pdversion=<ver> enforce a certain pd-version (e.g. 0.37)])
+AC_ARG_WITH(extension, [ --with-extension=<ext> enforce a certain extension for the dynamic library (e.g. dll)])
+AC_ARG_WITH(pdpath, [ --with-pd=</path/to/pd> where to look for pd-headers and and -libs])
+AC_ARG_ENABLE(PIC, [ --disable-PIC disable compilation with PIC-flag])
+AC_ARG_ENABLE(library,[ --disable-library split the library into single externals])
+
+if test "xno" != "x${enable_library}" ; then
+dnl LATER: find a more generic way to generate the .._LIBRARY define
+ BUILDLIBRARY="-DBUILD_LIBRARY"
+fi
+
+dnl Checks for libraries.
+dnl Replace `main' with a function in -lc:
+AC_CHECK_LIB(c, main)
+AC_CHECK_LIB(crtdll, fclose)
+
+dnl Replace `main' with a function in -lm:
+AC_CHECK_LIB(m, main)
+dnl Replace `main' with a function in -lpthread:
+dnl AC_CHECK_LIB(pthread, main)
+dnl Replace `main' with a function in -lstk:
+dnl AC_CHECK_LIB(stk, main, STK=yes)
+
+if test "x$with_pd" != "x"; then
+ if test -d "${with_pd}/src"; then
+ INCLUDES="-I${with_pd}/src ${INCLUDES}"
+ fi
+ if test -d "${with_pd}/bin"; then
+ LIBS="-L${with_pd}/bin ${LIBS}"
+ fi
+fi
+
+if test "x$includedir" != "x"; then
+ for id in $includedir
+ do
+ if test -d $id; then INCLUDES="-I$id $INCLUDES"; fi
+ done
+fi
+if test "x$libdir" != "x"; then
+ for id in $libdir
+ do
+ if test -d $id; then LIBS="-L$id $LIBS"; fi
+ done
+fi
+
+AC_CHECK_LIB(pd, nullfn)
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(stdlib.h stdio.h string.h math.h time.h sys/time.h)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_HEADER_TIME
+
+dnl Checks for library functions.
+AC_FUNC_MMAP
+AC_CHECK_FUNCS(select socket strerror)
+
+
+### make-depend flags
+if test "x$ac_cv_c_compiler_gnu" = "xyes"; then
+ AC_SUBST(MAKEDEP_FLAGS, "-MM")
+else
+ AC_SUBST(MAKEDEP_FLAGS, "-M")
+fi
+
+dnl check for "-mms-bitfields" cflag
+dnl why is there no generic compiler-check for a given flag ?
+dnl it would make things so easy: AC_CHECK_FLAG([-mms-bitfields],,)
+AC_MSG_CHECKING("ms-bitfields")
+cat > conftest.c << EOF
+int main(){
+ return 0;
+}
+EOF
+if ${CC} ${INCLUDES} ${DFLAGS} -o conftest.o conftest.c ${CFLAGS} -mms-bitfields > /dev/null 2>&1
+then
+ echo "yes"
+ CFLAGS="${CFLAGS} -mms-bitfields"
+else
+ echo "no"
+fi
+
+
+
+
+dnl isn't there a better way to check for good linker/stripper ?
+
+dnl if we don't have $LD set, we set it to $(CC)
+dnl LD=${LD:=$CC}
+if test "x$LD" = "x"
+then
+ if test "x$host" != "x"
+ then
+ LD=${host}-ld
+ if $(which ${LD} > /dev/null)
+ then
+ :
+ else
+ LD=""
+ fi
+ fi
+fi
+LD=${LD:=$CC}
+
+dnl if we don't have $STRIP set, we set it to ${host}-strip or strip
+AC_CHECK_TOOL([STRIP], [strip], [true])
+AC_MSG_CHECKING([if strip is GNU strip])
+if $STRIP -V 2>&1 | grep GNU > /dev/null
+then
+ AC_SUBST(STRIPFLAGS, "--strip-unneeded")
+ AC_MSG_RESULT([yes])
+else
+ AC_SUBST(STRIPFLAGS,"-x")
+ AC_MSG_RESULT([no])
+fi
+
+DFLAGS=""
+
+
+if test "x$enable_PIC" != "xno"; then
+AC_MSG_CHECKING("PIC")
+cat > conftest.c << EOF
+int main(){
+ return 0;
+}
+EOF
+if ${CC} ${INCLUDES} ${DFLAGS} -o conftest.o conftest.c ${CFLAGS} -fPIC > /dev/null 2>&1
+then
+ echo "yes"
+ CFLAGS="${CFLAGS} -fPIC"
+else
+ echo "no"
+fi
+fi
+
+
+dnl
+dnl OK, checks for machines are here now
+dnl
+if test `uname -s` = Linux;
+then
+ LFLAGS="-export_dynamic -shared"
+ CFLAGS="$CFLAGS"
+ EXT=pd_linux
+fi
+
+dnl This should use '-bundle_loader /path/to/pd/bin/pd' instead of'-undefined suppress'
+dnl then strip might do something
+if test `uname -s` = Darwin;
+then
+ LD=cc
+ LFLAGS="-bundle -undefined suppress -flat_namespace"
+ EXT=pd_darwin
+fi
+
+if test `uname | sed -e 's/^MINGW.*/NT/'` = NT;
+then
+ LD=gcc
+ INCLUDES="-I@prefix@/src"
+ DFLAGS="-D__WIN32__"
+ LFLAGS="-shared @prefix@/bin/pd.dll"
+ EXT=dll
+else
+ PDLIBDIR="/lib/pd"
+fi
+
+if test `uname -s` = IRIX64;
+then
+ LFLAGS="-n32 -DUNIX -DIRIX -DN32 -woff 1080,1064,1185 \
+ -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \
+ -shared -rdata_shared"
+ EXT=pd_irix6
+ dnl DFLAGS="-DUNIX -DIRIX6"
+fi
+
+if test `uname -s` = IRIX32;
+then
+ LFLAGS="-o32 -DUNIX -DIRIX -O2
+ -shared -rdata_shared"
+ EXT=pd_irix5
+ dnl DFLAGS="-DUNIX -DIRIX5"
+fi
+
+
+if test "x$with_extension" != "x"
+then
+ EXT=$with_extension
+fi
+
+
+dnl Checks for pd-version, to set the correct help-path
+AC_MSG_CHECKING("pd\>=0.37")
+
+if test "$with_pdversion" != ""
+then
+echo -n "($with_pdversion)... "
+ PD_VERSION="$with_pdversion"
+else
+if test "x$cross_compiling" = "xno"
+then
+
+cat > conftest.c << EOF
+#include <stdio.h>
+#include "m_pd.h"
+int main(){
+ printf("%d.%d\n", PD_MAJOR_VERSION, PD_MINOR_VERSION);
+ return 0;
+}
+EOF
+
+ if $CC $INCLUDES -o conftest.o conftest.c > /dev/null 2>&1
+ then
+ PD_VERSION=`./conftest.o`
+ else
+ PD_VERSION=""
+ fi
+ echo -n $PD_VERSION
+else
+dnl we are cross-compiling...
+ echo -n "(X)..."
+ PD_VERSION="0.38"
+fi
+fi
+
+let PD_MAJORVERSION=`echo $PD_VERSION | cut -d"." -f1`+0
+let PD_MINORVERSION=`echo $PD_VERSION | cut -d"." -f2`+0
+
+
+
+if test "$PD_MAJORVERSION" -gt 0 || test "$PD_MINORVERSION" -ge 37
+then
+ REFERENCEPATH=extra/
+ echo " yes"
+else
+ REFERENCEPATH=doc/5.reference/
+ echo " no"
+fi
+
+AC_OUTPUT(Make.config)
+
+rm -f conftest.*
diff --git a/MIDIvice/src/motormix.c b/MIDIvice/src/motormix.c
new file mode 100644
index 0000000..46355ee
--- /dev/null
+++ b/MIDIvice/src/motormix.c
@@ -0,0 +1,868 @@
+/* (c) copyleft 2002-2008 IOhannes m zmölnig
+ * forum::für::umläute
+ * Institute of Electronic Music and Acoustics (IEM)
+ * University of Music and Dramatic Arts Graz (KUG)
+ */
+
+/*
+ motormix by CM-labs
+*/
+
+#include "MIDIvice.h"
+#include <string.h>
+#include <ctype.h>
+
+#define LCD_TEXT 0x10
+#define LCD_GRAPH 0x11
+#define SEG7 0x12
+
+static int x_port = 0;
+static t_symbol *ctlin_sym;
+static t_symbol *notein_sym;
+
+void outmidi_noteon(int portno, int channel, int pitch, int velo);
+void outmidi_controlchange(int portno, int channel, int ctlno, int value);
+void sys_putmidibyte(int portno, int byte);
+
+static void outmidi_byte(unsigned char byte)
+{ sys_putmidibyte(x_port, byte); }
+
+
+
+/* ------------------------- LCDtext ------------------------------- */
+
+static t_class *LCDtext_class;
+typedef struct _LCDtext
+{
+ t_object x_obj;
+
+ int pos;
+ int length;
+ t_binbuf *bbuf;
+} t_LCDtext;
+
+
+static void LCD_header(unsigned char type)
+{
+ outmidi_byte(0xF0);
+ outmidi_byte(0x00);
+ outmidi_byte(0x01);
+ outmidi_byte(0x0F);
+ outmidi_byte(0x00);
+ outmidi_byte(0x11);
+ outmidi_byte(0x00);
+ outmidi_byte(type);
+}
+static void LCD_footer(void)
+{
+ outmidi_byte(0xF7);
+}
+
+static void LCDtext_text(t_LCDtext *x)
+{
+ char *c, *str=0;
+ int n, slen;
+ int pos = x->pos;
+ int len = x->length;
+ int rest=0;
+
+ if (!x->bbuf)return;
+
+ binbuf_gettext(x->bbuf, &str, &slen);
+ c=str;
+ if (len>slen){
+ n=slen;
+ rest=len-slen;
+ } else n=len?len:slen;
+
+ if (n>0x50-pos)n=0x50-pos;
+
+ LCD_header(LCD_TEXT);
+ outmidi_byte(pos);
+ while(n--)outmidi_byte(*c++);
+ while(rest--)outmidi_byte(' ');
+ LCD_footer();
+
+ freebytes(str, slen);
+}
+
+static void LCDtext_clear(t_LCDtext *x, t_float fmode)
+{
+ int mode = fmode;
+ int offset = (mode==2)?0x28:0x00;
+ int n = (mode>0)?0x28:0x50;
+
+ // post("offset=%d\tn=%d", offset, n);
+
+ LCD_header(LCD_TEXT);
+ outmidi_byte(offset);
+
+ while(n--)outmidi_byte(' ');
+
+ LCD_footer();
+}
+
+static void LCDtext_any(t_LCDtext *x, t_symbol *s, int argc, t_atom *argv)
+{
+ // t_atom a;
+ binbuf_clear(x->bbuf);
+
+ if ((s != &s_list)&&(s != &s_float)&&(s != &s_symbol)){
+ t_atom a;
+ SETSYMBOL(&a, s);
+ binbuf_add(x->bbuf, 1, &a);
+ }
+ binbuf_add(x->bbuf, argc, argv);
+
+ LCDtext_text(x);
+}
+
+static void LCDtext_pos(t_LCDtext *x, t_float pos, t_float len)
+{
+ if (pos<0 )pos=0;
+ if (pos>80)pos=80;
+ if (len<0 )len=0;
+
+ x->pos=pos;
+ x->length=len;
+}
+
+
+static void *LCDtext_new(t_floatarg f)
+{
+ t_LCDtext *x = (t_LCDtext *)pd_new(LCDtext_class);
+ int pos = f;
+
+ if (pos<0)pos=0;
+ if (pos>0x4f)pos=0x4f;
+ x->pos=pos;
+ // floatinlet_new(&x->x_obj, &x->pos);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym(""));
+
+ x->bbuf = binbuf_new();
+
+ return (x);
+}
+static void LCDtext_setup(void)
+{
+ LCDtext_class = class_new(gensym("motormix_LCDtext"), (t_newmethod)LCDtext_new,
+ 0, sizeof(t_LCDtext), 0, A_DEFFLOAT, 0);
+ class_addcreator((t_newmethod)LCDtext_new, gensym("mm_LCDtext"), A_DEFFLOAT, 0);
+
+ class_addmethod(LCDtext_class, (t_method)LCDtext_pos, gensym(""), A_DEFFLOAT, A_DEFFLOAT, 0);
+ class_addmethod(LCDtext_class, (t_method)LCDtext_clear, gensym("clear"), A_DEFFLOAT, 0);
+ class_addbang(LCDtext_class, (t_method)LCDtext_text);
+
+ class_addanything(LCDtext_class, LCDtext_any);
+ class_sethelpsymbol(LCDtext_class, gensym("MIDIvice/motormix"));
+}
+/* ------------------------- LCDgraph ------------------------------- */
+
+static t_class *LCDgraph_class;
+typedef struct _LCDgraph
+{
+ t_object x_obj;
+
+ t_float pos;
+ int type;
+} t_LCDgraph;
+
+static void LCDgraph_valpos(int pos, int val)
+{
+ if ((pos<1)||(pos>8))return;
+ if (val<0)val=0;
+ if (val>127)val=127;
+ outmidi_byte(pos-1);
+ outmidi_byte(val);
+}
+
+static void LCDgraph_float(t_LCDgraph *x, t_float f)
+{
+ if ((x->pos<1)||(x->pos>8))return;
+
+ LCD_header(LCD_GRAPH);
+ outmidi_byte(x->type);
+ LCDgraph_valpos(x->pos, f);
+ LCD_footer();
+}
+
+static void LCDgraph_clear(t_LCDgraph *x)
+{
+ int n = 0x28;
+ int pos=0x28;
+
+ LCD_header(LCD_TEXT);
+ outmidi_byte(pos);
+ while(n--)outmidi_byte(' ');
+ LCD_footer();
+}
+
+
+static void LCDgraph_pos(t_LCDgraph *x, t_float pos)
+{
+ x->pos=pos;
+}
+static void LCDgraph_typ(t_LCDgraph *x, t_float type)
+{
+ if (type<0 )type=0;
+ if (type>7)type=7;
+
+ x->type=type;
+}
+static void *LCDgraph_new(t_float ftype)
+{
+ t_LCDgraph *x = (t_LCDgraph *)pd_new(LCDgraph_class);
+
+ LCDgraph_typ(x, ftype);
+ floatinlet_new(&x->x_obj, &x->pos);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("type"));
+ x->pos=0;
+
+ return (x);
+}
+static void LCDgraph_setup(void)
+{
+ LCDgraph_class = class_new(gensym("motormix_LCDgraph"), (t_newmethod)LCDgraph_new,
+ 0, sizeof(t_LCDgraph), 0, A_DEFFLOAT, 0);
+ class_addcreator((t_newmethod)LCDgraph_new, gensym("mm_LCDgraph"), A_DEFFLOAT, 0);
+
+ class_addmethod(LCDgraph_class, (t_method)LCDgraph_typ, gensym("type"), A_DEFFLOAT, 0);
+ class_addmethod(LCDgraph_class, (t_method)LCDgraph_pos, gensym(""), A_DEFFLOAT, 0);
+ class_addmethod(LCDgraph_class, (t_method)LCDgraph_clear, gensym("clear"), 0);
+ class_addfloat (LCDgraph_class, (t_method)LCDgraph_float);
+
+ // class_addbang(LCDgraph_class, (t_method)LCDgraph_graph);
+ class_sethelpsymbol(LCDgraph_class, gensym("MIDIvice/motormix"));
+}
+
+/* ------------------------- seg7 ------------------------------- */
+
+static t_class *seg7_class;
+typedef struct _seg7
+{
+ t_object x_obj;
+
+ t_float point1, point2;
+ char c1, c2;
+} t_seg7;
+
+static void seg7_nibblebyte(char c, int point)
+{
+ char C = toupper(c);
+ char lo=C&0x0F;
+ char hi=(C>>4)&0x0F;
+ if (point)hi|=0x40;
+ // post("C=%c=%c", c, C);
+ // post("hi=%x\tlo=%x", hi, lo);
+
+ outmidi_byte(hi);
+ outmidi_byte(lo);
+}
+
+static void seg7_write(char c1, int pt1, char c2, int pt2)
+{
+ LCD_header(SEG7);
+
+ seg7_nibblebyte(c1, pt1);
+ seg7_nibblebyte(c2, pt2);
+
+ LCD_footer();
+}
+
+static void seg7_clear(t_seg7 *x, t_float fmode)
+{
+ int mode = fmode;
+
+ LCD_header(SEG7);
+ if (mode!=2)seg7_nibblebyte(' ', 0);
+ else seg7_nibblebyte(0, 0);
+ if (mode!=1)seg7_nibblebyte(' ', 0);
+ else seg7_nibblebyte(0, 0);
+
+ LCD_footer();
+}
+
+static void seg7_list(t_seg7 *x, t_symbol *s, int argc, t_atom* argv)
+{
+ char seg1, seg2;
+ int pt1=(x->point1 != 0);
+ int pt2=(x->point2 != 0);
+
+ seg1=(argv->a_type==A_SYMBOL)?
+ *atom_getsymbol(argv)->s_name:
+ (atom_getint(argv)%10+0x30);
+ argv++;
+ if (argc>1)seg2=(argv->a_type==A_SYMBOL)?*atom_getsymbol(argv)->s_name:(atom_getint(argv)%10+0x30);
+ else {
+ seg2=seg1;
+ seg1=' ';
+ }
+ // post("seg1=%c\tseg2=%c", seg1, seg2);
+
+ x->c1=seg1;
+ x->c2=seg2;
+
+ seg7_write(seg1, pt1, seg2, pt2);
+}
+static void seg7_symbol(t_seg7 *x, t_symbol *s)
+{
+ int pt1=(x->point1 != 0);
+ int pt2=(x->point2 != 0);
+ char c1=*s->s_name;
+ char c2=x->c2;
+
+ x->c1=c1;
+
+ // post("c1=%c\tc2=%c", c1, c2);
+
+ seg7_write(c1, pt1, c2, pt2);
+}
+static void seg7_symbol2(t_seg7 *x, t_symbol *s)
+{
+ x->c2=*s->s_name;
+}
+static void seg7_bang(t_seg7 *x)
+{
+ int pt1=(x->point1 != 0);
+ int pt2=(x->point2 != 0);
+ char c1=x->c1;
+ char c2=x->c2;
+
+ seg7_write(c1, pt1, c2, pt2);
+}
+
+static void *seg7_new(void)
+{
+ t_seg7 *x = (t_seg7 *)pd_new(seg7_class);
+
+ floatinlet_new(&x->x_obj, &x->point1);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("symbol"), gensym(""));
+ floatinlet_new(&x->x_obj, &x->point2);
+
+ x->point1=x->point2=0;
+ x->c1=x->c2=0;
+
+ return (x);
+}
+static void seg7_setup(void)
+{
+ seg7_class = class_new(gensym("motormix_7seg"), (t_newmethod)seg7_new,
+ 0, sizeof(t_seg7), 0, A_GIMME, 0);
+ class_addcreator((t_newmethod)seg7_new, gensym("mm_7seg"), A_GIMME, 0);
+ class_addbang(seg7_class, seg7_bang);
+ class_addlist(seg7_class, seg7_list);
+ class_addsymbol(seg7_class, seg7_symbol);
+ class_addmethod(seg7_class, (t_method)seg7_clear, gensym("clear"), A_DEFFLOAT, 0);
+ class_addmethod(seg7_class, (t_method)seg7_symbol2, gensym(""), A_DEFSYMBOL, 0);
+ class_sethelpsymbol(seg7_class, gensym("MIDIvice/motormix"));
+}
+
+/* ------------------------- LED ------------------------------- */
+
+static t_class *LED_class;
+typedef struct _LED
+{
+ t_object x_obj;
+
+ t_float LED;
+ t_float state;
+} t_LED;
+
+static void LED_float(t_LED *x, t_float f)
+{
+ int MSB, LSB;
+ int state = x->state;
+ int offset = 0;
+ int id = f;
+
+ if (id<0)return;
+ LSB=id%8;
+ MSB=id/8;
+
+ x->LED=id;
+
+
+ if (state>0)offset=0x40;
+ else if (state<0)offset=0x50;
+
+ // if (MSB<8)offset++;
+#if 0
+ post("LED: %x %x %x %x", 0x0C, MSB, 0x2C, LSB+offset);
+ // post("LED: %d %d %d %d\n", 0x0C, MSB, 0x2C, LSB+offset);
+#endif
+ outmidi_controlchange(x_port>>4, x_port&15, 0x0C, MSB);
+ outmidi_controlchange(x_port>>4, x_port&15, 0x2C, LSB+offset);
+}
+
+static void LED_symbol(t_LED *x, t_symbol *s)
+{
+ post("motormix_LED: no method for symbol");
+}
+
+static void LED_bang(t_LED *x)
+{
+ LED_float(x, x->LED);
+}
+
+static void *LED_new(void)
+{
+ t_LED *x = (t_LED *)pd_new(LED_class);
+
+ x->LED=-1;
+ floatinlet_new(&x->x_obj, &x->state);
+
+ x->state=0;
+
+ return (x);
+}
+static void LED_setup(void)
+{
+ LED_class = class_new(gensym("motormix_LED"), (t_newmethod)LED_new,
+ 0, sizeof(t_LED), 0, 0);
+ class_addcreator((t_newmethod)LED_new, gensym("mm_LED"), 0);
+
+ class_addsymbol(LED_class, LED_symbol);
+ class_addfloat (LED_class, LED_float);
+ class_addbang (LED_class, LED_bang);
+ class_sethelpsymbol(LED_class, gensym("MIDIvice/motormix"));
+}
+
+
+/* button */
+static t_class *button_class;
+typedef struct _button
+{
+ t_object x_obj;
+ int o_itsme; // the fader changed
+ int o_imtouched; // the fader was touched
+
+ t_outlet *o_value; // actual fader-value
+ t_outlet *o_touch; // 1=touched; 0=released
+
+ int activefader;
+ int fader;
+ unsigned char MSB, LSB;
+} t_button;
+
+static void button_touch(t_button *x, unsigned char value, unsigned char control)
+{
+ int pressed=0;
+ int MSB=x->MSB;
+ int LSB=0;
+
+ x->o_imtouched=0;
+
+ if (x->MSB<8){
+ if ((value==0x00) || (value==0x40))return;
+ }
+
+ LSB=value;
+
+ if (LSB>=0x40){
+ pressed=1;
+ LSB-=0x40;
+ }
+
+ outlet_float(x->o_touch, pressed);
+ outlet_float(x->o_value, MSB*8+LSB);
+}
+
+static void button_list(t_button *x, t_symbol *s, int argc, t_atom *argv)
+{
+ unsigned char ctl = atom_getfloatarg(0, argc, argv);
+ unsigned char val = atom_getfloatarg(1, argc, argv);
+ // int channel = atom_getfloatarg(2, argc, argv);
+
+ if (x->o_imtouched)button_touch(x, val, ctl);
+
+ if (ctl==0x0f){
+ x->o_imtouched=1;
+ x->MSB=val;
+ }
+}
+static void button_free(t_button *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, ctlin_sym);
+}
+static void *button_new()
+{
+ t_button *x = (t_button *)pd_new(button_class);
+
+ x->o_value=outlet_new(&x->x_obj, &s_float);
+ x->o_touch=outlet_new(&x->x_obj, &s_float);
+
+ x->o_imtouched = 0;
+
+ pd_bind(&x->x_obj.ob_pd, ctlin_sym);
+
+ return (x);
+}
+static void button_setup(void)
+{
+ button_class = class_new(gensym("motormix_button"), (t_newmethod)button_new, (t_method)button_free,
+ sizeof(t_button), CLASS_NOINLET, A_DEFFLOAT, 0);
+ class_addcreator((t_newmethod)button_new, gensym("mm_button"), A_DEFFLOAT, 0);
+
+ class_addlist(button_class, button_list);
+ class_sethelpsymbol(button_class, gensym("MIDIvice/motormix"));
+}
+
+/* ------------------------- rotary ------------------------------- */
+
+
+static t_class *rotary_class;
+typedef struct _rotary
+{
+ t_object x_obj;
+
+ t_outlet *o_value; // actual rot-value
+ t_outlet *o_rot; // rot-number
+
+ int rot;
+} t_rotary;
+
+static void rotary_list(t_rotary *x, t_symbol *s, int argc, t_atom *argv)
+{
+ unsigned char ctl = atom_getfloatarg(0, argc, argv);
+ unsigned char val = atom_getfloatarg(1, argc, argv);
+ // int channel = atom_getfloatarg(2, argc, argv);
+
+ if ((ctl>=0x40) && (ctl<0x48)) {
+ if (x->rot) {
+ if (ctl+1-0x40==x->rot){
+ // int value = (val>=64)?val-64:-val;
+ outlet_float(x->o_value, (val>=64)?val-64:-val);
+ }
+ } else {
+ outlet_float(x->o_rot, (t_float)(ctl-0x40+1));
+ outlet_float(x->o_value, (val>=64)?val-64:-val);
+ }
+ }
+}
+static void rotary_free(t_rotary *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, ctlin_sym);
+}
+static void *rotary_new(t_floatarg f)
+{
+ t_rotary *x = (t_rotary *)pd_new(rotary_class);
+
+ if ((f<0) || (f>=9)){
+ post("motormix_rotary: rot [%d] specified. only rotariess 1..8(&0) are valid", (int)f);
+ f=0;
+ }
+ x->rot = f;
+
+ x->o_value=outlet_new(&x->x_obj, &s_float);
+ if (!x->rot) x->o_rot=outlet_new(&x->x_obj, &s_float);
+
+ pd_bind(&x->x_obj.ob_pd, ctlin_sym);
+
+ return (x);
+}
+static void rotary_setup(void)
+{
+ rotary_class = class_new(gensym("motormix_rotary"), (t_newmethod)rotary_new, (t_method)rotary_free,
+ sizeof(t_rotary), CLASS_NOINLET, A_DEFFLOAT, 0);
+ class_addcreator((t_newmethod)rotary_new, gensym("mm_rotary"), A_DEFFLOAT, 0);
+
+ class_addlist(rotary_class, rotary_list);
+ class_sethelpsymbol(rotary_class, gensym("MIDIvice/motormix"));
+}
+
+/* ------------------------- encoder ------------------------------- */
+
+static t_class *encoder_class;
+typedef struct _encoder
+{
+ t_object x_obj;
+
+ t_outlet *o_value; // actual rot-value
+ t_outlet *o_push; // pushed
+} t_encoder;
+
+static void encoder_list(t_encoder *x, t_symbol *s, int argc, t_atom *argv)
+{
+ unsigned char ctl = atom_getfloatarg(0, argc, argv);
+ unsigned char val = atom_getfloatarg(1, argc, argv);
+ // int channel = atom_getfloatarg(2, argc, argv);
+
+ if (ctl==0x48) outlet_float(x->o_value, (val>=64)?val-64:-val);
+ else if (ctl==0x49)outlet_float(x->o_push, (t_float)(val==0x01));
+}
+static void encoder_free(t_encoder *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, ctlin_sym);
+}
+static void *encoder_new(void)
+{
+ t_encoder *x = (t_encoder *)pd_new(encoder_class);
+
+ x->o_value=outlet_new(&x->x_obj, &s_float);
+ x->o_push =outlet_new(&x->x_obj, &s_float);
+
+ pd_bind(&x->x_obj.ob_pd, ctlin_sym);
+
+ return (x);
+}
+static void encoder_setup(void)
+{
+ encoder_class = class_new(gensym("motormix_encoder"), (t_newmethod)encoder_new, (t_method)encoder_free,
+ sizeof(t_encoder), CLASS_NOINLET, 0);
+ class_addcreator((t_newmethod)encoder_new, gensym("mm_encoder"), 0);
+
+ class_addlist(encoder_class, encoder_list);
+ class_sethelpsymbol(encoder_class, gensym("MIDIvice/motormix"));
+}
+
+
+/* ------------------------- faderIn ------------------------------- */
+
+static t_class *faderIn_class;
+typedef struct _faderIn
+{
+ t_object x_obj;
+ int o_itsme; // the fader changed
+ int o_imtouched; // the fader was touched
+
+ t_outlet *o_value; // actual fader-value
+ t_outlet *o_touch; // 1=touched; 0=released
+ t_outlet *o_fader; // fader-number
+
+ int activefader;
+ int fader;
+ unsigned char MSB, LSB;
+} t_faderIn;
+
+static void faderIn_parse(t_faderIn *x, unsigned char value, unsigned char control)
+{
+ unsigned int fader = (x->fader)?x->fader:x->activefader;
+ x->o_itsme=0;
+
+ if (fader+31==control){
+ x->LSB=value;
+ if (!x->fader)outlet_float(x->o_fader, x->activefader);
+ outlet_float(x->o_value, x->MSB+(1./128)*x->LSB);
+ }
+}
+static void faderIn_touch(t_faderIn *x, unsigned char value, unsigned char control)
+{
+ x->o_imtouched=0;
+
+ if (control==0x2F){
+ switch (value) {
+ case 0x00:
+ if (!x->fader)outlet_float(x->o_fader, x->activefader);
+ outlet_float(x->o_touch, 0.);
+ break;
+ case 0x40:
+ if (!x->fader)outlet_float(x->o_fader, x->activefader);
+ outlet_float(x->o_touch, 1.);
+ default:
+ break;
+ }
+ }
+}
+
+static void faderIn_list(t_faderIn *x, t_symbol *s, int argc, t_atom *argv)
+{
+ unsigned char ctl = atom_getfloatarg(0, argc, argv);
+ unsigned char val = atom_getfloatarg(1, argc, argv);
+ // int channel = atom_getfloatarg(2, argc, argv);
+
+ if (x->o_itsme) faderIn_parse(x, val, ctl);
+ if (x->o_imtouched)faderIn_touch(x, val, ctl);
+
+ if (ctl==0x0f){
+ if (x->fader) {
+ if (val+1==x->fader){ // touched me
+ x->o_imtouched=1;
+ }
+ } else {
+ if (val<=7){ // touched us
+ x->o_imtouched=1;
+ x->activefader=val+1;
+ }
+ }
+ } else if (ctl<0x08) {
+ if (x->fader) {
+ if (ctl+1==x->fader){
+ x->o_itsme=1;
+ x->MSB=val;
+ }
+ } else {
+ x->o_itsme=1;
+ x->activefader=ctl+1;
+ x->MSB=val;
+ }
+ }
+}
+static void faderIn_free(t_faderIn *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, ctlin_sym);
+}
+static void *faderIn_new(t_floatarg f)
+{
+ t_faderIn *x = (t_faderIn *)pd_new(faderIn_class);
+
+ if ((f<0) || (f>=9)){
+ post("motormix_fader: fader [%d] specified. only faders 1..8(&0) are valid", (int)f);
+ f=0;
+ }
+ x->fader = f;
+
+ x->o_value=outlet_new(&x->x_obj, &s_float);
+ x->o_touch=outlet_new(&x->x_obj, &s_float);
+ if (!x->fader) x->o_fader=outlet_new(&x->x_obj, &s_float);
+
+ x->o_itsme = 0;
+ x->o_imtouched = 0;
+ x->activefader = 0;
+
+ pd_bind(&x->x_obj.ob_pd, ctlin_sym);
+
+ return (x);
+}
+static void faderIn_setup(void)
+{
+ faderIn_class = class_new(gensym("motormix_faderIn"), (t_newmethod)faderIn_new, (t_method)faderIn_free,
+ sizeof(t_faderIn), CLASS_NOINLET, A_DEFFLOAT, 0);
+ class_addcreator((t_newmethod)faderIn_new, gensym("mm_faderIn"), A_DEFFLOAT, 0);
+
+ class_addlist(faderIn_class, faderIn_list);
+ class_sethelpsymbol(faderIn_class, gensym("MIDIvice/motormix"));
+}
+
+/* ------------------------- faderOut ------------------------------- */
+
+static t_class *faderOut_class;
+typedef struct _faderOut
+{
+ t_object x_obj;
+
+ t_float fader;
+} t_faderOut;
+
+static void faderOut_float(t_faderOut *x, t_float f)
+{
+ int fader=x->fader;
+ unsigned char MSB, LSB;
+ if (f>=128)f=128;
+ else if (f<0)f=0;
+ if ((fader<1) || (fader>8))return;
+
+ MSB=f;
+ LSB=(f-MSB)*128;
+ outmidi_controlchange(x_port>>4, x_port&15, fader-1, MSB);
+ outmidi_controlchange(x_port>>4, x_port&15, fader-1+32, LSB);
+}
+
+static void *faderOut_new(t_floatarg f)
+{
+ t_faderOut *x = (t_faderOut *)pd_new(faderOut_class);
+
+ if ((f<0) || (f>=9)){
+ post("motormix_fader: fader [%d] specified. only faders 1..8(&0) are valid", (int)f);
+ f=0;
+ }
+ x->fader=f;
+ if (!x->fader)floatinlet_new(&x->x_obj, &x->fader);
+
+ return (x);
+}
+static void faderOut_setup(void)
+{
+ faderOut_class = class_new(gensym("motormix_faderOut"), (t_newmethod)faderOut_new,
+ 0, sizeof(t_faderOut), 0, A_DEFFLOAT, 0);
+ class_addcreator((t_newmethod)faderOut_new, gensym("mm_faderOut"), A_DEFFLOAT, 0);
+
+ class_addfloat(faderOut_class, faderOut_float);
+ class_sethelpsymbol(faderOut_class, gensym("MIDIvice/motormix"));
+}
+
+/* ------------------------- motormix ------------------------------- */
+
+static t_class *motormix_class;
+typedef struct _motormix
+{
+ t_object x_obj;
+} t_motormix;
+static void motormix_return(t_motormix *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int p = atom_getfloatarg(0, argc, argv);
+ int v = atom_getfloatarg(1, argc, argv);
+
+ if ((p==0x00) && (v==0x7F))
+ outlet_bang(x->x_obj.ob_outlet);
+}
+
+static void motormix_bang(t_motormix *x)
+{ outmidi_noteon(x_port>>4, x_port&15,0,0); }
+
+static void motormix_help(t_motormix *x)
+{
+ post("MIDIvice - motormix\n===================");
+ post("support for MotorMix (tm) by cm-labs (r)");
+ post(" \tmotormix_faderIn"
+ "\n\tmotormix_rotary"
+ "\n\tmotormix_encoder"
+ "\n\tmotormix_button"
+ "\n\tmotormix_faderOut"
+ "\n\tmotormix_LED"
+ "\n\tmotormix_LCDtext"
+ "\n\tmotormix_LCDgraph"
+ "\n\tmotormix_7seg");
+ post("(l) forum::für::umläute @ IEM/KUG, Graz; 2002");
+}
+static void motormix_reset(t_motormix *x)
+{ /* this LOOKs like a reset; i don't know what it really is... */
+ LCD_header(SEG7);
+ LCD_footer();
+}
+static void motormix_free(t_motormix *x)
+{
+ pd_unbind(&x->x_obj.ob_pd, notein_sym);
+}
+static void *motormix_new(t_floatarg f)
+{
+ t_motormix *x = (t_motormix *)pd_new(motormix_class);
+ x_port=f;
+ outlet_new(&x->x_obj, &s_float);
+ pd_bind(&x->x_obj.ob_pd, notein_sym);
+ // outmidi_noteon(x_port>>4,x_port&15,0,0); // isn't this dangerous ???
+ return (x);
+}
+
+void motormix_setup(void)
+{
+ motormix_class = class_new(gensym("motormix"), (t_newmethod)motormix_new, (t_method)motormix_free,
+ sizeof(t_motormix), 0/*0CLASS_NOINLET*/, /*A_DEFFLOAT,*/ 0);
+
+ class_addcreator((t_newmethod)motormix_new, gensym("MotorMix"), /*A_DEFFLOAT,*/ 0);
+
+ class_addmethod(motormix_class, (t_method)motormix_help, gensym("help"), 0);
+ class_addbang(motormix_class, motormix_bang);
+ class_addmethod(motormix_class, (t_method)motormix_reset, gensym("reset"), 0);
+ class_addlist(motormix_class, motormix_return);
+
+ class_sethelpsymbol(motormix_class, gensym("MIDIvice/motormix"));
+
+ ctlin_sym = gensym("#ctlin");
+ notein_sym = gensym("#notein");
+
+ button_setup();
+ faderIn_setup();
+ faderOut_setup();
+ rotary_setup();
+ encoder_setup();
+ LED_setup();
+
+ LCDtext_setup();
+ LCDgraph_setup();
+ seg7_setup();
+
+ /* we have to send a motormix_ping once before it starts reacting.
+ * this seems to be a problem with pd, but i don't have hardware to test it right now
+ */
+ outmidi_noteon(x_port>>4,x_port&15,0,0);
+}
diff --git a/MIDIvice/src/motormix_buttons.txt b/MIDIvice/src/motormix_buttons.txt
new file mode 100644
index 0000000..47c4004
--- /dev/null
+++ b/MIDIvice/src/motormix_buttons.txt
@@ -0,0 +1,374 @@
+
+static int LED_sym2id(t_symbol *s)
+{
+ int id=-1;
+
+ if (s==gensym("1"))id=1; // +
+ else if (s==gensym("2"))id=2; // +
+ else if (s==gensym("3"))id=3; // +
+ else if (s==gensym("4"))id=4; // +
+ else if (s==gensym("5"))id=5; // +
+ else if (s==gensym("6"))id=6; // +
+ else if (s==gensym("7"))id=7; // +
+ else if (s==gensym("8"))id=8; // +
+ else if (s==gensym("a"))id=10;
+ else if (s==gensym("b"))id=11;
+ else if (s==gensym("c"))id=12;
+ else if (s==gensym("d"))id=13;
+ else if (s==gensym("e"))id=14;
+ else if (s==gensym("f"))id=15;
+ else if (s==gensym("g"))id=16;
+ else if (s==gensym("h"))id=17;
+ else if (s==gensym("i"))id=18;// +
+ else if (s==gensym("j"))id=19;// +
+ else if (s==gensym("k"))id=20;// +
+ else if (s==gensym("l"))id=21;// +
+ else if (s==gensym("m"))id=22;// +
+ else if (s==gensym("n"))id=23;// +
+ else if (s==gensym("o"))id=24;// +
+ else if (s==gensym("p"))id=25;// +
+ else if (s==gensym("q"))id=26;// +
+ else if (s==gensym("r"))id=27;// +
+ else if (s==gensym("s"))id=28;// +
+ else if (s==gensym("t"))id=29;// +
+ else if (s==gensym("u"))id=30;// +
+ else if (s==gensym("v"))id=31;// +
+ else if (s==gensym("w"))id=32;// +
+ else if (s==gensym("x"))id=33;// +
+ else if (s==gensym("y"))id=34;// +
+ else if (s==gensym("z"))id=35;// +
+ else if (s==gensym("0"))id=0;// +
+ else if (s==gensym("9"))id=9;// +
+ else if (s==gensym("*"))id=36;// +
+ else if (s==gensym("="))id=37;// +
+ else if (s==gensym("/"))id=38;// +
+ else if (s==gensym("."))id=39;// +
+ else if (s==gensym("shift")) id=40;
+ else if (s==gensym("undo")) id=41;
+ else if (s==gensym("default"))id=42;
+ else if (s==gensym("all")) id=43;
+ else if (s==gensym("window")) id=44;
+ else if (s==gensym("plug-in"))id=45;//...
+ else if (s==gensym("suspend"))id=46;
+ else if (s==gensym("auto")) id=47;
+
+ else if (s==gensym("escape")) id=48;//
+ else if (s==gensym("enter")) id=49;
+ else if (s==gensym("last")) id=40;
+ else if (s==gensym("next")) id=51;
+ else if (s==gensym("rewind")) id=52;
+ else if (s==gensym("f-fwd")) id=53;//
+ else if (s==gensym("stop")) id=54;
+ else if (s==gensym("play")) id=55;
+
+ else if (s==gensym("bank")) id=56;
+ else if (s==gensym("group")) id=57;
+ else if (s==gensym("record")) id=58;
+ else if (s==gensym("functA")) id=59;
+ else if (s==gensym("write")) id=50;
+ else if (s==gensym("functB")) id=61;
+ else if (s==gensym("burn")) id=62;
+ else if (s==gensym("functC")) id=63;
+ else if (s==gensym("fx")) id=64;//...
+ else if (s==gensym("fxbyps")) id=65;//...
+ else if (s==gensym("effect1"))id=66;//.
+ else if (s==gensym("eff-1")) id=67;//.
+ else if (s==gensym("mute")) id=68;//...
+ else if (s==gensym("s-mute")) id=69;//...
+ else if (s==gensym("effect2"))id=60;//.
+ else if (s==gensym("eff-2")) id=71;//.
+ else if (s==gensym("pre")) id=72;//...
+ else if (s==gensym("post")) id=73;//...
+ else if (s==gensym("pre/pst"))id=74;//...
+ else if (s==gensym("effect3"))id=75;//.
+ else if (s==gensym("eff-3")) id=76;//.
+ else if (s==gensym("sel")) id=77;//...
+ else if (s==gensym("select")) id=77;//...
+ else if (s==gensym("effect4"))id=78;//.
+ else if (s==gensym("eff-4")) id=79;//.
+
+
+ return id;
+}
+
+static void LED_id2rowcol(int id, int* row, int *col)
+{
+ *row=-1;
+ *col=-1;
+
+ if (id==1)*row=0,*col=0; //1
+ else if (id==2)*row=0,*col=1;
+ else if (id==3)*row=0,*col=2;
+ else if (id==4)*row=0,*col=3;
+ else if (id==5)*row=0,*col=4;
+ else if (id==6)*row=0,*col=5;
+ else if (id==7)*row=0,*col=6;
+ else if (id==8)*row=0,*col=7;
+
+ else if (id==10)*row=4,*col=0;//a
+ else if (id==11)*row=4,*col=1;
+ else if (id==12)*row=4,*col=2;
+ else if (id==13)*row=4,*col=3;
+ else if (id==14)*row=4,*col=4;
+ else if (id==15)*row=4,*col=5;
+ else if (id==16)*row=4,*col=6;
+ else if (id==17)*row=4,*col=7;
+ else if (id==18)*row=3,*col=0;//i
+ else if (id==19)*row=3,*col=1;
+ else if (id==20)*row=3,*col=2;
+ else if (id==21)*row=3,*col=3;
+ else if (id==22)*row=3,*col=4;
+ else if (id==23)*row=3,*col=5;
+ else if (id==24)*row=3,*col=6;
+ else if (id==25)*row=3,*col=7;
+ else if (id==26)*row=2,*col=0;//q
+ else if (id==27)*row=2,*col=1;
+ else if (id==28)*row=2,*col=2;
+ else if (id==29)*row=2,*col=3;
+ else if (id==30)*row=2,*col=4;
+ else if (id==31)*row=2,*col=5;
+ else if (id==32)*row=2,*col=6;
+ else if (id==33)*row=2,*col=7;
+ else if (id==34)*row=1,*col=0;//y
+ else if (id==35)*row=1,*col=1;
+ else if (id==0) *row=1,*col=2;
+ else if (id==9) *row=1,*col=3;
+ else if (id==36)*row=1,*col=4;
+ else if (id==37)*row=1,*col=5;
+ else if (id==38)*row=1,*col=6;
+ else if (id==39)*row=1,*col=7;
+
+ else if (id==40)*row=0,*col=8;//shift -- LEFT SIDE
+ else if (id==41)*row=1,*col=8;
+ else if (id==42)*row=2,*col=8;
+ else if (id==43)*row=3,*col=8;
+ else if (id==44)*row=4,*col=8;
+ else if (id==45)*row=5,*col=8;
+ else if (id==46)*row=6,*col=8;
+ else if (id==47)*row=7,*col=8;
+ else if (id==48)*row=0,*col=9;//escape -- RIGHT SIDE
+ else if (id==49)*row=1,*col=9;
+ else if (id==50)*row=2,*col=9;
+ else if (id==51)*row=3,*col=9;
+ else if (id==52)*row=4,*col=9;
+ else if (id==53)*row=5,*col=9;
+ else if (id==54)*row=6,*col=9;
+ else if (id==55)*row=7,*col=9;
+ else if (id==56)*row=0,*col=10;//left-arrow -- VIEW
+ else if (id==57)*row=1,*col=10;
+ else if (id==58)*row=2,*col=10;
+ else if (id==59)*row=3,*col=10;
+ else if (id==50)*row=4,*col=10;
+ else if (id==61)*row=5,*col=10;
+ else if (id==62)*row=6,*col=10;
+ else if (id==63)*row=7,*col=10;
+ else if (id==64)*row=0,*col=11;
+ else if (id==65)*row=0,*col=11;
+ else if (id==66)*row=1,*col=11;
+ else if (id==67)*row=1,*col=11;
+ else if (id==68)*row=2,*col=11;
+ else if (id==69)*row=2,*col=11;
+ else if (id==60)*row=3,*col=11;
+ else if (id==71)*row=3,*col=11;
+ else if (id==72)*row=4,*col=11;
+ else if (id==73)*row=4,*col=11;
+ else if (id==74)*row=4,*col=11;
+ else if (id==75)*row=5,*col=11;
+ else if (id==76)*row=5,*col=11;
+ else if (id==77)*row=6,*col=11;
+ else if (id==78)*row=7,*col=11;
+ else if (id==79)*row=7,*col=11;
+}
+
+
+static t_class *LED_class;
+typedef struct _LED
+{
+ t_object x_obj;
+
+ t_float LED;
+ t_float state;
+} t_LED;
+
+static void LED_float(t_LED *x, t_float f)
+{
+ int row, col;
+ int state = x->state;
+ int offset = 0;
+ int id = f;
+
+ LED_id2rowcol(id, &row, &col);
+
+ if (id<0)return;
+
+ if (state>0)offset=0x40;
+ else if (state<0)offset=0x50;
+
+
+ if (col<8)offset++;
+#if 0
+ post("LED: %x %x %x %x", 0x0C, col, 0x2C, row+offset);
+ post("LED: %d %d %d %d\n", 0x0C, col, 0x2C, row+offset);
+#endif
+ outmidi_controlchange(x_port>>4, x_port&15, 0x0C, col);
+ outmidi_controlchange(x_port>>4, x_port&15, 0x2C, row+offset);
+}
+
+static void LED_symbol(t_LED *x, t_symbol *s)
+{
+ LED_float(x, LED_sym2id(s));
+}
+
+static void LED_bang(t_LED *x)
+{
+
+}
+
+static void *LED_new(void)
+{
+ t_LED *x = (t_LED *)pd_new(LED_class);
+
+ x->LED=-1;
+ floatinlet_new(&x->x_obj, &x->state);
+
+ x->state=0;
+
+ return (x);
+}
+static void LED_setup(void)
+{
+ LED_class = class_new(gensym("motormix_LED"), (t_newmethod)LED_new,
+ 0, sizeof(t_LED), 0, 0);
+ class_addcreator((t_newmethod)LED_new, gensym("LED"), 0);
+
+ class_addsymbol(LED_class, LED_symbol);
+ class_addfloat (LED_class, LED_float);
+ class_addbang (LED_class, LED_bang);
+ class_sethelpsymbol(LED_class, gensym("zexy/motormix"));
+}
+
+/* button */
+static t_class *button_class;
+typedef struct _button
+{
+ t_object x_obj;
+ int o_itsme; // the fader changed
+ int o_imtouched; // the fader was touched
+
+ t_outlet *o_value; // actual fader-value
+ t_outlet *o_touch; // 1=touched; 0=released
+
+ int activefader;
+ int fader;
+ unsigned char MSB, LSB;
+} t_button;
+
+static void button_touch(t_button *x, unsigned char value, unsigned char control)
+{
+ int pressed=0;
+ int MSB=x->MSB;
+ int LSB=0;
+ int id=0;
+
+ x->o_imtouched=0;
+
+ if (x->MSB<8){
+ if ((value==0x00) || (value==0x40))return;
+ }
+
+ if (value>=0x40){
+ pressed=1;
+ value-=0x40;
+ }
+
+ LSB=value;
+
+ post("MSB=%d, LSB=%d", MSB, LSB);
+
+ switch (MSB) {
+ case 0x08: /* left side */
+ id=MSB+40;
+ break;
+ case 0x09: /* right side */
+ id=MSB+
+ break;
+ case 0x0A: /* VIEW */
+ break;
+ case 0x0B: /* fx */
+ break;
+ default:
+ if (MSB>0x08)return;
+ switch (LSB) {
+ case 0x01: /* 1..8 */
+ id=MSB+1;
+ break;
+ case 0x05: /* a..h */
+ id=MSB+10;
+ break;
+ case 0x04: /* i..p */
+ id=MSB+18;
+ break;
+ case 0x03: /* q..x */
+ id=MSB+26;
+ break;
+ case 0x02: /* y.. . */
+ switch (MSB){
+ case 0: case 1:
+ id=MSB+34;
+ break;
+ case 2:
+ id=0;
+ break;
+ case 3:
+ id=9;
+ break;
+ default:
+ id=MSB+32;
+ }
+ break;
+ default:
+ return;
+ }
+ }
+
+
+ outlet_float(x->o_touch, pressed);
+ outlet_float(x->o_value, id);
+}
+
+static void button_list(t_button *x, t_symbol *s, int argc, t_atom *argv)
+{
+ unsigned char ctl = atom_getfloatarg(0, argc, argv);
+ unsigned char val = atom_getfloatarg(1, argc, argv);
+ // int channel = atom_getfloatarg(2, argc, argv);
+
+ if (x->o_imtouched)button_touch(x, val, ctl);
+
+ if (ctl==0x0f){
+ x->o_imtouched=1;
+ x->MSB=val;
+ }
+}
+
+static void *button_new()
+{
+ t_button *x = (t_button *)pd_new(button_class);
+
+ x->o_value=outlet_new(&x->x_obj, &s_float);
+ x->o_touch=outlet_new(&x->x_obj, &s_float);
+
+ x->o_imtouched = 0;
+
+ pd_bind(&x->x_obj.ob_pd, ctlin_sym);
+
+ return (x);
+}
+static void button_setup(void)
+{
+ button_class = class_new(gensym("motormix_button"), (t_newmethod)button_new,
+ 0, sizeof(t_button), CLASS_NOINLET, A_DEFFLOAT, 0);
+ class_addcreator((t_newmethod)button_new, gensym("button"), A_DEFFLOAT, 0);
+
+ class_addlist(button_class, button_list);
+ class_sethelpsymbol(button_class, gensym("zexy/motormix"));
+}
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..8820460
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,8 @@
+externals that are highly hardware-dependent, probably OS-dependent too
+
+i think it is better to put them somewhere separately
+and not to mix them up with "normal", more general externals/libraries.
+
+
+mfg.a.sdr
+IOhannes
diff --git a/aka.wiiremote/COPYRIGHT.txt b/aka.wiiremote/COPYRIGHT.txt
new file mode 100644
index 0000000..b33233c
--- /dev/null
+++ b/aka.wiiremote/COPYRIGHT.txt
@@ -0,0 +1,13 @@
+Max porting by Masayuki Akamatsu
+Copyright (c) 2006, Masayuki Akamatsu
+Based on "DarwiinRemote" by Hiroaki Kimura
+Copyright (c) 2006, Hiroaki Kimura
+All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+3. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/aka.wiiremote/HISTORY.txt b/aka.wiiremote/HISTORY.txt
new file mode 100644
index 0000000..a0687b1
--- /dev/null
+++ b/aka.wiiremote/HISTORY.txt
@@ -0,0 +1,54 @@
+aka.wiiremote Version History
+
+1.0B6 : 2007.04.24
+
+- The Classic Controller is supported.
+- The help patch is updated.
+- The URLs in [info] subpatch are updated.
+
+- 'expansion' message enables to use the expansion controller(Nunchuk or Classic Controller).
+- 'nunchuk' message is obsolete.
+ You should use 'expansion' instead of it even though you can use it for the backward compatibility only.
+
+- The IR sensor and the expansion controller (ex. Nunchuk) can be used together.
+
+- The stability on PPC-Mac is a little improved.
+ You have to do the following steps every time you use the Wii Remote on PPC-Mac.
+ (You would set up only once on Intel-Mac.)
+1: Delete "Nintendo RVL-CNT-01" on "Device" tab in "Bluetooth" System Preference.
+2: Do the setup for Wii Remote. See http://max.iamas.ac.jp/2061/articles/121.html
+3: Open "aka.wiiremote.help" and connect it.
+
+
+1.0B5 : 2007.02.03
+
+- Nunchuk is supported.
+( Classic Controller is NOT supported.)
+- device address is supported.
+- outlets and output messages are changed.
+- some input messages are simplified.
+- help patch is expanded for Nunchuk.
+
+
+1.0B4 : 2006.12.24
+
+- multiple Wii remotes are supported.
+
+
+1.0B3 : 2006.12.20
+
+- data acquisition is improved.
+- unusual vibration sound is resolved.
+
+
+1.0B2 : 2006.12.15
+
+- connection/disconnection is improved.
+- IR sensor is supported.
+- status report is supported
+- help patch is rewritten.
+
+
+1.0B1 : 2006.12.12
+
+- first release \ No newline at end of file
diff --git a/aka.wiiremote/Makefile b/aka.wiiremote/Makefile
new file mode 100644
index 0000000..e222c9b
--- /dev/null
+++ b/aka.wiiremote/Makefile
@@ -0,0 +1,33 @@
+TARGET := $(shell pwd | sed 's|.*/\(.*\)$$|\1|')
+EXTERNALS_ROOT := $(shell pwd | sed 's|^\(/.*externals\).*|\1|')
+
+default:
+ make -C $(EXTERNALS_ROOT) $(TARGET)
+
+install:
+ make -C $(EXTERNALS_ROOT) $(TARGET)_install
+
+clean:
+ make -C $(EXTERNALS_ROOT) $(TARGET)_clean
+
+test_locations:
+ make -C $(EXTERNALS_ROOT) test_locations
+
+# for emacs
+etags:
+ etags ../../../pd/src/*.h *.[ch]
+ make etags_`uname -s`
+
+etags_Darwin:
+ etags -a \
+ /System/Library/Frameworks/ForceFeedback.framework/Headers/*.h \
+ /System/Library/Frameworks/CoreFoundation.framework/Headers/*.h \
+ /System/Library/Frameworks/Carbon.framework/Headers/*.h \
+ /System/Library/Frameworks/IOBluetooth.framework/Headers/*.[ch]
+
+etags_Linux:
+ etags -a /usr/include/*.h linux/input.h /usr/include/sys/*.h
+
+etags_MINGW:
+ etags -a /usr/include/*.h /usr/include/sys/*.h \
+ /usr/local/include/*.h /usr/local/include/sys/*.h
diff --git a/aka.wiiremote/README.txt b/aka.wiiremote/README.txt
new file mode 100644
index 0000000..bdaf778
--- /dev/null
+++ b/aka.wiiremote/README.txt
@@ -0,0 +1,6 @@
+
+This is a port of the Max class aka.wiiremote by Masayuki Akamatsu. Its
+available here:
+
+http://www.iamas.ac.jp/~aka/max/#aka_wiiremote
+
diff --git a/aka.wiiremote/TODO b/aka.wiiremote/TODO
new file mode 100644
index 0000000..a8da43d
--- /dev/null
+++ b/aka.wiiremote/TODO
@@ -0,0 +1,7 @@
+
+
+ - try out IOBluetoothLocalDeviceAvailable(wiiremote->inquiry); to see if that will set up the event loops (http://lists.apple.com/archives/bluetooth-dev/2006/Dec/msg00001.html)
+
+
+- try CFRunLoopRun(); then CFRunLoopStop(CFRunLoopGetCurrent()); in akawiiremote_clock() and akawiiremote_bang()
+
diff --git a/aka.wiiremote/aka.wiiremote.c b/aka.wiiremote/aka.wiiremote.c
new file mode 100644
index 0000000..7a113b6
--- /dev/null
+++ b/aka.wiiremote/aka.wiiremote.c
@@ -0,0 +1,646 @@
+// aka.wiiremote.c
+// Copyright by Masayuki Akamatsu
+// Code for PD by Hans-Christoph Steiner
+// 1.0B1 : 2006.12.12
+// 1.0B2 : 2006.12.15
+// 1.0B3 : 2006.12.20
+// 1.0B4 : 2006.12.24
+// 1.0B5 : 2007.02.03
+// 1.0B6 : 2007.04.24
+
+#ifdef PD
+#include "m_pd.h"
+#include "m_imp.h"
+#define SETSYM SETSYMBOL
+#define SETLONG SETFLOAT
+#define method t_method
+//#define addbang(x) class_addbang(wiiremote_class, (x))
+//#define addmess(x) class_addmessage(class_wiiremote, (x))
+// a CFRunLoop is needed for Pd since the 'pd' process is not a Carbon app
+static t_class *akawiiremote_class;
+#else /* Max */
+#include "ext.h"
+
+void *akawiiremote_class; // the number of instance of this object
+#endif /* PD */
+
+#include "wiiremote.h"
+#include <stdio.h>
+#include <string.h>
+
+#define kInterval 100
+#define kMaxTrial 100
+
+typedef struct _akawiiremote
+{
+#ifdef PD
+ t_object x_obj;
+#else /* Max */
+ struct object obj;
+#endif
+
+ WiiRemoteRef wiiremote;
+ char address[32];
+
+ void *clock;
+ Boolean connected;
+
+ void *statusOut;
+ void *dataOut;
+} t_akawiiremote;
+
+void akawiiremote_bang(t_akawiiremote *x);
+void akawiiremote_address(t_akawiiremote *x, t_symbol *s);
+void akawiiremote_connect(t_akawiiremote *x);
+void akawiiremote_disconnect(t_akawiiremote *x);
+void akawiiremote_motionsensor(t_akawiiremote *x, long enable);
+void akawiiremote_irsensor(t_akawiiremote *x, long enable);
+void akawiiremote_vibration(t_akawiiremote *x, long enable);
+void akawiiremote_led(t_akawiiremote *x, long enable1, long enable2, long enable3, long enable4);
+void akawiiremote_expansion(t_akawiiremote *x, long enable);
+void akawiiremote_extraoutput(t_akawiiremote *x, long enable);
+
+void akawiiremote_getbattery(t_akawiiremote *x);
+void akawiiremote_getexpansion(t_akawiiremote *x);
+void akawiiremote_getled(t_akawiiremote *x);
+void akawiiremote_getaddress(t_akawiiremote *x);
+void akawiiremote_getcalibration(t_akawiiremote *x);
+
+void akawiiremote_assist(t_akawiiremote *x, void *b, long m, long a, char *s);
+void akawiiremote_clock(t_akawiiremote *x);
+void *akawiiremote_new(t_symbol *s, short ac, t_atom *av);
+void akawiiremote_free(t_akawiiremote *x);
+
+char remoteStr[] = "remote";
+char nunchukStr[] = "nunchuk";
+char classicStr[] = "classic";
+
+#ifdef PD
+void akawiiremote_setup()
+#else /* Max */
+void main()
+#endif /* PD */
+{
+ NumVersion outSoftwareVersion;
+ BluetoothHCIVersionInfo outHardwareVersion;
+
+ post("aka.wiiremote 1.0B7-UB by Masayuki Akamatsu");
+
+ if (IOBluetoothGetVersion(&outSoftwareVersion, &outHardwareVersion)==kIOReturnSuccess) // B7
+ {
+ if (outSoftwareVersion.majorRev < 1 && outSoftwareVersion.minorAndBugRev < 0x63)
+ {
+ error("requires Blutooth version 1.6.3 or later.");
+ return;
+ }
+ }
+ else
+ {
+ error("can't get Bluetooth version.");
+ return;
+ }
+
+#ifdef PD
+ post("\tPd port by Hans-Christoph Steiner");
+
+ akawiiremote_class = class_new(gensym("akawiiremote"),
+ (t_newmethod)akawiiremote_new,
+ (t_method)akawiiremote_free,
+ sizeof(t_akawiiremote),
+ CLASS_DEFAULT,
+ A_GIMME,0);
+
+ class_addbang(akawiiremote_class,(t_method)akawiiremote_bang);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_address,gensym("address"),A_DEFSYMBOL, 0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_connect,gensym("connect"),0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_disconnect,gensym("disconnect"),0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_motionsensor,gensym("motion"), A_DEFFLOAT, 0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_irsensor,gensym("ir"), A_DEFFLOAT, 0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_vibration,gensym("vibration"), A_DEFFLOAT, 0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_led,gensym("led"), A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_expansion,gensym("expansion"), A_DEFFLOAT, 0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_expansion,gensym("nunchuk"), A_DEFFLOAT, 0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_extraoutput,gensym("extraoutput"), A_DEFFLOAT, 0); // B7
+
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_getbattery,gensym("getbattery"),0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_getexpansion,gensym("getexpansion"),0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_getled,gensym("getled"),0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_getaddress,gensym("getaddress"),0);
+ class_addmethod(akawiiremote_class,(t_method)akawiiremote_getcalibration,gensym("getcalibration"), 0);
+#else /* Max */
+ setup((t_messlist **)&akawiiremote_class, (method)akawiiremote_new, (method)akawiiremote_free, (short)sizeof(t_akawiiremote), 0L, A_GIMME, 0);
+
+ addbang((method)akawiiremote_bang);
+ addmess((method)akawiiremote_address,"address",A_DEFSYM, 0);
+ addmess((method)akawiiremote_connect,"connect", 0);
+ addmess((method)akawiiremote_disconnect,"disconnect",0);
+ addmess((method)akawiiremote_motionsensor,"motion", A_DEFLONG, 0);
+ addmess((method)akawiiremote_irsensor,"ir", A_DEFLONG, 0);
+ addmess((method)akawiiremote_vibration,"vibration", A_DEFLONG, 0);
+ addmess((method)akawiiremote_led,"led", A_DEFLONG, A_DEFLONG, A_DEFLONG, A_DEFLONG, 0);
+ addmess((method)akawiiremote_expansion,"expansion", A_DEFLONG, 0);
+ addmess((method)akawiiremote_expansion,"nunchuk", A_DEFLONG, 0);
+ addmess((method)akawiiremote_extraoutput,"extraoutput", A_DEFLONG, 0); // B7
+
+ addmess((method)akawiiremote_getbattery,"getbattery",0);
+ addmess((method)akawiiremote_getexpansion,"getexpansion",0);
+ addmess((method)akawiiremote_getled,"getled",0);
+ addmess((method)akawiiremote_getaddress,"getaddress",0);
+ addmess((method)akawiiremote_getcalibration,"getcalibration", 0);
+
+ addmess((method)akawiiremote_assist,"assist",A_CANT,0);
+#endif /* PD */
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_bang(t_akawiiremote *x)
+{
+ t_atom av[7];
+
+ if (x->wiiremote->device == nil)
+ return; // do nothing
+#ifdef PD
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true);
+#endif
+ if (x->wiiremote->isExpansionPortAttached && x->wiiremote->isExpansionPortEnabled)
+ {
+ // Classic Controller
+ if (x->wiiremote->expType == WiiClassicController)
+ {
+ // Buttons
+ SETSYM(av, gensym("buttons"));
+ SETLONG(av + 1, x->wiiremote->cButtonData);
+ outlet_anything(x->dataOut, gensym(classicStr), 2, av);
+
+ // Joystick 1
+ SETSYM(av, gensym("stick1"));
+ SETLONG(av + 1, x->wiiremote->cStickX1);
+ SETLONG(av + 2, x->wiiremote->cStickY1);
+ outlet_anything(x->dataOut, gensym(classicStr), 3, av);
+
+ // Joystick 2
+ SETSYM(av, gensym("stick2"));
+ SETLONG(av + 1, x->wiiremote->cStickX2);
+ SETLONG(av + 2, x->wiiremote->cStickY2);
+ outlet_anything(x->dataOut, gensym(classicStr), 3, av);
+
+ // Analog
+ SETSYM(av, gensym("analog"));
+ SETLONG(av + 1, x->wiiremote->cAnalogL);
+ SETLONG(av + 2, x->wiiremote->cAnalogR);
+ outlet_anything(x->dataOut, gensym(classicStr), 3, av);
+ }
+
+ // Nunchuk
+ if (x->wiiremote->expType == WiiNunchuk)
+ {
+ // Buttons
+ SETSYM(av, gensym("buttons"));
+ SETLONG(av + 1, x->wiiremote->nButtonData);
+ outlet_anything(x->dataOut, gensym(nunchukStr), 2, av);
+
+ // Joystick
+ SETSYM(av, gensym("stick"));
+ SETLONG(av + 1, x->wiiremote->nStickX);
+ SETLONG(av + 2, x->wiiremote->nStickY);
+ outlet_anything(x->dataOut, gensym(nunchukStr), 3, av);
+
+ if (x->wiiremote->isExtraOutputEnabled)
+ {
+ SETSYM(av, gensym("stick_calibration"));
+ SETLONG(av + 1, x->wiiremote->nunchukJoyStickCalibData.x_min);
+ SETLONG(av + 2, x->wiiremote->nunchukJoyStickCalibData.x_max);
+ SETLONG(av + 3, x->wiiremote->nunchukJoyStickCalibData.x_center);
+ SETLONG(av + 4, x->wiiremote->nunchukJoyStickCalibData.y_min);
+ SETLONG(av + 5, x->wiiremote->nunchukJoyStickCalibData.y_max);
+ SETLONG(av + 6, x->wiiremote->nunchukJoyStickCalibData.y_center);
+ outlet_anything(x->dataOut, gensym(nunchukStr), 7, av);
+ }
+
+ // Motion Sensor
+ if (x->wiiremote->isMotionSensorEnabled)
+ {
+ SETSYM(av, gensym("motion"));
+ SETLONG(av + 1, x->wiiremote->nAccX);
+ SETLONG(av + 2, x->wiiremote->nAccY);
+ SETLONG(av + 3, x->wiiremote->nAccZ);
+ SETLONG(av + 4, x->wiiremote->nOrientation);
+ outlet_anything(x->dataOut, gensym(nunchukStr), 5, av);
+
+ if (x->wiiremote->isExtraOutputEnabled)
+ {
+ SETSYM(av, gensym("motion_calibration"));
+ SETLONG(av + 1, x->wiiremote->nunchukCalibData.accX_zero);
+ SETLONG(av + 2, x->wiiremote->nunchukCalibData.accY_zero);
+ SETLONG(av + 3, x->wiiremote->nunchukCalibData.accZ_zero);
+ SETLONG(av + 4, x->wiiremote->nunchukCalibData.accX_1g);
+ SETLONG(av + 5, x->wiiremote->nunchukCalibData.accY_1g);
+ SETLONG(av + 6, x->wiiremote->nunchukCalibData.accZ_1g);
+ outlet_anything(x->dataOut, gensym(nunchukStr), 7, av);
+ }
+ }
+ }
+ }
+
+ // Wii Remote
+
+ // Buttons
+ SETSYM(av, gensym("buttons"));
+ SETLONG(av + 1, x->wiiremote->buttonData);
+ outlet_anything(x->dataOut, gensym(remoteStr), 2, av);
+
+ // IR Sensor
+ if (x->wiiremote->isIRSensorEnabled)
+ {
+ SETSYM(av, gensym("ir"));
+ SETFLOAT(av + 1, x->wiiremote->posX); // posX and posY are "float"????
+ SETFLOAT(av + 2, x->wiiremote->posY);
+ SETFLOAT(av + 3, x->wiiremote->angle);
+ SETLONG (av + 4, x->wiiremote->tracking);
+ outlet_anything(x->dataOut, gensym(remoteStr), 5, av);
+
+ if (x->wiiremote->isExtraOutputEnabled) // B7
+ {
+ SETSYM(av, gensym("irraw"));
+ SETLONG(av + 1, 0);
+ SETLONG(av + 2, x->wiiremote->irData[0].x);
+ SETLONG(av + 3, x->wiiremote->irData[0].y);
+ SETLONG(av + 4, x->wiiremote->irData[0].s);
+ outlet_anything(x->dataOut, gensym(remoteStr), 5, av);
+ SETLONG(av + 1, 1);
+ SETLONG(av + 2, x->wiiremote->irData[1].x);
+ SETLONG(av + 3, x->wiiremote->irData[1].y);
+ SETLONG(av + 4, x->wiiremote->irData[1].s);
+ outlet_anything(x->dataOut, gensym(remoteStr), 5, av);
+ SETLONG(av + 1, 2);
+ SETLONG(av + 2, x->wiiremote->irData[2].x);
+ SETLONG(av + 3, x->wiiremote->irData[2].y);
+ SETLONG(av + 4, x->wiiremote->irData[2].s);
+ outlet_anything(x->dataOut, gensym(remoteStr), 5, av);
+ SETLONG(av + 1, 3);
+ SETLONG(av + 2, x->wiiremote->irData[3].x);
+ SETLONG(av + 3, x->wiiremote->irData[3].y);
+ SETLONG(av + 4, x->wiiremote->irData[3].s);
+ outlet_anything(x->dataOut, gensym(remoteStr), 5, av);
+ }
+ }
+
+ // Motion Sensor
+ if (x->wiiremote->isMotionSensorEnabled)
+ {
+ SETSYM(av, gensym("motion"));
+ SETLONG(av + 1, x->wiiremote->accX);
+ SETLONG(av + 2, x->wiiremote->accY);
+ SETLONG(av + 3, x->wiiremote->accZ);
+ SETLONG(av + 4, x->wiiremote->orientation);
+ outlet_anything(x->dataOut, gensym(remoteStr), 5, av);
+
+ if (x->wiiremote->isExtraOutputEnabled) // B7
+ {
+ SETSYM(av, gensym("motion_calibration"));
+ SETLONG(av + 1, x->wiiremote->wiiCalibData.accX_zero);
+ SETLONG(av + 2, x->wiiremote->wiiCalibData.accY_zero);
+ SETLONG(av + 3, x->wiiremote->wiiCalibData.accZ_zero);
+ SETLONG(av + 4, x->wiiremote->wiiCalibData.accX_1g);
+ SETLONG(av + 5, x->wiiremote->wiiCalibData.accY_1g);
+ SETLONG(av + 6, x->wiiremote->wiiCalibData.accZ_1g);
+ outlet_anything(x->dataOut, gensym(remoteStr), 7, av);
+ }
+ }
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_address(t_akawiiremote *x, t_symbol *s)
+{
+ if (*(s->s_name) == 0) // if null string
+ *(x->address) = 0;
+ else
+ strcpy(x->address, s->s_name);
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_connect(t_akawiiremote *x)
+{
+ post("akawiiremote_connect");
+ t_atom status;
+ Boolean result;
+
+ if (wiiremote_isconnected(x->wiiremote))
+ {
+ SETLONG(&status, -1);
+ outlet_anything(x->statusOut, gensym("connect"), 1, &status);
+ }
+ else
+ {
+ result = wiiremote_search(x->wiiremote, x->address); // start searching the device
+ x->connected = false;
+ clock_unset(x->clock); // stop clock
+ clock_delay(x->clock, 0); // start clock to check the device found
+ }
+}
+
+void akawiiremote_foundFunc(t_akawiiremote *x)
+{
+}
+
+void akawiiremote_disconnect(t_akawiiremote *x)
+{
+ post("akawiiremote_disconnect");
+
+ Boolean result;
+ t_atom status;
+
+ clock_unset(x->clock); // stop clock
+ wiiremote_stopsearch(x->wiiremote);
+
+ result = wiiremote_disconnect(x->wiiremote);
+ SETLONG(&status, result);
+ outlet_anything(x->statusOut, gensym("disconnect"), 1, &status);
+
+ x->connected = !result;
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_motionsensor(t_akawiiremote *x, long enable)
+{
+ Boolean result;
+
+ result = wiiremote_motionsensor(x->wiiremote, enable);
+ //SETLONG(&status, result);
+ //outlet_anything(x->statusOut, gensym("motion"), 1, &status);
+}
+
+void akawiiremote_irsensor(t_akawiiremote *x, long enable)
+{
+ Boolean result;
+
+ result = wiiremote_irsensor(x->wiiremote, enable);
+ //SETLONG(&status, result);
+ //outlet_anything(x->statusOut, gensym("ir"), 1, &status);
+}
+
+void akawiiremote_extraoutput(t_akawiiremote *x, long enable) // B7
+{
+ x->wiiremote->isExtraOutputEnabled = enable;
+}
+
+void akawiiremote_expansion(t_akawiiremote *x, long enable)
+{
+ Boolean result;
+
+ result = wiiremote_expansion(x->wiiremote, enable);
+ //SETLONG(&status, result);
+ //outlet_anything(x->statusOut, gensym("nunchuk"), 1, &status);
+}
+
+void akawiiremote_vibration(t_akawiiremote *x, long enable)
+{
+ Boolean result;
+
+ result = wiiremote_vibration(x->wiiremote, enable);
+ //SETLONG(&status, result);
+ //outlet_anything(x->statusOut, gensym("vibration"), 1, &status);
+}
+
+void akawiiremote_led(t_akawiiremote *x, long enable1, long enable2, long enable3, long enable4)
+{
+ Boolean result;
+
+ result = wiiremote_led(x->wiiremote, enable1, enable2, enable3, enable4);
+ //SETLONG(&status, result);
+ //outlet_anything(x->statusOut, gensym("led"), 1, &status);
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_getbattery(t_akawiiremote *x)
+{
+ if (x->wiiremote->device == nil)
+ {
+ outlet_anything(x->statusOut, gensym("battery"), 0, nil);
+ }
+ else
+ {
+ t_atom status;
+
+ SETFLOAT(&status, x->wiiremote->batteryLevel);
+ outlet_anything(x->statusOut, gensym("battery"), 1, &status);
+ }
+}
+
+void akawiiremote_getexpansion(t_akawiiremote *x)
+{
+ if (x->wiiremote->device == nil)
+ {
+ outlet_anything(x->statusOut, gensym("expansion"), 0, nil);
+ }
+ else
+ {
+ t_atom status;
+ if (x->wiiremote->isExpansionPortAttached)
+ SETLONG(&status, x->wiiremote->expType);
+ else
+ SETLONG(&status, 0);
+ outlet_anything(x->statusOut, gensym("expansion"), 1, &status);
+ }
+}
+
+void akawiiremote_getled(t_akawiiremote *x)
+{
+ if (x->wiiremote->device == nil)
+ {
+ outlet_anything(x->statusOut, gensym("led"), 0, nil);
+ }
+ else
+ {
+ t_atom list[4];
+
+ SETLONG(list, x->wiiremote->isLED1Illuminated);
+ SETLONG(list + 1, x->wiiremote->isLED2Illuminated);
+ SETLONG(list + 2, x->wiiremote->isLED3Illuminated);
+ SETLONG(list + 3, x->wiiremote->isLED4Illuminated);
+ outlet_anything(x->statusOut, gensym("led"), 4, list);
+ }
+}
+
+void akawiiremote_getcalibration(t_akawiiremote *x)
+{
+ if (x->wiiremote->device == nil)
+ {
+ outlet_anything(x->statusOut, gensym("calibration"), 0, nil);
+ }
+ else
+ {
+ t_atom list[8];
+
+ if (x->wiiremote->isExpansionPortAttached)
+ {
+ SETSYM(list, gensym(nunchukStr));
+ SETSYM(list + 1, gensym("stick"));
+ SETLONG(list + 2, x->wiiremote->nunchukJoyStickCalibData.x_max);
+ SETLONG(list + 3, x->wiiremote->nunchukJoyStickCalibData.x_min);
+ SETLONG(list + 4, x->wiiremote->nunchukJoyStickCalibData.x_center);
+ SETLONG(list + 5, x->wiiremote->nunchukJoyStickCalibData.y_max);
+ SETLONG(list + 6, x->wiiremote->nunchukJoyStickCalibData.y_min);
+ SETLONG(list + 7, x->wiiremote->nunchukJoyStickCalibData.y_center);
+ outlet_anything(x->statusOut, gensym("calibration"), 8, list);
+
+ SETSYM(list + 1, gensym("motion"));
+ SETLONG(list + 2, x->wiiremote->nunchukCalibData.accX_zero);
+ SETLONG(list + 3, x->wiiremote->nunchukCalibData.accY_zero);
+ SETLONG(list + 4, x->wiiremote->nunchukCalibData.accZ_zero);
+ SETLONG(list + 5, x->wiiremote->nunchukCalibData.accX_1g);
+ SETLONG(list + 6, x->wiiremote->nunchukCalibData.accY_1g);
+ SETLONG(list + 7, x->wiiremote->nunchukCalibData.accZ_1g);
+ outlet_anything(x->statusOut, gensym("calibration"), 8, list);
+ }
+
+ SETSYM(list, gensym(remoteStr));
+ SETSYM(list + 1, gensym("motion"));
+ SETLONG(list + 2, x->wiiremote->wiiCalibData.accX_zero);
+ SETLONG(list + 3, x->wiiremote->wiiCalibData.accY_zero);
+ SETLONG(list + 4, x->wiiremote->wiiCalibData.accZ_zero);
+ SETLONG(list + 5, x->wiiremote->wiiCalibData.accX_1g);
+ SETLONG(list + 6, x->wiiremote->wiiCalibData.accY_1g);
+ SETLONG(list + 7, x->wiiremote->wiiCalibData.accZ_1g);
+ outlet_anything(x->statusOut, gensym("calibration"), 8, list);
+ }
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_getaddress(t_akawiiremote *x)
+{
+ if (x->wiiremote->device == nil)
+ {
+ outlet_anything(x->statusOut, gensym("address"), 0, nil);
+ }
+ else
+ {
+ char str[32];
+ t_atom address;
+
+ wiiremote_getaddress(x->wiiremote, str);
+ SETSYM(&address, gensym(str));
+ outlet_anything(x->statusOut, gensym("address"), 1, &address);
+ }
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_clock(t_akawiiremote *x)
+{
+ Boolean connection;
+ t_atom status;
+
+ connection = wiiremote_isconnected(x->wiiremote);
+
+ if (x->connected == false && connection == true) // if the device is connected...
+ {
+ wiiremote_getstatus(x->wiiremote);
+ x->connected = true;
+ SETLONG(&status, 1);
+ outlet_anything(x->statusOut, gensym("connect"), 1, &status);
+ }
+
+ if (x->connected == true && connection == false)
+ {
+ x->connected = false;
+ SETLONG(&status, 0);
+ outlet_anything(x->statusOut, gensym("connect"), 1, &status);
+ }
+
+ clock_delay(x->clock, kInterval); // restart clock
+}
+
+//--------------------------------------------------------------------------------------------
+
+void akawiiremote_assist(t_akawiiremote *x, void *b, long m, long a, char *s)
+{
+#ifndef PD /* Max */
+ if (m==ASSIST_INLET)
+ {
+ sprintf(s,"connect, bang, disconnect....");
+ }
+ else
+#endif /* NOT PD */
+ {
+ switch(a)
+ {
+ case 0: sprintf(s,"data messages"); break;
+ case 2: sprintf(s,"status messages"); break;
+ }
+ }
+}
+
+//--------------------------------------------------------------------------------------------
+
+void *akawiiremote_new(t_symbol *s, short ac, t_atom *av)
+{
+#ifdef PD
+ t_akawiiremote *x = (t_akawiiremote *)pd_new(akawiiremote_class);
+ t_symbol *first_argument;
+
+ x->statusOut = outlet_new(&x->x_obj, 0);
+ x->dataOut = outlet_new(&x->x_obj, &s_list);
+
+/* this sets the device name from the object arguments */
+ first_argument = atom_getsymbolarg(0, ac, av);
+ if(first_argument != &s_)
+ atom_string(av, x->address, MAXPDSTRING-1);
+#else /* Max */
+ t_akawiiremote *x;
+
+ x = (t_akawiiremote *)newobject(akawiiremote_class);
+
+ x->statusOut = outlet_new(x, 0);
+ x->dataOut = outlet_new(x, 0);
+
+ if (ac>0 && av[0].a_type == A_SYM)
+ strcpy(x->address, av[0].a_w.w_sym->s_name);
+#endif /* PD */
+
+ x->wiiremote = (WiiRemoteRef)getbytes(sizeof(WiiRemoteRec));
+ if (x->wiiremote != nil)
+ {
+ wiiremote_init(x->wiiremote);
+ x->wiiremote->isMotionSensorEnabled = true;
+ x->wiiremote->isIRSensorEnabled = false;
+ x->wiiremote->isVibrationEnabled = false;
+ x->wiiremote->isExpansionPortEnabled = false;
+ x->wiiremote->isLED1Illuminated = false;
+ x->wiiremote->isLED2Illuminated = false;
+ x->wiiremote->isLED3Illuminated = false;
+ x->wiiremote->isLED4Illuminated = false;
+ x->wiiremote->isExtraOutputEnabled = false;
+ }
+
+ x->clock = clock_new(x, (method)akawiiremote_clock);
+
+ x->connected = false;
+
+ return x;
+}
+
+void akawiiremote_free(t_akawiiremote *x)
+{
+ if (x->wiiremote != nil)
+ {
+ if (wiiremote_isconnected(x->wiiremote))
+ wiiremote_disconnect(x->wiiremote);
+ freebytes(x->wiiremote, sizeof(WiiRemoteRec));
+ x->wiiremote = nil;
+ }
+
+ clock_unset(x->clock);
+#ifdef PD
+ clock_free(x->clock);
+#else /* Max */
+ freeobject((t_object *)x->clock);
+#endif /* PD */
+}
+
diff --git a/aka.wiiremote/akawiiremote-help.pd b/aka.wiiremote/akawiiremote-help.pd
new file mode 100644
index 0000000..b2e85ed
--- /dev/null
+++ b/aka.wiiremote/akawiiremote-help.pd
@@ -0,0 +1,82 @@
+#N canvas 287 96 744 626 10;
+#X msg 128 207 connect;
+#X msg 141 228 disconnect;
+#X obj 218 203 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 294 202 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 127 285 metro 100;
+#X obj 127 264 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X msg 89 283 bang;
+#X text 49 49 - Use the Bluetooth Setup Assistant to setup the WiiRemote
+with your computer (only required for the first use).;
+#X text 49 79 - Press the "Sync" button on the WiiRemote \, four LEDs
+will start blinking.;
+#X text 49 109 - Click on the [connect( message \, the LEDs will stop
+blinking once its connected;
+#X text 49 139 - Start the [metro] to get updates from [aka.wiiremote]
+;
+#X obj 399 526 pddp/print;
+#X obj 354 556 pddp/print;
+#X obj 354 410 route remote;
+#X msg 503 290 getbattery;
+#X msg 503 310 getled;
+#X msg 503 330 getexpansion;
+#X obj 489 431 print RIGHT;
+#X obj 253 430 print LEFT;
+#X obj 354 476 route motion buttons ir;
+#X obj 444 497 pddp/print;
+#X msg 294 224 ir \$1;
+#X msg 218 225 motion \$1;
+#X obj 344 202 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X msg 344 224 vibration \$1;
+#X obj 434 202 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X msg 434 224 expansion \$1;
+#X obj 524 202 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X msg 524 223 extraoutput \$1;
+#N canvas 254 342 450 300 address 0;
+#X obj 176 252 outlet;
+#X msg 206 126 getaddress;
+#X msg 176 72 address 00-1e-35-4c-e6-f1;
+#X connect 1 0 0 0;
+#X connect 2 0 0 0;
+#X restore 25 462 pd address;
+#X msg 503 352 getaddress;
+#X obj 631 536 dac~;
+#X obj 632 408 pddp/dsp;
+#X obj 640 500 osc~ 100;
+#X obj 303 388 akawiiremote;
+#X obj 334 589 print;
+#X connect 0 0 34 0;
+#X connect 1 0 34 0;
+#X connect 2 0 22 0;
+#X connect 3 0 21 0;
+#X connect 4 0 34 0;
+#X connect 5 0 4 0;
+#X connect 6 0 34 0;
+#X connect 13 0 19 0;
+#X connect 13 1 17 0;
+#X connect 14 0 34 0;
+#X connect 15 0 34 0;
+#X connect 16 0 34 0;
+#X connect 19 0 12 0;
+#X connect 19 0 35 0;
+#X connect 19 1 11 0;
+#X connect 19 2 20 0;
+#X connect 21 0 34 0;
+#X connect 22 0 34 0;
+#X connect 23 0 24 0;
+#X connect 24 0 34 0;
+#X connect 25 0 26 0;
+#X connect 26 0 34 0;
+#X connect 27 0 28 0;
+#X connect 28 0 34 0;
+#X connect 30 0 34 0;
+#X connect 33 0 31 0;
+#X connect 33 0 31 1;
+#X connect 34 0 18 0;
+#X connect 34 1 13 0;
diff --git a/aka.wiiremote/wiiremote.c b/aka.wiiremote/wiiremote.c
new file mode 100644
index 0000000..92efeb0
--- /dev/null
+++ b/aka.wiiremote/wiiremote.c
@@ -0,0 +1,1100 @@
+// wiiremote.c
+// Copyright by Masayuki Akamatsu
+// Based on "DarwiinRemote" by Hiroaki Kimura
+
+#include "wiiremote.h"
+#include <unistd.h>
+
+// this type is used a lot (data array):
+typedef unsigned char darr[];
+
+#define kTrial 10
+#define kWait 10000
+// the unit of kWait is microseconds, thus 10000 means 10ms
+
+#define kWiiIRPixelsWidth 1024.0
+#define kWiiIRPixelsHeight 768.0
+
+
+Boolean requestUpdates(WiiRemoteRef wiiremote);
+void myEventListener(IOBluetoothL2CAPChannelRef channel, void *refCon, IOBluetoothL2CAPChannelEvent *event);
+
+#define DEBUG(x)
+
+//--------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------
+
+void wiiremote_init(WiiRemoteRef wiiremote)
+{
+ wiiremote->inquiry = nil;
+ wiiremote->device = nil;
+ wiiremote->ichan = nil;
+ wiiremote->cchan = nil;
+
+ wiiremote->address = nil;
+
+ wiiremote->accX = 0x10;
+ wiiremote->accY = 0x10;
+ wiiremote->accZ = 0x10;
+ wiiremote->buttonData = 0;
+
+ wiiremote->lowZ = 0;
+ wiiremote->lowX = 0;
+ wiiremote->leftPoint = -1;
+ wiiremote->tracking = false;
+
+ wiiremote->batteryLevel = 0;
+
+ wiiremote->readingRegister = false;
+ wiiremote->isMotionSensorEnabled = false;
+ wiiremote->isVibrationEnabled = false;
+ wiiremote->isIRSensorEnabled = false;
+ wiiremote->wiiIRMode = kWiiIRModeExtended;
+ wiiremote->isExpansionPortEnabled = false;
+ wiiremote->isExpansionPortAttached = false;
+ wiiremote->expType = WiiExpNotAttached;
+
+ wiiremote->isLED1Illuminated = false;
+ wiiremote->isLED2Illuminated = false;
+ wiiremote->isLED3Illuminated = false;
+ wiiremote->isLED4Illuminated = false;
+
+ wiiremote->nAccX = 0x10;
+ wiiremote->nAccY = 0x10;
+ wiiremote->nAccZ = 0x10;
+ wiiremote->nButtonData = 0;
+
+ wiiremote->nLowZ = 0;
+ wiiremote->nLowX = 0;
+
+}
+
+//--------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------
+
+Boolean openCChan(WiiRemoteRef wiiremote)
+{
+ DEBUG(post("openCChan"););
+ short i;
+ IOReturn ret;
+
+ // open L2CAPChannel : BluetoothL2CAPPSM = 17
+ for (i=0; i<kTrial; i++)
+ {
+ ret = IOBluetoothDeviceOpenL2CAPChannelSync(wiiremote->device, &(wiiremote->cchan), 17, myEventListener, (void *)wiiremote);
+ if ( ret == kIOReturnSuccess)
+ break;
+ usleep(kWait); // wait 10ms
+ }
+ if (i==kTrial)
+ {
+ wiiremote->cchan = nil;
+ IOBluetoothDeviceCloseConnection(wiiremote->device);
+ return false;
+ }
+ IOBluetoothObjectRetain(wiiremote->cchan);
+
+ return (ret==kIOReturnSuccess);
+}
+
+Boolean openIChan(WiiRemoteRef wiiremote)
+{
+ DEBUG(post("openCChan"););
+ short i;
+ IOReturn ret;
+
+ // open L2CAPChannel : BluetoothL2CAPPSM = 19
+ for (i=0; i<kTrial; i++)
+ {
+ ret = IOBluetoothDeviceOpenL2CAPChannelSync(wiiremote->device, &(wiiremote->ichan), 19, myEventListener, (void *)wiiremote);
+ if ( ret == kIOReturnSuccess)
+ break;
+ usleep(kWait); // wait 10ms
+ }
+ if (i==kTrial)
+ {
+ wiiremote->ichan = nil;
+ IOBluetoothL2CAPChannelCloseChannel(wiiremote->cchan);
+ IOBluetoothObjectRelease(wiiremote->cchan);
+ IOBluetoothDeviceCloseConnection(wiiremote->device);
+ return false;
+ }
+ IOBluetoothObjectRetain(wiiremote->ichan);
+
+ return (ret==kIOReturnSuccess);
+}
+
+//--------------------------------------------------------------------------------------------
+
+Boolean sendCommand(WiiRemoteRef wiiremote, unsigned char *data, unsigned short length)
+{
+ unsigned char buf[40];
+ IOReturn ret;
+ int i;
+
+ memset(buf,0,40);
+ buf[0] = 0x52;
+ memcpy(buf+1, data, length);
+ if (buf[1] == 0x16)
+ length=23;
+ else
+ length++;
+
+ usleep(kWait); // wait 10ms // Done to make sure commands don't happen too fast.
+
+ for (i = 0; i<kTrial; i++)
+ {
+ ret = IOBluetoothL2CAPChannelWriteSync(wiiremote->cchan, buf, length);
+ if (ret == kIOReturnSuccess)
+ break;
+ usleep(kWait);
+ }
+
+ if (ret != kIOReturnSuccess)
+ wiiremote_disconnect(wiiremote);
+
+ return (ret==kIOReturnSuccess);
+}
+
+Boolean writeData(WiiRemoteRef wiiremote, const unsigned char *data, unsigned long address, unsigned short length)
+{
+ unsigned char cmd[22];
+ int i;
+ unsigned long addr = address;
+
+
+ for(i=0 ; i<length ; i++)
+ cmd[i+6] = data[i];
+
+ for(;i<16 ; i++)
+ cmd[i+6]= 0;
+
+ cmd[0] = 0x16;
+ cmd[1] = (addr>>24) & 0xFF;
+ cmd[2] = (addr>>16) & 0xFF;
+ cmd[3] = (addr>> 8) & 0xFF;
+ cmd[4] = (addr>> 0) & 0xFF;
+ cmd[5] = length;
+
+ // and of course the vibration flag, as usual
+ if (wiiremote->isVibrationEnabled) cmd[1] |= 0x01;
+
+ data = cmd;
+
+ return sendCommand(wiiremote, cmd, 22);
+}
+
+Boolean readData(WiiRemoteRef wiiremote, unsigned long address, unsigned short length)
+{
+
+ unsigned char cmd[7];
+ unsigned long addr = address;
+ unsigned short len = length;
+
+ cmd[0] = 0x17;
+ cmd[1] = (addr>>24)&0xFF;
+ cmd[2] = (addr>>16)&0xFF;
+ cmd[3] = (addr>> 8)&0xFF;
+ cmd[4] = (addr>> 0)&0xFF;
+
+ cmd[5] = (len >> 8)&0xFF;
+ cmd[6] = (len >> 0)&0xFF;
+
+ if (wiiremote->isVibrationEnabled) cmd[1] |= 0x01;
+ if (cmd[1] & 0x02) wiiremote->readingRegister = true;
+
+ return sendCommand(wiiremote, cmd, 7);
+}
+
+//--------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------
+
+void checkDevice(WiiRemoteRef wiiremote, IOBluetoothDeviceRef device)
+{
+ DEBUG(post("checkDevice"););
+ CFStringRef name;
+ CFStringRef address;
+
+ if (wiiremote_isconnected(wiiremote))
+ return;
+
+ name = IOBluetoothDeviceGetName(device);
+ address = IOBluetoothDeviceGetAddressString(device);
+ if (name != nil && address != nil)
+ {
+ if (CFStringCompare(name, CFSTR("Nintendo RVL-CNT-01"), 0) == kCFCompareEqualTo)
+ {
+ if ( CFStringGetLength(wiiremote->address) == 0
+ || CFStringCompare(address, wiiremote->address, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ {
+ wiiremote->device = IOBluetoothObjectRetain(device);
+ if ( wiiremote_connect(wiiremote) == false )
+ wiiremote_disconnect(wiiremote);
+ }
+ }
+ }
+}
+
+void myFoundFunc(void *refCon, IOBluetoothDeviceInquiryRef inquiry, IOBluetoothDeviceRef device)
+{
+ DEBUG(post("myFoundFunc"););
+ checkDevice((WiiRemoteRef)refCon, device);
+}
+
+void myUpdatedFunc(void *refCon, IOBluetoothDeviceInquiryRef inquiry, IOBluetoothDeviceRef device, uint32_t devicesRemaining)
+{
+ DEBUG(post("myUpdatedFunc"););
+
+ checkDevice((WiiRemoteRef)refCon, device);
+}
+
+void myCompleteFunc(void *refCon, IOBluetoothDeviceInquiryRef inquiry, IOReturn error, Boolean aborted)
+{
+ IOReturn ret;
+ DEBUG(post("myCompleteFunc"););
+
+ if (aborted) return; // called by stop ;)
+
+ if (error != kIOReturnSuccess)
+ {
+ wiiremote_stopsearch((WiiRemoteRef)refCon);
+ return;
+ }
+/*
+ ret = IOBluetoothDeviceInquiryStart(((WiiRemoteRef)refCon)->inquiry);
+ if (ret != kIOReturnSuccess)
+ {
+ wiiremote_stopsearch((WiiRemoteRef)refCon);
+ }
+*/
+#ifdef PD
+ // PD doesn't use a CFRunLoop, so we have to manually control it
+ CFRunLoopStop( CFRunLoopGetCurrent() );
+#endif
+}
+
+//--------------------------------------------------------------------------------------------
+
+Boolean wiiremote_isconnected(WiiRemoteRef wiiremote)
+{
+ Boolean result;
+
+ result = wiiremote->device != nil && IOBluetoothDeviceIsConnected(wiiremote->device);
+ return result;
+}
+
+Boolean wiiremote_search(WiiRemoteRef wiiremote, char *address)
+{
+ DEBUG(post("wiiremote_search"););
+ IOReturn ret;
+
+ if (wiiremote->inquiry != nil)
+ return true;
+
+ wiiremote->inquiry = IOBluetoothDeviceInquiryCreateWithCallbackRefCon((void *)wiiremote);
+ IOBluetoothDeviceInquirySetDeviceFoundCallback(wiiremote->inquiry, myFoundFunc);
+ IOBluetoothDeviceInquirySetDeviceNameUpdatedCallback(wiiremote->inquiry, myUpdatedFunc);
+ IOBluetoothDeviceInquirySetCompleteCallback(wiiremote->inquiry, myCompleteFunc);
+
+ if (wiiremote->address != nil)
+ CFRelease(wiiremote->address);
+ wiiremote->address = CFStringCreateWithCString(nil, address, kCFStringEncodingMacRoman);
+
+ ret = IOBluetoothDeviceInquiryStart(wiiremote->inquiry);
+ if (ret != kIOReturnSuccess)
+ {
+ IOBluetoothDeviceInquiryDelete(wiiremote->inquiry);
+ wiiremote->inquiry = nil;
+ return false;
+ }
+#ifdef PD
+ // PD doesn't use a CFRunLoop, so we have to manually control it
+ CFRunLoopRun();
+#endif
+ return true;
+}
+
+Boolean wiiremote_stopsearch(WiiRemoteRef wiiremote)
+{
+ IOReturn ret;
+
+ if (wiiremote->inquiry == nil)
+ {
+ return true; // already stopped
+ }
+
+ ret = IOBluetoothDeviceInquiryStop(wiiremote->inquiry);
+
+ if (ret != kIOReturnSuccess && ret != kIOReturnNotPermitted)
+ {
+ // kIOReturnNotPermitted is if it's already stopped
+ }
+
+ IOBluetoothDeviceInquiryDelete(wiiremote->inquiry);
+ wiiremote->inquiry = nil;
+
+ return (ret==kIOReturnSuccess);
+}
+
+//--------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------
+
+unsigned char decrypt(unsigned char data)
+{
+ return (data ^ 0x17) + 0x17;
+}
+
+//--------------------------------------------------------------------------------------------
+
+/**
+* Handle report 0x21 (Read Data) from wiimote.
+ * dp[0] = Bluetooth header
+ * dp[1] = (0x21) Report/Channel ID
+ * dp[2] = Wiimote Buttons
+ * dp[3] = Wiimote Buttons
+ * dp[4] = High 4 bits = payload size; Low 4 bits = Error flag (0 = all good)
+ * dp[5] = Offset of memory read
+ * dp[6] = Offset of memory read
+ * dp[7+] = the Data.
+ **/
+
+void handleRAMData(WiiRemoteRef wiiremote, unsigned char *dp, size_t dataLength)
+{
+ // specify attached expasion device
+ if ((dp[5] == 0x00) && (dp[6] == 0xF0))
+ {
+ if (decrypt(dp[21]) == 0x00)
+ {
+ wiiremote->expType = WiiNunchuk;
+ }
+ else
+ if (decrypt(dp[21]) == 0x01)
+ {
+ wiiremote->expType = WiiClassicController;
+ }
+ else
+ {
+ wiiremote->expType = WiiExpNotAttached;
+ }
+ // initExpPort = NO;
+ return;
+ }
+
+ // wiimote calibration data
+ if (!wiiremote->readingRegister && dp[5] == 0x00 && dp[6] == 0x20)
+ {
+ wiiremote->wiiCalibData.accX_zero = dp[7];
+ wiiremote->wiiCalibData.accY_zero = dp[8];
+ wiiremote->wiiCalibData.accZ_zero = dp[9];
+
+ //dp[10] - unknown/unused
+
+ wiiremote->wiiCalibData.accX_1g = dp[11];
+ wiiremote->wiiCalibData.accY_1g = dp[12];
+ wiiremote->wiiCalibData.accZ_1g = dp[13];
+ return;
+ }
+
+ // expansion device calibration data.
+ if (wiiremote->readingRegister && dp[5] == 0x00 && dp[6] == 0x20)
+ {
+ if (wiiremote->expType == WiiNunchuk)
+ {
+ //nunchuk calibration data
+ wiiremote->nunchukCalibData.accX_zero = decrypt(dp[7]);
+ wiiremote->nunchukCalibData.accY_zero = decrypt(dp[8]);
+ wiiremote->nunchukCalibData.accZ_zero = decrypt(dp[9]);
+
+ wiiremote->nunchukCalibData.accX_1g = decrypt(dp[11]);
+ wiiremote->nunchukCalibData.accY_1g = decrypt(dp[12]);
+ wiiremote->nunchukCalibData.accZ_1g = decrypt(dp[13]);
+
+ wiiremote->nunchukJoyStickCalibData.x_max = decrypt(dp[15]);
+ wiiremote->nunchukJoyStickCalibData.x_min = decrypt(dp[16]);
+ wiiremote->nunchukJoyStickCalibData.x_center = decrypt(dp[17]);
+
+ wiiremote->nunchukJoyStickCalibData.y_max = decrypt(dp[18]);
+ wiiremote->nunchukJoyStickCalibData.y_min = decrypt(dp[19]);
+ wiiremote->nunchukJoyStickCalibData.y_center = decrypt(dp[20]);
+
+ return;
+ }
+ else
+ if (wiiremote->expType == WiiClassicController)
+ {
+ //classic controller calibration data (probably)
+ }
+ }
+
+ // wii remote buttons
+ wiiremote->buttonData = ((short)dp[2] << 8) + dp[3];
+}
+
+void handleStatusReport(WiiRemoteRef wiiremote, unsigned char *dp, size_t dataLength)
+{
+ wiiremote->batteryLevel = (double)dp[7];
+ wiiremote->batteryLevel /= (double)0xC0; // C0 = fully charged.
+
+ if ((dp[4] & 0x02)) //some device attached to Wiimote
+ {
+ wiiremote->isExpansionPortAttached = true;
+ // initExpPort = YES;
+
+ Boolean ret = writeData(wiiremote, (darr){0x00}, 0x04A40040, 1); // Initialize the device
+
+ if (ret == false)
+ {
+ wiiremote->isExpansionPortAttached = false;
+ return;
+ }
+
+ usleep(kWait); // Give the write a chance to be processed.
+
+ ret = readData(wiiremote, 0x04A400F0, 16); // read expansion device type
+ if (ret == false)
+ {
+ wiiremote->isExpansionPortAttached = false;
+ }
+ }
+ else
+ { // unplugged
+ wiiremote->isExpansionPortAttached = false;
+ wiiremote->expType = WiiExpNotAttached;
+ }
+
+ if (dp[4] & 0x10)
+ wiiremote->isLED1Illuminated = true;
+ else
+ wiiremote->isLED1Illuminated = false;
+
+ if (dp[4] & 0x20)
+ wiiremote->isLED2Illuminated = true;
+ else
+ wiiremote->isLED2Illuminated = false;
+
+ if (dp[4] & 0x40)
+ wiiremote->isLED3Illuminated = true;
+ else
+ wiiremote->isLED3Illuminated = false;
+
+ if (dp[4] & 0x80)
+ wiiremote->isLED4Illuminated = true;
+ else
+ wiiremote->isLED4Illuminated = false;
+}
+
+void handleExtensionData(WiiRemoteRef wiiremote, unsigned char *dp, size_t dataLength)
+{
+ unsigned char startByte;
+
+ switch (dp[1]) {
+ case 0x34 :
+ startByte = 4;
+ break;
+ case 0x35 :
+ startByte = 7;
+ break;
+ case 0x36 :
+ startByte = 14;
+ break;
+ case 0x37 :
+ startByte = 17;
+ break;
+ default:
+ return; // This shouldn't ever happen.
+ break;
+ }
+
+ if (wiiremote->expType == WiiNunchuk)
+ {
+ wiiremote->nStickX = decrypt(dp[startByte]);
+ wiiremote->nStickY = decrypt(dp[startByte +1]);
+ wiiremote->nAccX = decrypt(dp[startByte +2]);
+ wiiremote->nAccY = decrypt(dp[startByte +3]);
+ wiiremote->nAccZ = decrypt(dp[startByte +4]);
+ wiiremote->nButtonData = decrypt(dp[startByte +5]);
+
+ wiiremote->nLowZ = wiiremote->nLowZ * .9 + wiiremote->nAccZ * .1;
+ wiiremote->nLowX = wiiremote->nLowX * .9 + wiiremote->nAccX * .1;
+
+ float absx = abs(wiiremote->nLowX - 128);
+ float absz = abs(wiiremote->nLowZ - 128);
+
+ if (wiiremote->nOrientation == 0 || wiiremote->nOrientation == 2) absx -= 5;
+ if (wiiremote->nOrientation == 1 || wiiremote->nOrientation == 3) absz -= 5;
+
+ if (absz >= absx)
+ {
+ if (absz > 5)
+ wiiremote->nOrientation = (wiiremote->nLowZ > 128) ? 0 : 2;
+ }
+ else
+ {
+ if (absx > 5)
+ wiiremote->nOrientation = (wiiremote->nLowX > 128) ? 3 : 1;
+ }
+ }
+ else
+ if (wiiremote->expType == WiiClassicController)
+ {
+ wiiremote->cButtonData = (unsigned short)(decrypt(dp[startByte + 4]) << 8) + decrypt(dp[startByte + 5]);
+ wiiremote->cButtonData = ~wiiremote->cButtonData; // bit reverse
+
+ wiiremote->cStickX1 = decrypt(dp[startByte]) & 0x3F;
+ wiiremote->cStickY1 = decrypt(dp[startByte + 1]) & 0x3F;
+
+ wiiremote->cStickX2 = (((decrypt(dp[startByte +0]) & 0xC0) >> 3) |
+ ((decrypt(dp[startByte +1]) & 0xC0) >> 5) |
+ ((decrypt(dp[startByte +2]) & 0x80) >> 7)) & 0x1F;
+ wiiremote->cStickY2 = decrypt(dp[startByte + 2]) & 0x1F;
+
+ wiiremote->cAnalogL = (((decrypt(dp[startByte +2]) & 0x60) >> 2) |
+ ((decrypt(dp[startByte +3]) & 0xE0) >> 5)) & 0x1F;
+ wiiremote->cAnalogR = decrypt(dp[startByte + 3]) & 0x1F;
+ }
+}
+
+void handleIRData(WiiRemoteRef wiiremote, unsigned char *dp, size_t dataLength)
+{
+ int i;
+
+ if (dp[1] == 0x33)
+ { // 12 IR bytes
+ int startByte = 0;
+ for(i=0 ; i < 4 ; i++)
+ {
+ startByte = 7 + 3 * i;
+ wiiremote->irData[i].x = (dp[startByte +0] | ((dp[startByte +2] & 0x30) << 4)) & 0x3FF;
+ wiiremote->irData[i].y = (dp[startByte +1] | ((dp[startByte +2] & 0xC0) << 2)) & 0x3FF;
+ wiiremote->irData[i].s = dp[startByte +2] & 0x0F;
+ }
+ }
+ else
+ { // 10 IR bytes
+ int shift = (dp[1] == 0x36) ? 4 : 7;
+ int startByte = 0;
+ for (i=0; i < 2; i++) {
+ startByte = shift + 5 * i;
+ wiiremote->irData[2*i].x = (dp[startByte +0] | ((dp[startByte +2] & 0x30) << 4)) & 0x3FF;
+ wiiremote->irData[2*i].y = (dp[startByte +1] | ((dp[startByte +2] & 0xC0) << 2)) & 0x3FF;
+ wiiremote->irData[2*i].s = ((wiiremote->irData[2*i].x == wiiremote->irData[2*i].y) && (wiiremote->irData[2*i].x == 0x3FF)) ? 0x0F : 0x05; // No size is given in 10 byte report.
+
+ wiiremote->irData[(2*i)+1].x = (dp[startByte +3] | ((dp[startByte +2] & 0x03) << 8)) & 0x3FF;
+ wiiremote->irData[(2*i)+1].y = (dp[startByte +4] | ((dp[startByte +2] & 0x0C) << 6)) & 0x3FF;
+ wiiremote->irData[(2*i)+1].s = ((wiiremote->irData[(2*i)+1].x == wiiremote->irData[(2*i)+1].y) && (wiiremote->irData[(2*i)+1].x == 0x3FF)) ? 0x0F : 0x05; // No size is given in 10 byte report.
+ }
+ }
+
+ int p1 = -1;
+ int p2 = -1;
+ // we should modify this loop to take the points with the lowest s (the brightest ones)
+ for (i=0 ; i<4 ; i++) {
+ if (p1 == -1) {
+ if (wiiremote->irData [i].s < 0x0F)
+ p1 = i;
+ } else {
+ if (wiiremote->irData [i].s < 0x0F) {
+ p2 = i;
+ break;
+ }
+ }
+ }
+
+ double ox, oy;
+ if ((p1 > -1) && (p2 > -1))
+ {
+ int l = wiiremote->leftPoint;
+ if (wiiremote->leftPoint == -1)
+ {
+ switch (wiiremote->orientation)
+ {
+ case 0: l = (wiiremote->irData[p1].x < wiiremote->irData[p2].x) ? 0 : 1; break;
+ case 1: l = (wiiremote->irData[p1].y > wiiremote->irData[p2].y) ? 0 : 1; break;
+ case 2: l = (wiiremote->irData[p1].x > wiiremote->irData[p2].x) ? 0 : 1; break;
+ case 3: l = (wiiremote->irData[p1].y < wiiremote->irData[p2].y) ? 0 : 1; break;
+ }
+
+ wiiremote->leftPoint = l;
+ }
+
+ int r = 1-l;
+
+ double dx = wiiremote->irData[r].x - wiiremote->irData[l].x;
+ double dy = wiiremote->irData[r].y - wiiremote->irData[l].y;
+ double d = hypot (dx, dy);
+
+ dx /= d;
+ dy /= d;
+
+ double cx = (wiiremote->irData[l].x + wiiremote->irData[r].x)/kWiiIRPixelsWidth - 1;
+ double cy = (wiiremote->irData[l].y + wiiremote->irData[r].y)/kWiiIRPixelsHeight - 1;
+
+ ox = -dy*cy-dx*cx;
+ oy = -dx*cy+dy*cx;
+
+ // cam:
+ // Compensate for distance. There must be fewer than 0.75*768 pixels between the spots for this to work.
+ // In other words, you have to be far enough away from the sensor bar for the two spots to have enough
+ // space on the image sensor to travel without one of the points going off the image.
+ // note: it is working very well ...
+ double gain = 4;
+ if (d < (0.75 * kWiiIRPixelsHeight))
+ gain = 1 / (1 - d/kWiiIRPixelsHeight);
+
+ ox *= gain;
+ oy *= gain;
+
+ wiiremote->angle = atan2(dy, dx);
+ wiiremote->tracking = true;
+ }
+ else
+ {
+ ox = oy = -100;
+ wiiremote->leftPoint = -1; // not tracking
+ wiiremote->angle = -100;
+ wiiremote->tracking = false;
+ }
+
+ wiiremote->posX = ox;
+ wiiremote->posY = oy;
+}
+
+void handleButtonReport(WiiRemoteRef wiiremote, unsigned char *dp, size_t dataLength)
+{
+ // wiimote buttons
+ wiiremote->buttonData = ((short)dp[2] << 8) + dp[3];
+
+ // report contains extension data
+ switch (dp[1])
+ {
+ case 0x34 :
+ case 0x35 :
+ case 0x36 :
+ case 0x37 :
+ handleExtensionData(wiiremote, dp, dataLength);
+ break;
+ }
+
+ // report contains IR data
+ if (dp[1] & 0x02)
+ {
+ handleIRData(wiiremote, dp, dataLength);
+ }
+
+ // report contains motion sensor data
+ if (dp[1] & 0x01)
+ {
+ wiiremote->accX = dp[4];
+ wiiremote->accY = dp[5];
+ wiiremote->accZ = dp[6];
+
+ wiiremote->lowZ = wiiremote->lowZ * 0.9 + wiiremote->accZ * 0.1;
+ wiiremote->lowX = wiiremote->lowX * 0.9 + wiiremote->accX * 0.1;
+
+ float absx = abs(wiiremote->lowX-128);
+ float absz = abs(wiiremote->lowZ-128);
+
+ if (wiiremote->orientation == 0 || wiiremote->orientation == 2) absx -= 5;
+ if (wiiremote->orientation == 1 || wiiremote->orientation == 3) absz -= 5;
+
+ if (absz >= absx)
+ {
+ if (absz > 5)
+ wiiremote->orientation = (wiiremote->lowZ > 128)?0:2;
+ }
+ else
+ {
+ if (absx > 5)
+ wiiremote->orientation = (wiiremote->lowX > 128)?3:1;
+ }
+ }
+}
+
+//--------------------------------------------------------------------------------------------
+
+ void myDataListener(IOBluetoothL2CAPChannelRef channel, void *dataPointer, UInt16 dataLength, void *refCon)
+{
+ DEBUG(post("myDataListener"););
+ WiiRemoteRef wiiremote = (WiiRemoteRef)refCon;
+ unsigned char* dp = (unsigned char*)dataPointer;
+
+ if (!wiiremote->device)
+ return;
+
+ //controller status (expansion port and battery level data) - received when report 0x15 sent to Wiimote (getCurrentStatus:) or status of expansion port changes.
+ if (dp[1] == 0x20 && dataLength >= 8)
+ {
+ handleStatusReport(wiiremote, dp, dataLength);
+ requestUpdates(wiiremote); // Make sure we keep getting state change reports.
+ return;
+ }
+
+ if (dp[1] == 0x21)
+ {
+ handleRAMData(wiiremote, dp, dataLength);
+ return;
+ }
+
+ if (dp[1] == 0x22)
+ { // Write data response
+ //NSLog(@"Write data response: %00x %00x %00x %00x", dp[2], dp[3], dp[4], dp[5]);
+ return;
+ }
+
+ // report contains button info
+ if ((dp[1] & 0xF0) == 0x30)
+ {
+ handleButtonReport(wiiremote, dp, dataLength);
+ }
+}
+
+void myEventListener(IOBluetoothL2CAPChannelRef channel, void *refCon, IOBluetoothL2CAPChannelEvent *event)
+{
+ DEBUG(post("myEventListener"););
+ if (event->eventType == kIOBluetoothL2CAPChannelEventTypeData)
+ {
+ // In thise case:
+ // event->u.newData.dataPtr is a pointer to the block of data received.
+ // event->u.newData.dataSize is the size of the block of data.
+ myDataListener(channel, event->u.data.dataPtr, event->u.data.dataSize, refCon);
+ }
+ else
+ if (event->eventType == kIOBluetoothL2CAPChannelEventTypeClosed)
+ {
+ // In this case:
+ // event->u.terminatedChannel is the channel that was terminated. It can be converted in an IOBluetoothL2CAPChannel
+ // object with [IOBluetoothL2CAPChannel withL2CAPChannelRef:]. (see below).
+ }
+}
+
+void myDisconnectedFunc(void * refCon, IOBluetoothUserNotificationRef inRef, IOBluetoothObjectRef objectRef)
+{
+ CFStringRef itsAddress, myAddress;
+
+ itsAddress = IOBluetoothDeviceGetAddressString(objectRef);
+ if (itsAddress != nil)
+ {
+ myAddress = IOBluetoothDeviceGetAddressString(((WiiRemoteRef)refCon)->device);
+ if (myAddress != nil)
+ {
+ if (CFStringCompare(itsAddress, myAddress, 0) == kCFCompareEqualTo)
+ {
+ wiiremote_disconnect((WiiRemoteRef)refCon);
+ }
+ CFRelease(myAddress);
+ }
+ CFRelease(itsAddress);
+ }
+}
+
+//--------------------------------------------------------------------------------------------
+
+void wiiremote_getaddress(WiiRemoteRef wiiremote, char *address)
+{
+ CFStringRef cfstring;
+
+ cfstring = IOBluetoothDeviceGetAddressString(wiiremote->device);
+ CFStringGetCString(cfstring, address, 32, kCFStringEncodingMacRoman);
+ CFRelease(cfstring);
+
+}
+
+//--------------------------------------------------------------------------------------------
+
+Boolean wiiremote_connect(WiiRemoteRef wiiremote)
+{
+ IOReturn ret;
+ Boolean result;
+ short i;
+
+ if (wiiremote->device == nil)
+ return false;
+
+ // connect the device
+ for (i=0; i<kTrial; i++)
+ {
+ ret = IOBluetoothDeviceOpenConnection(wiiremote->device, nil, nil);
+ if ( ret == kIOReturnSuccess)
+ break;
+ usleep(kWait); // wait 10ms
+ }
+ if (i==kTrial)
+ return false;
+
+ wiiremote->disconnectNotification = IOBluetoothDeviceRegisterForDisconnectNotification(wiiremote->device, myDisconnectedFunc, (void *)wiiremote);
+
+ // performs an SDP query
+ for (i=0; i<kTrial; i++)
+ {
+ ret = IOBluetoothDevicePerformSDPQuery(wiiremote->device, nil, nil);
+ if ( ret == kIOReturnSuccess)
+ break;
+ usleep(kWait); // wait 10ms
+ }
+ if (i==kTrial)
+ return false;
+
+ result = openCChan(wiiremote);
+ result = openIChan(wiiremote);
+
+ if (result)
+ {
+ result = wiiremote_led(wiiremote, wiiremote->isLED1Illuminated, wiiremote->isLED2Illuminated, wiiremote->isLED3Illuminated, wiiremote->isLED4Illuminated);
+ }
+
+ if (result == false)
+ {
+ wiiremote_disconnect(wiiremote);
+ return result;
+ }
+
+ wiiremote_getstatus(wiiremote);
+ requestUpdates(wiiremote);
+
+ readData(wiiremote, 0x0020, 7); // Get Accelerometer callibration data
+
+ return true;
+}
+
+
+Boolean wiiremote_disconnect(WiiRemoteRef wiiremote)
+{
+ short i;
+
+ if (wiiremote->cchan)
+ {
+ if (IOBluetoothDeviceIsConnected(wiiremote->device))
+ {
+ for (i=0; i<kTrial; i++)
+ {
+ if (IOBluetoothL2CAPChannelCloseChannel(wiiremote->cchan) == kIOReturnSuccess)
+ break;
+ usleep(kWait); // wait 10ms
+ }
+ }
+ if (i==kTrial) return false;
+ IOBluetoothObjectRelease(wiiremote->cchan);
+ wiiremote->cchan = nil;
+ }
+
+ if (wiiremote->ichan)
+ {
+ if (IOBluetoothDeviceIsConnected(wiiremote->device))
+ {
+ for (i=0; i<kTrial; i++)
+ {
+ if (IOBluetoothL2CAPChannelCloseChannel(wiiremote->ichan) == kIOReturnSuccess)
+ break;
+ }
+ }
+ if (i==kTrial) return false;
+ IOBluetoothObjectRelease(wiiremote->ichan);
+ wiiremote->ichan = nil;
+ }
+
+ if (wiiremote->device)
+ {
+ if (IOBluetoothDeviceIsConnected(wiiremote->device))
+ {
+ for (i=0; i<kTrial; i++)
+ {
+ if (IOBluetoothDeviceCloseConnection(wiiremote->device) == kIOReturnSuccess)
+ break;
+ }
+ }
+ if (i==kTrial) return false;
+ IOBluetoothObjectRelease(wiiremote->device);
+ wiiremote->device = nil;
+ }
+
+ if (wiiremote->disconnectNotification != nil)
+ {
+ IOBluetoothUserNotificationUnregister(wiiremote->disconnectNotification);
+ wiiremote->disconnectNotification = nil;
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------
+
+Boolean requestUpdates(WiiRemoteRef wiiremote)
+{
+ Boolean result;
+
+ // Set the report type the Wiimote should send.
+ unsigned char cmd[] = {0x12, 0x02, 0x30}; // Just buttons.
+
+ if (wiiremote->isVibrationEnabled) cmd[1] |= 0x01;
+
+ /*
+ There are numerous status report types that can be requested.
+ The IR reports must be matched with the data format set when initializing the IR camera:
+ 0x36, 0x37 - 10 IR bytes go with Basic mode
+ 0x33 - 12 IR bytes go with Extended mode
+ 0x3e/0x3f - 36 IR bytes go with Full mode
+
+ The Nunchuk and Classic controller use 6 bytes to report their state, so the reports that
+ give more extension bytes don't provide any more info.
+
+ Buttons | Accelerometer | IR | Extension
+ --------------------+-------------------+-----------+-------------
+ 0x30: Core Buttons | | |
+ 0x31: Core Buttons | Accelerometer | |
+ 0x32: Core Buttons | | | 8 bytes
+ 0x33: Core Buttons | Accelerometer | 12 bytes |
+ 0x34: Core Buttons | | | 19 bytes
+ 0x35: Core Buttons | Accelerometer | | 16 bytes
+ 0x36: Core Buttons | | 10 bytes | 9 bytes
+ 0x37: Core Buttons | Accelerometer | 10 bytes | 6 bytes
+ ?? 0x38: Core Buttons and Accelerometer with 16 IR bytes ??
+ 0x3d: | | | 21 bytes
+
+ 0x3e / 0x3f: Interleaved Core Buttons and Accelerometer with 16/36 IR bytes
+
+ */
+
+ if (wiiremote->isIRSensorEnabled)
+ {
+ if (wiiremote->isExpansionPortEnabled)
+ {
+ cmd[2] = 0x36; // Buttons, 10 IR Bytes, 9 Extension Bytes
+ wiiremote->wiiIRMode = kWiiIRModeBasic;
+ }
+ else
+ {
+ cmd[2] = 0x33; // Buttons, Accelerometer, and 12 IR Bytes.
+ wiiremote->wiiIRMode = kWiiIRModeExtended;
+ }
+
+ // Set IR Mode
+ writeData(wiiremote, (darr){ wiiremote->wiiIRMode }, 0x04B00033, 1);
+ usleep(kWait); // wait 10ms
+ }
+ else
+ {
+ if (wiiremote->isExpansionPortEnabled)
+ {
+ cmd[2] = 0x34; // Buttons, 19 Extension Bytes
+ }
+ else
+ {
+ cmd[2] = 0x30; // Buttons
+ }
+ }
+
+ if (wiiremote->isMotionSensorEnabled) cmd[2] |= 0x01; // Add Accelerometer
+
+ usleep(kWait); // wait 10ms
+ result = sendCommand(wiiremote, cmd, 3);
+
+ return(result);
+}
+
+//--------------------------------------------------------------------------------------------
+
+Boolean wiiremote_motionsensor(WiiRemoteRef wiiremote, Boolean enabled)
+{
+ wiiremote->isMotionSensorEnabled = enabled;
+ return requestUpdates(wiiremote);
+}
+
+Boolean wiiremote_vibration(WiiRemoteRef wiiremote, Boolean enabled)
+{
+
+ wiiremote->isVibrationEnabled = enabled;
+ return requestUpdates(wiiremote);
+}
+
+Boolean wiiremote_led(WiiRemoteRef wiiremote, Boolean enabled1, Boolean enabled2, Boolean enabled3, Boolean enabled4)
+{
+ unsigned char cmd[] = {0x11, 0x00};
+ if (wiiremote->isVibrationEnabled) cmd[1] |= 0x01;
+ if (enabled1) cmd[1] |= 0x10;
+ if (enabled2) cmd[1] |= 0x20;
+ if (enabled3) cmd[1] |= 0x40;
+ if (enabled4) cmd[1] |= 0x80;
+
+ wiiremote->isLED1Illuminated = enabled1;
+ wiiremote->isLED2Illuminated = enabled2;
+ wiiremote->isLED3Illuminated = enabled3;
+ wiiremote->isLED4Illuminated = enabled4;
+
+ return sendCommand(wiiremote, cmd, 2);
+}
+
+Boolean wiiremote_expansion(WiiRemoteRef wiiremote, Boolean enabled)
+{
+ wiiremote->isExpansionPortEnabled = enabled;
+ if (wiiremote->isExpansionPortAttached == false)
+ {
+ wiiremote->isExpansionPortEnabled = false;
+ }
+ else
+ {
+ readData(wiiremote, 0x04A40020, 16); //get calbdata
+ }
+
+ return requestUpdates(wiiremote);
+}
+
+Boolean wiiremote_irsensor(WiiRemoteRef wiiremote, Boolean enabled)
+{
+ Boolean ret;
+
+ wiiremote->isIRSensorEnabled = enabled;
+
+ // ir enable 1
+ unsigned char cmd[] = {0x13, 0x00};
+ if (wiiremote->isVibrationEnabled) cmd[1] |= 0x01;
+ if (wiiremote->isIRSensorEnabled) cmd[1] |= 0x04;
+ if ((ret = sendCommand(wiiremote, cmd, 2)) == false)
+ return ret;
+ usleep(kWait);
+
+ // set register 0x1a (ir enable 2)
+ unsigned char cmd2[] = {0x1a, 0x00};
+ if (enabled) cmd2[1] |= 0x04;
+ if ((ret = sendCommand(wiiremote, cmd2, 2)) == false)
+ return ret;
+ usleep(kWait);
+
+ if(enabled)
+ {
+ // based on marcan's method, found on wiili wiki:
+ // tweaked to include some aspects of cliff's setup procedure in the hopes
+ // of it actually turning on 100% of the time (was seeing 30-40% failure rate before)
+ // the sleeps help it it seems
+ usleep(kWait);
+ if ((ret = writeData(wiiremote, (darr){0x01}, 0x04B00030, 1)) == false) return ret;
+ usleep(kWait);
+ if ((ret = writeData(wiiremote, (darr){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0xC0}, 0x04B00000, 9)) == false) return ret;
+ usleep(kWait);
+ if ((ret = writeData(wiiremote, (darr){0x40, 0x00}, 0x04B0001A, 2)) == false) return ret;
+ usleep(kWait);
+ if ((ret = writeData(wiiremote, (darr){0x08}, 0x04B00030, 1)) == false) return ret;
+ usleep(kWait);
+
+ requestUpdates(wiiremote);
+ }
+ else
+ {
+ // probably should do some writes to power down the camera, save battery
+ // but don't know how yet.
+
+ ret = wiiremote_motionsensor(wiiremote, wiiremote->isMotionSensorEnabled);
+ ret = wiiremote_vibration(wiiremote, wiiremote->isVibrationEnabled);
+ ret = wiiremote_expansion(wiiremote, wiiremote->isExpansionPortEnabled);
+ }
+
+ return ret;
+}
+
+Boolean wiiremote_getstatus(WiiRemoteRef wiiremote)
+{
+ unsigned char cmd[] = {0x15, 0x00};
+ return sendCommand(wiiremote, cmd, 2);
+}
+
+
diff --git a/aka.wiiremote/wiiremote.h b/aka.wiiremote/wiiremote.h
new file mode 100644
index 0000000..560e401
--- /dev/null
+++ b/aka.wiiremote/wiiremote.h
@@ -0,0 +1,177 @@
+// wiiremote.h
+// Copyright by Masayuki Akamatsu
+// Based on "DarwiinRemote" by Hiroaki Kimura
+
+#define BLUETOOTH_VERSION_USE_CURRENT // gets rid of deprecated warnings
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOBluetooth/Bluetooth.h>
+#include <IOBluetooth/IOBluetoothUserLib.h>
+#include <stdio.h>
+#include <string.h>
+
+// Macros for PD for compability with Max macros
+#ifdef PD
+#define SETSYM SETSYMBOL
+#define SETLONG SETFLOAT
+#endif
+
+typedef unsigned char WiiIRModeType;
+enum {
+ kWiiIRModeBasic = 0x01,
+ kWiiIRModeExtended = 0x03,
+ kWiiIRModeFull = 0x05
+};
+
+typedef struct {
+ int x, y, s;
+} IRData;
+
+typedef struct {
+ unsigned char accX_zero, accY_zero, accZ_zero, accX_1g, accY_1g, accZ_1g;
+} WiiAccCalibData;
+
+typedef struct {
+ unsigned char x_min, x_max, x_center, y_min, y_max, y_center;
+} WiiJoyStickCalibData;
+
+typedef UInt16 WiiButtonType;
+enum {
+ WiiRemoteAButton,
+ WiiRemoteBButton,
+ WiiRemoteOneButton,
+ WiiRemoteTwoButton,
+ WiiRemoteMinusButton,
+ WiiRemoteHomeButton,
+ WiiRemotePlusButton,
+ WiiRemoteUpButton,
+ WiiRemoteDownButton,
+ WiiRemoteLeftButton,
+ WiiRemoteRightButton,
+
+ WiiNunchukZButton,
+ WiiNunchukCButton,
+
+ WiiClassicControllerXButton,
+ WiiClassicControllerYButton,
+ WiiClassicControllerAButton,
+ WiiClassicControllerBButton,
+ WiiClassicControllerLButton,
+ WiiClassicControllerRButton,
+ WiiClassicControllerZLButton,
+ WiiClassicControllerZRButton,
+ WiiClassicControllerUpButton,
+ WiiClassicControllerDownButton,
+ WiiClassicControllerLeftButton,
+ WiiClassicControllerRightButton,
+ WiiClassicControllerMinusButton,
+ WiiClassicControllerHomeButton,
+ WiiClassicControllerPlusButton
+};
+
+typedef UInt16 WiiExpansionPortType;
+enum{
+ WiiExpNotAttached,
+ WiiNunchuk,
+ WiiClassicController
+};
+
+typedef UInt16 WiiAccelerationSensorType;
+enum{
+ WiiRemoteAccelerationSensor,
+ WiiNunchukAccelerationSensor
+};
+
+
+typedef UInt16 WiiJoyStickType;
+enum{
+ WiiNunchukJoyStick,
+ WiiClassicControllerLeftJoyStick,
+ WiiClassicControllerRightJoyStick
+};
+
+
+typedef struct _WiiRemoteRec
+{
+ IOBluetoothDeviceInquiryRef inquiry;
+ IOBluetoothDeviceRef device;
+ IOBluetoothL2CAPChannelRef ichan;
+ IOBluetoothL2CAPChannelRef cchan;
+
+ CFStringRef address;
+
+ unsigned char accX;
+ unsigned char accY;
+ unsigned char accZ;
+ unsigned short buttonData;
+
+ float lowZ;
+ float lowX;
+ int orientation;
+ int leftPoint; // is point 0 or 1 on the left. -1 when not tracking.
+
+ float posX;
+ float posY;
+ float angle;
+ Boolean tracking;
+
+ WiiExpansionPortType expType;
+ WiiAccCalibData wiiCalibData, nunchukCalibData;
+ WiiJoyStickCalibData nunchukJoyStickCalibData;
+ WiiIRModeType wiiIRMode;
+ IRData irData[4];
+ double batteryLevel;
+
+ Boolean readingRegister;
+ Boolean isMotionSensorEnabled;
+ Boolean isIRSensorEnabled;
+ Boolean isVibrationEnabled;
+ Boolean isExpansionPortEnabled;
+ Boolean initExpPort;
+ Boolean isLED1Illuminated;
+ Boolean isLED2Illuminated;
+ Boolean isLED3Illuminated;
+ Boolean isLED4Illuminated;
+ Boolean isExtraOutputEnabled;
+
+ Boolean isExpansionPortAttached;
+
+ IOBluetoothUserNotificationRef disconnectNotification;
+
+ //nunchuk
+ unsigned char nStickX;
+ unsigned char nStickY;
+ unsigned char nAccX;
+ unsigned char nAccY;
+ unsigned char nAccZ;
+ unsigned char nButtonData;
+
+ float nLowZ;
+ float nLowX;
+ int nOrientation;
+
+ //classic controller
+ unsigned short cButtonData;
+ unsigned char cStickX1;
+ unsigned char cStickY1;
+ unsigned char cStickX2;
+ unsigned char cStickY2;
+ unsigned char cAnalogL;
+ unsigned char cAnalogR;
+
+} WiiRemoteRec, *WiiRemoteRef;
+
+void wiiremote_init(WiiRemoteRef wiiremote);
+Boolean wiiremote_isconnected(WiiRemoteRef wiiremote);
+Boolean wiiremote_search(WiiRemoteRef wiiremote, char *address);
+Boolean wiiremote_stopsearch(WiiRemoteRef wiiremote);
+Boolean wiiremote_connect(WiiRemoteRef wiiremote);
+Boolean wiiremote_disconnect(WiiRemoteRef wiiremote);
+void wiiremote_getaddress(WiiRemoteRef wiiremote, char *address);
+Boolean wiiremote_motionsensor(WiiRemoteRef wiiremote, Boolean enabled);
+Boolean wiiremote_irsensor(WiiRemoteRef wiiremote, Boolean enabled);
+Boolean wiiremote_vibration(WiiRemoteRef wiiremote, Boolean enabled);
+Boolean wiiremote_led(WiiRemoteRef wiiremote, Boolean enabled1, Boolean enabled2, Boolean enabled3, Boolean enabled4);
+Boolean wiiremote_expansion(WiiRemoteRef wiiremote, Boolean enabled);
+Boolean wiiremote_getstatus(WiiRemoteRef wiiremote);
+
diff --git a/dmx512/FAQ.txt b/dmx512/FAQ.txt
new file mode 100644
index 0000000..484dc8a
--- /dev/null
+++ b/dmx512/FAQ.txt
@@ -0,0 +1,2 @@
+Q: Where do I find answers to questions not answered here?
+A: try the "Pure Data" community mailinglist http://lists.puredata.info
diff --git a/dmx512/GnuGPL.txt b/dmx512/GnuGPL.txt
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/dmx512/GnuGPL.txt
@@ -0,0 +1,340 @@
+ 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.
+
+ GNU GENERAL PUBLIC LICENSE
+ 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
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/dmx512/README.dmx4linux.txt b/dmx512/README.dmx4linux.txt
new file mode 100644
index 0000000..94c5bfc
--- /dev/null
+++ b/dmx512/README.dmx4linux.txt
@@ -0,0 +1,79 @@
+DMX512
+======
+
+THIS IS NOT A DOCUMENTATION.
+NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED!
+
+these are just random notes on what i found useful in getting dmx4linux running.
+it might freeze your computer or boil your lights.
+
+
+A. setting up dmx4linux
+=======================
+note: there are debian(etch)-packages for dmx4linux,
+ but these seem to be pretty old (2.5)
+ i haven't really tried these.
+ instead i used dmx4linux-2.6.1
+
+first get dmx4linux from http://llg.cubic.org/dmx4linux/
+and extract it.
+
+the drivers should compile fine with 2.6.18 kernels, but
+alas! i am using 2.6.25 and there are some quirks to make
+these work.
+
+first of all i had problems compiling the ISA/PCI/parport drivers,
+but since i only wanted to use a USB device, i just disabled those.
+second, dmx4linux's build-system tries to override CFLAGS when building
+the kernel-modules, which newer kernel versions (e.g. 2.6.25) do not like
+at all. i had to modify the makefiles in order to use the EXTRA_CFLAGS
+
+all the changes i did can be found in the dmx4linux2.6.1.patch
+just run:
+% patch -p1 < dmx4linux2.6.1.patch
+
+then do
+% ./configure
+(which will produce a /tmp/dmxconfig.mk)
+and run
+% make
+
+finally become root and do
+# make install
+
+after all has gone well, load the appropriate kernel modules
+
+
+btw, it is always a good idea to read the readme that comes with dmx4linux...
+
+
+B. permissions
+==============
+the dmx device-files created by udev will be owned by root.root and not be
+read/writeable by anyone but root.
+in order to use them as an ordinary user, become root and create a group
+"dmx" and add users who need access to the dmx-devices to this group:
+# addgroup dmx
+# adduser zmoelnig dmx
+
+in theory this should be enough to allow you access to your dmx devices
+the next time you load a dmx-driver
+if you have problems, try plugging your device out and in again
+
+if you don't care for a clean setup, you could also just grant everyone read/write permissions.
+# chmod a+rw /dev/dmx*
+be aware that this might be a security risk.
+
+
+
+C. more drivers
+===============
+for using a "JMS USB2DMX" device, i had some driver problems.
+finally i found
+http://www.opendmx.net/index.php/Linux_ArtNet_Node
+which directed me to
+http://www.erwinrol.com/index.php?opensource/dmxusb.php
+and the "dmx_usb" module which seems to work fine.
+i guess, it will also work for the "enttec opendmx" device
+
+
diff --git a/dmx512/README.txt b/dmx512/README.txt
new file mode 100644
index 0000000..327c744
--- /dev/null
+++ b/dmx512/README.txt
@@ -0,0 +1,48 @@
+DMX512
+======
+
+controlling DMX from within Pd
+
+this readme assumes that you have a running dmx4linux setup.
+if not, read the README.dmx4linux.txt file for hints on how to
+get it going...
+
+A. compiling the Pd-objects
+===========================
+for this, change into the "./src" directory of the iem/dmx512/ folder
+(this might well be the folder that holds this README.txt you are currently
+reading)
+
+if you have obtained the source-code via subversion, you will first have to run
+% autoconf
+
+(this should not be needed if you downloaded the sources as a release tarball;
+that is: if the person who created the tarball has not forgotten to do it for you)
+
+then run
+% configure
+% make
+
+you should now have 2 binary files in the src/ folder called [dmxin] and [dmxout]
+
+
+B. Installation
+===============
+you should install the binaries (+helpfiles) somewhere Pd can find them.
+i would suggest to put them into
+ </path/to/pd>/extra/dmx512/
+and add this path to the startup-flags of Pd.
+
+
+C. Usage
+========
+there should be help-files in the ./help directory
+if not, the useage should be very similar to that of [ctlin] and [ctlout]
+(it's just using DMX512 instead of MIDI)
+
+
+D. Help!
+========
+read the FAQ
+
+
diff --git a/dmx512/dmx4linux2.6.1.patch b/dmx512/dmx4linux2.6.1.patch
new file mode 100644
index 0000000..842a3ba
--- /dev/null
+++ b/dmx512/dmx4linux2.6.1.patch
@@ -0,0 +1,196 @@
+diff -Naur dmx4linux-2.6.1/configure dmx4linux-2.6.1.new/configure
+--- dmx4linux-2.6.1/configure 2008-04-25 02:13:31.000000000 +0200
++++ dmx4linux-2.6.1.new/configure 2008-06-14 16:56:21.000000000 +0200
+@@ -43,7 +43,7 @@
+
+ echo "AS31=$DMXROOT/tools/as31-unix" >> /tmp/dmxconfig.mk
+
+-echo "CFLAGS+=-Wall -O2 -I$DMXROOT/include" >> /tmp/dmxconfig.mk
++echo "DMX_CFLAGS+=-Wall -O2 -I$DMXROOT/include" >> /tmp/dmxconfig.mk
+ echo "LDFLAGS+=-L$DMXROOT/libs" >> /tmp/dmxconfig.mk
+
+ if [ -f /usr/include/gpm.h -o -f /usr/local/include/gpm.h ] ; then
+diff -Naur dmx4linux-2.6.1/drivers/devices/dgm/Makefile dmx4linux-2.6.1.new/drivers/devices/dgm/Makefile
+--- dmx4linux-2.6.1/drivers/devices/dgm/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/dgm/Makefile 2008-06-14 16:53:25.000000000 +0200
+@@ -17,7 +17,7 @@
+ endif
+
+ ifneq ($(KERNELRELEASE),)
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\"
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" $(DMX_CFLAGS)
+ else
+
+ PWD := $(shell pwd)
+diff -Naur dmx4linux-2.6.1/drivers/devices/isa/Makefile dmx4linux-2.6.1.new/drivers/devices/isa/Makefile
+--- dmx4linux-2.6.1/drivers/devices/isa/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/isa/Makefile 2008-06-14 16:54:02.000000000 +0200
+@@ -5,7 +5,7 @@
+ obj-m := dmxenlight.o
+
+ ifneq ($(KERNELRELEASE),)
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\"
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" $(DMX_CFLAGS)
+ else
+
+ PWD := $(shell pwd)
+diff -Naur dmx4linux-2.6.1/drivers/devices/isa/soundlight/Makefile dmx4linux-2.6.1.new/drivers/devices/isa/soundlight/Makefile
+--- dmx4linux-2.6.1/drivers/devices/isa/soundlight/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/isa/soundlight/Makefile 2008-06-14 16:53:57.000000000 +0200
+@@ -4,7 +4,7 @@
+
+ ifneq ($(KERNELRELEASE),)
+ dmxsoundlight-objs=soundlight.o autoprobe.o slh_general.o slh1512a.o slh1512b.o slh1512c.o card_access.o
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\"
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" $(DMX_CFLAGS)
+
+ $(src)/%.h : $(src)/%.asm
+ $(AS31)/as31 -Fbin -s $< | sh $(src)/bin2hex.sh $< > $@
+diff -Naur dmx4linux-2.6.1/drivers/devices/Makefile dmx4linux-2.6.1.new/drivers/devices/Makefile
+--- dmx4linux-2.6.1/drivers/devices/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/Makefile 2008-06-14 16:58:50.000000000 +0200
+@@ -5,15 +5,15 @@
+ ifneq ($(CONFIG_USB),)
+ $(MAKE) -C usb $@
+ endif
+-ifneq ($(CONFIG_ISA),)
+- $(MAKE) -C isa $@
+-endif
+-ifneq ($(CONFIG_PCI),)
+- $(MAKE) -C pci $@
+-endif
+-ifneq ($(CONFIG_PARPORT),)
+- $(MAKE) -C parport $@
+-endif
++#ifneq ($(CONFIG_ISA),)
++# $(MAKE) -C isa $@
++#endif
++#ifneq ($(CONFIG_PCI),)
++# $(MAKE) -C pci $@
++#endif
++#ifneq ($(CONFIG_PARPORT),)
++# $(MAKE) -C parport $@
++#endif
+
+ # PCMCIA dmx drivers are currently not supported on 2.6 kernels
+ #ifneq ($(CONFIG_PCMCIA),)
+diff -Naur dmx4linux-2.6.1/drivers/devices/misc/Makefile dmx4linux-2.6.1.new/drivers/devices/misc/Makefile
+--- dmx4linux-2.6.1/drivers/devices/misc/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/misc/Makefile 2008-06-14 16:54:07.000000000 +0200
+@@ -3,7 +3,7 @@
+ obj-m := dmxdummy.o
+
+ ifneq ($(KERNELRELEASE),)
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\"
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" $(DMX_CFLAGS)
+ else
+
+ PWD := $(shell pwd)
+diff -Naur dmx4linux-2.6.1/drivers/devices/parport/Makefile dmx4linux-2.6.1.new/drivers/devices/parport/Makefile
+--- dmx4linux-2.6.1/drivers/devices/parport/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/parport/Makefile 2008-06-14 16:53:52.000000000 +0200
+@@ -5,7 +5,7 @@
+ obj-m := avrdmx.o dmx30.o dmx43.o dmxpcp.o okddmx.o lpr2dmx.o
+
+ ifneq ($(KERNELRELEASE),)
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\"
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" $(DMX_CFLAGS)
+ else
+
+ PWD := $(shell pwd)
+diff -Naur dmx4linux-2.6.1/drivers/devices/pci/Makefile dmx4linux-2.6.1.new/drivers/devices/pci/Makefile
+--- dmx4linux-2.6.1/drivers/devices/pci/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/pci/Makefile 2008-06-14 16:53:47.000000000 +0200
+@@ -5,7 +5,7 @@
+ obj-m += slh1514pci.o
+
+ ifneq ($(KERNELRELEASE),)
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" -DDMXPCI_DMX4LINUX=1
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" -DDMXPCI_DMX4LINUX=1 $(DMX_CFLAGS)
+ else
+
+ PWD := $(shell pwd)
+diff -Naur dmx4linux-2.6.1/drivers/devices/pcmcia/Makefile dmx4linux-2.6.1.new/drivers/devices/pcmcia/Makefile
+--- dmx4linux-2.6.1/drivers/devices/pcmcia/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/pcmcia/Makefile 2008-06-14 16:53:38.000000000 +0200
+@@ -5,7 +5,7 @@
+ obj-m += digimedia_cs.o
+
+ ifneq ($(KERNELRELEASE),)
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\"
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" $(DMX_CFLAGS)
+ else
+
+ PWD := $(shell pwd)
+diff -Naur dmx4linux-2.6.1/drivers/devices/usb/Makefile dmx4linux-2.6.1.new/drivers/devices/usb/Makefile
+--- dmx4linux-2.6.1/drivers/devices/usb/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/devices/usb/Makefile 2008-06-14 16:53:14.000000000 +0200
+@@ -5,7 +5,7 @@
+ obj-m := usb2dmx.o ftdi2dmx.o sunlite.o
+
+ ifneq ($(KERNELRELEASE),)
+-EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\"
++EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" $(DMX_CFLAGS)
+ else
+
+ PWD := $(shell pwd)
+diff -Naur dmx4linux-2.6.1/drivers/dmxdev/Makefile dmx4linux-2.6.1.new/drivers/dmxdev/Makefile
+--- dmx4linux-2.6.1/drivers/dmxdev/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/drivers/dmxdev/Makefile 2008-06-14 16:54:22.000000000 +0200
+@@ -8,7 +8,8 @@
+ dmxdev-objs:=dmx_dev.o dmx_proc.o dmx_props.o dmx_family.o dmx_driver.o dmx_interface.o dmx_universe.o dmx_fileinfo.o
+ EXTRA_CFLAGS:=-DDMXVERSION=\"${DMXVERSION}\" \
+ -DDMXOUTMINOR=$(DMXOUTMINOR) -DDMXINMINOR=$(DMXINMINOR) \
+- -DVERSIONMAJOR=$(VERSIONMAJOR) -DVERSIONMINOR=$(VERSIONMINOR)
++ -DVERSIONMAJOR=$(VERSIONMAJOR) -DVERSIONMINOR=$(VERSIONMINOR) \
++ $(DMX_CFLAGS)
+
+ else
+
+diff -Naur dmx4linux-2.6.1/examples/htmlexamples/Makefile dmx4linux-2.6.1.new/examples/htmlexamples/Makefile
+--- dmx4linux-2.6.1/examples/htmlexamples/Makefile 2008-04-25 02:13:31.000000000 +0200
++++ dmx4linux-2.6.1.new/examples/htmlexamples/Makefile 2008-06-14 16:54:42.000000000 +0200
+@@ -2,7 +2,7 @@
+
+ TARGETS= nonblockread selectread simpleread simplewrite
+
+-CFLAGS+= -Wall
++CFLAGS+= $(DMX_CFLAGS) -Wall
+
+ all: $(TARGETS)
+
+diff -Naur dmx4linux-2.6.1/examples/Makefile dmx4linux-2.6.1.new/examples/Makefile
+--- dmx4linux-2.6.1/examples/Makefile 2008-04-25 02:13:31.000000000 +0200
++++ dmx4linux-2.6.1.new/examples/Makefile 2008-06-14 17:00:15.000000000 +0200
+@@ -6,6 +6,8 @@
+
+ -include /tmp/dmxconfig.mk
+
++CFLAGS += $(DMX_CFLAGS)
++
+ TARGETS= pingdmx setdmx dmxinfo dmxdump
+
+ all: $(TARGETS)
+diff -Naur dmx4linux-2.6.1/tools/Makefile dmx4linux-2.6.1.new/tools/Makefile
+--- dmx4linux-2.6.1/tools/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/tools/Makefile 2008-06-14 16:55:16.000000000 +0200
+@@ -4,7 +4,7 @@
+ MANINSTALLPATH=$(DMXPREFIX)/man/man1
+
+ DMXCONSOLELIBS+= -Lpointer pointer/pointer.a
+-CFLAGS+= -Ipointer
++CFLAGS+= -Ipointer $(DMX_CFLAGS)
+
+ ifdef CONFIG_HAVE_LIRC
+ CFLAGS+= -DHAVE_LIRC=1
+diff -Naur dmx4linux-2.6.1/tools/pointer/Makefile dmx4linux-2.6.1.new/tools/pointer/Makefile
+--- dmx4linux-2.6.1/tools/pointer/Makefile 2008-04-25 02:13:32.000000000 +0200
++++ dmx4linux-2.6.1.new/tools/pointer/Makefile 2008-06-14 16:59:35.000000000 +0200
+@@ -1,5 +1,7 @@
+ -include /tmp/dmxconfig.mk
+
++CFLAGS+=$(DMX_CFLAGS)
++
+ C= js.c pointer.c ps2.c serial.c
+ ifdef CONFIG_HAVE_GPM
+ C+= gpm.c
diff --git a/dmx512/help/dmxout-help.pd b/dmx512/help/dmxout-help.pd
new file mode 100644
index 0000000..36f8c6c
--- /dev/null
+++ b/dmx512/help/dmxout-help.pd
@@ -0,0 +1,36 @@
+#N canvas 0 0 719 485 10;
+#X obj 102 31 dmxout;
+#X text 158 31 control DMX512-devices from within Pd;
+#X text 85 106 DMX512 is a protocol for controlling lights and magic
+\, similar to MIDI;
+#X obj 62 240 dmxout;
+#X floatatom 62 171 5 0 255 1 value - -;
+#X floatatom 101 195 5 0 255 1 channel - -;
+#X floatatom 232 171 5 0 255 1 value - -;
+#X obj 232 240 dmxout 5;
+#X text 232 261 default channel: 5;
+#X obj 402 270 dmxout 5 4;
+#X text 402 291 default channel: 5 6 7 8;
+#X msg 402 169 \$1 100 \$1 100;
+#X text 502 170 4 values \, one for each channel;
+#X floatatom 402 151 5 0 255 1 value - -;
+#X msg 488 239 6;
+#X text 518 241 channels 6 7 ...;
+#X msg 469 202 10 3;
+#X text 504 201 channels 10 11 12;
+#X text 80 336 [dmxout] is currently linux only. ports to other OSs
+are desired but not high priority.;
+#X text 80 378 by default \, [dmxout] will try to open the device /dev/dmx
+;
+#X text 79 401 you can change the default dmx-device globally by setting
+the "DMX" environment variable prior to creating the [dmxout] object
+(e.g. before you start Pd);
+#X text 501 34 (c) 2008 IOhannes m zmölnig;
+#X text 559 53 iem @ KUG;
+#X connect 4 0 3 0;
+#X connect 5 0 3 1;
+#X connect 6 0 7 0;
+#X connect 11 0 9 0;
+#X connect 13 0 11 0;
+#X connect 14 0 9 1;
+#X connect 16 0 9 1;
diff --git a/dmx512/src/Make.config.in b/dmx512/src/Make.config.in
new file mode 100644
index 0000000..3386e58
--- /dev/null
+++ b/dmx512/src/Make.config.in
@@ -0,0 +1,31 @@
+LIBNAME ?= @LIBNAME@
+VERSION ?= @VERSION@
+
+PREFIX =@prefix@@PDLIBDIR@
+
+INSTALL_BIN=$(PREFIX)/extra
+INSTALL_DOC=$(PREFIX)/@REFERENCEPATH@$(LIBNAME)
+
+EXT = @EXT@
+VERSION ?= @VERSION@
+DEFS = @DFLAGS@ -DACONNECT_VERSION=\"$(VERSION)\"
+IFLAGS = -I. @INCLUDES@ $(INCLUDES)
+
+CC = @CC@
+LD = @LD@
+STRIP = @STRIP@ @STRIPFLAGS@
+
+AFLAGS =
+LFLAGS = @LFLAGS@
+WFLAGS =
+
+MAKEDEP_FLAGS = @MAKEDEP_FLAGS@
+
+TARNAME = $(LIBNAME)-$(VERSION).tgz
+BINTARNAME = $(LIBNAME)-$(VERSION)-bin.tgz
+
+
+DMX4PD_CFLAGS = $(DEFS) $(IFLAGS) $(WFLAGS) @CFLAGS@
+
+LIBS = @LIBS@
+
diff --git a/dmx512/src/Make.version b/dmx512/src/Make.version
new file mode 100644
index 0000000..0bdb8b4
--- /dev/null
+++ b/dmx512/src/Make.version
@@ -0,0 +1 @@
+VERSION = $(shell svnversion | grep -e "[0-9]")
diff --git a/dmx512/src/Makefile b/dmx512/src/Makefile
new file mode 100644
index 0000000..36b92bc
--- /dev/null
+++ b/dmx512/src/Makefile
@@ -0,0 +1,76 @@
+default: all
+
+include Make.version
+
+
+Make.config: Make.config.in configure
+ ./configure
+
+-include Make.config
+
+#VERSIONDEFINE = _$(strip $(VERSION))_
+ifneq ($(strip $(VERSION)),)
+VERSIONDEFINE = -DDMX4PD_VERSION="\"rev.$(VERSION)\""
+endif
+
+.SUFFIXES: .$(EXT)
+
+SOURCES=$(sort $(filter %.c, $(wildcard *.c)))
+TARGETS = $(SOURCES:.c=.o)
+OBJECTS = $(SOURCES:.c=.$(EXT))
+
+all: $(OBJECTS)
+
+clean:
+ -rm -f *.o *.d
+
+binclean: clean
+ -rm -f *.$(EXT) *.dll *.pd_linux *.pd_darwin *.l_i386 *.l_ia64 *.d_ppc *.d_fat
+
+mrproper: distclean
+ -rm Make.config configure *.d.*
+
+distclean: clean binclean
+ -rm -f *~ _* config.*
+ -rm -rf autom4te.cache
+
+install: install-bin install-doc
+
+install-bin:
+ -install -d $(INSTALL_BIN)
+ -install -m 644 $(LIBNAME).$(EXT) $(INSTALL_BIN)
+
+install-doc:
+ -install -d $(INSTALL_BIN)
+ -install -m 644 *.pd $(INSTALL_BIN)
+
+dist: distclean
+ (cd ..;tar czvf $(TARNAME) $(LIBNAME))
+
+distbin: distclean all clean
+ (cd ..; tar cvzf $(BINTARNAME) $(LIBNAME))
+
+everything: clean all install distclean
+
+
+$(TARGETS): %.o : %.c Make.config
+ $(CC) $(DMX4PD_CFLAGS) $(VERSIONDEFINE) -c -o $@ $*.c
+
+$(OBJECTS): %.$(EXT) : %.o
+ $(LD) $(LFLAGS) -o $@ $*.o $(LIBS)
+ $(STRIP) $(STRIPFLAGS) $@
+
+
+ifeq (,$(findstring clean, $(MAKECMDGOALS)))
+## dependencies: as proposed by the GNU-make documentation
+## see http://www.gnu.org/software/make/manual/html_node/make_47.html#SEC51
+-include $(SOURCES:.c=.d)
+%.d: %.c
+ @set -e; rm -f $@; \
+ $(CC) $(MAKEDEP_FLAGS) $(DMX4PD_CFLAGS) $< > $@.$$$$; \
+ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
+ rm -f $@.$$$$
+
+configure: configure.ac
+ autoconf
+endif
diff --git a/dmx512/src/configure.ac b/dmx512/src/configure.ac
new file mode 100644
index 0000000..eff616a
--- /dev/null
+++ b/dmx512/src/configure.ac
@@ -0,0 +1,177 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(dmx4pd.h)
+
+LIBNAME=dmx4pd
+
+dnl Checks for programs.
+AC_PROG_CC
+
+AC_SUBST(STK)
+AC_SUBST(DFLAGS)
+AC_SUBST(LFLAGS)
+AC_SUBST(EXT)
+AC_SUBST(LD)
+AC_SUBST(STRIP)
+AC_SUBST(STRIPFLAGS)
+AC_SUBST(REFERENCEPATH)
+AC_SUBST(PDLIBDIR)
+AC_SUBST(INCLUDES)
+AC_SUBST(LIBNAME)
+
+AC_SUBST(DMX4LINUX_LFLAGS)
+AC_SUBST(DMX4LINUX_CFLAGS)
+
+
+AC_ARG_WITH(pdversion, [ --with-pdversion=<ver> enforce a certain pd-version (e.g. 0.37)])
+AC_ARG_WITH(extension, [ --with-extension=<ext> enforce a certain extension for the dynamic library (e.g. dll)])
+
+
+dnl Checks for libraries.
+dnl Replace `main' with a function in -lc:
+AC_CHECK_LIB(c, main)
+AC_CHECK_LIB(crtdll, fclose)
+
+dnl Replace `main' with a function in -lm:
+AC_CHECK_LIB(m, main)
+dnl Replace `main' with a function in -lpthread:
+dnl AC_CHECK_LIB(pthread, main)
+dnl Replace `main' with a function in -lstk:
+dnl AC_CHECK_LIB(stk, main, STK=yes)
+
+
+AC_CHECK_LIB(dmx4l, DMXsleep)
+AC_CHECK_LIB(dmx4linux, DMXsleep)
+#if test "x$ac_cv_lib_dmx4linux_DMXsleep" = "xyes"; then
+# DMX4LINUX_LFLAGS="-ldmx4linux"
+#fi
+
+if test "x$includedir" != "x"; then
+ for id in $includedir
+ do
+ if test -d $id; then INCLUDES="-I$id $INCLUDES"; fi
+ done
+fi
+if test "x$libdir" != "x"; then
+ for id in $libdir
+ do
+ if test -d $id; then LIBS="-L$id $LIBS"; fi
+ done
+fi
+
+AC_CHECK_LIB(pd, nullfn)
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(stdlib.h stdio.h string.h math.h time.h sys/time.h)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_HEADER_TIME
+
+dnl Checks for library functions.
+AC_FUNC_MMAP
+AC_CHECK_FUNCS(select socket strerror)
+
+dnl check for "-mms-bitfields" cflag
+dnl why is there no generic compiler-check for a given flag ?
+dnl it would make things so easy: AC_CHECK_FLAG([-mms-bitfields],,)
+AC_MSG_CHECKING("ms-bitfields")
+cat > conftest.c << EOF
+int main(){
+ return 0;
+}
+EOF
+if ${CC} ${INCLUDES} ${DFLAGS} -o conftest.o conftest.c ${CFLAGS} -mms-bitfields > /dev/null 2>&1
+then
+ echo "yes"
+ CFLAGS="${CFLAGS} -mms-bitfields"
+else
+ echo "no"
+fi
+
+
+### make-depend flags
+if test "x$ac_cv_c_compiler_gnu" = "xyes"; then
+ AC_SUBST(MAKEDEP_FLAGS, "-MM")
+else
+ AC_SUBST(MAKEDEP_FLAGS, "-M")
+fi
+
+dnl isn't there a better way to check for good linker/stripper ?
+
+dnl if we don't have $LD set, we set it to $(CC)
+dnl LD=${LD:=$CC}
+AC_CHECK_TOOL([LD], [ld], [${CC}])
+
+dnl if we don't have $STRIP set, we set it to ${host}-strip or strip
+
+dnl if we don't have $STRIP set, we set it to ${host}-strip or strip
+AC_CHECK_TOOL([STRIP], [strip], [true])
+AC_MSG_CHECKING([if strip is GNU strip])
+if $STRIP -V | grep GNU > /dev/null
+then
+ AC_SUBST(STRIPFLAGS, "--strip-unneeded")
+ AC_MSG_RESULT([yes])
+else
+ AC_SUBST(STRIPFLAGS,"-x")
+ AC_MSG_RESULT([no])
+fi
+
+dnl
+dnl OK, checks for machines are here now
+dnl
+if test `uname -s` = Linux;
+then
+ LFLAGS="-export_dynamic -shared"
+ CFLAGS="-fPIC $CFLAGS"
+ EXT=pd_linux
+fi
+
+dnl This should use '-bundle_loader /path/to/pd/bin/pd' instead of'-undefined suppress'
+dnl then strip might do something
+if test `uname -s` = Darwin;
+then
+ LD=cc
+ LFLAGS="-bundle -undefined suppress -flat_namespace"
+ EXT=pd_darwin
+ STRIPFLAGS=
+fi
+
+if test `uname | sed -e 's/^MINGW.*/NT/'` = NT;
+then
+ LD=gcc
+ INCLUDES="-I@prefix@/src"
+ DFLAGS="-D__WIN32__ ${DFLAGS}"
+ LFLAGS="-shared @prefix@/bin/pd.dll"
+ EXT=dll
+else
+ PDLIBDIR="/lib/pd"
+fi
+
+if test `uname -s` = IRIX64;
+then
+ LFLAGS="-n32 -DUNIX -DIRIX -DN32 -woff 1080,1064,1185 \
+ -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \
+ -shared -rdata_shared"
+ EXT=pd_irix6
+ dnl DFLAGS="-DUNIX -DIRIX6 ${DFLAGS}"
+ STRIPFLAGS="--strip-unneeded"
+fi
+
+if test `uname -s` = IRIX32;
+then
+ LFLAGS="-o32 -DUNIX -DIRIX -O2
+ -shared -rdata_shared"
+ EXT=pd_irix5
+ dnl DFLAGS="-DUNIX -DIRIX5 ${DFLAGS}"
+ STRIPFLAGS="--strip-unneeded"
+fi
+
+if test "x$with_extension" != "x"
+then
+ EXT=$with_extension
+fi
+
+
+AC_OUTPUT(Make.config)
+
+rm -f conftest.*
diff --git a/dmx512/src/dmx4pd.h b/dmx512/src/dmx4pd.h
new file mode 100644
index 0000000..059c794
--- /dev/null
+++ b/dmx512/src/dmx4pd.h
@@ -0,0 +1,41 @@
+
+/******************************************************
+ *
+ * dmx4pd - header file
+ *
+ * copyleft (c) IOhannes m zm-bölnig-A
+ *
+ * 0603:forum::f-bür::umläute:2008-A
+ *
+ * institute of electronic music and acoustics (iem)
+ *
+ ******************************************************
+ *
+ * license: GNU General Public License v.2
+ *
+ ******************************************************/
+
+#ifndef INCLUDE_DMX4PD_H__
+#define INCLUDE_DMX4PD_H__
+
+#ifndef DMX4PD_VERSION
+# define DMX4PD_VERSION __DATE__
+#endif
+
+
+
+#include "m_pd.h"
+#include <dmx/dmx.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+#define DMX4PD_POSTBANNER \
+ do { \
+ post("DMX4PD ("DMX4PD_VERSION"): (c) 2008 IOhannes m zmölnig - iem @ kug"); \
+ } while(0)
+
+
+#endif /* INCLUDE_DMX4PD_H__ */
diff --git a/dmx512/src/dmxin.c b/dmx512/src/dmxin.c
new file mode 100644
index 0000000..c009570
--- /dev/null
+++ b/dmx512/src/dmxin.c
@@ -0,0 +1,139 @@
+/******************************************************
+ *
+ * dmxin - implementation file
+ *
+ * copyleft (c) IOhannes m zmölnig
+ *
+ * 0603:forum::für::umläute:2008
+ *
+ * institute of electronic music and acoustics (iem)
+ *
+ ******************************************************
+ *
+ * license: GNU General Public License v.2
+ *
+ ******************************************************/
+
+
+#include "dmx4pd.h"
+
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static t_class *dmxin_class;
+
+typedef struct _dmxin
+{
+ t_object x_obj;
+ int x_device;
+ int x_port;
+
+ dmx_t x_dmxbuffer[512];
+
+
+ t_outlet*x_outlet1, *x_outlet2;
+} t_dmxin;
+
+static void dmx_doread(t_dmxin*x) {
+ int dmxin=x->x_device;
+ fd_set readset;
+ post("dmxin_doread: %d", dmxin);
+ if(dmxin<=0)return;
+
+ FD_ZERO(&readset);
+ FD_SET(dmxin, &readset);
+ FD_SET(0, &readset);
+
+ int n=select(dmxin+1, &readset, NULL,NULL, NULL);
+ if(n>0 && FD_ISSET(dmxin, &readset)) {
+ dmx_t dmxbuffer[512];
+ int i=0;
+ lseek (dmxin, 0, SEEK_SET);
+ n=read (dmxin, dmxbuffer, sizeof(dmxbuffer));
+ for(i=0; i<512; i+=2) {
+ int c=dmxbuffer[i];
+ if(c!=x->x_dmxbuffer[i]) {
+ x->x_dmxbuffer[i]=c;
+ post("read %03d @ %03d", c, i);
+ }
+ }
+ }
+}
+static void dmxin_bang(t_dmxin*x)
+{
+ dmx_doread(x);
+}
+
+static void dmxin_close(t_dmxin*x)
+{
+ if(x->x_device>=0) {
+ close(x->x_device);
+ }
+ x->x_device=-1;
+}
+
+
+static void dmxin_open(t_dmxin*x, t_symbol*s_devname)
+{
+ int argc=2;
+ const char *args[2] = {"--dmxin", s_devname->s_name};
+ const char**argv=args;
+ char*devname="";
+ int fd;
+
+ if(s_devname && s_devname->s_name)
+ devname=s_devname->s_name;
+
+ // strncpy(args[0], "--dmx", MAXPDSTRING);
+ // strncpy(args[1], devname, MAXPDSTRING);
+
+ fd = open (DMXINdev(&argc, argv), O_RDONLY);
+
+ if(fd!=-1) {
+ dmxin_close(x);
+ x->x_device=fd;
+ }
+}
+
+
+static void *dmxin_new(void)
+{
+ int i=0;
+ t_dmxin *x = (t_dmxin *)pd_new(dmxin_class);
+
+ x->x_device=0;
+ x->x_port=0;
+
+ for(i=0; i<sizeof(x->x_dmxbuffer); i++) {
+ x->x_dmxbuffer[i]=0;
+ }
+
+
+ x->x_outlet1=outlet_new(&x->x_obj, &s_float);
+ x->x_outlet2=outlet_new(&x->x_obj, &s_float);
+
+
+
+ dmxin_open(x, gensym(""));
+ return (x);
+}
+static void dmxin_free(t_dmxin*x)
+{
+ dmxin_close(x);
+}
+
+void dmxin_setup(void)
+{
+ dmxin_class = class_new(gensym("dmxin"), (t_newmethod)dmxin_new, (t_method)dmxin_free,
+ sizeof(t_dmxin),
+ 0,
+ A_NULL);
+
+ class_addbang(dmxin_class, dmxin_bang);
+
+#ifdef DMX4PD_POSTBANNER
+ DMX4PD_POSTBANNER;
+#endif
+}
diff --git a/dmx512/src/dmxout.c b/dmx512/src/dmxout.c
new file mode 100644
index 0000000..18358ce
--- /dev/null
+++ b/dmx512/src/dmxout.c
@@ -0,0 +1,299 @@
+/******************************************************
+ *
+ * dmxout - implementation file
+ *
+ * copyleft (c) IOhannes m zmölnig
+ *
+ * 0603:forum::für::umläute:2008
+ *
+ * institute of electronic music and acoustics (iem)
+ *
+ ******************************************************
+ *
+ * license: GNU General Public License v.2
+ *
+ ******************************************************/
+
+#include "dmx4pd.h"
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <pthread.h>
+
+
+static t_class *dmxout_class;
+static t_class *dmxout_class2;
+
+#define NUM_DMXVALUES 512
+
+static pthread_t g_thread_id;
+static pthread_mutex_t *g_mutex;
+static dmx_t g_values[NUM_DMXVALUES];
+static int g_device;
+static int g_thread_running, g_thread_continue;
+
+typedef struct _dmxout
+{
+ t_object x_obj;
+
+ t_inlet *x_portinlet;
+ t_float x_port;
+ int x_portrange;
+
+
+} t_dmxout;
+
+
+static void *dmxout_thread(void*you)
+{
+ pthread_mutex_t *mutex=g_mutex;
+ struct timeval timout;
+
+ g_thread_running=1;
+
+ while(g_thread_continue) {
+ timout.tv_sec = 0;
+ timout.tv_usec=100;
+ select(0,0,0,0,&timout);
+
+ pthread_mutex_lock(g_mutex);
+ if(g_device>0) {
+ lseek (g_device, 0, SEEK_SET); /* set to the current channel */
+ write (g_device, g_values, NUM_DMXVALUES); /* write the channel */
+ }
+ pthread_mutex_unlock(g_mutex);
+ }
+ g_thread_running=0;
+
+ return NULL;
+}
+
+static void dmxout_close()
+{
+ if(g_device>=0) {
+ close(g_device);
+ }
+ g_device=-1;
+
+ if(g_thread_running) {
+ /* terminate the current thread! */
+ void*dummy=0;
+ int counter=0;
+ g_thread_continue=0;
+ pthread_join(g_thread_id, &dummy);
+ while(g_thread_running) {
+ counter++;
+ }
+ }
+ g_thread_id=0;
+ if(g_mutex) {
+ pthread_mutex_destroy(g_mutex);
+ freebytes(g_mutex, sizeof(pthread_mutex_t));
+ g_mutex=NULL;
+ }
+}
+
+
+static void dmxout_open(t_symbol*s_devname)
+{
+ int argc=2;
+ const char *args[2] = {"--dmx", s_devname->s_name};
+ const char**argv=args;
+ const char*devname="";
+ int fd;
+
+ dmxout_close();
+
+ if(s_devname && s_devname->s_name)
+ devname=s_devname->s_name;
+
+ // strncpy(args[0], "--dmx", MAXPDSTRING);
+ // strncpy(args[1], devname, MAXPDSTRING);
+ verbose(2, "[dmxout]: trying to open '%s'", args[1]);
+ devname=DMXdev(&argc, argv);
+ if(!devname){
+ error("couldn't find DMX device");
+ return;
+ }
+ verbose(1, "[dmxout] opening %s", devname);
+
+ fd = open (devname, O_WRONLY);
+
+ if(fd!=-1) {
+ g_device=fd;
+
+ g_thread_running=0;
+ g_thread_continue=0;
+ g_mutex=(pthread_mutex_t*) malloc(sizeof(pthread_mutex_t));
+ if ( pthread_mutex_init(g_mutex, NULL) < 0 ) {
+ error("couldn't create mutex");
+ } else {
+ g_thread_continue = 1;
+ pthread_create(&g_thread_id, 0, dmxout_thread, NULL);
+ }
+ } else {
+ error("failed to open DMX-device '%s'",devname);
+ }
+}
+
+static void dmxout_doout(t_dmxout*x) {
+ if(g_device<=0) {
+ pd_error(x, "no DMX universe found");
+ return;
+ }
+}
+
+
+static void dmxout_doout1(t_dmxout*x, short port, unsigned char value)
+{
+ g_values[port]=value;
+ dmxout_doout(x);
+}
+
+
+static void dmxout_float(t_dmxout*x, t_float f)
+{
+ unsigned char val=(unsigned char)f;
+ short port = (short)x->x_port;
+ if(f<0. || f>255.) {
+ pd_error(x, "value %f out of bounds [0..255]", f);
+ return;
+ }
+ if(x->x_port<0. || x->x_port>NUM_DMXVALUES) {
+ pd_error(x, "port %f out of bounds [0..%d]", x->x_port, NUM_DMXVALUES);
+ return;
+ }
+
+ dmxout_doout1(x, port, val);
+}
+
+static void dmxout_list(t_dmxout*x, t_symbol*s, int argc, t_atom*argv)
+{
+ int count=(argc<x->x_portrange)?argc:x->x_portrange;
+ int i=0;
+ int errors=0;
+
+ int port=x->x_port;
+ if((port+count)>=NUM_DMXVALUES) {
+ if(count>NUM_DMXVALUES)count=NUM_DMXVALUES;
+ port=NUM_DMXVALUES-count;
+ }
+
+ for(i=0; i<count; i++) {
+ t_float f=atom_getfloat(argv+i);
+ if(f<0. || f>255.) {
+ errors++;
+ if(f<0.)f=0.;
+ if(f>255)f=255;
+ }
+ g_values[port+i]=(unsigned char)f;
+ }
+ if(errors) {
+ pd_error(x, "%d valu%s out of bound [0..255]", errors, (1==errors)?"e":"es");
+ }
+
+ dmxout_doout(x);
+}
+
+static void dmxout_port(t_dmxout*x, t_float f_baseport, t_floatarg f_portrange)
+{
+ short baseport =(short)f_baseport;
+ short portrange=(short)f_portrange;
+
+
+ if(baseport<0 || baseport>=NUM_DMXVALUES) {
+ pd_error(x, "port %f out of bounds [0..%d]", f_baseport, NUM_DMXVALUES);
+ baseport =0;
+ }
+ x->x_port = baseport;
+
+ if(portrange<0) {
+ pd_error(x, "portrange %f<0! setting to 1", portrange);
+ portrange=1;
+ } else if (portrange==0) {
+ portrange=x->x_portrange;
+ }
+
+ if (baseport+portrange>NUM_DMXVALUES) {
+ pd_error(x, "upper port exceeds %d! clamping", NUM_DMXVALUES);
+ portrange=NUM_DMXVALUES-baseport;
+ }
+ x->x_portrange=portrange;
+}
+
+static void *dmxout_new(t_symbol*s, int argc, t_atom*argv)
+{
+ t_floatarg baseport=0.f, portrange=0.f;
+ t_dmxout *x = 0;
+
+ switch(argc) {
+ case 2:
+ x=(t_dmxout *)pd_new(dmxout_class2);
+ x->x_portinlet=inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("port"));
+ baseport=atom_getfloat(argv);
+ portrange=atom_getfloat(argv+1);
+ dmxout_port(x, baseport, portrange);
+ break;
+ case 1:
+ baseport=atom_getfloat(argv);
+ case 0:
+ x=(t_dmxout *)pd_new(dmxout_class);
+ x->x_portinlet=floatinlet_new(&x->x_obj, &x->x_port);
+ x->x_port = baseport;
+ x->x_portrange = -1;
+ break;
+ default:
+ return 0;
+ }
+ return (x);
+}
+
+static void *dmxout_free(t_dmxout*x)
+{
+ // dmxout_close();
+}
+
+static void dmxout_init(void) {
+ int i=0;
+ g_thread_id=0;
+ g_mutex=NULL;
+ for(i=0; i<NUM_DMXVALUES; i++) g_values[i]=0;
+
+ g_device=-1;
+ g_thread_running=0;
+ g_thread_continue=0;
+
+ dmxout_open(gensym(""));
+}
+
+
+void dmxout_setup(void)
+{
+
+#ifdef DMX4PD_POSTBANNER
+ DMX4PD_POSTBANNER;
+#endif
+
+ dmxout_class = class_new(gensym("dmxout"), (t_newmethod)dmxout_new, (t_method)dmxout_free,
+ sizeof(t_dmxout),
+ 0,
+ A_GIMME, A_NULL);
+
+ class_addfloat(dmxout_class, dmxout_float);
+
+ dmxout_class2 = class_new(gensym("dmxout"), (t_newmethod)dmxout_new, (t_method)dmxout_free,
+ sizeof(t_dmxout),
+ 0,
+ A_GIMME, A_NULL);
+
+ class_addlist(dmxout_class2, dmxout_list);
+
+
+ class_addmethod(dmxout_class2, (t_method)dmxout_port, gensym("port"),
+ A_FLOAT, A_DEFFLOAT, A_NULL);
+
+
+ dmxout_init();
+}
diff --git a/dmx512/src/dmxout_b.c b/dmx512/src/dmxout_b.c
new file mode 100644
index 0000000..9623397
--- /dev/null
+++ b/dmx512/src/dmxout_b.c
@@ -0,0 +1,240 @@
+/******************************************************
+ *
+ * dmxout_b - implementation file
+ *
+ * this is the "blocking" version
+ *
+ * copyleft (c) IOhannes m zmölnig
+ *
+ * 0603:forum::für::umläute:2008
+ *
+ * institute of electronic music and acoustics (iem)
+ *
+ ******************************************************
+ *
+ * license: GNU General Public License v.2
+ *
+ ******************************************************/
+
+#include "dmx4pd.h"
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+static t_class *dmxout_b_class;
+static t_class *dmxout_b_class2;
+
+#define NUM_DMXVALUES 512
+
+typedef struct _dmxout_b
+{
+ t_object x_obj;
+
+ t_inlet *x_portinlet;
+
+ int x_device;
+ t_float x_port;
+ int x_portrange;
+
+ dmx_t x_values[NUM_DMXVALUES];
+} t_dmxout_b;
+
+static void dmxout_b_clearbuf(t_dmxout_b*x)
+{
+ int i=0;
+ for(i=0; i<NUM_DMXVALUES; i++) x->x_values[i]=0;
+}
+
+static void dmxout_b_close(t_dmxout_b*x)
+{
+ if(x->x_device>=0) {
+ close(x->x_device);
+ }
+ x->x_device=-1;
+}
+
+
+static void dmxout_b_open(t_dmxout_b*x, t_symbol*s_devname)
+{
+ int argc=2;
+ const char *args[2] = {"--dmx", s_devname->s_name};
+ const char**argv=args;
+ const char*devname="";
+ int fd;
+
+ dmxout_b_close(x);
+
+ if(s_devname && s_devname->s_name)
+ devname=s_devname->s_name;
+
+ verbose(2, "[dmxout_b]: trying to open '%s'", args[1]);
+ devname=DMXdev(&argc, argv);
+ if(!devname){
+ pd_error(x, "couldn't find DMX device");
+ return;
+ }
+ verbose(1, "[dmxout_b] opening %s", devname);
+
+ fd = open (devname, O_WRONLY | O_NONBLOCK);
+
+ if(fd!=-1) {
+ x->x_device=fd;
+ dmxout_b_clearbuf(x);
+ } else {
+ pd_error(x, "failed to open DMX-device '%s'",devname);
+ }
+}
+
+static void dmxout_b_doout(t_dmxout_b*x) {
+ int device = x->x_device;
+ if(device<=0) {
+ pd_error(x, "no DMX universe found");
+ return;
+ }
+
+ lseek (device, 0, SEEK_SET); /* set to the current channel */
+ write (device, x->x_values, NUM_DMXVALUES); /* write the channel */
+}
+
+
+static void dmxout_b_doout1(t_dmxout_b*x, short port, unsigned char value)
+{
+ x->x_values[port]=value;
+ dmxout_b_doout(x);
+}
+
+
+static void dmxout_b_float(t_dmxout_b*x, t_float f)
+{
+ unsigned char val=(unsigned char)f;
+ short port = (short)x->x_port;
+ if(f<0. || f>255.) {
+ pd_error(x, "value %f out of bounds [0..255]", f);
+ return;
+ }
+ if(x->x_port<0. || x->x_port>NUM_DMXVALUES) {
+ pd_error(x, "port %f out of bounds [0..%d]", x->x_port, NUM_DMXVALUES);
+ return;
+ }
+
+ dmxout_b_doout1(x, port, val);
+}
+
+static void dmxout_b_list(t_dmxout_b*x, t_symbol*s, int argc, t_atom*argv)
+{
+ int count=(argc<x->x_portrange)?argc:x->x_portrange;
+ int i=0;
+ int errors=0;
+
+ int port=x->x_port;
+ if((port+count)>=NUM_DMXVALUES) {
+ if(count>NUM_DMXVALUES)count=NUM_DMXVALUES;
+ port=NUM_DMXVALUES-count;
+ }
+
+ for(i=0; i<count; i++) {
+ t_float f=atom_getfloat(argv+i);
+ if(f<0. || f>255.) {
+ errors++;
+ if(f<0.)f=0.;
+ if(f>255)f=255;
+ }
+ x->x_values[port+i]=(unsigned char)f;
+ }
+ if(errors) {
+ pd_error(x, "%d valu%s out of bound [0..255]", errors, (1==errors)?"e":"es");
+ }
+
+ dmxout_b_doout(x);
+}
+
+static void dmxout_b_port(t_dmxout_b*x, t_float f_baseport, t_floatarg f_portrange)
+{
+ short baseport =(short)f_baseport;
+ short portrange=(short)f_portrange;
+
+
+ if(baseport<0 || baseport>=NUM_DMXVALUES) {
+ pd_error(x, "port %f out of bounds [0..%d]", f_baseport, NUM_DMXVALUES);
+ baseport =0;
+ }
+ x->x_port = baseport;
+
+ if(portrange<0) {
+ pd_error(x, "portrange %f<0! setting to 1", portrange);
+ portrange=1;
+ } else if (portrange==0) {
+ portrange=x->x_portrange;
+ }
+
+ if (baseport+portrange>NUM_DMXVALUES) {
+ pd_error(x, "upper port exceeds %d! clamping", NUM_DMXVALUES);
+ portrange=NUM_DMXVALUES-baseport;
+ }
+ x->x_portrange=portrange;
+}
+
+static void *dmxout_b_new(t_symbol*s, int argc, t_atom*argv)
+{
+ t_floatarg baseport=0.f, portrange=0.f;
+ t_dmxout_b *x = 0;
+
+ switch(argc) {
+ case 2:
+ x=(t_dmxout_b *)pd_new(dmxout_b_class2);
+ x->x_portinlet=inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("port"));
+ baseport=atom_getfloat(argv);
+ portrange=atom_getfloat(argv+1);
+ dmxout_b_port(x, baseport, portrange);
+ break;
+ case 1:
+ baseport=atom_getfloat(argv);
+ case 0:
+ x=(t_dmxout_b *)pd_new(dmxout_b_class);
+ x->x_portinlet=floatinlet_new(&x->x_obj, &x->x_port);
+ x->x_port = baseport;
+ x->x_portrange = -1;
+ break;
+ default:
+ return 0;
+ }
+ x->x_device=-1;
+
+ dmxout_b_open(x, gensym(""));
+ return (x);
+}
+
+static void *dmxout_b_free(t_dmxout_b*x)
+{
+ dmxout_b_close(x);
+}
+
+
+void dmxout_b_setup(void)
+{
+#ifdef DMX4PD_POSTBANNER
+ DMX4PD_POSTBANNER;
+#endif
+
+ dmxout_b_class = class_new(gensym("dmxout_b"), (t_newmethod)dmxout_b_new, (t_method)dmxout_b_free,
+ sizeof(t_dmxout_b),
+ 0,
+ A_GIMME, A_NULL);
+
+ class_addfloat(dmxout_b_class, dmxout_b_float);
+ class_addmethod(dmxout_b_class, (t_method)dmxout_b_open, gensym("open"), A_SYMBOL, A_NULL);
+
+ dmxout_b_class2 = class_new(gensym("dmxout_b"), (t_newmethod)dmxout_b_new, (t_method)dmxout_b_free,
+ sizeof(t_dmxout_b),
+ 0,
+ A_GIMME, A_NULL);
+
+ class_addlist(dmxout_b_class2, dmxout_b_list);
+
+
+ class_addmethod(dmxout_b_class2, (t_method)dmxout_b_port, gensym("port"),
+ A_FLOAT, A_DEFFLOAT, A_NULL);
+
+ class_addmethod(dmxout_b_class2, (t_method)dmxout_b_open, gensym("open"), A_SYMBOL, A_NULL);
+}
diff --git a/itrax2/GnuGPL.txt b/itrax2/GnuGPL.txt
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/itrax2/GnuGPL.txt
@@ -0,0 +1,340 @@
+ 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.
+
+ GNU GENERAL PUBLIC LICENSE
+ 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
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/itrax2/LICENSE.txt b/itrax2/LICENSE.txt
new file mode 100644
index 0000000..b38f26e
--- /dev/null
+++ b/itrax2/LICENSE.txt
@@ -0,0 +1,21 @@
+itrax2 - interface InterSense tracking devices from within pd
+
+(c) 2000 - 2004 Thomas Musil, IEM KUG Graz Austria
+
+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; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+In the official flext distribution, the GNU General Public License is
+in the file GnuGPL.txt
+
diff --git a/itrax2/itrax2.c b/itrax2/itrax2.c
new file mode 100644
index 0000000..8c9a28b
--- /dev/null
+++ b/itrax2/itrax2.c
@@ -0,0 +1,128 @@
+/* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution.
+
+itrax2 written by Thomas Musil (c) IEM KUG Graz Austria 2000 - 2004 */
+
+#ifdef NT
+#pragma warning( disable : 4244 )
+#pragma warning( disable : 4305 )
+#endif
+
+#include "m_pd.h"
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include "iemlib.h"
+#include "isense.h"
+
+typedef struct _itrax2
+{
+ t_object x_obj;
+ float x_poll;
+ ISD_TRACKER_HANDLE x_handle;
+ void *x_out2;
+ void *x_out3;
+ void *x_clock;
+} t_itrax2;
+
+static t_class *itrax2_class;
+
+static void itrax2_tick(t_itrax2 *x)
+{
+ if(x->x_handle > 0)
+ {
+ ISD_TRACKER_DATA_TYPE data;
+
+ clock_delay(x->x_clock, x->x_poll);
+ ISD_GetData(x->x_handle, &data);
+ outlet_float(x->x_out3, (float)(data.Station[0].Orientation[2]));
+ outlet_float(x->x_out2, (float)(data.Station[0].Orientation[1]));
+ outlet_float(x->x_obj.ob_outlet, (float)(data.Station[0].Orientation[0]));
+ }
+ else
+ clock_unset(x->x_clock);
+}
+
+static void itrax2_init(t_itrax2 *x)
+{
+ x->x_handle = ISD_OpenTracker((Hwnd)NULL, 0, FALSE, FALSE);
+ if(x->x_handle <= 0)
+ post("Tracker not found");
+ else
+ post("Intertrax2 dedected, OK");
+}
+
+static void itrax2_reset(t_itrax2 *x)
+{
+ ISD_ResetHeading(x->x_handle, 1);
+}
+
+static void itrax2_bang(t_itrax2 *x)
+{
+ clock_delay(x->x_clock, x->x_poll);
+}
+
+static void itrax2_start(t_itrax2 *x)
+{
+ itrax2_bang(x);
+}
+
+static void itrax2_stop(t_itrax2 *x)
+{
+ clock_unset(x->x_clock);
+}
+
+static void itrax2_float(t_itrax2 *x, t_float cmd)
+{
+ if(cmd == 0.0)
+ itrax2_stop(x);
+ else
+ itrax2_bang(x);
+}
+
+static void itrax2_ft1(t_itrax2 *x, t_float polltime_ms)
+{
+ if(polltime_ms < 8.0)
+ {
+ polltime_ms = 8.0;
+ post("serial polling-time clipped to 8 ms");
+ }
+ x->x_poll = polltime_ms;
+}
+
+static void *itrax2_new(t_floatarg polltime_ms)
+{
+ t_itrax2 *x = (t_itrax2 *)pd_new(itrax2_class);
+
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1"));
+ outlet_new(&x->x_obj, &s_float);
+ x->x_out2 = outlet_new(&x->x_obj, &s_float);
+ x->x_out3 = outlet_new(&x->x_obj, &s_float);
+ x->x_clock = clock_new(x, (t_method)itrax2_tick);
+ itrax2_ft1(x, polltime_ms);
+ x->x_handle = 0;
+ return(x);
+}
+
+static void itrax2_ff(t_itrax2 *x)
+{
+ clock_free(x->x_clock);
+ if(x->x_handle > 0)
+ ISD_CloseTracker(x->x_handle);
+}
+
+void itrax2_setup(void)
+{
+ itrax2_class = class_new(gensym("itrax2"), (t_newmethod)itrax2_new,
+ (t_method)itrax2_ff, sizeof(t_itrax2), 0, A_DEFFLOAT, 0);
+ class_addbang(itrax2_class, itrax2_bang);
+ class_addfloat(itrax2_class, itrax2_float);
+ class_addmethod(itrax2_class, (t_method)itrax2_start, gensym("start"), 0);
+ class_addmethod(itrax2_class, (t_method)itrax2_stop, gensym("stop"), 0);
+ class_addmethod(itrax2_class, (t_method)itrax2_init, gensym("init"), 0);
+ class_addmethod(itrax2_class, (t_method)itrax2_reset, gensym("reset"), 0);
+ class_addmethod(itrax2_class, (t_method)itrax2_ft1, gensym("ft1"), A_FLOAT, 0);
+ class_sethelpsymbol(itrax2_class, gensym("iemhelp/help-itrax2"));
+
+ post("itrax2 (R-1.15) library loaded!");
+}
diff --git a/itrax2/makefile b/itrax2/makefile
new file mode 100644
index 0000000..516d908
--- /dev/null
+++ b/itrax2/makefile
@@ -0,0 +1,54 @@
+current: all
+
+PDSOURCE ?= ../../../pd/src
+IEMLIBSRC ?= ../../iemlib/iemlib1/src/
+INTERSENSESDK ?= /tmp/zmoelnig/isense/SDK/
+
+.SUFFIXES: .pd_linux
+VPATH = $(INTERSENSESDK)
+
+INCLUDE = -I. -I$(PDSOURCE) -I$(IEMLIBSRC) -I$(INTERSENSESDK)
+
+
+LDFLAGS = -export-dynamic -shared -L$(INTERSENSESDK)/Linux
+LIB = -ldl -lm -lpthread
+
+#select either the DBG and OPT compiler flags below:
+
+CFLAGS = -DPD -DUNIX -W -Werror -Wno-unused \
+ -Wno-parentheses -Wno-switch -O6 -funroll-loops -fomit-frame-pointer \
+ -DDL_OPEN
+
+SYSTEM = $(shell uname -m)
+
+# the sources
+
+SRC = isense.c \
+ itrax2.c
+
+TARGET = itrax2.pd_linux
+
+
+OBJ = $(SRC:.c=.o)
+
+#
+# ------------------ targets ------------------------------------
+#
+
+clean:
+ -rm $(TARGET)
+ -rm *.o
+
+all: $(TARGET)
+
+$(TARGET): $(OBJ)
+ @echo :: $(OBJ)
+ $(LD) $(LDFLAGS) -o $@ $< $(LIB)
+ strip --strip-unneeded $(TARGET)
+
+$(OBJ) : %.o : %.c
+ $(CC) $(CFLAGS) $(INCLUDE) -c -o $@ $<
+
+
+
+
diff --git a/itrax2/makefile_win b/itrax2/makefile_win
new file mode 100644
index 0000000..ac4b514
--- /dev/null
+++ b/itrax2/makefile_win
@@ -0,0 +1,38 @@
+
+all: ..\..\lib\itrax2.dll
+
+VIS_CPP_PATH = "C:\Programme\Microsoft Visual Studio\Vc98"
+
+PD_INST_PATH = "C:\Programme\pd-0.37-3"
+
+PD_WIN_INCLUDE_PATH = /I. /I$(PD_INST_PATH)\src /I$(VIS_CPP_PATH)\include /I.\InterSense
+
+PD_WIN_C_FLAGS = /nologo /W3 /WX /DMSW /DNT /DPD /DWIN32 /DWINDOWS /DUSRDLL /DINTERTRAX2_EXPORTS /Ox -DPA_LITTLE_ENDIAN
+
+PD_WIN_L_FLAGS = /nologo
+
+PD_WIN_LIB = /NODEFAULTLIB:libc /NODEFAULTLIB:oldnames /NODEFAULTLIB:kernel /NODEFAULTLIB:uuid \
+ $(VIS_CPP_PATH)\lib\libc.lib \
+ $(VIS_CPP_PATH)\lib\oldnames.lib \
+ $(VIS_CPP_PATH)\lib\kernel32.lib \
+ $(VIS_CPP_PATH)\lib\wsock32.lib \
+ $(VIS_CPP_PATH)\lib\winmm.lib \
+ $(PD_INST_PATH)\bin\pthreadVC.lib \
+ $(PD_INST_PATH)\bin\pd.lib
+
+
+SRC = isense.c \
+ itrax2.c
+
+
+OBJ = $(SRC:.c=.obj)
+
+.c.obj:
+ cl $(PD_WIN_C_FLAGS) $(PD_WIN_INCLUDE_PATH) /c $*.c
+
+..\..\lib\itrax2.dll: $(OBJ)
+ link $(PD_WIN_L_FLAGS) /dll /export:itrax2_setup \
+ /out:..\..\lib\itrax2.dll $(OBJ) $(PD_WIN_LIB)
+
+clean:
+ del *.obj
diff --git a/lanbox/lanbox-help.pd b/lanbox/lanbox-help.pd
new file mode 100644
index 0000000..75a14ed
--- /dev/null
+++ b/lanbox/lanbox-help.pd
@@ -0,0 +1,183 @@
+#N canvas 169 245 687 517 10;
+#X obj 28 461 lanbox;
+#X floatatom 84 141 5 0 0 0 - - -;
+#X floatatom 182 140 5 0 0 0 - - -;
+#X floatatom 280 140 5 0 0 0 - - -;
+#X obj 84 197 pack f f f f f f;
+#X msg 182 118 1;
+#X obj 182 96 loadbang;
+#X msg 280 117 1;
+#X obj 280 95 loadbang;
+#X msg 84 225 send 42 67 57 \$3 \$4 \$5 \$6 \$1 \$2 35 10;
+#X text 335 138 value \, engine \, chanel;
+#X obj 28 18 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1
+;
+#X text 52 19 connect / disconnect;
+#X text 84 66 change the value of 1 chanel;
+#X msg 110 302 send 42 53 54 48 49 48 48 54 52 48 48 35 10;
+#X text 381 320 #;
+#X text 169 321 5;
+#X text 189 321 6;
+#X text 212 321 0;
+#X text 253 321 0;
+#X text 275 321 0;
+#X text 337 321 0;
+#X text 360 321 0;
+#X text 232 321 1;
+#X text 148 320 *;
+#X text 214 351 eng#;
+#X text 216 330 ___;
+#X text 251 331 ___________;
+#X text 268 350 =100d;
+#X text 261 366 cue list;
+#X text 295 321 6;
+#X text 316 320 4;
+#X text 343 332 ___;
+#X text 341 353 cue step;
+#X text 109 280 change cue step (engine = 1 \, cue list = 100 \, cue
+step= 0);
+#X text 103 401 see lanbox documentation for all possible messages
+;
+#N canvas 0 0 790 364 i2decascii 0;
+#X obj -59 155 / 16;
+#X obj -59 182 i;
+#X obj 43 156 % 16;
+#X obj -59 118 t f f;
+#X obj -59 210 moses 9.5;
+#X obj -59 263 i;
+#X obj 43 210 moses 9.5;
+#X obj 43 263 i;
+#X obj -59 232 + 48;
+#X obj -1 232 + 55;
+#X obj 101 232 + 55;
+#X obj 43 233 + 48;
+#X obj -59 44 inlet;
+#X obj -59 287 outlet;
+#X obj 43 287 outlet;
+#X text 199 13 convert the decimal float to a 2 digit hexadecimal number
+;
+#X text 258 37 then to 2 ascii char;
+#X text 255 59 then to 2 decimal int = ascii value of this 2 chars
+;
+#X obj -59 68 min 255;
+#X obj -59 92 max 0;
+#X connect 0 0 1 0;
+#X connect 1 0 4 0;
+#X connect 2 0 6 0;
+#X connect 3 0 0 0;
+#X connect 3 1 2 0;
+#X connect 4 0 8 0;
+#X connect 4 1 9 0;
+#X connect 5 0 13 0;
+#X connect 6 0 11 0;
+#X connect 6 1 10 0;
+#X connect 7 0 14 0;
+#X connect 8 0 5 0;
+#X connect 9 0 5 0;
+#X connect 10 0 7 0;
+#X connect 11 0 7 0;
+#X connect 12 0 18 0;
+#X connect 18 0 19 0;
+#X connect 19 0 3 0;
+#X restore 84 161 pd i2decascii;
+#N canvas 0 0 790 364 i2decascii 0;
+#X obj -59 155 / 16;
+#X obj -59 182 i;
+#X obj 43 156 % 16;
+#X obj -59 118 t f f;
+#X obj -59 210 moses 9.5;
+#X obj -59 263 i;
+#X obj 43 210 moses 9.5;
+#X obj 43 263 i;
+#X obj -59 232 + 48;
+#X obj -1 232 + 55;
+#X obj 101 232 + 55;
+#X obj 43 233 + 48;
+#X obj -59 44 inlet;
+#X obj -59 287 outlet;
+#X obj 43 287 outlet;
+#X text 199 13 convert the decimal float to a 2 digit hexadecimal number
+;
+#X text 258 37 then to 2 ascii char;
+#X text 255 59 then to 2 decimal int = ascii value of this 2 chars
+;
+#X obj -59 68 min 255;
+#X obj -59 92 max 0;
+#X connect 0 0 1 0;
+#X connect 1 0 4 0;
+#X connect 2 0 6 0;
+#X connect 3 0 0 0;
+#X connect 3 1 2 0;
+#X connect 4 0 8 0;
+#X connect 4 1 9 0;
+#X connect 5 0 13 0;
+#X connect 6 0 11 0;
+#X connect 6 1 10 0;
+#X connect 7 0 14 0;
+#X connect 8 0 5 0;
+#X connect 9 0 5 0;
+#X connect 10 0 7 0;
+#X connect 11 0 7 0;
+#X connect 12 0 18 0;
+#X connect 18 0 19 0;
+#X connect 19 0 3 0;
+#X restore 182 161 pd i2decascii;
+#N canvas 0 0 790 364 i2decascii 0;
+#X obj -59 155 / 16;
+#X obj -59 182 i;
+#X obj 43 156 % 16;
+#X obj -59 118 t f f;
+#X obj -59 210 moses 9.5;
+#X obj -59 263 i;
+#X obj 43 210 moses 9.5;
+#X obj 43 263 i;
+#X obj -59 232 + 48;
+#X obj -1 232 + 55;
+#X obj 101 232 + 55;
+#X obj 43 233 + 48;
+#X obj -59 44 inlet;
+#X obj -59 287 outlet;
+#X obj 43 287 outlet;
+#X text 199 13 convert the decimal float to a 2 digit hexadecimal number
+;
+#X text 258 37 then to 2 ascii char;
+#X text 255 59 then to 2 decimal int = ascii value of this 2 chars
+;
+#X obj -59 68 min 255;
+#X obj -59 92 max 0;
+#X connect 0 0 1 0;
+#X connect 1 0 4 0;
+#X connect 2 0 6 0;
+#X connect 3 0 0 0;
+#X connect 3 1 2 0;
+#X connect 4 0 8 0;
+#X connect 4 1 9 0;
+#X connect 5 0 13 0;
+#X connect 6 0 11 0;
+#X connect 6 1 10 0;
+#X connect 7 0 14 0;
+#X connect 8 0 5 0;
+#X connect 9 0 5 0;
+#X connect 10 0 7 0;
+#X connect 11 0 7 0;
+#X connect 12 0 18 0;
+#X connect 18 0 19 0;
+#X connect 19 0 3 0;
+#X restore 280 161 pd i2decascii;
+#X connect 1 0 36 0;
+#X connect 2 0 37 0;
+#X connect 3 0 38 0;
+#X connect 4 0 9 0;
+#X connect 5 0 2 0;
+#X connect 6 0 5 0;
+#X connect 7 0 3 0;
+#X connect 8 0 7 0;
+#X connect 9 0 0 0;
+#X connect 11 0 0 0;
+#X connect 14 0 0 0;
+#X connect 36 0 4 0;
+#X connect 36 1 4 1;
+#X connect 37 0 4 2;
+#X connect 37 1 4 3;
+#X connect 38 0 4 4;
+#X connect 38 1 4 5;
diff --git a/lanbox/lanbox.pd b/lanbox/lanbox.pd
new file mode 100644
index 0000000..5fb8d88
--- /dev/null
+++ b/lanbox/lanbox.pd
@@ -0,0 +1,61 @@
+#N canvas 604 96 562 571 10;
+#X obj 46 30 inlet;
+#X msg 46 78 disconnect;
+#X msg 75 100 connect 192.168.0.77 777;
+#X text 283 157 keep connection alive;
+#X text 258 98 IP and port number of the lanbox;
+#X text 47 523 password (ascii value of : 777);
+#X obj 46 54 route 0 1;
+#X obj 138 291 delay 30000;
+#X obj 191 155 metro 30000;
+#X obj 106 240 sel 0 1;
+#X msg 113 265 stop;
+#X msg 138 317 1;
+#X msg 106 317 0;
+#X obj 106 345 f;
+#X obj 46 212 tcpclient;
+#X obj 110 421 list2symbol;
+#X msg 182 398 symbol _;
+#X msg 314 500 connected;
+#X obj 314 524 print lanbox;
+#X obj 110 448 sel 101_110_116_101_114_32_112_97_115_115_119_111_114_100_32_58
+99_111_110_110_101_99_116_101_100;
+#X msg 46 504 send 55 55 55 10;
+#X obj 182 376 loadbang;
+#X obj 183 256 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1
+1;
+#X msg 191 179 send 42 54 53 48 48 35 10;
+#X obj 105 149 spigot 0;
+#X msg 388 498 1;
+#X msg 77 266 0;
+#X connect 0 0 6 0;
+#X connect 1 0 14 0;
+#X connect 2 0 14 0;
+#X connect 6 0 1 0;
+#X connect 6 1 2 0;
+#X connect 6 2 24 0;
+#X connect 7 0 11 0;
+#X connect 8 0 23 0;
+#X connect 9 0 10 0;
+#X connect 9 0 12 0;
+#X connect 9 0 26 0;
+#X connect 9 1 7 0;
+#X connect 10 0 7 0;
+#X connect 11 0 13 0;
+#X connect 12 0 13 0;
+#X connect 13 0 8 0;
+#X connect 14 0 15 0;
+#X connect 14 2 9 0;
+#X connect 14 2 22 0;
+#X connect 15 0 19 0;
+#X connect 16 0 15 1;
+#X connect 17 0 18 0;
+#X connect 19 0 20 0;
+#X connect 19 1 17 0;
+#X connect 19 1 25 0;
+#X connect 20 0 14 0;
+#X connect 21 0 16 0;
+#X connect 23 0 14 0;
+#X connect 24 0 14 0;
+#X connect 25 0 24 1;
+#X connect 26 0 24 1;
diff --git a/memPIO/README.txt b/memPIO/README.txt
new file mode 100644
index 0000000..ad4d063
--- /dev/null
+++ b/memPIO/README.txt
@@ -0,0 +1,40 @@
+memPIO: an external to read from/write to the "mem-PIO"-device
+ by bmcm ( httP://www.bmcm.de ) with pure-data.
+
+the "mem-PIO" is a USB-device that offers digital (this is: 1 & 0) I/O.
+there are 3 ports, each 8 channels.
+each port can be set to either input or output
+(not quite true: port3 is split into 2 subports (4 channels each)
+that can be set to input resp. output
+separately; however this is not supported by this external)
+
+
+
+general::
+memPIO is a plugin for miller.s.puckette's realtime-computermusic-environment
+"puredata" (or abbreviated "pd")
+this software will be of no use, if you don't have a running version of pd on your system.
+check out for http://pd.iem.at to learn more about pd and how to get it.
+
+note: [memPIO] should be published under the Gnu General Public License.
+However, it is heavily based on the example-code provided by bmcm,
+which does not come with a copyright notice (at least i didn't find one)
+
+
+IMPORTANT NOTE:
+unfortunately [memPIO] is WINDOZE only (there are no drivers for other OS! shame on bmcm)
+
+win32 :
+extract the memPIO-0_x.zip.
+start pd with the extraction path included (e.g: pd -path PATH\TO\MY\MEMPIO\)
+create an object [memPIO]
+there is a helpfile help-memPIO.pd that explains what is happening.
+
+compiling:
+if you want to compile it for yourself, use the workspace memPIO.dsw.
+i am not sure, whether it works with anything but m$ visual-c++ 6.0
+
+authors::
+this software is copyleft by iohannes m zmoelnig <zmoelnig@iem.at>
+
+
diff --git a/memPIO/memPIO-help.pd b/memPIO/memPIO-help.pd
new file mode 100644
index 0000000..1353d07
--- /dev/null
+++ b/memPIO/memPIO-help.pd
@@ -0,0 +1,52 @@
+#N canvas 0 0 963 494 12;
+#X msg 49 90 bang;
+#X obj 49 40 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1
+;
+#X floatatom 92 243 5 0 0 1 port3 - -;
+#X msg 303 87 mode \$1;
+#X obj 303 68 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1
+;
+#X msg 635 107 set \$1;
+#X floatatom 635 167 3 0 255 1 8bit - -;
+#X msg 635 190 set 255 \$1 0;
+#X msg 303 129 mode 0 1 1;
+#X obj 49 210 memPIO;
+#X floatatom 70 264 5 0 0 1 port2 - -;
+#X floatatom 49 285 5 0 0 1 port1 - -;
+#X obj 49 64 metro 50;
+#X text 57 109 read readable ports;
+#X text 59 7 reading;
+#X text 633 23 writing;
+#X text 307 4 set port-direction:;
+#X text 302 23 0__readable (default);
+#X text 302 39 1__writeable;
+#X text 325 64 set all ports;
+#X text 306 113 set ports independently;
+#X floatatom 635 85 3 0 255 1 8bit - -;
+#X text 604 41 mode must be set to "writable";
+#X text 592 69 set all writable ports to the same value;
+#X text 617 150 write ports independently;
+#X text 238 325 note: to understand the values \, you will need some
+knowledge about bits.;
+#X floatatom 49 341 1 0 0 0 - - -;
+#X obj 49 318 & 1;
+#X text 65 342 line1 of port1;
+#X text 89 418 IMPORTANT NOTE: do not (un)plug the device while using
+[memPIO] !!;
+#X msg 251 242 help;
+#X connect 0 0 9 0;
+#X connect 1 0 12 0;
+#X connect 3 0 9 0;
+#X connect 4 0 3 0;
+#X connect 5 0 9 0;
+#X connect 6 0 7 0;
+#X connect 7 0 9 0;
+#X connect 8 0 9 0;
+#X connect 9 0 11 0;
+#X connect 9 1 10 0;
+#X connect 9 2 2 0;
+#X connect 11 0 27 0;
+#X connect 12 0 0 0;
+#X connect 21 0 5 0;
+#X connect 27 0 26 0;
+#X connect 30 0 9 0;
diff --git a/memPIO/memPIO.cpp b/memPIO/memPIO.cpp
new file mode 100644
index 0000000..e5f5158
--- /dev/null
+++ b/memPIO/memPIO.cpp
@@ -0,0 +1,192 @@
+/* ...this is an external for the memPIO interface...
+ *
+ * memPIO is a USB-interface with 3 8bit ports
+ * each port can be either input or output
+ * (honestly: port3 is divided into 2 subports of 4bits with independent directions,
+ * however, this is not yet implemented)
+ *
+ *
+ * "mode <port0> <port1> <port2> <port3>" : mode can be either 0 (input) or 1 (output)
+ * "mode <allports>": like above, but set all ports at once to the same value
+ * "bang": manual polling
+ * "set <val0> <val1> <val2> <val3>": write values to ports in output-mode
+ * "set <val>" write the same value to all ports
+ *
+ * TODO: currently two [memPIO]s interfere
+ *
+ *
+ * copyleft:forum::für::umläute:2004
+ */
+
+/* This only works on Microsoft Windows */
+#ifdef _WIN32
+
+#include <conio.h>
+
+#include "m_pd.h"
+
+
+// IMPORTANT: Adjust this path to your needs
+//#import "c:\windows\system\memx.ocx"
+#import "c:\winnt\system32\memx.ocx"
+
+/* do a little help thing */
+
+typedef struct memPIO
+{
+ t_object x_obj;
+ MEMXLib::_DmeMPIOPtr pio;
+ int input[3];
+ t_outlet *outlet[3];
+} t_memPIO;
+
+t_class *memPIO_class;
+
+static void memPIO_help(void)
+{
+ post("\n\n...mem-PIO for pd"
+ "\n\n(l) forum::für::umläute 2004\n"
+ "this software is under the GnuGPL that is provided with these files\n");
+ post("usage:");
+ post("\t'mode <1|0>': set all ports to writable(1) or readable(0=default)");
+ post("\t'mode <1|0> <1|0> <1|0>': set the read/write-mode of the individual ports");
+ post("\t'bang': returns the values of all readable ports");
+ post("\t'set <val>': set all writable ports to the 8bit-value");
+ post("\t'set <val1> <val2> <val3>': set writable ports to the corresponding value");
+}
+
+static void memPIO_bang(t_memPIO*x){
+ int port=3;
+
+ x->pio->UpdateCache ();
+
+ while(port--){
+ if (!x->input[port]){
+ long l=x->pio->CachedPort [port+1];
+ outlet_float(x->outlet[port], l);
+ }
+ }
+}
+
+
+static void memPIO_mode(t_memPIO*x, t_symbol *s, int argc, t_atom*argv){
+ MEMXLib::IO dir;
+ bool out;
+
+ switch(argc){
+ default:
+ error("memPIO: \"mode\" message needs 1 or 3 arguments, have %d", argc);
+ return;
+ case 1:
+ out=(atom_getfloat(argv)>0.f);
+ dir=(out)?MEMXLib::DirOut:MEMXLib::DirIn;
+ x->pio->DirPort1 =dir; x->input[0]=out;
+ x->pio->DirPort2 =dir; x->input[1]=out;
+ x->pio->DirPort3H=dir; x->input[2]=out;
+ x->pio->DirPort3L=dir;
+ break;
+ case 3:
+ out=(atom_getfloat(argv+0)>0.f);
+ x->pio->DirPort1 =(out)?MEMXLib::DirOut:MEMXLib::DirIn; x->input[0]=out;
+ out=(atom_getfloat(argv+1)>0.f);
+ x->pio->DirPort2 =(out)?MEMXLib::DirOut:MEMXLib::DirIn; x->input[1]=out;
+ out=(atom_getfloat(argv+2)>0.f);
+ x->pio->DirPort3H=(out)?MEMXLib::DirOut:MEMXLib::DirIn; x->input[2]=out;
+ x->pio->DirPort3L=(out)?MEMXLib::DirOut:MEMXLib::DirIn;
+ }
+}
+
+
+static void memPIO_set(t_memPIO*x, t_symbol*s, int argc, t_atom*argv){
+ int port=3;
+
+ unsigned char val[4]={0,0,0,0};
+
+ switch(argc){
+ case 1:
+ val[0]=val[1]=val[2]=val[3]=atom_getfloat(argv);
+ break;
+ case 3:
+ val[0]=atom_getfloat(argv+0);
+ val[1]=atom_getfloat(argv+1);
+ val[2]=atom_getfloat(argv+2);
+ break;
+ default:
+ break;
+ }
+
+ while(port--)
+ if(x->input[port]){
+ x->pio->port[port+1]=val[port];
+ }
+
+}
+
+
+static void memPIO_free(t_memPIO*x)
+{
+ x->pio.Release ();
+}
+
+void *memPIO_new(void)
+{
+ int i;
+ bool found=false;
+ t_memPIO *x = (t_memPIO *)pd_new(memPIO_class);
+ // because we use a ActiveX we need to initialize OLE for this app
+ OleInitialize (NULL);
+ // try to create the ActiveX-instance
+ x->pio.CreateInstance (__uuidof(MEMXLib::meMPIO));
+ // on error, there is no meM-ActiveX installed
+ if (x->pio == NULL)
+ {
+ error("meM ActiveX not installed!");
+ return 0;
+ }
+
+ found=false;
+ // search until the highest-possible Card-ID
+ for(i = 0; i < x->pio->LastAttached; i++)
+ {
+ // select a device
+ x->pio->CardId = i+1;
+ // if selected device is attached, return
+ if (x->pio->Attached){
+ found=true;
+ break;
+ }
+ }
+ i++;
+ if(!found){
+ memPIO_free(x);
+ return 0;
+ }
+
+ /* these are all outputs */
+ for (int n=0; n<3; n++) {
+ x->outlet[n] = outlet_new(&x->x_obj, 0);
+ x->input[n]=0;
+ }
+
+ return (void *)x;
+}
+
+void memPIO_setup(void)
+{
+ post("\t the memPIO external");
+ post("\t (l) forum::für::umläute");
+ post("\t compiled: "__DATE__"");
+
+ memPIO_class = class_new(gensym("memPIO"),
+ (t_newmethod)memPIO_new,
+ (t_method)memPIO_free,
+ sizeof(t_memPIO),
+ 0, A_NULL);
+ class_addmethod(memPIO_class, (t_method)memPIO_help, gensym("help"), A_NULL);
+ class_addmethod(memPIO_class, (t_method)memPIO_mode, gensym("mode"), A_GIMME, A_NULL);
+ class_addmethod(memPIO_class, (t_method)memPIO_set, gensym("set"), A_GIMME, A_NULL);
+
+ class_addbang(memPIO_class, (t_method)memPIO_bang);
+}
+
+#endif /* _WIN32 */
diff --git a/memPIO/memPIO.dsp b/memPIO/memPIO.dsp
new file mode 100644
index 0000000..12dcf57
--- /dev/null
+++ b/memPIO/memPIO.dsp
@@ -0,0 +1,67 @@
+# Microsoft Developer Studio Project File - Name="memPIO" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** NICHT BEARBEITEN **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=memPIO - WIN32 RELEASE
+!MESSAGE Dies ist kein gültiges Makefile. Zum Erstellen dieses Projekts mit NMAKE
+!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und führen Sie den Befehl
+!MESSAGE
+!MESSAGE NMAKE /f "memPIO.mak".
+!MESSAGE
+!MESSAGE Sie können beim Ausführen von NMAKE eine Konfiguration angeben
+!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel:
+!MESSAGE
+!MESSAGE NMAKE /f "memPIO.mak" CFG="memPIO - WIN32 RELEASE"
+!MESSAGE
+!MESSAGE Für die Konfiguration stehen zur Auswahl:
+!MESSAGE
+!MESSAGE "memPIO - Win32 Release" (basierend auf "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 1
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ""
+# PROP Intermediate_Dir "obj\"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "memPIO_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /I "C:\Programme\pd\src" /D "WIN32" /D "NT" /D "_WINDOWS" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /win32
+# SUBTRACT MTL /mktyplib203
+# ADD BASE RSC /l 0xc07 /d "NDEBUG"
+# ADD RSC /l 0xc07
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 pd.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib ole32.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /libpath:"C:\Programme\pd\bin" /export:memPIO_setup
+# SUBTRACT LINK32 /pdb:none /nodefaultlib
+# Begin Target
+
+# Name "memPIO - Win32 Release"
+# Begin Group "Quellcodedateien"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\memPIO.cpp
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/memPIO/memPIO.dsw b/memPIO/memPIO.dsw
new file mode 100644
index 0000000..3ef1c58
--- /dev/null
+++ b/memPIO/memPIO.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNUNG: DIESE ARBEITSBEREICHSDATEI DARF NICHT BEARBEITET ODER GELÖSCHT WERDEN!
+
+###############################################################################
+
+Project: "memPIO"=.\memPIO.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/multio/Makefile b/multio/Makefile
new file mode 100755
index 0000000..ede6349
--- /dev/null
+++ b/multio/Makefile
@@ -0,0 +1,72 @@
+NAME=multio
+CSYM=multio
+
+current: pd_linux
+
+pd_src = ../../../pd
+
+# ----------------------- NT -----------------------
+
+pd_nt: $(NAME).dll
+
+.SUFFIXES: .dll
+
+VC="C:\Programmi\Microsoft Visual Studio .NET\Vc7"
+PDPATH="H:\PureData\pd-0.38-3.msw\pd"
+LIBUSBPATH="H:\PureData\multIO\libusb-win32-device-bin-0.1.10.1"
+PDNTCFLAGS = /W3 /WX /DNT /DPD /nologo
+
+
+PDNTINCLUDE = /I. (PDPATH)\tcl\include /I$(PDPATH)\src /I$(VC)\include /I$(LIBUSBPATH)\include
+
+PDNTLDIR = $(VC)\lib
+PDNTLIB = $(PDNTLDIR)\libc.lib \
+ $(PDNTLDIR)\oldnames.lib \
+ $(PDNTLDIR)\kernel32.lib \
+ $(LIBUSBPATH)\lib\msvc\libusb.lib \
+ $(PDPATH)\bin\pd.lib \
+ $(PDPATH)\bin\pthreadVC.lib
+
+.c.dll:
+ cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $*.c
+ link /dll /export:$(CSYM)_setup $*.obj $(PDNTLIB)
+
+# ----------------------- LINUX i386 -----------------------
+
+pd_linux: $(NAME).pd_linux
+
+.SUFFIXES: .pd_linux
+
+LINUXCFLAGS = -DPD -O2 -funroll-loops -fomit-frame-pointer -fPIC \
+ -Wall -W -Wshadow -Wstrict-prototypes \
+ -Wno-unused -Wno-parentheses -Wno-switch $(CFLAGS)
+
+LINUXINCLUDE = -I../../src -I$(pd_src)/src
+
+.c.pd_linux:
+ $(CC) $(LINUXCFLAGS) $(LINUXINCLUDE) -o $*.o -c $*.c
+ ld --export-dynamic -shared -o $*.pd_linux $*.o -lc -lm -lusb
+ strip --strip-unneeded $*.pd_linux
+ rm -f $*.o
+
+# ----------------------- Mac OSX -----------------------
+
+pd_darwin: $(NAME).pd_darwin
+
+.SUFFIXES: .pd_darwin
+
+DARWINCFLAGS = -DPD -O2 -Wall -W
+
+DARWININCLUDE = -I../../src -I$(pd_src)/src -I/sw/include
+DARWINLIBS = -L/sw/lib -lusb
+
+.c.pd_darwin:
+ $(CC) $(DARWINCFLAGS) $(DARWININCLUDE) -o $*.o -c $*.c
+ $(CC) -bundle -bundle_loader $(pd_src)/bin/pd $(DARWINLIBS) \
+ -o $*.pd_darwin $*.o
+ rm -f $*.o
+
+# ----------------------------------------------------------
+
+clean:
+ rm -f *.o *.pd_* so_locations
diff --git a/multio/multio-help.pd b/multio/multio-help.pd
new file mode 100644
index 0000000..eaae25b
--- /dev/null
+++ b/multio/multio-help.pd
@@ -0,0 +1,95 @@
+#N canvas 112 95 1011 631 12;
+#X obj 132 124 tgl 30 0 empty empty empty 0 -6 0 8 -62784 -1 -1 0 1
+;
+#X obj 506 210 unpack f f;
+#X floatatom 421 247 5 0 0 0 - - -;
+#X floatatom 499 248 5 0 0 0 - - -;
+#X msg 132 161 open;
+#X obj 132 202 multio;
+#X msg 132 69 readout_time 20;
+#X msg 132 98 readout_time 1000;
+#X msg 132 25 readout_time 1;
+#X text 185 163 <-- open the device (opened by default if possible)
+;
+#X text 170 132 <-- start/stop readout of the device (on by default)
+;
+#X text 290 39 <--+ | <--+-> set the time between readouts (msec) |
+20 msec is the default <--+;
+#X obj 123 357 vsl 30 128 0 4096 0 0 empty empty empty 0 -8 0 8 -128992
+-1 -1 59 1;
+#X obj 154 357 vsl 30 128 0 4096 0 0 empty empty empty 0 -8 0 8 -128992
+-1 -1 198 1;
+#X obj 185 357 vsl 30 128 0 4096 0 0 empty empty empty 0 -8 0 8 -128992
+-1 -1 177 1;
+#X obj 216 357 vsl 30 128 0 4096 0 0 empty empty empty 0 -8 0 8 -128992
+-1 -1 155 1;
+#X obj 247 357 vsl 30 128 0 4096 0 0 empty empty empty 0 -8 0 8 -128992
+-1 -1 3 1;
+#X obj 278 357 vsl 30 128 0 4096 0 0 empty empty empty 0 -8 0 8 -128992
+-1 -1 0 1;
+#X obj 309 357 vsl 30 128 0 4096 0 0 empty empty empty 0 -8 0 8 -128992
+-1 -1 186 1;
+#X obj 340 357 vsl 30 128 0 4096 0 0 empty empty empty 0 -8 0 8 -128992
+-1 -1 769 1;
+#X obj 371 357 vsl 30 128 0 4096 0 0 empty empty empty 0 -8 0 8 -128992
+-1 -1 353 1;
+#X obj 402 357 vsl 30 128 0 4096 0 0 empty empty empty 0 -8 0 8 -128992
+-1 -1 1659 1;
+#X obj 433 357 vsl 30 128 0 4096 0 0 empty empty empty 0 -8 0 8 -128992
+-1 -1 1380 1;
+#X obj 123 274 route 0 1 2 3 4 5 6 7 8 9 10;
+#X obj 433 311 nbx 4 16 -1e+037 1e+037 0 0 empty empty empty 0 -6 0
+14 -262144 -1 -1 445 256;
+#X obj 402 330 nbx 4 16 -1e+037 1e+037 0 0 empty empty empty 0 -6 0
+14 -262144 -1 -1 535 256;
+#X obj 371 311 nbx 4 16 -1e+037 1e+037 0 0 empty empty empty 0 -6 0
+14 -262144 -1 -1 114 256;
+#X obj 340 330 nbx 4 16 -1e+037 1e+037 0 0 empty empty empty 0 -6 0
+14 -262144 -1 -1 248 256;
+#X obj 309 311 nbx 4 16 -1e+037 1e+037 0 0 empty empty empty 0 -6 0
+14 -262144 -1 -1 60 256;
+#X obj 278 330 nbx 4 16 -1e+037 1e+037 0 0 empty empty empty 0 -6 0
+14 -262144 -1 -1 0 256;
+#X obj 247 311 nbx 4 16 -1e+037 1e+037 0 0 empty empty empty 0 -6 0
+14 -262144 -1 -1 1 256;
+#X obj 216 330 nbx 4 16 -1e+037 1e+037 0 0 empty empty empty 0 -6 0
+14 -262144 -1 -1 50 256;
+#X obj 185 311 nbx 4 16 -1e+037 1e+037 0 0 empty empty empty 0 -6 0
+14 -262144 -1 -1 57 256;
+#X obj 154 330 nbx 4 16 -1e+037 1e+037 0 0 empty empty empty 0 -6 0
+14 -262144 -1 -1 64 256;
+#X obj 123 311 nbx 4 16 -1e+037 1e+037 0 0 empty empty empty 0 -6 0
+14 -262144 -1 -1 19 256;
+#X msg 130 46 readout_time 5;
+#X connect 0 0 5 0;
+#X connect 1 0 2 0;
+#X connect 1 1 3 0;
+#X connect 4 0 5 0;
+#X connect 5 0 23 0;
+#X connect 5 1 1 0;
+#X connect 6 0 5 0;
+#X connect 7 0 5 0;
+#X connect 8 0 5 0;
+#X connect 23 0 34 0;
+#X connect 23 1 33 0;
+#X connect 23 2 32 0;
+#X connect 23 3 31 0;
+#X connect 23 4 30 0;
+#X connect 23 5 29 0;
+#X connect 23 6 28 0;
+#X connect 23 7 27 0;
+#X connect 23 8 26 0;
+#X connect 23 9 25 0;
+#X connect 23 10 24 0;
+#X connect 24 0 22 0;
+#X connect 25 0 21 0;
+#X connect 26 0 20 0;
+#X connect 27 0 19 0;
+#X connect 28 0 18 0;
+#X connect 29 0 17 0;
+#X connect 30 0 16 0;
+#X connect 31 0 15 0;
+#X connect 32 0 14 0;
+#X connect 33 0 13 0;
+#X connect 34 0 12 0;
+#X connect 35 0 5 0;
diff --git a/multio/multio.c b/multio/multio.c
new file mode 100644
index 0000000..0bda71f
--- /dev/null
+++ b/multio/multio.c
@@ -0,0 +1,614 @@
+/*
+multio
+
+connects to multIO and listens..
+left outlet for analog
+middle for digital
+
+output is a list of 2 floats (channel, value)
+
+
+*/
+#ifdef _WIN32
+#include <windows.h>
+#endif /* _WIN32 */
+
+#include "m_pd.h"
+#include "usb.h"
+#include "pthread.h"
+
+
+#define DEFDELTIME 20 // time between readouts in msec
+#define TIMEOUT 1000 // timeout time in usb interrupts reading and writing
+#define MAXBUF 1024
+
+/* PICkit USB values */
+static const int multio_vendorID=0xdead; // Microchip, Inc
+static const int multio_productID=0xbeef; // PICkit 1 FLASH starter kit
+static const int multio_configuration=2; /* 1: HID; 2: vendor specific */
+static const int multio_interface=0;
+static const int reqLen=8;
+static char is_open;
+typedef unsigned char byte;
+
+static t_class *multio_class;
+
+typedef struct _multio
+{
+ t_object x_obj; // myself
+ usb_dev_handle *d; // handle to multIO
+ t_clock *x_clock; // as in metro
+ double x_deltime; // as in metro
+ int x_hit; // as in metro
+ pthread_attr_t multio_thread_attr;
+ pthread_t x_threadid;
+ unsigned char double_buffer[2][MAXBUF]; // a double buffer: thread writes one, cyclic read of the other one
+ // the second parameter should be deafult 1000 but setable via
+ // object parameters
+ int buf_count[2]; // how many bytes are in a buffer
+ unsigned char whichbuf; // which one to read from
+ char old_digi[8]; // buffer of digital input, is a byte, 8 values at a time
+ char digi_outs[8]; // buffer of digital input, is a byte, 8 values at a time
+ int analog_buffer[64]; // buffered analog outs
+ int x_verbose;
+ t_outlet *a_out, *d_out, *s_out; // outlets
+} t_multio;
+
+static void *usb_read_thread(void *w)
+{
+ t_multio *x = (t_multio*) w;
+ int cnt = 0;
+ int bytesread = 0;
+ unsigned char mybuf = 1;
+ unsigned char buffer[8];
+ while(1) // never ending
+ {
+ pthread_testcancel();
+ if(x->d) // only read if the device is opened
+ {
+ if(x->buf_count[x->whichbuf] <= 0) // check if the read buffer is empty
+ {
+ mybuf = x->whichbuf; // if so, use it for writing
+ x->whichbuf = !(x->whichbuf &1); // and toggle the read buffer
+ }
+ bytesread = usb_interrupt_read(x->d, 0x81, buffer, 8, 1000);
+ if(bytesread > 0)
+ {
+ if(x->buf_count[mybuf]+bytesread > MAXBUF)
+ x->buf_count[mybuf] = 0;
+ x->double_buffer[mybuf][x->buf_count[mybuf]++] = bytesread; // store the number of bytes for that message
+ for(cnt = 0; cnt < bytesread; cnt++) // append the message data into the buffer
+ {
+ x->double_buffer[mybuf][x->buf_count[mybuf]++] = buffer[cnt];
+ }
+// if(x->x_verbose)post("thread read %i bytes to buffer %i (now %i bytes)",bytesread, mybuf,x->buf_count[mybuf] );
+ }
+ }
+#ifdef _WIN32
+ Sleep(1);
+#endif /* _WIN32 */
+ }
+}
+
+static void start_thread(t_multio *x)
+{
+
+// create the worker thread
+ if(pthread_attr_init(&x->multio_thread_attr) < 0)
+ {
+ error("multio: could not launch receive thread");
+ return;
+ }
+ if(pthread_attr_setdetachstate(&x->multio_thread_attr, PTHREAD_CREATE_DETACHED) < 0)
+ {
+ error("multio: could not launch receive thread");
+ return;
+ }
+ if(pthread_create(&x->x_threadid, &x->multio_thread_attr, usb_read_thread, x) < 0)
+ {
+ error("multio: could not launch receive thread");
+ return;
+ }
+ else
+ {
+ if(x->x_verbose)post("multio: thread %d launched", (int)x->x_threadid );
+ }
+}
+
+// methods invoked by the inlets
+static void multio_analog_write(t_multio *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int channel;
+ int value;
+ unsigned char buffer[8];
+ int bytesread;
+
+ if (argc<2)
+ {
+ error("multio: multio_analog_write error: i need minimum 2 values list");
+ return;
+ }
+
+ if (!(x->d))
+ {
+ error("multio: multI/O not initialized");
+ return;
+ }
+
+ channel = atom_getfloat(argv++);
+ value = atom_getfloat(argv);
+
+ if(channel < 0 || channel > 63)
+ {
+ error("multio: inconsistent dac output channel: %d", channel);
+ return;
+ }
+
+ if (value != x->analog_buffer[channel])
+ {
+ x->analog_buffer[channel] = value;
+ buffer[0] = 97 + channel; // channel is 0 based
+ buffer[1] = value & 0xff;
+ buffer[2] = (value & 0xff00) >> 8;
+ bytesread = usb_interrupt_write(x->d, 1, buffer, 3, TIMEOUT);
+ }
+}
+
+
+static void multio_digi_write(t_multio *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int channel;
+ int value;
+ unsigned char buffer[8];
+ int bytesread;
+ char testbit = 0x01;
+ int count;
+ int group;
+ int channel_in_group;
+ char ctmp;
+ char bitmask;
+
+ if (argc<2)
+ {
+ error("multio: multio_digi_write error: i need minimum 2 values list");
+ return;
+ }
+ channel = atom_getfloat(argv++);
+ value = atom_getfloat(argv);
+
+ if(channel < 0 || channel > 63)
+ {
+ error("multio: inconsistent digital output channel: %d", channel);
+ return;
+ }
+
+ group = channel / 8 ;
+ channel_in_group = channel % 8;
+
+ bitmask = 0x01 << channel_in_group;
+ ctmp = x->digi_outs[group] & ~bitmask;
+ if (value)
+ ctmp = ctmp | bitmask;
+ if(ctmp != x->digi_outs[group])
+ {
+ x->digi_outs[group] = ctmp;
+ buffer[0] = group + 1; // + 1 is the offset for digi outs (1..9)
+ buffer[1] = ctmp;
+ bytesread = usb_interrupt_write(x->d, 1, buffer, 3, TIMEOUT);
+ if(x->x_verbose)post("multio: writing %i to group %i", ctmp, group);
+ }
+
+}
+
+static void multio_system_write(t_multio *x, t_symbol *s, int argc, t_atom *argv)
+{
+
+ unsigned char cmd, bvalue, smallvalue;
+ unsigned short int value;
+ unsigned char buffer[5];
+
+ if (argc<3)
+ {
+ error("multio: multio_system_write error: i need minimum 3 values list");
+ return;
+ }
+ cmd = atom_getfloat(argv++);
+ bvalue = atom_getfloat(argv++);
+
+ buffer[0] = 161;
+ buffer[1] = cmd;
+ buffer[2] = bvalue;
+
+ switch(cmd)
+ {
+ case 0:
+ case 1: usb_interrupt_write(x->d, 1, buffer, 3, TIMEOUT); break;
+ case 2:
+ case 3:
+ case 4: value = atom_getfloat(argv);
+ buffer[3] = value & 0x00ff;
+ buffer[4] = value >> 8;
+ usb_interrupt_write(x->d, 1, buffer, 5, TIMEOUT);
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8: buffer[3] = atom_getfloat(argv);
+ usb_interrupt_write(x->d, 1, buffer, 4, TIMEOUT);
+ break;
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17: usb_interrupt_write(x->d, 1, buffer, 3, TIMEOUT); break;
+ default: error("multio: unknown system command"); break;
+ }
+}
+
+// read & process the buffer
+// this will be called each tick in the pd object
+static void process_buffer(t_multio *x)
+{
+ int cnt;
+ if(x->buf_count[x->whichbuf] > 0)
+ {
+// post("process %i bytes buffer\n", x->buf_count[x->whichbuf]);
+ for(cnt = 0; cnt < x->buf_count[x->whichbuf]; cnt++)
+ {
+ if(x->double_buffer[x->whichbuf][cnt] == 2)
+ {
+// post("process buf: %i msglen: %i id: %i val: %i", x->whichbuf, x->double_buffer[x->whichbuf][cnt],x->double_buffer[x->whichbuf][cnt+1],x->double_buffer[x->whichbuf][cnt+2]);
+ if (x->double_buffer[x->whichbuf][cnt+1]>=1 && x->double_buffer[x->whichbuf][cnt+1] <= 8)
+ {
+ // digital input
+ //send_digi(x, first-1, buffer[1]);
+ char testbit = 0x01;
+ t_atom lista[2];
+ int count;
+ int group = x->double_buffer[x->whichbuf][cnt+1]-1;
+
+ for(count = 0; count < 8; count++)
+ {
+ if((x->double_buffer[x->whichbuf][cnt+2] & testbit) != (x->old_digi[group] & testbit))
+ {
+ SETFLOAT(lista, (group*8)+count);
+ if(x->double_buffer[x->whichbuf][cnt+2] & testbit)
+ SETFLOAT(lista+1, 1);
+ else
+ SETFLOAT(lista+1, 0);
+ outlet_anything(x->d_out, gensym("list") , 2, lista);
+ }
+ testbit <<= 1;
+ }
+ x->old_digi[group] = x->double_buffer[x->whichbuf][cnt+2];
+ }
+ cnt += 2;
+ }
+ else if(x->double_buffer[x->whichbuf][cnt] == 3)
+ {
+// post("process buf: %i msglen: %i id: %i val: %i", x->whichbuf, x->double_buffer[x->whichbuf][cnt],x->double_buffer[x->whichbuf][cnt+1],x->double_buffer[x->whichbuf][cnt+2] + (x->double_buffer[x->whichbuf][cnt+3] << 8));
+ if (x->double_buffer[x->whichbuf][cnt+1]>=9 && x->double_buffer[x->whichbuf][cnt+1] <=96)
+ {
+ // analog input
+ t_atom lista[2];
+ int result;
+ int channel = x->double_buffer[x->whichbuf][cnt+1]-9;
+
+ result = x->double_buffer[x->whichbuf][cnt+2] + (x->double_buffer[x->whichbuf][cnt+3] << 8);
+ x->analog_buffer[channel] = result;
+
+ SETFLOAT(lista, channel);
+ SETFLOAT(lista+1, result);
+ outlet_anything(x->a_out, gensym("list"),2 , lista);
+ }
+ cnt += 3;
+ }
+ else
+ cnt += x->double_buffer[x->whichbuf][cnt];
+ }
+ x->buf_count[x->whichbuf] = 0;
+ }
+}
+
+/*
+// method invoked by the timer
+static void multio_read(t_multio *x)
+{
+ unsigned char buffer[8];
+ int first;
+ int bytesread;
+ byte retData[8];
+ int reads=64;
+
+ if (x->d)
+ {
+ } else
+ {
+ error("multIO: connection not inizialized");
+ return;
+ }
+
+ while(usb_interrupt_read(x->d, 0x81, buffer, 8, 2) > 0)
+
+ {
+// reads--;
+//if (usb_interrupt_read(x->d, 0x81, buffer, 8, TIMEOUT) > 0)
+//{
+ first = buffer[0];
+
+ if (first>=1 && first <= 8)
+ {
+ // digital input
+ //send_digi(x, first-1, buffer[1]);
+ char testbit = 0x01;
+ t_atom lista[2];
+ int count;
+ int group = first-1;
+
+ for(count = 0; count < 8; count++)
+ {
+ if((buffer[1] & testbit) != (x->old_digi[group] & testbit))
+ {
+ SETFLOAT(lista, (group*8)+count);
+ if(buffer[1] & testbit)
+ SETFLOAT(lista+1, 1);
+ else
+ SETFLOAT(lista+1, 0);
+ outlet_anything(x->d_out, gensym("list") , 2, lista);
+ }
+ testbit <<= 1;
+ }
+ x->old_digi[group] = buffer[1];
+ }
+
+ if (first>=9 && first <=96)
+ {
+ // analog input
+ t_atom lista[2];
+ int result;
+ int channel = first-9;
+
+ result = buffer[1] + (buffer[2] << 8);
+ x->analog_buffer[channel] = result;
+
+ SETFLOAT(lista, channel);
+ SETFLOAT(lista+1, result);
+ outlet_anything(x->a_out, gensym("list"),2 , lista);
+ }
+
+ if (first==161)
+ {
+ t_atom list2[2];
+ t_atom list3[3];
+
+ switch(buffer[1])
+ {
+ case 0:
+ case 1: SETFLOAT(list2, buffer[1]);
+ SETFLOAT(list2+1, buffer[2]);
+ outlet_anything(x->s_out, gensym("list"),2 , list2);
+ break;
+ case 2:
+ case 3:
+ case 4: SETFLOAT(list3, buffer[1]);
+ SETFLOAT(list3+1, buffer[2]);
+ SETFLOAT(list3+2, (float)(buffer[3] + (buffer[4]<<8)) );
+ outlet_anything(x->s_out, gensym("list"),3 , list3);
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8: SETFLOAT(list3, buffer[1]);
+ SETFLOAT(list3+1, buffer[2]);
+ SETFLOAT(list3+2, buffer[3]);
+ outlet_anything(x->s_out, gensym("list"),3 , list3);
+ break;
+ case 9: SETFLOAT(list2, buffer[1] - 9);
+ outlet_anything(x->a_out, gensym("list"),2 , list2);
+ break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17: SETFLOAT(list3, buffer[1]);
+ SETFLOAT(list3+1, buffer[2]);
+ SETFLOAT(list3+2, buffer[3]);
+ outlet_anything(x->s_out, gensym("list"),3 , list3);
+ break;
+ default: error("unknown system command echo"); break;
+ }
+ // system input
+ }
+ }
+}
+*/
+
+static void multio_open(t_multio *x)
+{
+ struct usb_device *device;
+ struct usb_bus* bus;
+ unsigned char buffer[8];
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+
+
+ for (bus=usb_get_busses();bus!=NULL;bus=bus->next)
+ {
+ struct usb_device* usb_devices = bus->devices;
+
+
+ for(device=usb_devices;device;device=device->next)
+ {
+
+ if (device->descriptor.idVendor == multio_vendorID
+ &&device->descriptor.idProduct == multio_productID)
+ {
+
+ post( "multio: Found mamalala multI/O as device '%s' on USB bus %s",
+ device->filename,
+ device->bus->dirname);
+ x->d=usb_open(device);
+
+ if (x->d)
+ { /* This is our device-- claim it */
+
+ if (usb_set_configuration(x->d,multio_configuration)) {
+ post("multio: Error setting USB configuration.");
+ usb_close(x->d);
+ return;
+ }
+ else post("multio: Selecting non-HID config");
+
+
+ if (usb_claim_interface(x->d,multio_interface)) {
+ post("multio: Claim failed-- the mamalala multI/O is in use by another driver.\n"
+ "multio: Do a `dmesg` to see which kernel driver has claimed it--\n"
+ "multio: You may need to `rmmod hid` or patch your kernel's hid driver.");
+ usb_close(x->d);
+ return;
+
+ }
+ else post("multio: Claimed interface");
+ while(usb_interrupt_read(x->d, 0x81, buffer, 8, 10) > 0)
+ {
+
+ }
+ return;
+ }
+ }
+ }
+ }
+ // if i am here then i couldn't find mutlIO!
+ error("multio: unable to find multI/O !");
+}
+
+
+
+
+static void multio_tick(t_multio *x)
+{
+ x->x_hit = 0;
+// multio_read(x);
+process_buffer(x);
+ if (!x->x_hit) clock_delay(x->x_clock, x->x_deltime);
+}
+
+static void multio_float(t_multio *x, t_float f)
+{
+ if (f != 0) multio_tick(x);
+ else clock_unset(x->x_clock);
+ x->x_hit = 1;
+}
+
+static void multio_start(t_multio *x)
+{
+ multio_float(x, 1);
+}
+
+static void multio_stop(t_multio *x)
+{
+ multio_float(x, 0);
+}
+
+static void multio_ft1(t_multio *x, t_floatarg g)
+{
+ if (g < 1) g = 1;
+ x->x_deltime = g;
+}
+static void multio_verbose(t_multio *x, t_floatarg g)
+{
+ x->x_verbose=(g > 0) ;
+}
+
+
+
+static void multio_free(t_multio *x)
+{
+if(is_open)
+ {
+ clock_free(x->x_clock);
+ if(x->d)
+ {
+ while(pthread_cancel(x->x_threadid) < 0)
+ if(x->x_verbose)post("multio: killing thread\n");
+ if(x->x_verbose)post("multio: thread canceled\n");
+ usb_close(x->d);
+ }
+ is_open = 0;
+ }
+ else
+ if(x->x_verbose)post("multio: not active object");
+}
+
+
+static void *multio_new(t_symbol *s, int argc, t_atom *argv)
+{
+if(!is_open)
+{
+ t_multio *x = (t_multio *)pd_new(multio_class);
+ x->x_clock = clock_new(x, (t_method)multio_tick);
+ x->x_deltime = DEFDELTIME;
+ x->x_verbose = 0;
+ x->a_out = outlet_new(&x->x_obj, &s_list);
+ x->d_out = outlet_new(&x->x_obj, &s_list);
+ x->s_out = outlet_new(&x->x_obj, &s_list);
+ // look for multIO
+
+ multio_open(x);
+ if(x->d)
+ start_thread(x);
+ // inlets for digital and system
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("list"), gensym("digi_write")); // remap to digi_write
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("list"), gensym("system_write"));
+ is_open = 1;
+ multio_start(x);
+ return (x);
+}
+else
+{
+error("multio: object already exists");
+return(0);
+}
+}
+
+void multio_setup(void)
+{
+ multio_class = class_new(gensym("multio"), (t_newmethod)multio_new,
+ (t_method)multio_free, sizeof(t_multio), CLASS_DEFAULT, A_GIMME, 0);
+ //class_addbang(multio_class, (t_method)multio_bang);
+ // to set the time between 2 readouts
+ class_addmethod(multio_class, (t_method)multio_ft1, gensym("readout_time"),
+ A_FLOAT, 0);
+ class_addfloat(multio_class, (t_method)multio_float); // start/stop using a toggle
+ // to stop reading
+ class_addmethod(multio_class, (t_method)multio_stop, gensym("stop"), 0);
+ // to start reading
+ class_addmethod(multio_class, (t_method)multio_start, gensym("start"), 0);
+ // open the device
+ class_addmethod(multio_class, (t_method)multio_open, gensym("open"), 0);
+ // write analog data using leftmost inlet
+ class_addlist(multio_class, (t_method)multio_analog_write);
+ class_addmethod(multio_class, (t_method)multio_digi_write, gensym("digi_write"),
+ A_GIMME, 0);
+ class_addmethod(multio_class, (t_method)multio_system_write, gensym("system_write"),
+ A_GIMME, 0);
+is_open = 0;
+
+ class_addmethod(multio_class, (t_method)multio_verbose, gensym("verbose"), A_FLOAT, 0);
+
+ // welcome message
+ post("\nmultio: a pd driver for the multI/O USB device");
+ post("multio: www.davidemorelli.it - multio.mamalala.de");
+
+
+}
+
+
diff --git a/multio/multio.libs b/multio/multio.libs
new file mode 100644
index 0000000..d567bab
--- /dev/null
+++ b/multio/multio.libs
@@ -0,0 +1 @@
+-lusb
diff --git a/sixaxis/Makefile b/sixaxis/Makefile
new file mode 100644
index 0000000..51c9331
--- /dev/null
+++ b/sixaxis/Makefile
@@ -0,0 +1,242 @@
+# To use this Makefile for your project, first put the name of your library in
+# LIBRARY_NAME variable. The folder for your project should have the same name
+# as your library.
+LIBRARY_NAME = sixaxis
+LIBRARY_VERSION = 0.1
+
+# Next, add your .c source files to the SOURCES variable. The help files will
+# be included automatically
+SOURCES =
+
+# For objects that only build on certain platforms, add those to the SOURCES
+# line for the right platforms.
+SOURCES_android =
+SOURCES_cygwin =
+SOURCES_macosx =
+SOURCES_iphoneos =
+SOURCES_linux = sixaxis.c
+SOURCES_windows =
+
+# list all pd objects (i.e. myobject.pd) files here, and their helpfiles will
+# be included automatically
+PDOBJECTS =
+
+# if you want to include any other files in the source and binary tarballs,
+# list them here. This can be anything from header files, READMEs, example
+# patches, documentation, etc.
+EXTRA_DIST =
+
+
+#------------------------------------------------------------------------------#
+#
+# you shouldn't need to edit anything below here, if we did it right :)
+#
+#------------------------------------------------------------------------------#
+
+# where Pd lives
+PD_PATH = ../../../pd
+# where to install the library
+prefix = /usr/local
+libdir = $(prefix)/lib
+pkglibdir = $(libdir)/pd-externals
+objectsdir = $(pkglibdir)
+
+
+INSTALL = install
+INSTALL_FILE = $(INSTALL) -p -m 644
+INSTALL_DIR = $(INSTALL) -p -m 755 -d
+
+CFLAGS = -DPD -I$(PD_PATH)/src -Wall -W -g
+LDFLAGS =
+LIBS =
+ALLSOURCES := $(SOURCES) $(SOURCES_android) $(SOURCES_cygwin) $(SOURCES_macosx) \
+ $(SOURCES_iphoneos) $(SOURCES_linux) $(SOURCES_windows)
+
+UNAME := $(shell uname -s)
+ifeq ($(UNAME),Darwin)
+ CPU := $(shell uname -p)
+ ifeq ($(CPU),arm) # iPhone/iPod Touch
+ SOURCES += $(SOURCES_macosx)
+ EXTENSION = pd_darwin
+ OS = iphoneos
+ IPHONE_BASE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin
+ CC=$(IPHONE_BASE)/gcc
+ CPP=$(IPHONE_BASE)/cpp
+ CXX=$(IPHONE_BASE)/g++
+ ISYSROOT = -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk
+ IPHONE_CFLAGS = -miphoneos-version-min=3.0 $(ISYSROOT) -arch armv6
+ OPT_CFLAGS = -fast -funroll-loops -fomit-frame-pointer
+ CFLAGS := $(IPHONE_CFLAGS) $(OPT_CFLAGS) $(CFLAGS) \
+ -I/Applications/Pd-extended.app/Contents/Resources/include
+ LDFLAGS += -arch armv6 -bundle -undefined dynamic_lookup $(ISYSROOT)
+ LIBS += -lc
+ STRIP = strip -x
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+ else # Mac OS X
+ SOURCES += $(SOURCES_macosx)
+ EXTENSION = pd_darwin
+ OS = macosx
+ OPT_CFLAGS = -ftree-vectorize -ftree-vectorizer-verbose=2 -fast
+ FAT_FLAGS = -arch i386 -arch ppc -mmacosx-version-min=10.4
+ CFLAGS += $(FAT_FLAGS) -fPIC -I/sw/include \
+ -I/Applications/Pd-extended.app/Contents/Resources/include
+ LDFLAGS += $(FAT_FLAGS) -bundle -undefined dynamic_lookup -L/sw/lib
+ # if the 'pd' binary exists, check the linking against it to aid with stripping
+ LDFLAGS += $(shell test -e $(PD_PATH)/bin/pd && echo -bundle_loader $(PD_PATH)/bin/pd)
+ LIBS += -lc
+ STRIP = strip -x
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+ endif
+endif
+ifeq ($(UNAME),Linux)
+ SOURCES += $(SOURCES_linux)
+ EXTENSION = pd_linux
+ OS = linux
+ OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer
+ CFLAGS += -fPIC
+ LDFLAGS += -Wl,--export-dynamic -shared -fPIC
+ LIBS += -lc
+ STRIP = strip --strip-unneeded -R .note -R .comment
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m)
+endif
+ifeq (CYGWIN,$(findstring CYGWIN,$(UNAME)))
+ SOURCES += $(SOURCES_cygwin)
+ EXTENSION = dll
+ OS = cygwin
+ OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer
+ CFLAGS +=
+ LDFLAGS += -Wl,--export-dynamic -shared -L$(PD_PATH)/src
+ LIBS += -lc -lpd
+ STRIP = strip --strip-unneeded -R .note -R .comment
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+endif
+ifeq (MINGW,$(findstring MINGW,$(UNAME)))
+ SOURCES += $(SOURCES_windows)
+ EXTENSION = dll
+ OS = windows
+ OPT_CFLAGS = -O3 -funroll-loops -fomit-frame-pointer
+ WINDOWS_HACKS = -D'O_NONBLOCK=1'
+ CFLAGS += -mms-bitfields $(WINDOWS_HACKS)
+ LDFLAGS += -s -shared -Wl,--enable-auto-import
+ LIBS += -L$(PD_PATH)/src -L$(PD_PATH)/bin -L$(PD_PATH)/obj -lpd -lwsock32 -lkernel32 -luser32 -lgdi32
+ STRIP = strip --strip-unneeded -R .note -R .comment
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+endif
+
+CFLAGS += $(OPT_CFLAGS)
+
+
+.PHONY = install libdir_install single_install install-doc install-exec install-examples clean dist etags
+
+all: $(SOURCES:.c=.$(EXTENSION))
+
+%.o: %.c
+ $(CC) $(CFLAGS) -o "$*.o" -c "$*.c"
+
+%.$(EXTENSION): %.o
+ $(CC) $(LDFLAGS) -o "$*.$(EXTENSION)" "$*.o" $(LIBS)
+ chmod a-x "$*.$(EXTENSION)"
+
+# this links everything into a single binary file
+$(LIBRARY_NAME): $(SOURCES:.c=.o) $(LIBRARY_NAME).o
+ $(CC) $(LDFLAGS) -o $(LIBRARY_NAME).$(EXTENSION) $(SOURCES:.c=.o) $(LIBRARY_NAME).o $(LIBS)
+ chmod a-x $(LIBRARY_NAME).$(EXTENSION)
+
+
+install: libdir_install
+
+# The meta and help files are explicitly installed to make sure they are
+# actually there. Those files are not optional, then need to be there.
+libdir_install: $(SOURCES:.c=.$(EXTENSION)) install-doc install-examples
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ test -z "$(strip $(SOURCES))" || (\
+ $(INSTALL_FILE) $(SOURCES:.c=.$(EXTENSION)) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) && \
+ $(STRIP) $(addprefix $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/,$(SOURCES:.c=.$(EXTENSION))))
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS) \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+
+# install library linked as single binary
+single_install: $(LIBRARY_NAME) install-doc install-exec
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(INSTALL_FILE) $(LIBRARY_NAME).$(EXTENSION) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(STRIP) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/$(LIBRARY_NAME).$(EXTENSION)
+
+install-doc:
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ test -z "$(strip $(SOURCES))" || \
+ $(INSTALL_FILE) $(SOURCES:.c=-help.pd) \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+# this is the only bit not really handled well...
+ $(INSTALL_FILE) README.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/
+
+install-examples:
+ test ! -d examples || (\
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples && \
+ $(INSTALL_FILE) examples/*.* $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples)
+
+
+clean:
+ -rm -f -- $(SOURCES:.c=.o)
+ -rm -f -- $(SOURCES:.c=.$(EXTENSION))
+ -rm -f -- $(LIBRARY_NAME).$(EXTENSION)
+
+distclean: clean
+ -rm -f -- $(DISTBINDIR).tar.gz
+ -rm -rf -- $(DISTBINDIR)
+ -rm -f -- $(DISTDIR).tar.gz
+ -rm -rf -- $(DISTDIR)
+
+
+$(DISTBINDIR):
+ $(INSTALL_DIR) $(DISTBINDIR)
+
+libdir: all $(DISTBINDIR)
+ $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTBINDIR)
+ $(INSTALL_FILE) $(SOURCES) $(DISTBINDIR)
+ $(INSTALL_FILE) $(SOURCES:.c=-help.pd) $(DISTBINDIR)
+ test -z "$(strip $(EXTRA_DIST))" || \
+ $(INSTALL_FILE) $(EXTRA_DIST) $(DISTBINDIR)
+# tar --exclude-vcs -czpf $(DISTBINDIR).tar.gz $(DISTBINDIR)
+
+$(DISTDIR):
+ $(INSTALL_DIR) $(DISTDIR)
+
+dist: $(DISTDIR)
+ $(INSTALL_FILE) Makefile $(DISTDIR)
+ $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTDIR)
+ test -z "$(strip $(ALLSOURCES))" || \
+ $(INSTALL_FILE) $(ALLSOURCES) $(DISTDIR)
+ test -z "$(strip $(ALLSOURCES))" || \
+ $(INSTALL_FILE) $(ALLSOURCES:.c=-help.pd) $(DISTDIR)
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS) $(DISTDIR)
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) $(DISTDIR)
+ test -z "$(strip $(EXTRA_DIST))" || \
+ $(INSTALL_FILE) $(EXTRA_DIST) $(DISTDIR)
+ tar --exclude-vcs -czpf $(DISTDIR).tar.gz $(DISTDIR)
+
+
+etags:
+ etags *.h $(SOURCES) ../../pd/src/*.[ch] /usr/include/*.h /usr/include/*/*.h
+
+showpaths:
+ @echo "PD_PATH: $(PD_PATH)"
+ @echo "objectsdir: $(objectsdir)"
+ @echo "LIBRARY_NAME: $(LIBRARY_NAME)"
+ @echo "SOURCES: $(SOURCES)"
+ @echo "ALLSOURCES: $(ALLSOURCES)"
+ @echo "UNAME: $(UNAME)"
+ @echo "CPU: $(CPU)"
+
diff --git a/sixaxis/sixaxis-help.pd b/sixaxis/sixaxis-help.pd
new file mode 100644
index 0000000..b5fae4f
--- /dev/null
+++ b/sixaxis/sixaxis-help.pd
@@ -0,0 +1,127 @@
+#N canvas 197 95 565 482 10;
+#X obj -71 5 cnv 15 550 25 empty empty sixaxis 20 12 1 16 -228992 -66577
+0;
+#N canvas 746 51 411 235 see 0;
+#N canvas 108 318 543 264 route 0;
+#X obj 27 14 inlet;
+#X obj 72 226 outlet;
+#X obj 19 226 outlet;
+#X obj 172 226 outlet;
+#X obj 222 204 symbol;
+#X obj 222 226 outlet;
+#X obj 272 204 symbol;
+#X obj 272 226 outlet;
+#X obj 322 204 symbol;
+#X obj 322 226 outlet;
+#X obj 372 204 symbol;
+#X obj 372 226 outlet;
+#X obj 122 225 outlet;
+#X obj 422 204 symbol;
+#X obj 422 226 outlet;
+#X obj 472 204 symbol;
+#X obj 472 226 outlet;
+#X obj 26 63 route open device poll total product manufacturer transport
+type vendorID productID;
+#X connect 0 0 17 0;
+#X connect 4 0 5 0;
+#X connect 6 0 7 0;
+#X connect 8 0 9 0;
+#X connect 10 0 11 0;
+#X connect 13 0 14 0;
+#X connect 15 0 16 0;
+#X connect 17 0 2 0;
+#X connect 17 1 1 0;
+#X connect 17 2 12 0;
+#X connect 17 3 3 0;
+#X connect 17 4 4 0;
+#X connect 17 5 6 0;
+#X connect 17 6 8 0;
+#X connect 17 7 10 0;
+#X connect 17 8 13 0;
+#X connect 17 9 15 0;
+#X restore 117 70 pd route info;
+#X obj 221 96 tgl 15 0 empty empty open 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 110 22 inlet;
+#X obj 123 43 print info;
+#X floatatom 156 140 5 0 0 0 device - -;
+#X floatatom 156 162 5 0 0 0 poll - -;
+#X floatatom 156 182 5 0 0 0 total - -;
+#X connect 0 0 1 0;
+#X connect 0 1 4 0;
+#X connect 0 2 5 0;
+#X connect 0 3 6 0;
+#X connect 2 0 0 0;
+#X connect 2 0 3 0;
+#X restore 82 239 pd see device info;
+#X obj -7 181 tgl 30 0 empty empty empty 17 7 0 10 -4034 -1 -1 0 1
+;
+#X msg 89 188 info;
+#X msg 48 171 close;
+#X obj -20 406 hsl 128 15 0 1 0 0 empty empty empty -2 -8 0 10 -262144
+-1 -1 5904 1;
+#X obj 120 406 hsl 128 15 0 1 0 0 empty empty empty -2 -8 0 10 -262144
+-1 -1 8699 1;
+#X obj 260 406 hsl 128 15 0 1 0 0 empty empty empty -2 -8 0 10 -262144
+-1 -1 5152 1;
+#X obj 43 276 route accelerometer;
+#X obj 43 308 route x y z;
+#X msg 43 151 open 4;
+#N canvas 162 133 570 420 serin 0;
+#X obj 286 61 cnv 15 30 15 empty \$0-open-canvas 4 4 8 0 14 -233017
+-1 0;
+#X obj 60 61 hradio 15 1 1 15 empty empty empty 0 -6 0 8 -225271 -1
+-1 4;
+#X obj 60 13 inlet;
+#X msg 200 202 label \$1;
+#X obj 200 180 makefilename %d;
+#X obj 59 108 int;
+#X obj 59 337 outlet;
+#X msg 201 306 set \$1 \$2;
+#X obj 59 266 trigger bang anything;
+#X obj 201 286 list;
+#X msg 60 210 open \$1;
+#X obj 200 225 send \$0-open-canvas;
+#X connect 1 0 5 0;
+#X connect 2 0 1 0;
+#X connect 3 0 11 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 5 0 10 0;
+#X connect 7 0 6 0;
+#X connect 8 0 6 0;
+#X connect 8 1 9 0;
+#X connect 9 0 7 0;
+#X connect 10 0 8 0;
+#X coords 0 -1 1 1 257 17 1 60 60;
+#X restore 43 129 pd serin;
+#X obj 257 375 mapping/autoscale;
+#X obj 117 375 mapping/autoscale;
+#X obj -23 375 mapping/autoscale;
+#X obj 43 216 sixaxis;
+#N canvas 6 77 450 300 more 0;
+#X text -17 56 By default \, [sixaxis] uses /dev/hidraw? for the device
+name to get data from. You can override it using the [devname( message
+\, like this:;
+#X msg 30 154 devname /dev/my/strange/custom/hidraw;
+#X restore -15 444 pd more on device names;
+#X text -63 44 This objectclass supports getting accelerometer data
+from the Sony SIXAXIS controller. Use this in conjuction with [hid]
+to get all of the data from the SIXAXIS;
+#X obj -59 83 pddp/pddplink http://www.pabr.org/sixlinux/sixlinux.en.html
+-text Using the PlayStation 3 controller in Bluetooth mode with Linux
+;
+#X connect 2 0 15 0;
+#X connect 3 0 15 0;
+#X connect 4 0 15 0;
+#X connect 8 0 9 0;
+#X connect 9 0 14 0;
+#X connect 9 1 13 0;
+#X connect 9 2 12 0;
+#X connect 10 0 15 0;
+#X connect 11 0 10 0;
+#X connect 12 0 7 0;
+#X connect 13 0 6 0;
+#X connect 14 0 5 0;
+#X connect 15 0 8 0;
+#X connect 15 1 1 0;
diff --git a/sixaxis/sixaxis.c b/sixaxis/sixaxis.c
new file mode 100644
index 0000000..d110c71
--- /dev/null
+++ b/sixaxis/sixaxis.c
@@ -0,0 +1,412 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "m_pd.h"
+
+//#define DEBUG(x)
+#define DEBUG(x) x
+
+#define DEFAULT_DELAY 10
+#define SIXAXIS_DEVNAME "/dev/hidraw"
+
+static char *version = "$Revision: 1.1 $";
+
+/*------------------------------------------------------------------------------
+ * GLOBAL DECLARATIONS
+ */
+
+/* hidraw data format */
+struct sixaxis_state {
+ double time;
+ int ax, ay, az; // Raw accelerometer data
+ double ddx, ddy, ddz; // Acceleration
+ double dx, dy, dz; // Speed
+ double x, y, z; // Position
+};
+
+/* pre-generated symbols */
+static t_symbol *ps_open, *ps_device, *ps_poll, *ps_total, *ps_range, *ps_devname;
+static t_symbol *ps_x, *ps_y, *ps_z;
+static t_symbol *ps_accelerometer, *ps_acceleration, *ps_speed, *ps_position;
+
+/* mostly for status querying */
+static unsigned short device_count;
+
+/* previous state for calculating position, speed, acceleration */
+//static struct sixaxis_state prev;
+
+/*------------------------------------------------------------------------------
+ * CLASS DEF
+ */
+static t_class *sixaxis_class;
+
+typedef struct _sixaxis {
+ t_object x_obj;
+ t_int x_fd;
+ t_clock *x_clock;
+ short x_device_number;
+ short x_instance;
+ t_int x_device_open;
+ int x_read_ok;
+ int x_started;
+ int x_delay;
+ unsigned char buf[128];
+ struct sixaxis_state x_sixaxis_state;
+ t_atom x_output_atoms[3];
+ t_outlet *x_data_outlet;
+ t_outlet *x_status_outlet;
+} t_sixaxis;
+
+
+
+/*------------------------------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ */
+
+static void output_status(t_sixaxis *x, t_symbol *selector, t_float output_value)
+{
+ t_atom *output_atom = (t_atom *)getbytes(sizeof(t_atom));
+ SETFLOAT(output_atom, output_value);
+ outlet_anything( x->x_status_outlet, selector, 1, output_atom);
+ freebytes(output_atom,sizeof(t_atom));
+}
+
+static void output_open_status(t_sixaxis *x)
+{
+ output_status(x, ps_open, x->x_device_open);
+}
+
+static void output_device_number(t_sixaxis *x)
+{
+ output_status(x, ps_device, x->x_device_number);
+}
+
+static void output_poll_time(t_sixaxis *x)
+{
+ output_status(x, ps_poll, x->x_delay);
+}
+
+static void output_device_count(t_sixaxis *x)
+{
+ output_status(x, ps_total, device_count);
+}
+
+static short get_device_number_from_arguments(int argc, t_atom *argv)
+{
+ short device_number = -1;
+ t_symbol *first_argument;
+
+ if(argc == 1)
+ {
+ first_argument = atom_getsymbolarg(0,argc,argv);
+ if(first_argument == &s_)
+ { // single float arg means device #
+ device_number = (short) atom_getfloatarg(0,argc,argv);
+ }
+ }
+ return device_number;
+}
+/*------------------------------------------------------------------------------
+ * CLASS METHODS
+ */
+
+void sixaxis_stop(t_sixaxis* x)
+{
+ DEBUG(post("sixaxis_stop"););
+
+ if (x->x_fd >= 0 && x->x_started) {
+ clock_unset(x->x_clock);
+ post("sixaxis: polling stopped");
+ x->x_started = 0;
+ }
+}
+
+static void sixaxis_close(t_sixaxis *x)
+{
+ DEBUG(post("sixaxis_close"););
+
+/* just to be safe, stop it first */
+ sixaxis_stop(x);
+
+ if(x->x_fd < 0)
+ return;
+ close(x->x_fd);
+ post("[sixaxis] closed %s%d", SIXAXIS_DEVNAME, x->x_device_number);
+ x->x_device_open = 0;
+ output_open_status(x);
+}
+
+static t_int sixaxis_open_device(t_sixaxis *x, short device_number)
+{
+ DEBUG(post("sixaxis_open_device"););
+
+ char block_device[FILENAME_MAX];
+
+ x->x_fd = -1;
+
+ if(device_number < 0)
+ {
+ pd_error(x,"[sixaxis] invalid device number: %d (must be 0 or greater)"
+ , device_number);
+ return EXIT_FAILURE;
+ }
+
+ x->x_device_number = device_number;
+ snprintf(block_device, FILENAME_MAX, "%s%d", SIXAXIS_DEVNAME, x->x_device_number);
+
+ /* open the device read-only, non-exclusive */
+ x->x_fd = open(block_device, O_RDONLY | O_NONBLOCK);
+ /* test if device open */
+ if(x->x_fd < 0 )
+ {
+ pd_error(x,"[sixaxis] open %s failed",block_device);
+ x->x_fd = -1;
+ return EXIT_FAILURE;
+ }
+ post ("[sixaxis] opened device %d (%s)", x->x_device_number, block_device);
+
+ return EXIT_SUCCESS;
+}
+
+/* sixaxis_open behavoir
+ * current state action
+ * ---------------------------------------
+ * closed / same device open
+ * open / same device no action
+ * closed / different device open
+ * open / different device close, open
+ */
+static void sixaxis_open(t_sixaxis *x, t_symbol *s, int argc, t_atom *argv)
+{
+ DEBUG(post("sixaxis_open"););
+ short new_device_number = get_device_number_from_arguments(argc, argv);
+// t_int started = x->x_started; // store state to restore after device is opened
+
+ if (new_device_number < 0)
+ {
+ pd_error(x,"[sixaxis] invalid device number: %d (must be 0 or greater)",
+ new_device_number);
+ return;
+ }
+ /* check whether we have to close previous device */
+ if (x->x_device_open && new_device_number != x->x_device_number)
+ {
+ sixaxis_close(x);
+ }
+ /* no device open, so open one now */
+ if (!x->x_device_open)
+ {
+ if(sixaxis_open_device(x, new_device_number) == EXIT_SUCCESS)
+ {
+ x->x_device_open = 1;
+ x->x_device_number = new_device_number;
+ /* restore the polling state so that when I [tgl] is used to
+ * start/stop [sixaxis], the [tgl]'s state will continue to
+ * accurately reflect [sixaxis]'s state */
+ post("[sixaxis] set device# to %d",new_device_number);
+ output_device_number(x);
+ }
+ else
+ {
+ x->x_device_number = -1;
+ pd_error(x, "[sixaxis] can not open device %d",new_device_number);
+ }
+ }
+
+ /* always output open result so you can test for success in Pd space */
+ output_open_status(x);
+}
+
+static void sixaxis_read(t_sixaxis *x)
+{
+ if(x->x_fd < 0)
+ return;
+ if(read(x->x_fd, &(x->buf), sizeof(x->buf)) > -1) {
+// if ( nr < 0 ) { perror("read(stdin)"); exit(1); }
+// if ( nr != 48 ) { fprintf(stderr, "Unsupported report\n"); exit(1); }
+
+/* struct timeval tv; */
+/* if ( gettimeofday(&tv, NULL) ) { */
+/* perror("gettimeofday"); */
+/* return; */
+/* } */
+/* x->x_sixaxis_state.time = tv.tv_sec + tv.tv_usec*1e-6; */
+/* x->x_sixaxis_state.ax = x->buf[40]<<8 | x->buf[41]; */
+/* x->x_sixaxis_state.ay = x->buf[42]<<8 | x->buf[43]; */
+/* x->x_sixaxis_state.az = x->buf[44]<<8 | x->buf[45]; */
+/* if ( ! prev.time ) { */
+/* prev.time = x->x_sixaxis_state.time; */
+/* prev.ax = x->x_sixaxis_state.ax; */
+/* prev.ay = x->x_sixaxis_state.ay; */
+/* prev.az = x->x_sixaxis_state.az; */
+/* } */
+/* double dt = x->x_sixaxis_state.time - prev.time; */
+/* double rc_dd = 2.0; // Time constant for highpass filter on acceleration */
+/* double alpha_dd = rc_dd / (rc_dd+dt); */
+/* x->x_sixaxis_state.ddx = alpha_dd*(prev.ddx + (x->x_sixaxis_state.ax-prev.ax)*0.01); */
+/* x->x_sixaxis_state.ddy = alpha_dd*(prev.ddy + (x->x_sixaxis_state.ay-prev.ay)*0.01); */
+/* x->x_sixaxis_state.ddz = alpha_dd*(prev.ddz - (x->x_sixaxis_state.az-prev.az)*0.01); */
+/* double rc_d = 2.0; // Time constant for highpass filter on speed */
+/* double alpha_d = rc_d / (rc_d+dt); */
+/* x->x_sixaxis_state.dx = alpha_d*(prev.dx + x->x_sixaxis_state.ddx*dt); */
+/* x->x_sixaxis_state.dy = alpha_d*(prev.dy + x->x_sixaxis_state.ddy*dt); */
+/* x->x_sixaxis_state.dz = alpha_d*(prev.dz + x->x_sixaxis_state.ddz*dt); */
+/* double rc = 1.0; // Time constant for highpass filter on position */
+/* double alpha = rc / (rc+dt); */
+/* x->x_sixaxis_state.x = alpha*(prev.x + x->x_sixaxis_state.dx*dt); */
+/* x->x_sixaxis_state.y = alpha*(prev.y + x->x_sixaxis_state.dy*dt); */
+/* x->x_sixaxis_state.z = alpha*(prev.z + x->x_sixaxis_state.dz*dt); */
+ /* raw accelerometer data */
+ SETSYMBOL(x->x_output_atoms, ps_x);
+ SETFLOAT(x->x_output_atoms + 1, x->buf[40]<<8 | x->buf[41]);
+ outlet_anything(x->x_data_outlet, ps_accelerometer, 2, x->x_output_atoms);
+ SETSYMBOL(x->x_output_atoms, ps_y);
+ SETFLOAT(x->x_output_atoms + 1, x->buf[42]<<8 | x->buf[43]);
+ outlet_anything(x->x_data_outlet, ps_accelerometer, 2, x->x_output_atoms);
+ SETSYMBOL(x->x_output_atoms, ps_z);
+ SETFLOAT(x->x_output_atoms + 1, x->buf[44]<<8 | x->buf[45]);
+ outlet_anything(x->x_data_outlet, ps_accelerometer, 2, x->x_output_atoms);
+/* SETFLOAT(x->x_output_atoms, x->x_sixaxis_state.ax); */
+/* SETFLOAT(x->x_output_atoms + 1, x->x_sixaxis_state.ay); */
+/* SETFLOAT(x->x_output_atoms + 2, x->x_sixaxis_state.az); */
+/* outlet_anything(x->x_data_outlet, ps_accelerometer, 3, x->x_output_atoms); */
+/* /\* acceleration data *\/ */
+/* SETFLOAT(x->x_output_atoms, x->x_sixaxis_state.ddx); */
+/* SETFLOAT(x->x_output_atoms + 1, x->x_sixaxis_state.ddy); */
+/* SETFLOAT(x->x_output_atoms + 2, x->x_sixaxis_state.ddz); */
+/* outlet_anything(x->x_data_outlet, ps_acceleration, 3, x->x_output_atoms); */
+/* /\* speed data *\/ */
+/* SETFLOAT(x->x_output_atoms, x->x_sixaxis_state.dx); */
+/* SETFLOAT(x->x_output_atoms + 1, x->x_sixaxis_state.dy); */
+/* SETFLOAT(x->x_output_atoms + 2, x->x_sixaxis_state.dz); */
+/* outlet_anything(x->x_data_outlet, ps_speed, 3, x->x_output_atoms); */
+/* /\* position data *\/ */
+/* SETFLOAT(x->x_output_atoms, x->x_sixaxis_state.x); */
+/* SETFLOAT(x->x_output_atoms + 1, x->x_sixaxis_state.y); */
+/* SETFLOAT(x->x_output_atoms + 2, x->x_sixaxis_state.z); */
+/* outlet_anything(x->x_data_outlet, ps_position, 3, x->x_output_atoms); */
+ }
+ if(x->x_started) {
+ clock_delay(x->x_clock, x->x_delay);
+ }
+}
+// double ddx, ddy, ddz; // Acceleration
+// double dx, dy, dz; // Speed
+// double x, y, z; // Position
+
+/* Actions */
+
+static void sixaxis_info(t_sixaxis *x)
+{
+ output_open_status(x);
+ output_device_number(x);
+ output_device_count(x);
+ output_poll_time(x);
+// TODO output ranges for sixaxis
+// output_element_ranges(x);
+}
+
+void sixaxis_start(t_sixaxis* x)
+{
+ DEBUG(post("sixaxis_start"););
+
+ if(!x->x_device_open) {
+ sixaxis_open_device(x, x->x_device_number);
+ }
+ if (x->x_fd > -1 && !x->x_started) {
+ clock_delay(x->x_clock, DEFAULT_DELAY);
+ post("sixaxis: polling started");
+ x->x_started = 1;
+ }
+}
+
+static void sixaxis_float(t_sixaxis* x, t_floatarg f)
+{
+ DEBUG(post("sixaxis_float"););
+
+ if (f > 0)
+ sixaxis_start(x);
+ else
+ sixaxis_stop(x);
+}
+
+/* setup functions */
+static void sixaxis_free(t_sixaxis* x)
+{
+ DEBUG(post("sixaxis_free"););
+
+ if (x->x_fd < 0) return;
+
+ sixaxis_stop(x);
+ clock_free(x->x_clock);
+ close(x->x_fd);
+}
+
+static void *sixaxis_new(t_symbol *s, int argc, t_atom *argv)
+{
+ t_sixaxis *x = (t_sixaxis *)pd_new(sixaxis_class);
+
+ DEBUG(post("sixaxis_new"););
+
+ post("[sixaxis] %s, written by Hans-Christoph Steiner <hans@eds.org>",version);
+
+ /* init vars */
+ x->x_fd = -1;
+ x->x_read_ok = 1;
+ x->x_started = 0;
+ x->x_delay = DEFAULT_DELAY;
+ if(argc > 0)
+ x->x_device_number = get_device_number_from_arguments(argc, argv);
+ else
+ x->x_device_number = 0;
+
+ x->x_clock = clock_new(x, (t_method)sixaxis_read);
+
+ /* create standard "io"-style outlets */
+ x->x_data_outlet = outlet_new(&x->x_obj, 0);
+ x->x_status_outlet = outlet_new(&x->x_obj, 0);
+
+ return (x);
+}
+
+void sixaxis_setup(void)
+{
+ DEBUG(post("sixaxis_setup"););
+ sixaxis_class = class_new(gensym("sixaxis"),
+ (t_newmethod)sixaxis_new,
+ (t_method)sixaxis_free,
+ sizeof(t_sixaxis), 0, A_GIMME, 0);
+
+ /* add inlet datatype methods */
+ class_addfloat(sixaxis_class,(t_method) sixaxis_float);
+ class_addbang(sixaxis_class,(t_method) sixaxis_read);
+
+ /* add inlet message methods */
+ class_addmethod(sixaxis_class,(t_method) sixaxis_open,gensym("open"),A_GIMME,0);
+ class_addmethod(sixaxis_class,(t_method) sixaxis_close,gensym("close"),0);
+ class_addmethod(sixaxis_class,(t_method) sixaxis_info,gensym("info"),0);
+
+ /* pre-generate often used symbols */
+ ps_open = gensym("open");
+ ps_device = gensym("device");
+ ps_poll = gensym("poll");
+ ps_total = gensym("total");
+ ps_range = gensym("range");
+ ps_devname = gensym("devname");
+
+ ps_accelerometer = gensym("accelerometer");
+ ps_x = gensym("x");
+ ps_y = gensym("y");
+ ps_z = gensym("z");
+
+ ps_acceleration = gensym("acceleration");
+ ps_speed = gensym("speed");
+ ps_position = gensym("position");
+}
+
diff --git a/uDMX/README b/uDMX/README
new file mode 100644
index 0000000..1d9a8a1
--- /dev/null
+++ b/uDMX/README
@@ -0,0 +1,2 @@
+Requires libusb
+More information: http://www.anyma.ch/research/udmx/
diff --git a/uDMX/makefile b/uDMX/makefile
new file mode 100644
index 0000000..f030c66
--- /dev/null
+++ b/uDMX/makefile
@@ -0,0 +1,105 @@
+current:
+ echo make pd_linux, pd_nt, pd_irix5, pd_irix6 or pd_darwin, then make install
+
+clean: ; rm -f *.pd_* *.o
+
+# ----------------------- NT -----------------------
+
+pd_nt: edubeat.dll
+
+INSTALL_PREFIX="C:\pd\extra"
+EXT=dll
+.SUFFIXES: .obj .dll
+
+PDNTCFLAGS = /W3 /WX /DNT /DPD /nologo
+VC="D:\Program Files\Microsoft Visual Studio\Vc98"
+
+PDNTINCLUDE = /I. /I\tcl\include /I..\..\src /I$(VC)\include
+
+PDNTLDIR = $(VC)\lib
+PDNTLIB = $(PDNTLDIR)\libc.lib \
+ $(PDNTLDIR)\oldnames.lib \
+ $(PDNTLDIR)\kernel32.lib \
+ ..\..\bin\pd.lib
+
+.c.dll:
+ cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $*.c
+ link /dll /export:$*_setup $*.obj $(PDNTLIB)
+
+# ----------------------- IRIX 5.x -----------------------
+
+pd_irix5: edubeat.pd_irix5
+
+INSTALL_PREFIX=/usr/local
+EXT=pd_irix5
+.SUFFIXES: .pd_irix5
+
+SGICFLAGS5 = -o32 -DPD -DUNIX -DIRIX -O2
+
+SGIINCLUDE = -I/usr/local/include
+
+.c.pd_irix5:
+ cc $(SGICFLAGS5) $(SGIINCLUDE) -o $*.o -c $*.c
+ ld -elf -shared -rdata_shared -o $*.pd_irix5 $*.o
+ rm $*.o
+
+# ----------------------- IRIX 5.x -----------------------
+
+pd_irix6: edubeat.pd_irix6
+
+INSTALL_PREFIX=/usr/local
+EXT=pd_irix6
+.SUFFIXES: .pd_irix6
+
+SGICFLAGS5 = -o32 -DPD -DUNIX -DIRIX -O2
+
+SGIINCLUDE = -I/usr/local/include
+
+.c.pd_irix6:
+ cc $(SGICFLAGS5) $(SGIINCLUDE) -o $*.o -c $*.c
+ ld -elf -shared -rdata_shared -o $*.pd_irix6 $*.o
+ rm $*.o
+
+# ----------------------- LINUX i386 -----------------------
+
+pd_linux: uDMX.pd_linux
+
+INSTALL_PREFIX=/usr
+EXT=pd_linux
+.SUFFIXES: .pd_linux
+
+LINUXCFLAGS = -DPD -O2 -funroll-loops -fomit-frame-pointer -fPIC \
+ -Wall -W -Wshadow -Wstrict-prototypes \
+ -Wno-unused -Wno-parentheses -Wno-switch
+
+LINUXINCLUDE = -I/usr/include
+
+.c.pd_linux:
+ cc $(LINUXCFLAGS) $(LINUXINCLUDE) -o $*.o -c $*.c
+ ld -export_dynamic -shared -o $*.pd_linux $*.o -lc -lm -lusb
+ strip --strip-unneeded $*.pd_linux
+ rm $*.o
+
+# ----------------------- Mac OSX -----------------------
+
+pd_darwin: edubeat.pd_darwin
+
+INSTALL_PREFIX=/usr/local
+EXT=pd_darwin
+.SUFFIXES: .pd_darwin
+
+DARWINCFLAGS = -DPD -O2 -Wall -W -Wshadow -Wstrict-prototypes \
+ -Wno-unused -Wno-parentheses -Wno-switch
+
+.c.pd_darwin:
+ cc $(DARWINCFLAGS) $(LINUXINCLUDE) -o $*.o -c $*.c
+ cc -bundle -undefined suppress -flat_namespace -o $*.pd_darwin $*.o
+ rm -f $*.o
+
+# ----------------------------------------------
+
+install::
+ install -d $(INSTALL_PREFIX)/lib/pd/extra
+# install -m 644 *.$(EXT) $(INSTALL_PREFIX)/lib/pd/externs
+ -install -m 644 edubeat.$(EXT) $(INSTALL_PREFIX)/lib/pd/extra
+ install -m 644 *.pd $(INSTALL_PREFIX)/lib/pd/doc/5.reference
diff --git a/uDMX/uDMX-help.pd b/uDMX/uDMX-help.pd
new file mode 100644
index 0000000..54d08d3
--- /dev/null
+++ b/uDMX/uDMX-help.pd
@@ -0,0 +1,12 @@
+#N canvas 0 0 450 300 10;
+#X floatatom 203 74 5 0 0 0 - - -;
+#X msg 232 107 1;
+#X msg 103 57 255 255 255;
+#X obj 184 122 uDMX;
+#X msg 70 128 0 0 0;
+#X msg 101 175 127 127 127;
+#X connect 0 0 3 0;
+#X connect 1 0 3 1;
+#X connect 2 0 3 0;
+#X connect 4 0 3 0;
+#X connect 5 0 3 0;
diff --git a/uDMX/uDMX.c b/uDMX/uDMX.c
new file mode 100755
index 0000000..eadc53c
--- /dev/null
+++ b/uDMX/uDMX.c
@@ -0,0 +1,275 @@
+/*
+ uDMX.c
+
+ pd-Interface to the [ a n y m a | uDMX - Open Source USB Sensor Box ]
+
+ Authors: Michael Egger
+ Copyright: 2007 [ a n y m a ]
+ Website: www.anyma.ch
+
+ License: GNU GPL 2.0 www.gnu.org
+
+ Version: 0.2 2009-06-30
+ 0.1 2007-01-28
+ */
+
+#include "m_pd.h"
+
+#include "uDMX_cmds.h"
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <usb.h> /* this is libusb, see http://libusb.sourceforge.net/ */
+
+#define USBDEV_SHARED_VENDOR 0x16C0 /* VOTI */
+#define USBDEV_SHARED_PRODUCT 0x05DC /* Obdev's free shared PID */
+
+typedef struct _uDMX // defines our object's internal variables for each instance in a patch
+{
+ t_object p_ob; // object header - ALL objects MUST begin with this...
+ usb_dev_handle *dev_handle; // handle to the uDMX usb device
+ int debug_flag;
+ int channel; // int value - received from the right inlet and stored internally for each object instance
+} t_uDMX;
+
+void *uDMX_class; // global pointer to the object class - so max can reference the object
+
+
+
+// these are prototypes for the methods that are defined below
+void uDMX_int(t_uDMX *x, long n);
+void uDMX_setchannel(t_uDMX *x, t_floatarg f);
+void uDMX_debug(t_uDMX *x, t_symbol *s, short ac, t_atom *av);
+void uDMX_list(t_uDMX *x, t_symbol *s, short ac, t_atom *av);
+void uDMX_open(t_uDMX *x);
+void uDMX_close(t_uDMX *x);
+void *uDMX_new(long n);
+static int usbGetStringAscii(usb_dev_handle *dev, int dex, int langid, char *buf, int buflen);
+void find_device(t_uDMX *x);
+
+//--------------------------------------------------------------------------
+
+void uDMX_setup(void)
+{
+
+ uDMX_class = class_new ( gensym("uDMX"),(t_newmethod)uDMX_new, 0, sizeof(t_uDMX), CLASS_DEFAULT,0);
+
+ class_addfloat(uDMX_class, (t_method)uDMX_int); // the method for an int in the left inlet (inlet 0)
+ class_addmethod(uDMX_class, (t_method)uDMX_debug,gensym("debug"), A_GIMME, 0);
+ class_addlist(uDMX_class, (t_method)uDMX_list);
+ class_addmethod(uDMX_class, (t_method)uDMX_open, gensym("open"), 0);
+ class_addmethod(uDMX_class, (t_method)uDMX_close, gensym("close"), 0);
+ class_addmethod(uDMX_class, (t_method)uDMX_setchannel, gensym("setchannel"), A_FLOAT, 0);
+
+ post("uDMX version 0.9 - (c) 2006 [ a n y m a ]",0); // post any important info to the max window when our object is laoded
+}
+//--------------------------------------------------------------------------
+
+void uDMX_int(t_uDMX *x, long n) // x = the instance of the object; n = the int received in the left inlet
+{
+ unsigned char buffer[8];
+ int nBytes;
+
+ if (n > 255) n=255;
+ if (n < 0) n=0;
+ if (x->channel > 512) x->channel=512;
+ if (x->channel < 0) x->channel=0;
+
+ if (!(x->dev_handle)) find_device(x);
+ else {
+ nBytes = usb_control_msg(x->dev_handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT,
+ cmd_SetSingleChannel, n, x->channel, buffer, sizeof(buffer), 5000);
+ if(nBytes < 0)
+ if (x->debug_flag) error("uDMX: USB error: %s", usb_strerror());
+ }
+}
+
+void uDMX_setchannel(t_uDMX *x, t_floatarg f)
+{
+ x->channel = f;
+}
+
+//--------------------------------------------------------------------------
+
+void uDMX_list(t_uDMX *x, t_symbol *s, short ac, t_atom *av)
+{
+ int i;
+ unsigned char* buf = malloc(ac);
+ int nBytes;
+ int n;
+
+ if (x->channel > 512) x->channel=512;
+ if (x->channel < 0) x->channel=0;
+
+ if (!(x->dev_handle)) find_device(x);
+ else {
+
+ if (x->debug_flag) post("uDMX: ac: %i\n", ac);
+ for(i=0; i<ac; ++i,av++) {
+ if (av->a_type==A_FLOAT) {
+ n = (int) av->a_w.w_float;
+ if (n > 255) n=255;
+ if (n < 0) n=0;
+
+ buf[i] = n;
+ } else
+ buf[i] = 0;
+ }
+ nBytes = usb_control_msg(x->dev_handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT,
+ cmd_SetChannelRange, ac, x->channel, buf, ac, 5000);
+ if (x->debug_flag) post( "bytes returned: %i\n", nBytes);
+ if(nBytes < 0)
+ if (x->debug_flag) error("uDMX: USB error: %s\n", usb_strerror());
+ else if(nBytes > 0) if (x->debug_flag) post("uDMX: returned: %i\n", (int)(buf[0]));
+ free(buf);
+ }
+}
+
+//--------------------------------------------------------------------------
+
+void uDMX_debug(t_uDMX *x, t_symbol *s, short ac, t_atom *av) // x = the instance of the object; n = the int received in the left inlet
+{
+ x->debug_flag = 1;
+ if (ac) {
+ if (av->a_type==A_FLOAT) x->debug_flag = av->a_w.w_float;
+ }
+}
+
+
+
+//--------------------------------------------------------------------------
+
+void uDMX_free(t_uDMX *x)
+{
+ if (x->dev_handle)
+ usb_close(x->dev_handle);
+}
+
+//--------------------------------------------------------------------------
+
+void uDMX_open(t_uDMX *x)
+{
+ if (x->dev_handle) {
+ post("uDMX: There is already a connection to www.anyma.ch/uDMX",0);
+ } else find_device(x);
+}
+
+//--------------------------------------------------------------------------
+
+void uDMX_close(t_uDMX *x)
+{
+ if (x->dev_handle) {
+ usb_close(x->dev_handle);
+ x->dev_handle = NULL;
+ post("uDMX: Closed connection to www.anyma.ch/uDMX",0);
+ } else
+ post("uDMX: There was no open connection to www.anyma.ch/uDMX",0);
+}
+
+
+//--------------------------------------------------------------------------
+
+void *uDMX_new(long n) // n = int argument typed into object box (A_DEFLONG) -- defaults to 0 if no args are typed
+{
+ t_uDMX *x; // local variable (pointer to a t_uDMX data structure)
+
+ x = (t_uDMX *)pd_new(uDMX_class); // create a new instance of this object
+
+ // create a second int inlet (leftmost inlet is automatic - all objects have one inlet by default)
+ // floatinlet_new(x, x->channel); //crashes on PD .... assigns float in inlet 2 directly to channel
+ inlet_new(&x->p_ob, &x->p_ob.ob_pd, gensym("float"), gensym("setchannel"));
+
+ x->channel = 0;
+ x->debug_flag = 0;
+ x->dev_handle = NULL;
+
+ find_device(x);
+
+ return(x); // return a reference to the object instance
+}
+
+//--------------------------------------------------------------------------
+
+
+static int usbGetStringAscii(usb_dev_handle *dev, int dex, int langid, char *buf, int buflen)
+{
+char buffer[256];
+int rval, i;
+
+ if((rval = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + dex, langid, buffer, sizeof(buffer), 1000)) < 0)
+ return rval;
+ if(buffer[1] != USB_DT_STRING)
+ return 0;
+ if((unsigned char)buffer[0] < rval)
+ rval = (unsigned char)buffer[0];
+ rval /= 2;
+ /* lossy conversion to ISO Latin1 */
+ for(i=1;i<rval;i++){
+ if(i > buflen) /* destination buffer overflow */
+ break;
+ buf[i-1] = buffer[2 * i];
+ if(buffer[2 * i + 1] != 0) /* outside of ISO Latin1 range */
+ buf[i-1] = '?';
+ }
+ buf[i-1] = 0;
+ return i-1;
+}
+
+//--------------------------------------------------------------------------
+
+
+void find_device(t_uDMX *x)
+{
+ usb_dev_handle *handle = NULL;
+ struct usb_bus *bus;
+ struct usb_device *dev;
+
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+ for(bus=usb_get_busses(); bus; bus=bus->next){
+ for(dev=bus->devices; dev; dev=dev->next){
+ if(dev->descriptor.idVendor == USBDEV_SHARED_VENDOR && dev->descriptor.idProduct == USBDEV_SHARED_PRODUCT){
+ char string[256];
+ int len;
+ handle = usb_open(dev); /* we need to open the device in order to query strings */
+ if(!handle){
+ error ("Warning: cannot open USB device: %s", usb_strerror());
+ continue;
+ }
+ /* now find out whether the device actually is uDMX */
+ len = usbGetStringAscii(handle, dev->descriptor.iManufacturer, 0x0409, string, sizeof(string));
+ if(len < 0){
+ post("uDMX: warning: cannot query manufacturer for device: %s", usb_strerror());
+ goto skipDevice;
+ }
+
+ // post("uDMX: seen device from vendor ->%s<-", string);
+ if(strcmp(string, "www.anyma.ch") != 0)
+ goto skipDevice;
+ len = usbGetStringAscii(handle, dev->descriptor.iProduct, 0x0409, string, sizeof(string));
+ if(len < 0){
+ post("uDMX: warning: cannot query product for device: %s", usb_strerror());
+ goto skipDevice;
+ }
+ // post("uDMX: seen product ->%s<-", string);
+ if(strcmp(string, "uDMX") == 0)
+ break;
+skipDevice:
+ usb_close(handle);
+ handle = NULL;
+ }
+ }
+ if(handle)
+ break;
+ }
+
+ if(!handle){
+ post("uDMX: Could not find USB device www.anyma.ch/uDMX");
+ x->dev_handle = NULL;
+ } else {
+ x->dev_handle = handle;
+ post("uDMX: Found USB device www.anyma.ch/uDMX");
+ }
+}
diff --git a/uDMX/uDMX_cmds.h b/uDMX/uDMX_cmds.h
new file mode 100644
index 0000000..12a72af
--- /dev/null
+++ b/uDMX/uDMX_cmds.h
@@ -0,0 +1,31 @@
+/*
+ * usb2dmx_cmds.h
+ *
+ *
+ * Created by Max Egger on 14.02.06.
+ *
+ */
+
+#define cmd_SetSingleChannel 1
+/* usb request for cmd_SetSingleChannel:
+ bmRequestType: ignored by device, should be USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT
+ bRequest: cmd_SetSingleChannel
+ wValue: value of channel to set [0 .. 255]
+ wIndex: channel index to set [0 .. 511]
+ wLength: ignored
+*/
+#define cmd_SetChannelRange 2
+/* usb request for cmd_SetChannelRange:
+ bmRequestType: ignored by device, should be USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT
+ bRequest: cmd_SetChannelRange
+ wValue: number of channels to set [1 .. 512-wIndex]
+ wIndex: index of first channel to set [0 .. 511]
+ wLength: length of data, must be >= wValue
+*/
+
+#define cmd_StartBootloader 0xf8
+// Start Bootloader for Software updates
+
+
+#define err_BadChannel 1
+#define err_BadValue 2
diff --git a/w32mote/VC2008/wiimote.sln b/w32mote/VC2008/wiimote.sln
new file mode 100644
index 0000000..c7704b1
--- /dev/null
+++ b/w32mote/VC2008/wiimote.sln
@@ -0,0 +1,17 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wiimote", "wiimote.vcproj", "{CFCCDAEA-44EA-4B2B-A410-B596570AC2C5}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CFCCDAEA-44EA-4B2B-A410-B596570AC2C5}.Release|Win32.ActiveCfg = Release|Win32
+ {CFCCDAEA-44EA-4B2B-A410-B596570AC2C5}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/w32mote/VC2008/wiimote.vcproj b/w32mote/VC2008/wiimote.vcproj
new file mode 100644
index 0000000..eed0508
--- /dev/null
+++ b/w32mote/VC2008/wiimote.vcproj
@@ -0,0 +1,379 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9,00"
+ Name="wiimote"
+ ProjectGUID="{CFCCDAEA-44EA-4B2B-A410-B596570AC2C5}"
+ RootNamespace="wiimote"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="131072"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="C:\WinDDK\7600.16385.1\inc\ddk;&quot;$(VCInstallDir)include&quot;;&quot;$(VCInstallDir)atlmfc\include&quot;;C:\WinDDK\7600.16385.1\inc\api;&quot;$(WindowsSdkDir)\include&quot;;&quot;$(FrameworkSDKDir)include&quot;;&quot;C:\Program Files\pd-0.43.0\src&quot;"
+ PreprocessorDefinitions="_WIN32;WIN32;_DEBUG;_CONSOLE;PD"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="kernel32.lib user32.lib pd.lib $(NOINHERIT)"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="C:\WinDDK\7600.16385.1\lib\win7\i386;&quot;C:\Program Files\pd-0.43.0\bin&quot;"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine=""
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="C:\WinDDK\7600.16385.1\inc\ddk;&quot;$(VCInstallDir)include&quot;;&quot;$(VCInstallDir)atlmfc\include&quot;;C:\WinDDK\7600.16385.1\inc\api;&quot;$(WindowsSdkDir)\include&quot;;&quot;$(FrameworkSDKDir)include&quot;;&quot;$(ProgramFiles)\pd\src&quot;;C:\Users\staff\Development\w32mote\WiiYourself"
+ PreprocessorDefinitions="_WIN32;WIN32;NDEBUG;_CONSOLE;PD"
+ RuntimeLibrary="0"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="kernel32.lib user32.lib pd.lib $(NOINHERIT)"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="C:\WinDDK\7600.16385.1\lib\win7\i386;&quot;$(ProgramFiles)\pd\bin&quot;"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ Description="- Copying .dll to ProjectDir -"
+ CommandLine="copy $(TargetPath) $(ProjectDir).."
+ />
+ </Configuration>
+ <Configuration
+ Name="DebugU|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="C:\WinDDK\7600.16385.1\inc\ddk;&quot;$(VCInstallDir)include&quot;;&quot;$(VCInstallDir)atlmfc\include&quot;;C:\WinDDK\7600.16385.1\inc\api;&quot;$(WindowsSdkDir)\include&quot;;&quot;$(FrameworkSDKDir)include&quot;;&quot;C:\Program Files\pd-0.43.0\src&quot;"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="kernel32.lib user32.lib pd.lib $(NOINHERIT)"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="C:\WinDDK\7600.16385.1\lib\win7\i386;&quot;C:\Program Files\pd-0.43.0\bin&quot;"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ Description="- Copying .exe to demo dir -"
+ CommandLine="copy $(TargetPath) $(ProjectDir)"
+ />
+ </Configuration>
+ <Configuration
+ Name="ReleaseU|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="C:\WinDDK\7600.16385.1\inc\ddk;&quot;$(VCInstallDir)include&quot;;&quot;$(VCInstallDir)atlmfc\include&quot;;C:\WinDDK\7600.16385.1\inc\api;&quot;$(WindowsSdkDir)\include&quot;;&quot;$(FrameworkSDKDir)include&quot;;&quot;C:\Program Files\pd-0.43.0\src&quot;"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="0"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="kernel32.lib user32.lib pd.lib $(NOINHERIT)"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="C:\WinDDK\7600.16385.1\lib\win7\i386;&quot;C:\Program Files\pd-0.43.0\bin&quot;"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ Description="- Copying .exe to demo dir -"
+ CommandLine="copy $(TargetPath) $(ProjectDir)\..\"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\wiimote4pd.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Library"
+ >
+ <File
+ RelativePath="..\WiiYourself\History.txt"
+ >
+ </File>
+ <File
+ RelativePath="..\WiiYourself\License.txt"
+ >
+ </File>
+ <File
+ RelativePath="..\WiiYourself\ReadMe.txt"
+ >
+ </File>
+ <File
+ RelativePath="..\WiiYourself\wiimote.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\WiiYourself\wiimote.h"
+ >
+ </File>
+ <File
+ RelativePath="..\WiiYourself\wiimote_common.h"
+ >
+ </File>
+ <File
+ RelativePath="..\WiiYourself\wiimote_state.h"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/w32mote/WiiYourself/History.txt b/w32mote/WiiYourself/History.txt
new file mode 100644
index 0000000..6601dc9
--- /dev/null
+++ b/w32mote/WiiYourself/History.txt
@@ -0,0 +1,281 @@
+____________________________________________________________
+
+ - WiiYourself! - native C++ Wiimote library v1.15
+ (c) gl.tter 2007-10 - http://gl.tter.org
+____________________________________________________________
+
+History:
+
+What's new since 1.01a? - Main Features (see ReadMe & full history for details)
+
+ + Balance Board support with automatic offset removal.
+
+ + Seemingly solid MotionPlus support.
+
+ + Library no longer includes project files - just add wiimote.cpp &
+ header to your project (avoids all build-settings releated issues)
+
+ + better MinGW support: (thanks Elmo)
+ adds functional _ASSERT/TRACE/WARN/DEEP_TRACE macros
+ non-MSYS dependent build option via 'make_mingw.bat'.
+ Demo builds & works under MinGW.
+
+ + new Python wrapper (by Robert Xiao, see 'Python' folder for details)
+
+ + library now compiles on Borland (thanks Griff - demo not tested).
+
+ + many fixes, connections should be more reliable.
+
+ + join my mailing list to give feedback, share ideas & stay informed:
+ http://gl.tter.org/mailman/listinfo/wiiyourself_gl.tter.org
+
+1.15 Final:
+ + fixed MotionPlus detection on stacks that require HID writes!
+ + Balance Board corner weight values have been quartered (.Total remains
+ unchanged). The non-raw corner weight incorrectly reported 4x their
+ real value.
+ + Wiimote calibration info is more reliably received (it may not have
+ arrived in many instances)
+ + exposed a partially-unique device ID (wiimote::UniqueID). This 64bit
+ number is set during the Connect() call, and is derived from the
+ device-specific calibration values. It's therefore not guaranteed to
+ be truly unique (several devices may conceivably hold the same calibration
+ values). However in practice it is likely to be unique between a
+ few wiimotes, so it can be used to eg. assign a particular wiimote to the
+ same player every time.
+ + internal: made the HID report output queue fixed-size to remove any
+ glitches from frequent dynamic memory allocation (thanks
+ Steve). The old STL-queue based code can still be reenabled
+ by defining USE_DYNAMIC_HIDQUEUE.
+
+1.15 RC2:
+ + added Python wrapper by Robert Xiao - you can now use WiiYourself! with
+ Python! (thanks Robert)
+ + hopefully fixed MotionPlus connection problems! (send report to my
+ mailing list)
+
+ + added virtual event-change notifier to the wiimote object (thanks Robert
+ Xio) - works the same as external callbacks. To use, derive your own
+ class from the wiimote object and override ChangedNotifier()
+
+ + changed the way callbacks work:
+
+ in previous versions, it was OK to access the wiimote object's state
+ from callbacks. This required an internal RefreshState() call just
+ before the callback function is executed - but this could then change
+ the internal state unexpectedly, so values could change in polling
+ loops even between the app's own RefreshState() calls.
+
+ to correct this, the callback functions now get a read-only copy
+ of the newest state passed in, you should only access this copy as
+ state in the wiimote object is likely out-of-date.
+
+ In short, the wiimote object's exposed state is now _only_ refreshed
+ by the application, not by callbacks.
+
+ (this also solved Motion+ connection and disabling failures).
+
+ + added new change event 'CONNECTED'
+
+ the demo previously used callbacks to set most of its report types, but
+ it also set them once shortly after connecting the wiimote, and this could
+ cause it to set the wrong one, breaking extension data. Instead it now
+ uses the CONNECTED event in the callback. It's best to only set these
+ in one place.
+
+1.15 RC:
+ + fixed missing Balance Board calibration values for the 34kg category
+ (thanks Benjamin Lassort).
+
+ + fixed Wiimote disconnecting in certain scenarios (ReportType wasn't
+ initialised, and this could sometimes be sent to the 'mote, causing
+ it to disconnect - thanks Robert Xiao).
+
+ + minor changes to support Robert Xiao's WiiYourself! Python Wrapper!
+ (next release).
+
+1.14 BETA:
+ - added new state & callback event: bBatteryDrained / BATTERY_DRAINED
+ this is sent went the wiimote signals that the batteries are nearly
+ empty.
+ - added MotionPlus extension events (ie. for extensions plugged into it):
+ MOTIONPLUS_EXTENSION_CONNECTED
+ MOTIONPLUS_EXTENSION_DISCONNECTED
+ wiimote::MotionPlusHasExtension()
+ wiimote::DisableMotionPlus()
+ wiimote::EnableMotionPlus()
+
+ (apps can now decide if they want to disable the MotionPlus to read the
+ extension instead, see demo for an example)
+
+ ** however **, MotionPlus disabling isn't reliable at the moment (it
+ rarely works), and so extension connected to an already enabled plus
+ rarely are activated. Could use some help on this one.
+
+1.13 BETA:
+ + ** BALANCE BOARDS no longer require setting a report type! **
+ there is only one type for it, and this is now set automatically.
+
+ + 'At Rest' offset removal added (currently only for Balance Boards).
+ this reads the current analogue sensor values after a Connect() call,
+ and then subtracts them from future values, to remove any unwanted
+ offsets (currently ~ +- 0-2.5kg with Balance Boards). 'raw' values
+ are not affected.
+
+ If the device was not at rest during Connect(), then the app can
+ remove the current offsets manually via CalibrateAtRest().
+
+ + ** PRELIMINARY MOTION PLUS SUPPORT! **
+
+ Motion Plus does not report itself until queried, so it's currently
+ queried every second. If detected, it is activated and is reported
+ like any other extension. Note that extensions plugged into the
+ MotionPlus itself can't currently be used at the same time (it's not
+ known if this is even possible). Right now you need to unplug the
+ MotionPlus to use another extension (I will add some way to toggle
+ the MotionPlus so that another extensions becomes available again)
+ in the next release.
+
+ According to this interview with the MotionPlus designers
+
+ there are two gyro sensitivity modes, but this has not been reverse
+ engineered yet. Also I'm not 100% certain of the correctness of the
+ values (although they seem right), or their actual scale (ie. how many
+ degrees rotations per second do the float values actually represent)?
+
+ + the Demo has been updated for both devices.
+ + ReadMe has been updated with new relevant info.
+
+1.12 BETA:
+ + ** REMOVED ALL LIBRARY PROJECTS **
+ instead just add wiimote.cpp to your application and include the header
+ as before (this removes all build/project related-problems, like matching
+ the runtime/Unicode settings etc).
+
+ + Balance Board is now working (thanks to Akihiko's donation of a board!)
+ + added wiimote::IsBalanceBoard() (Balance Boards are detected as wiimotes
+ with a permanent BALANCE_BOARD extension).
+ NOTE: Balance Boards require the IN_BUTTONS_BALANCE_BOARD report type
+ (see demo).
+ + changed some of the wiimote_state extension enums to ID a wider variety.
+ + no more invalid acceleration values from devices that don't support it.
+ + fixed some project settings.
+
+1.11 BETA:
+ + new way to detect extensions (supposedly works on all of them, including
+ wireless Nunchuks) - only tested on stock Nunchuk.
+ + longer sleep after SetReportType (may help data not being reported).
+
+1.1 BETA:
+ + beta Balance Board support!
+ + better MinGW support: (thanks Elmo)
+ adds functional _ASSERT/TRACE/WARN/DEEP_TRACE macros
+ non-MSYS dependent build option via 'make_mingw.bat'.
+ Demo builds & works under MinGW.
+ + directory reorganisation:
+ - Each compiler has own project dir (VC2005/VC2005/MinGW),
+ and equivialent lib/ sudir.
+ + now ships with working VC2005 SP1 / VC2008 / MinGW libraries
+ (and MinGW DLL).
+ + library now compiles on Borland (thanks Griff) - demo may not.
+
+1.01a: (1.01 had incorrect version defines)
+ + extensions now work when already connected before Connect(),
+ & also when an EXT SetReportType() is used initially.
+ + ** renamed wiimote_state::IR::dot::bVisible to 'bVisible'. **
+ + Disconnect() now waits for its threads to exit.
+ + made TRACE/WARN macros VC2005+ specific (as earlier VC versions don't
+ support variable arg macros).
+ + corrected wiimote.h Connect() comments (wiimote selection is 1-based,
+ not 0-based)
+
+1.00:
+ + ** major bug fix, write buffer was abused. ** might have caused various
+ problems.
+ + ** added delay to EnableIR(), fixed IR init problems for those that
+ had them (thanks Cameron) **. if you had to use your own delays
+ to get things to work, try removing them now.
+ + wiimote_state::classic_controller::buttons::TriggerL() / R were reversed
+ (thanks Vico).
+ + patch & Makefile for MSYS / MinGW (thanks Dario).
+ + updated ReadMe.
+
+0.99b:
+ + added support for the Guitar Hero controller (thanks Morgan).
+ It's just a Classic Controller with a different ID and is read the same,
+ but can be differentiated via wiimote_state::extension_type::CLASSIC_GUITAR.
+
+0.96b:
+ + fix ClassicButtonNameFromBit[]
+ + fix WIIYOURSELF_VERSION_MINOR2
+
+0.95b:
+ + Classic Controller button fixes (thanks Farshid).
+ + sightly longer Sleep() in Reset() - hopefully fixes some reports of
+ wiimote acceleration values not working.
+
+0.94b:
+ + deadzones weren't working.
+
+0.93b:
+ + ** compiled libs are now stored in /libs **
+ + ** up to 4 dots are now available in every IR mode **
+ + some 'state_change_flags' weren't quite generated correctly.
+ - removed 'wiimote_state::polling' flags (redundant, flags are already
+ returned via RefreshState()).
+ + various internal improvements
+
+0.92b:
+ ** Polling changes **
+ - now need to call RefreshState() once before each polling pass (see
+ header comments & demo). this was done to synchronise the threaded
+ state updates, so that data integrity is guaranteed.
+ ** Callback changes **
+ - combined 'wiimote_state_changed' and 'extension_state_changed' flags
+ into 'state_change_flags
+ - removed 'ExtensionChangedCallback' (only a single callback is used now)
+ - added 'CallbackTriggerFlags' to minimize callback overhead
+ (see header comments & demo)
+ + added Reset() (see header comments)
+ + button mask TRIGGER is now _B
+
+ Demo: removed 'wiimote2' line (debug leftover)
+
+0.82b:
+ ** code/demo failed pre-XP (HID writes require XP+). code now detects
+ HID write support dynamically. **
+ + tidied code & surpressed redundant warning (or just enable C++ exceptions).
+ + Improved debug output (mainly DEEP_TRACE)
+ + Connect() can now take (and defaults to) 'FIRST_AVAILABLE' as the wiimote
+ index (see header comments).
+ + 'wiimote_sample' is now auto-cleared on construction
+ + Adjusted max 'theoretical' raw IR coord values (1023x767) to largest
+ actually observed, to output full 0-1 float range.
+ + **Inverted** IR X float coord to match traditional 'left = 0' convention
+ (raw coords unaffected for now).
+ + Added state recording ability to aid state/motion analysis. See RecordState();
+ - removed RequestBatteryUpdate() (battery level is now periodically refreshed)
+ - disabled ...CALIBRATION_CHANGED flags (not useful)
+
+ Demo : should now work pre-XP.
+ ReadMe: added Wiimote/PC installation notes (MS stack is especially tricky).
+
+0.81b:
+ + connection loss is now detected (via failed writes)
+ + ConnectionWasLost() added
+ + report modes renamed for clarity.
+ + Connect(): added 'force_hidwrites' (for testing only).
+ + Extension connections now seem to be reliable.
+ + Battery is now periodically refreshed (also used for loss detection)
+ + 'BatteryRaw' was set incorrectly
+ + added 'wiimote::ClassicButtonNameFromBit[]'
+
+ + Demo : Classic Controller data shown.
+ + Demo : IR dot sizes now reported when possible (only if extension
+ data isn't requested as they're not available then).
+
+ + License: 'no harm' clause added.
+ + ReadMe : added build notes etc.
+
+0.1b:
+ First release. \ No newline at end of file
diff --git a/w32mote/WiiYourself/License.txt b/w32mote/WiiYourself/License.txt
new file mode 100644
index 0000000..fc41a9a
--- /dev/null
+++ b/w32mote/WiiYourself/License.txt
@@ -0,0 +1,42 @@
+____________________________________________________________
+
+ - WiiYourself! - native C++ Wiimote library v1.15
+ (c) gl.tter 2007-10 - http://gl.tter.org
+____________________________________________________________
+
+ LICENSE: My Wiimote library is free for any use (including
+ commercial), with the following conditions:
+
+1) You may not use it to harm anyone, directly or
+ indirectly. * this includes, but is not limited to, any
+ kind of direct or indirect MILITARY use or related
+ research *
+
+ (but bruising egos is fine ;).
+
+2) Any distribution in binary form (ie. linked with your
+ program) must include the following text in your
+ distribiutions's documentation (ReadMe file, help file,
+ About box and/or splash screen):
+
+ "contains WiiYourself! wiimote code by gl.tter
+ http://gl.tter.org"
+
+3) Any distribution in source code form must keep all my
+ copyright notices intact unmodified (you can add to
+ them if you've made changes), and must include this
+ license text (either include this file in your
+ distribution, or paste its contents into your
+ distribution's own licence file).
+
+4) You may not use the code to produce a competing
+ library, unless you rewrite all of it considerably
+ (for example to convert it to another language, but
+ you need to contact me for written permission first).
+
+ Instead please contribute new features, fixes and ideas
+ to my mailining list (see ReadMe.txt).
+__
+
+gl.tter (http://gl.tter.org | glATr-i-lDOTnet)
+
diff --git a/w32mote/WiiYourself/ReadMe.txt b/w32mote/WiiYourself/ReadMe.txt
new file mode 100644
index 0000000..85ca003
--- /dev/null
+++ b/w32mote/WiiYourself/ReadMe.txt
@@ -0,0 +1,202 @@
+__________________________________________________________________
+
+ - WiiYourself! - native C++ Wiimote library v1.15
+ (c) gl.tter 2007-10 - http://gl.tter.org
+__________________________________________________________________
+
+This marks the likely-final release of my free & fully-featured
+ Wiimote native C++ library for Windows.
+
+Originally based on Brian Peek's 'Managed Wiimote Library'
+ (http://blogs.msdn.com/coding4fun/archive/2007/03/14/1879033.aspx)
+ I then rewrote and extended it considerably.
+
+There's no documentation - check Brian's article for a good
+overview and general 'Wiimote with Windows' info - but the
+source code has extensive comments, and the demo app should help
+you make sense of it all. Any questions, join my mailing list
+(below).
+
+Check License.txt for the (few) conditions of use, and
+ History.txt for important changes from previous versions.
+_____
+
+Notes:
+
+ - the library consists only of wiimote.cpp & wiimote.h. Simply add
+ them to your project and include the header.
+
+ - VC 2005 & 2008 projects, and a MSYS makefile for MinGW for the
+ demo program are included.
+
+ - for MSYS:
+ at the MSYS prompt type: make -f Makefile.MSYS
+ (it will create a folder named MinGW whith the binaries
+ and proper folder structure)
+
+ - The Windows Driver Development Kit (WDK or DDK) is required to
+ build (for the HID API). It's a free download from MS page
+ (no need to register). Currently it's found here (or search
+ for it on microsoft.com):
+
+ 'Windows Driver Kit (WDK) 7.1.0'
+ http://www.microsoft.com/whdc/DevTools/WDK/WDKpkg.mspx
+
+ Unfortunately it changes frequently so the instructions below
+ may be wrong or incomplete. Google or ask on my mailing
+ list for help if needed:
+
+ - add its 'inc' and 'inc/api' dirs to your include paths
+ (make sure they are *above* other paths, ie. searched first)
+
+ - add its 'lib/win7/i386' dir to your library paths.
+
+ You can also use earlier versions known as the 'DDK'. The parts
+ of the HID API I use haven't changed in a long time. These paths
+ are usually used:
+
+ - add 'inc/wxp' dir to your include paths (*above* others)
+ - add 'lib/wxp/i386' to your library paths.
+
+ Notes:
+ - do _not_ add any 'STL' paths from the WDK/DDK as they can cause
+ build problems.
+ - the order of your include paths can be important. If you placed
+ them above the paths and are still getting compilation errors,
+ try moving them around.
+
+ - The library is Unicode-ready via <tchar.h> (see demo project).
+
+ - if you're not using VC you need to link with these libraries:
+ setupapi.lib
+ winmm.lib
+ hid.lib (from the DDK)
+__________________________
+
+Wiimote installation notes:
+
+ The Wiimote needs to be 'paired' (Bluetooth connected) with the
+ PC before you can install/use it. Pressing 1 & 2 simultaneously
+ puts it into 'discoverable' mode for a few seconds (LEDs will
+ flash - the number of LEDs reflects the battery level).
+
+ It will be detected as 'Nintendo RVL-CNT-01'.
+
+ Stack-specific instructions:
+
+ - Windows' built-in Bluetooth stack:
+
+ 1) open up the Bluetooth control panel.
+ 2) press _and hold_ 1 & 2 on the wiimote (LEDs flash) until the
+ installation is complete (otherwise the wiimote usually times
+ out half-way through the procedure, and although it may seem
+ to have installed it's never 'connected' and doesn't work).
+ 3) add a new device - it should find it. don't use a password.
+ 4) when the installation is fully complete, let go of 1&2. The
+ Bluetooth panel should now show it 'connected'.
+
+ if something goes wrong you need to uninstall it and try again.
+
+ if you un-pair the wiimote later (see below), it seems you need
+ to remove and install it all over again to get it to work (if you
+ know a workaround, let me know).
+
+ - Toshiba stack:
+
+ straight forward, press 1 & 2 on the Wiimote (you don't need to
+ hold them if you're quick) and click 'New Connection'.
+
+ once found, you can pair it anytime again by right-clicking its
+ device icon (and pressing 1 & 2 as before) - you can also set up
+ a desktop shortcut that enters discovery mode immediately.
+
+ - Widcomm stack:
+
+ 1) Open 'My Bluetooth Places'.
+ 2) Press and hold 1 & 2 (until the process is complete).
+ 3) Click 'View Devices in Range'.
+ 4) Wiimote is detected as Nintendo RVL-CNT-01.
+ 5) Select it, then click 'Bluetooth Setup Wizard'.
+ 5) Click 'Skip' (no password).
+ 6) Now it should be connected (you can let go of 1 & 2).
+
+ Troubleshooting:
+ - the device seems to be connected but the Demo can't find it
+ (CreateFile() fails with error 5 'Access Denied'),
+ or - it disconnects almost immediately after connection
+ or - asks for a password a few seconds after connection
+
+ Try uninstalling all HID devices from Device Manager, and then
+ redetecting them with 'Scan for hardware changes' (I had all
+ these problems and that fixed it for me).
+
+ - Other stacks
+
+ similar to the above (contribute instructions?)
+
+
+ - Disconnect/un-pair to save power (any stack):
+
+ hold the Wiimote Power button for a few seconds - it automatically
+ unpairs itself, re-enters pairing mode for a few seconds
+ (flashing LEDs), then times out and (effecively) switches off.
+
+__________________________
+
+Balance Boards notes:
+
+ Balance Boards are installed using the same procedure as wiimotes, by
+ holding the 'Sync' button in the battery compartment. The Bluetooth
+ stack detectes them as 'Nintendo RVL-WBC-01'.
+
+ They report to the library as wiimotes, with a permanent BALANCE_BOARD
+ extension. They only have one button (A), and no IR/Acceleration/Rumble
+ or Speaker support. There is only one supported 'report type' so the
+ library sets this automatically (see the demo for details).
+
+ You can detect them with the wiimote::IsBalanceBoard() call.
+
+ The boards tested so far all report up to ~ +-2.5KB weight offsets even
+ where there is no weight placed on them (ie. 'at rest'). It is unknown
+ if this is normal sensor inaccuracy/drift, or if the calibration values
+ read from the board are interpreted incorrectly. For now the library
+ automatically subtracts the first incoming sensor values after a Connect()
+ call from all future non-raw values (the raw values are never modified).
+
+ The offsets used are exposed in wiimote_state::balance_board::AtRestKG.
+
+ - if the board wasn't at rest during the Connect() call these offsets
+ will be wrong - the app can measure them manually (when the board
+ is at rest) by calling wiimote::CalibrateAtRest().
+
+__________________________
+
+MotionPlus notes:
+
+ Once activated it's reported like any other extension, but needs to be
+ manually enabled (see demo for an example):
+ - Test first if it's connected via MotionPlusConnected()
+ - Then call EnableMotionPlus()
+ - It will then replace any other extension connected to it, unil you
+ disable the Motion+ again with DisableMotionPlus().
+
+ The speed values are believed to be correct. The calibration values are not
+ yet understood, so the data is currently uncalibrated.
+
+ Some technical details are mentioned in this interview with the MotionPlus
+ hardware/software engineers:
+ http://uk.wii.com/wii/en_GB/software/iwata_asks_motionplus_volume_1_2162.html#top
+
+
+ * Special thanks to the guys at WiiBrew.org, and all contributing hackers *
+ for figuring out & documenting the wiimote protocols:
+ http://wiibrew.org/wiki/Wiimote/Extension_Controllers#Wii_Motion_Plus
+
+
+Sign up to the mailing list to stay in the loop, exchange ideas or help each
+ other out: http://gl.tter.org/mailman/listinfo/wiiyourself_gl.tter.org
+__
+
+gl.tter (glATr-i-lDOTnet)
+
+
diff --git a/w32mote/WiiYourself/wiimote.cpp b/w32mote/WiiYourself/wiimote.cpp
new file mode 100644
index 0000000..a4fb62b
--- /dev/null
+++ b/w32mote/WiiYourself/wiimote.cpp
@@ -0,0 +1,2806 @@
+// _______________________________________________________________________________
+//
+// - WiiYourself! - native C++ Wiimote library v1.15
+// (c) gl.tter 2007-10 - http://gl.tter.org
+//
+// see License.txt for conditions of use. see History.txt for change log.
+// _______________________________________________________________________________
+//
+// wiimote.cpp (tab = 4 spaces)
+
+// VC-specifics:
+#ifdef _MSC_VER
+ // disable warning "C++ exception handler used, but unwind semantics are not enabled."
+ // in <xstring> (I don't use it - or just enable C++ exceptions)
+# pragma warning(disable: 4530)
+// auto-link with the necessary libs
+# pragma comment(lib, "setupapi.lib")
+# pragma comment(lib, "hid.lib") // for HID API (from DDK)
+# pragma comment(lib, "winmm.lib") // for timeGetTime()
+#endif // _MSC_VER
+
+#include "wiimote.h"
+#include <setupapi.h>
+extern "C" {
+# ifdef __MINGW32__
+# include <ddk/hidsdi.h>// from WinDDK
+# else
+# include <hidsdi.h>
+# endif
+}
+#include <sys/types.h> // for _stat
+#include <sys/stat.h> // "
+#include <process.h> // for _beginthreadex()
+#ifdef __BORLANDC__
+# include <cmath.h> // for orientation
+#else
+# include <math.h> // "
+#endif
+#include <mmreg.h> // for WAVEFORMATEXTENSIBLE
+#include <mmsystem.h> // for timeGetTime()
+
+// apparently not defined in some compilers:
+#ifndef min
+# define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+// ------------------------------------------------------------------------------------
+// helpers
+// ------------------------------------------------------------------------------------
+template<class T> inline T sign (const T& val) { return (val<0)? T(-1) : T(1); }
+template<class T> inline T square(const T& val) { return val*val; }
+#define ARRAY_ENTRIES(array) (sizeof(array)/sizeof(array[0]))
+
+// ------------------------------------------------------------------------------------
+// Tracing & Debugging
+// ------------------------------------------------------------------------------------
+#define PREFIX _T("WiiYourself! : ")
+
+// comment these to auto-strip their code from the library:
+// (they currently use OutputDebugString() via _TRACE() - change to suit)
+#if (_MSC_VER >= 1400) // VC 2005+ (earlier versions don't support variable args)
+# define TRACE(fmt, ...) _TRACE(PREFIX fmt _T("\n"), __VA_ARGS__)
+# define WARN(fmt, ...) _TRACE(PREFIX _T("* ") fmt _T(" *") _T("\n"), __VA_ARGS__)
+#elif defined(__MINGW32__)
+# define TRACE(fmt, ...) _TRACE(PREFIX fmt _T("\n") , ##__VA_ARGS__)
+# define WARN(fmt, ...) _TRACE(PREFIX _T("* ") fmt _T(" *") _T("\n") , ##__VA_ARGS__)
+#endif
+// uncomment any of these for deeper debugging:
+//#define DEEP_TRACE(fmt, ...) _TRACE(PREFIX _T("|") fmt _T("\n"), __VA_ARGS__) // VC 2005+
+//#define DEEP_TRACE(fmt, ...) _TRACE(PREFIX _T("|") fmt _T("\n") , ##__VA_ARGS__) // mingw
+//#define BEEP_DEBUG_READS
+//#define BEEP_DEBUG_WRITES
+//#define BEEP_ON_ORIENTATION_ESTIMATE
+//#define BEEP_ON_PERIODIC_STATUSREFRESH
+
+// internals: auto-strip code from the macros if they weren't defined
+#ifndef TRACE
+# define TRACE
+#endif
+#ifndef DEEP_TRACE
+# define DEEP_TRACE
+#endif
+#ifndef WARN
+# define WARN
+#endif
+// ------------------------------------------------------------------------------------
+static void _cdecl _TRACE (const TCHAR* fmt, ...)
+ {
+ static TCHAR buffer[256];
+ if (!fmt) return;
+
+ va_list argptr;
+ va_start (argptr, fmt);
+#if (_MSC_VER >= 1400) // VC 2005+
+ _vsntprintf_s(buffer, ARRAY_ENTRIES(buffer), _TRUNCATE, fmt, argptr);
+#else
+ _vsntprintf (buffer, ARRAY_ENTRIES(buffer), fmt, argptr);
+#endif
+ va_end (argptr);
+
+ OutputDebugString(buffer);
+ }
+
+// ------------------------------------------------------------------------------------
+// wiimote
+// ------------------------------------------------------------------------------------
+// class statics
+HMODULE wiimote::HidDLL = NULL;
+unsigned wiimote::_TotalCreated = 0;
+unsigned wiimote::_TotalConnected = 0;
+hidwrite_ptr wiimote::_HidD_SetOutputReport = NULL;
+
+// (keep in sync with 'speaker_freq'):
+const unsigned wiimote::FreqLookup [TOTAL_FREQUENCIES] =
+ { 0, 4200, 3920, 3640, 3360,
+ 3130, 2940, 2760, 2610, 2470 };
+
+const TCHAR* wiimote::ButtonNameFromBit [TOTAL_BUTTON_BITS] =
+ { _T("Left") , _T("Right"), _T("Down"), _T("Up"),
+ _T("Plus") , _T("??") , _T("??") , _T("??") ,
+ _T("Two") , _T("One") , _T("B") , _T("A") ,
+ _T("Minus"), _T("??") , _T("??") , _T("Home") };
+
+const TCHAR* wiimote::ClassicButtonNameFromBit [TOTAL_BUTTON_BITS] =
+ { _T("??") , _T("TrigR") , _T("Plus") , _T("Home"),
+ _T("Minus"), _T("TrigL") , _T("Down") , _T("Right") ,
+ _T("Up") , _T("Left") , _T("ZR") , _T("X") ,
+ _T("A") , _T("Y") , _T("B") , _T("ZL") };
+// ------------------------------------------------------------------------------------
+wiimote::wiimote ()
+ :
+ DataRead (CreateEvent(NULL, FALSE, FALSE, NULL)),
+ Handle (INVALID_HANDLE_VALUE),
+ ReportType (IN_BUTTONS),
+ bStatusReceived (false), // for output method detection
+ bConnectInProgress (true ),
+ bInitInProgress (false),
+ bEnablingMotionPlus (false),
+ bConnectionLost (false), // set if write fails after connection
+ bMotionPlusDetected (false),
+ bMotionPlusEnabled (false),
+ bMotionPlusExtension (false),
+ bCalibrateAtRest (false),
+ bUseHIDwrite (false), // if OS supports it
+ ChangedCallback (NULL),
+ CallbackTriggerFlags (CHANGED_ALL),
+ InternalChanged (NO_CHANGE),
+ CurrentSample (NULL),
+ HIDwriteThread (NULL),
+ ReadParseThread (NULL),
+ SampleThread (NULL),
+ AsyncRumbleThread (NULL),
+ AsyncRumbleTimeout (0),
+ UniqueID (0) // not _guaranteed_ unique, see comments in header
+#ifdef ID2_FROM_DEVICEPATH // (see comments in header)
+ // UniqueID2 (0)
+#endif
+ {
+ _ASSERT(DataRead != INVALID_HANDLE_VALUE);
+
+ // if this is the first wiimote object, detect & enable HID write support
+ if(++_TotalCreated == 1)
+ {
+ HidDLL = LoadLibrary(_T("hid.dll"));
+ _ASSERT(HidDLL);
+ if(!HidDLL)
+ WARN(_T("Couldn't load hid.dll - shouldn't happen!"));
+ else{
+ _HidD_SetOutputReport = (hidwrite_ptr)
+ GetProcAddress(HidDLL, "HidD_SetOutputReport");
+ if(_HidD_SetOutputReport)
+ TRACE(_T("OS supports HID writes."));
+ else
+ TRACE(_T("OS doesn't support HID writes."));
+ }
+ }
+
+ // clear our public and private state data completely (including deadzones)
+ Clear (true);
+ Internal.Clear(true);
+
+ // and the state recording vars
+ memset(&Recording, 0, sizeof(Recording));
+
+ // for overlapped IO (Read/WriteFile)
+ memset(&Overlapped, 0, sizeof(Overlapped));
+ Overlapped.hEvent = DataRead;
+ Overlapped.Offset =
+ Overlapped.OffsetHigh = 0;
+
+ // for async HID output method
+ InitializeCriticalSection(&HIDwriteQueueLock);
+ // for polling
+ InitializeCriticalSection(&StateLock);
+
+ // request millisecond timer accuracy
+ timeBeginPeriod(1);
+ }
+// ------------------------------------------------------------------------------------
+wiimote::~wiimote ()
+ {
+ Disconnect();
+
+ // events & critical sections are kept open for the lifetime of the object,
+ // so tidy them up here:
+ if(DataRead != INVALID_HANDLE_VALUE)
+ CloseHandle(DataRead);
+
+ DeleteCriticalSection(&HIDwriteQueueLock);
+ DeleteCriticalSection(&StateLock);
+
+ // tidy up timer accuracy request
+ timeEndPeriod(1);
+
+ // release HID DLL (for dynamic HID write method)
+ if((--_TotalCreated == 0) && HidDLL)
+ {
+ FreeLibrary(HidDLL);
+ HidDLL = NULL;
+ _HidD_SetOutputReport = NULL;
+ }
+ }
+
+// ------------------------------------------------------------------------------------
+bool wiimote::Connect (unsigned wiimote_index, bool force_hidwrites)
+ {
+ if(wiimote_index == FIRST_AVAILABLE)
+ TRACE(_T("Connecting first available Wiimote:"));
+ else
+ TRACE(_T("Connecting Wiimote %u:"), wiimote_index);
+
+ // auto-disconnect if user is being naughty
+ if(IsConnected())
+ Disconnect();
+
+ // get the GUID of the HID class
+ GUID guid;
+ HidD_GetHidGuid(&guid);
+
+ // get a handle to all devices that are part of the HID class
+ // Brian: Fun fact: DIGCF_PRESENT worked on my machine just fine. I reinstalled
+ // Vista, and now it no longer finds the Wiimote with that parameter enabled...
+ HDEVINFO dev_info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_DEVICEINTERFACE);// | DIGCF_PRESENT);
+ if(!dev_info) {
+ WARN(_T("couldn't get device info"));
+ return false;
+ }
+
+ // enumerate the devices
+ SP_DEVICE_INTERFACE_DATA didata;
+ didata.cbSize = sizeof(didata);
+
+ unsigned index = 0;
+ unsigned wiimotes_found = 0;
+ while(SetupDiEnumDeviceInterfaces(dev_info, NULL, &guid, index, &didata))
+ {
+ // get the buffer size for this device detail instance
+ DWORD req_size = 0;
+ SetupDiGetDeviceInterfaceDetail(dev_info, &didata, NULL, 0, &req_size, NULL);
+
+ // (bizarre way of doing it) create a buffer large enough to hold the
+ // fixed-size detail struct components, and the variable string size
+ SP_DEVICE_INTERFACE_DETAIL_DATA *didetail =
+ (SP_DEVICE_INTERFACE_DETAIL_DATA*) new BYTE[req_size];
+ _ASSERT(didetail);
+ didetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+
+ // now actually get the detail struct
+ if(!SetupDiGetDeviceInterfaceDetail(dev_info, &didata, didetail,
+ req_size, &req_size, NULL)) {
+ WARN(_T("couldn't get devinterface info for %u"), index);
+ break;
+ }
+
+ // open a shared handle to the device to query it (this will succeed even
+ // if the wiimote is already Connect()'ed)
+ DEEP_TRACE(_T(".. querying device %s"), didetail->DevicePath);
+ Handle = CreateFile(didetail->DevicePath, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ if(Handle == INVALID_HANDLE_VALUE) {
+ DEEP_TRACE(_T(".... failed with err %x (probably harmless)."),
+ GetLastError());
+ goto skip;
+ }
+
+ // get the device attributes
+ HIDD_ATTRIBUTES attrib;
+ attrib.Size = sizeof(attrib);
+ if(HidD_GetAttributes(Handle, &attrib))
+ {
+ // is this a wiimote?
+ if((attrib.VendorID != VID) || (attrib.ProductID != PID))
+ goto skip;
+
+ // yes, but is it the one we're interested in?
+ ++wiimotes_found;
+ if((wiimote_index != FIRST_AVAILABLE) &&
+ (wiimote_index != wiimotes_found))
+ goto skip;
+
+ // the wiimote is installed, but it may not be currently paired:
+ if(wiimote_index == FIRST_AVAILABLE)
+ TRACE(_T(".. opening Wiimote %u:"), wiimotes_found);
+ else
+ TRACE(_T(".. opening:"));
+
+
+ // re-open the handle, but this time we don't allow write sharing
+ // (that way subsequent calls can still _discover_ wiimotes above, but
+ // will correctly fail here if they're already connected)
+ CloseHandle(Handle);
+
+ // note this also means that if another application has already opened
+ // the device, the library can no longer connect it (this may happen
+ // with software that enumerates all joysticks in the system, because
+ // even though the wiimote is not a standard joystick (and can't
+ // be read as such), it unfortunately announces itself to the OS
+ // as one. The SDL library was known to do grab wiimotes like this.
+ // If you cannot stop the application from doing it, you may change the
+ // call below to open the device in full shared mode - but then the
+ // library can no longer detect if you've already connected a device
+ // and will allow you to connect it twice! So be careful ...
+ Handle = CreateFile(didetail->DevicePath, GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL, OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED, NULL);
+ if(Handle == INVALID_HANDLE_VALUE) {
+ TRACE(_T(".... failed with err %x"), GetLastError());
+ goto skip;
+ }
+
+ // clear the wiimote state & buffers
+ Clear (false); // preserves existing deadzones
+ Internal.Clear(false); // "
+ InternalChanged = NO_CHANGE;
+ memset(ReadBuff , 0, sizeof(ReadBuff));
+ bConnectionLost = false;
+ bConnectInProgress = true; // don't parse extensions or request regular
+ // updates until complete
+ // enable async reading
+ BeginAsyncRead();
+
+ // autodetect which write method the Bluetooth stack supports,
+ // by requesting the wiimote status report:
+ if(force_hidwrites && !_HidD_SetOutputReport) {
+ TRACE(_T(".. can't force HID writes (not supported)"));
+ force_hidwrites = false;
+ }
+
+ if(force_hidwrites)
+ TRACE(_T(".. (HID writes forced)"));
+ else{
+ // - try WriteFile() first as it's the most efficient (it uses
+ // harware interrupts where possible and is async-capable):
+ bUseHIDwrite = false;
+ RequestStatusReport();
+ // and wait for the report to arrive:
+ DWORD last_time = timeGetTime();
+ while(!bStatusReceived && ((timeGetTime()-last_time) < 500))
+ Sleep(10);
+ TRACE(_T(".. WriteFile() %s."), bStatusReceived? _T("succeeded") :
+ _T("failed"));
+ }
+
+ // try HID write method (if supported)
+ if(!bStatusReceived && _HidD_SetOutputReport)
+ {
+ bUseHIDwrite = true;
+ RequestStatusReport();
+ // wait for the report to arrive:
+ DWORD last_time = timeGetTime();
+ while(!bStatusReceived && ((timeGetTime()-last_time) < 500))
+ Sleep(10);
+ // did we get it?
+ TRACE(_T(".. HID write %s."), bStatusReceived? _T("succeeded") :
+ _T("failed"));
+ }
+
+ // still failed?
+ if(!bStatusReceived) {
+ WARN(_T("output failed - wiimote is not connected (or confused)."));
+ Disconnect();
+ goto skip;
+ }
+
+//Sleep(500);
+ // reset it
+ Reset();
+
+ // read the wiimote calibration info
+ ReadCalibration();
+
+ // allow the result(s) to come in (so that the caller can immediately test
+ // MotionPlusConnected()
+ Sleep(300); // note, don't need it on my system, better to be safe though
+
+ // connected succesfully:
+ _TotalConnected++;
+
+ // use the first incomding analogue sensor values as the 'at rest'
+ // offsets (only supports the Balance Board currently)
+ bCalibrateAtRest = true;
+
+ // refresh the public state from the internal one (so that everything
+ // is available straight away
+ RefreshState();
+
+ // attempt to construct a unique hardware ID from the calibration
+ // data bytes (this is obviously not guaranteed to be unique across
+ // all devices, but may work fairly well in practice... ?)
+ memcpy(&UniqueID, &CalibrationInfo, sizeof(CalibrationInfo));
+
+ _ASSERT(UniqueID != 0); // if this fires, the calibration data didn't
+ // arrive - this shouldn't happen
+
+#ifdef ID2_FROM_DEVICEPATH // (see comments in header)
+ // create a 2nd alternative id by simply adding all the characters
+ // in the device path to create a single number
+ UniqueID2 = 0;
+ for(unsigned index=0; index<_tcslen(didetail->DevicePath); index++)
+ UniqueID2 += didetail->DevicePath[index];
+#endif
+ // and show when we want to trigger the next periodic status request
+ // (for battery level and connection loss detection)
+ NextStatusTime = timeGetTime() + REQUEST_STATUS_EVERY_MS;
+ NextMPlusDetectTime = timeGetTime() + DETECT_MPLUS_EVERY_MS;
+ MPlusDetectCount = DETECT_MPLUS_COUNT;
+
+ // tidy up
+ delete[] (BYTE*)didetail;
+ break;
+ }
+skip:
+ // tidy up
+ delete[] (BYTE*)didetail;
+
+ if(Handle != INVALID_HANDLE_VALUE) {
+ CloseHandle(Handle);
+ Handle = INVALID_HANDLE_VALUE;
+ }
+ // if this was the specified wiimote index, abort
+ if((wiimote_index != FIRST_AVAILABLE) &&
+ (wiimote_index == (wiimotes_found-1)))
+ break;
+
+ index++;
+ }
+
+ // clean up our list
+ SetupDiDestroyDeviceInfoList(dev_info);
+
+ bConnectInProgress = false;
+ if(IsConnected()) {
+ TRACE(_T(".. connected!"));
+ // notify the callbacks (if requested to do so)
+ if(CallbackTriggerFlags & CONNECTED)
+ {
+ ChangedNotifier(CONNECTED, Internal);
+ if(ChangedCallback)
+ ChangedCallback(*this, CONNECTED, Internal);
+ }
+ return true;
+ }
+ TRACE(_T(".. connection failed."));
+ return false;
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::CalibrateAtRest ()
+ {
+ _ASSERT(IsConnected());
+ if(!IsConnected())
+ return;
+
+ // the app calls this to remove 'at rest' offsets from the analogue sensor
+ // values (currently only works for the Balance Board):
+ if(IsBalanceBoard()) {
+ TRACE(_T(".. removing 'at rest' BBoard offsets."));
+ Internal.BalanceBoard.AtRestKg = Internal.BalanceBoard.Kg;
+ RefreshState();
+ }
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::Disconnect ()
+ {
+ if(Handle == INVALID_HANDLE_VALUE)
+ return;
+
+ TRACE(_T("Disconnect()."));
+
+ if(IsConnected())
+ {
+ _ASSERT(_TotalConnected > 0); // sanity
+ _TotalConnected--;
+
+ if(!bConnectionLost)
+ Reset();
+ }
+
+ CloseHandle(Handle);
+ Handle = INVALID_HANDLE_VALUE;
+ UniqueID = 0;
+#ifdef ID2_FROM_DEVICEPATH // (see comments in header)
+ UniqueID2 = 0;
+#endif
+
+ // close the read thread
+ if(ReadParseThread) {
+ // unblock it so it can realise we're closing and exit straight away
+ SetEvent(DataRead);
+ WaitForSingleObject(ReadParseThread, 3000);
+ CloseHandle(ReadParseThread);
+ ReadParseThread = NULL;
+ }
+ // close the rumble thread
+ if(AsyncRumbleThread) {
+ WaitForSingleObject(AsyncRumbleThread, 3000);
+ CloseHandle(AsyncRumbleThread);
+ AsyncRumbleThread = NULL;
+ AsyncRumbleTimeout = 0;
+ }
+ // and the sample streaming thread
+ if(SampleThread) {
+ WaitForSingleObject(SampleThread, 3000);
+ CloseHandle(SampleThread);
+ SampleThread = NULL;
+ }
+
+#ifndef USE_DYNAMIC_HIDQUEUE
+ HID.Deallocate();
+#endif
+
+ bStatusReceived = false;
+
+ // and clear the state
+ Clear (false); // (preserves deadzones)
+ Internal.Clear(false); // "
+ InternalChanged = NO_CHANGE;
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::Reset ()
+ {
+ TRACE(_T("Resetting wiimote."));
+
+ if(bMotionPlusEnabled)
+ DisableMotionPlus();
+
+ // stop updates (by setting report type to non-continuous, buttons-only)
+ if(IsBalanceBoard())
+ SetReportType(IN_BUTTONS_BALANCE_BOARD, false);
+ else
+ SetReportType(IN_BUTTONS, false);
+
+ SetRumble (false);
+ SetLEDs (0x00);
+// MuteSpeaker (true);
+ EnableSpeaker(false);
+
+ Sleep(150); // avoids loosing the extension calibration data on Connect()
+ }
+// ------------------------------------------------------------------------------------
+unsigned __stdcall wiimote::ReadParseThreadfunc (void* param)
+ {
+ // this thread waits for the async ReadFile() to deliver data & parses it.
+ // it also requests periodic status updates, deals with connection loss
+ // and ends state recordings with a specific duration:
+ _ASSERT(param);
+ wiimote &remote = *(wiimote*)param;
+ OVERLAPPED &overlapped = remote.Overlapped;
+ unsigned exit_code = 0; // (success)
+
+ while(1)
+ {
+ // wait until the overlapped read completes, or the timeout is reached:
+ DWORD wait = WaitForSingleObject(overlapped.hEvent, 500);
+
+ // before we deal with the result, let's do some housekeeping:
+
+ // if we were recently Disconect()ed, exit now
+ if(remote.Handle == INVALID_HANDLE_VALUE) {
+ DEEP_TRACE(_T("read thread: wiimote was disconnected"));
+ break;
+ }
+ // ditto if the connection was lost (eg. through a failed write)
+ if(remote.bConnectionLost)
+ {
+connection_lost:
+ TRACE(_T("read thread: connection to wiimote was lost"));
+ remote.Disconnect();
+ remote.InternalChanged = (state_change_flags)
+ (remote.InternalChanged | CONNECTION_LOST);
+ // report via the callback (if any)
+ if(remote.CallbackTriggerFlags & CONNECTION_LOST)
+ {
+ remote.ChangedNotifier(CONNECTION_LOST, remote.Internal);
+ if(remote.ChangedCallback)
+ remote.ChangedCallback(remote, CONNECTION_LOST, remote.Internal);
+ }
+ break;
+ }
+
+ DWORD time = timeGetTime();
+ // periodic events (but not if we're streaming audio,
+ // we don't want to cause a glitch)
+ if(remote.IsConnected() && !remote.bInitInProgress &&
+ !remote.IsPlayingAudio())
+ {
+ // status request due?
+ if(time > remote.NextStatusTime)
+ {
+#ifdef BEEP_ON_PERIODIC_STATUSREFRESH
+ Beep(2000,50);
+#endif
+ remote.RequestStatusReport();
+ // and schedule the next one
+ remote.NextStatusTime = time + REQUEST_STATUS_EVERY_MS;
+ }
+ // motion plus detection due?
+ if(!remote.IsBalanceBoard() &&
+// !remote.bConnectInProgress &&
+ !remote.bMotionPlusExtension &&
+ (remote.Internal.ExtensionType != MOTION_PLUS) &&
+ (remote.Internal.ExtensionType != PARTIALLY_INSERTED) &&
+ (time > remote.NextMPlusDetectTime))
+ {
+ remote.DetectMotionPlusExtensionAsync();
+ // we try several times in quick succession before the next
+ // delay:
+ if(--remote.MPlusDetectCount == 0) {
+ remote.NextMPlusDetectTime = time + DETECT_MPLUS_EVERY_MS;
+ remote.MPlusDetectCount = DETECT_MPLUS_COUNT;
+#ifdef _DEBUG
+ TRACE(_T("--"));
+#endif
+ }
+ }
+ }
+
+ // if we're state recording and have reached the specified duration, stop
+ if(remote.Recording.bEnabled && (remote.Recording.EndTimeMS != UNTIL_STOP) &&
+ (time >= remote.Recording.EndTimeMS))
+ remote.Recording.bEnabled = false;
+
+ // now handle the wait result:
+ // did the wait time out?
+ if(wait == WAIT_TIMEOUT) {
+ DEEP_TRACE(_T("read thread: timed out"));
+ continue; // wait again
+ }
+ // did an error occurr?
+ if(wait != WAIT_OBJECT_0) {
+ DEEP_TRACE(_T("read thread: error waiting!"));
+ remote.bConnectionLost = true;
+ // deal with it straight away to avoid a longer delay
+ goto connection_lost;
+ }
+
+ // data was received:
+#ifdef BEEP_DEBUG_READS
+ Beep(500,1);
+#endif
+ DWORD read = 0;
+ // get the data read result
+ GetOverlappedResult(remote.Handle, &overlapped, &read, TRUE);
+ // if we read data, parse it
+ if(read) {
+ DEEP_TRACE(_T("read thread: parsing data"));
+ remote.OnReadData(read);
+ }
+ else
+ DEEP_TRACE(_T("read thread: didn't get any data??"));
+ }
+
+ TRACE(_T("(ending read thread)"));
+#ifdef BEEP_DEBUG_READS
+ if(exit_code != 0)
+ Beep(200,1000);
+#endif
+ return exit_code;
+ }
+// ------------------------------------------------------------------------------------
+bool wiimote::BeginAsyncRead ()
+ {
+ // (this is also called before we're fully connected)
+ if(Handle == INVALID_HANDLE_VALUE)
+ return false;
+
+ DEEP_TRACE(_T(".. starting async read"));
+#ifdef BEEP_DEBUG_READS
+ Beep(1000,1);
+#endif
+
+ DWORD read;
+ if (!ReadFile(Handle, ReadBuff, REPORT_LENGTH, &read, &Overlapped)) {
+ DWORD err = GetLastError();
+ if(err != ERROR_IO_PENDING) {
+ DEEP_TRACE(_T(".... ** ReadFile() failed! **"));
+ return false;
+ }
+ }
+
+ // launch the completion wait/callback thread
+ if(!ReadParseThread) {
+ ReadParseThread = (HANDLE)_beginthreadex(NULL, 0, ReadParseThreadfunc,
+ this, 0, NULL);
+ DEEP_TRACE(_T(".... creating read thread"));
+ _ASSERT(ReadParseThread);
+ if(!ReadParseThread)
+ return false;
+ SetThreadPriority(ReadParseThread, WORKER_THREAD_PRIORITY);
+ }
+
+ // if ReadFile completed while we called, signal the thread to proceed
+ if(read) {
+ DEEP_TRACE(_T(".... got data right away"));
+ SetEvent(DataRead);
+ }
+ return true;
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::OnReadData (DWORD bytes_read)
+ {
+ _ASSERT(bytes_read == REPORT_LENGTH);
+
+ // copy our input buffer
+ BYTE buff [REPORT_LENGTH];
+ memcpy(buff, ReadBuff, bytes_read);
+
+ // start reading again
+ BeginAsyncRead();
+
+ // parse it
+ ParseInput(buff);
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::SetReportType (input_report type, bool continuous)
+ {
+ _ASSERT(IsConnected());
+ if(!IsConnected())
+ return;
+
+ // the balance board only uses one type of report
+ _ASSERT(!IsBalanceBoard() || type == IN_BUTTONS_BALANCE_BOARD);
+ if(IsBalanceBoard() && (type != IN_BUTTONS_BALANCE_BOARD))
+ return;
+
+#ifdef TRACE
+ #define TYPE2NAME(_type) (type==_type)? _T(#_type)
+ const TCHAR* name = TYPE2NAME(IN_BUTTONS) :
+ TYPE2NAME(IN_BUTTONS_ACCEL_IR) :
+ TYPE2NAME(IN_BUTTONS_ACCEL_EXT) :
+ TYPE2NAME(IN_BUTTONS_ACCEL_IR_EXT) :
+ TYPE2NAME(IN_BUTTONS_BALANCE_BOARD) :
+ _T("(unknown??)");
+ TRACE(_T("ReportType: %s (%s)"), name, (continuous? _T("continuous") :
+ _T("non-continuous")));
+#endif
+ ReportType = type;
+
+ switch(type)
+ {
+ case IN_BUTTONS_ACCEL_IR:
+ EnableIR(wiimote_state::ir::EXTENDED);
+ break;
+ case IN_BUTTONS_ACCEL_IR_EXT:
+ EnableIR(wiimote_state::ir::BASIC);
+ break;
+ default:
+ DisableIR();
+ break;
+ }
+
+ BYTE buff [REPORT_LENGTH] = {0};
+ buff[0] = OUT_TYPE;
+ buff[1] = (continuous ? 0x04 : 0x00) | GetRumbleBit();
+ buff[2] = (BYTE)type;
+ WriteReport(buff);
+// Sleep(15);
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::SetLEDs (BYTE led_bits)
+ {
+ _ASSERT(IsConnected());
+ if(!IsConnected() || bInitInProgress)
+ return;
+
+ _ASSERT(led_bits <= 0x0f);
+ led_bits &= 0xf;
+
+ BYTE buff [REPORT_LENGTH] = {0};
+ buff[0] = OUT_LEDs;
+ buff[1] = (led_bits<<4) | GetRumbleBit();
+ WriteReport(buff);
+
+ Internal.LED.Bits = led_bits;
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::SetRumble (bool on)
+ {
+ _ASSERT(IsConnected());
+ if(!IsConnected())
+ return;
+
+ if(Internal.bRumble == on)
+ return;
+
+ Internal.bRumble = on;
+
+ // if we're streaming audio, we don't need to send a report (sending it makes
+ // the audio glitch, and the rumble bit is sent with every report anyway)
+ if(IsPlayingAudio())
+ return;
+
+ BYTE buff [REPORT_LENGTH] = {0};
+ buff[0] = OUT_STATUS;
+ buff[1] = on? 0x01 : 0x00;
+ WriteReport(buff);
+ }
+// ------------------------------------------------------------------------------------
+unsigned __stdcall wiimote::AsyncRumbleThreadfunc (void* param)
+ {
+ // auto-disables rumble after x milliseconds:
+ _ASSERT(param);
+ wiimote &remote = *(wiimote*)param;
+
+ while(remote.IsConnected())
+ {
+ if(remote.AsyncRumbleTimeout)
+ {
+ DWORD current_time = timeGetTime();
+ if(current_time >= remote.AsyncRumbleTimeout)
+ {
+ if(remote.Internal.bRumble)
+ remote.SetRumble(false);
+ remote.AsyncRumbleTimeout = 0;
+ }
+ Sleep(1);
+ }
+ else
+ Sleep(4);
+ }
+ return 0;
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::RumbleForAsync (unsigned milliseconds)
+ {
+ // rumble for a fixed amount of time
+ _ASSERT(IsConnected());
+ if(!IsConnected())
+ return;
+
+ SetRumble(true);
+
+ // show how long thread should wait to disable rumble again
+ // (it it's currently rumbling it will just extend the time)
+ AsyncRumbleTimeout = timeGetTime() + milliseconds;
+
+ // create the thread?
+ if(AsyncRumbleThread)
+ return;
+
+ AsyncRumbleThread = (HANDLE)_beginthreadex(NULL, 0, AsyncRumbleThreadfunc, this,
+ 0, NULL);
+ _ASSERT(AsyncRumbleThread);
+ if(!AsyncRumbleThread) {
+ WARN(_T("couldn't create rumble thread!"));
+ return;
+ }
+ SetThreadPriority(AsyncRumbleThread, WORKER_THREAD_PRIORITY);
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::RequestStatusReport ()
+ {
+ // (this can be called before we're fully connected)
+ _ASSERT(Handle != INVALID_HANDLE_VALUE);
+ if(Handle == INVALID_HANDLE_VALUE)
+ return;
+
+ BYTE buff [REPORT_LENGTH] = {0};
+ buff[0] = OUT_STATUS;
+ buff[1] = GetRumbleBit();
+ WriteReport(buff);
+ }
+// ------------------------------------------------------------------------------------
+bool wiimote::ReadAddress (int address, short size)
+ {
+ // asynchronous
+ BYTE buff [REPORT_LENGTH] = {0};
+ buff[0] = OUT_READMEMORY;
+ buff[1] = (BYTE)(((address & 0xff000000) >> 24) | GetRumbleBit());
+ buff[2] = (BYTE)( (address & 0x00ff0000) >> 16);
+ buff[3] = (BYTE)( (address & 0x0000ff00) >> 8);
+ buff[4] = (BYTE)( (address & 0x000000ff));
+ buff[5] = (BYTE)( (size & 0xff00 ) >> 8);
+ buff[6] = (BYTE)( (size & 0xff));
+ return WriteReport(buff);
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::WriteData (int address, BYTE size, const BYTE* buff)
+ {
+ // asynchronous
+ BYTE write [REPORT_LENGTH] = {0};
+ write[0] = OUT_WRITEMEMORY;
+ write[1] = (BYTE)(((address & 0xff000000) >> 24) | GetRumbleBit());
+ write[2] = (BYTE)( (address & 0x00ff0000) >> 16);
+ write[3] = (BYTE)( (address & 0x0000ff00) >> 8);
+ write[4] = (BYTE)( (address & 0x000000ff));
+ write[5] = size;
+ memcpy(write+6, buff, size);
+ WriteReport(write);
+ }
+// ------------------------------------------------------------------------------------
+int wiimote::ParseInput (BYTE* buff)
+ {
+ int changed = 0;
+
+ // lock our internal state (so RefreshState() is blocked until we're done
+ EnterCriticalSection(&StateLock);
+
+ switch(buff[0])
+ {
+ case IN_BUTTONS:
+ DEEP_TRACE(_T(".. parsing buttons."));
+ changed |= ParseButtons(buff);
+ break;
+
+ case IN_BUTTONS_ACCEL:
+ DEEP_TRACE(_T(".. parsing buttons/accel."));
+ changed |= ParseButtons(buff);
+ if(!IsBalanceBoard())
+ changed |= ParseAccel(buff);
+ break;
+
+ case IN_BUTTONS_ACCEL_EXT:
+ DEEP_TRACE(_T(".. parsing extenion/accel."));
+ changed |= ParseButtons(buff);
+ changed |= ParseExtension(buff, 6);
+ if(!IsBalanceBoard())
+ changed |= ParseAccel(buff);
+ break;
+
+ case IN_BUTTONS_ACCEL_IR:
+ DEEP_TRACE(_T(".. parsing ir/accel."));
+ changed |= ParseButtons(buff);
+ if(!IsBalanceBoard()) {
+ changed |= ParseAccel(buff);
+ changed |= ParseIR(buff);
+ }
+ break;
+
+ case IN_BUTTONS_ACCEL_IR_EXT:
+ DEEP_TRACE(_T(".. parsing ir/extenion/accel."));
+ changed |= ParseButtons(buff);
+ changed |= ParseExtension(buff, 16);
+ if(!IsBalanceBoard()) {
+ changed |= ParseAccel(buff);
+ changed |= ParseIR (buff);
+ }
+ break;
+
+ case IN_BUTTONS_BALANCE_BOARD:
+ DEEP_TRACE(_T(".. parsing buttson/balance."));
+ changed |= ParseButtons(buff);
+ changed |= ParseExtension(buff, 3);
+ break;
+
+ case IN_READADDRESS:
+ DEEP_TRACE(_T(".. parsing read address."));
+ changed |= ParseButtons (buff);
+ changed |= ParseReadAddress(buff);
+ break;
+
+ case IN_STATUS:
+ DEEP_TRACE(_T(".. parsing status."));
+ changed |= ParseStatus(buff);
+ // show that we received the status report (used for output method
+ // detection during Connect())
+ bStatusReceived = true;
+ break;
+
+ default:
+ DEEP_TRACE(_T(".. ** unknown input ** (happens)."));
+ ///_ASSERT(0);
+ //Debug.WriteLine("Unknown report type: " + type.ToString());
+ LeaveCriticalSection(&StateLock);
+ return false;
+ }
+
+ // if we're recording and some state we care about has changed, insert it into
+ // the state history
+ if(Recording.bEnabled && (changed & Recording.TriggerFlags))
+ {
+ DEEP_TRACE(_T(".. adding state to history"));
+ state_event event;
+ event.time_ms = timeGetTime();
+ event.state = *(wiimote_state*)this;
+ Recording.StateHistory->push_back(event);
+ }
+
+ // for polling: show which state has changed since the last RefreshState()
+ InternalChanged = (state_change_flags)(InternalChanged | changed);
+
+ LeaveCriticalSection(&StateLock);
+
+ // callbacks: call it (if set & state the app is interested in has changed)
+ if(changed & CallbackTriggerFlags)
+ {
+ DEEP_TRACE(_T(".. calling state change callback"));
+ ChangedNotifier((state_change_flags)changed, Internal);
+ if(ChangedCallback)
+ ChangedCallback(*this, (state_change_flags)changed, Internal);
+ }
+
+ DEEP_TRACE(_T(".. parse complete."));
+ return true;
+ }
+// ------------------------------------------------------------------------------------
+state_change_flags wiimote::RefreshState ()
+ {
+ // nothing changed since the last call?
+ if(InternalChanged == NO_CHANGE)
+ return NO_CHANGE;
+
+ // copy the internal state to our public data members:
+ // synchronise the interal state with the read/parse thread (we don't want
+ // values changing during the copy)
+ EnterCriticalSection(&StateLock);
+
+ // remember which state changed since the last call
+ state_change_flags changed = InternalChanged;
+
+ // preserve the application-set deadzones (if any)
+ joystick::deadzone nunchuk_deadzone = Nunchuk.Joystick.DeadZone;
+ joystick::deadzone classic_joyl_deadzone = ClassicController.JoystickL.DeadZone;
+ joystick::deadzone classic_joyr_deadzone = ClassicController.JoystickR.DeadZone;
+
+ // copy the internal state to the public one
+ *(wiimote_state*)this = Internal;
+ InternalChanged = NO_CHANGE;
+
+ // restore the application-set deadzones
+ Nunchuk.Joystick.DeadZone = nunchuk_deadzone;
+ ClassicController.JoystickL.DeadZone = classic_joyl_deadzone;
+ ClassicController.JoystickR.DeadZone = classic_joyr_deadzone;
+
+ LeaveCriticalSection(&StateLock);
+
+ return changed;
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::DetectMotionPlusExtensionAsync ()
+ {
+#ifdef _DEBUG
+ TRACE(_T("(looking for motion plus)"));
+#endif
+ // show that we're expecting the result shortly
+ MotionPlusDetectCount++;
+ // MotionPLus reports at a different address than other extensions (until
+ // activated, when it maps itself into the usual extension registers), so
+ // try to detect it first:
+ ReadAddress(REGISTER_MOTIONPLUS_DETECT, 6);
+ }
+// ------------------------------------------------------------------------------------
+bool wiimote::EnableMotionPlus ()
+ {
+ _ASSERT(bMotionPlusDetected);
+ if(!bMotionPlusDetected)
+ return false;
+ if(bMotionPlusEnabled)
+ return true;
+
+ TRACE(_T("Enabling Motion Plus:"));
+
+ bMotionPlusExtension = false;
+ bInitInProgress = true;
+ bEnablingMotionPlus = true;
+
+ // Initialize it:
+ WriteData(REGISTER_MOTIONPLUS_INIT , 0x55);
+// Sleep(50);
+ // Enable it (this maps it to the standard extension port):
+ WriteData(REGISTER_MOTIONPLUS_ENABLE, 0x04);
+// Sleep(50);
+Sleep(500);
+ return true;
+ }
+// ------------------------------------------------------------------------------------
+bool wiimote::DisableMotionPlus ()
+ {
+ if(!bMotionPlusDetected || !bMotionPlusEnabled)
+ return false;
+
+ TRACE(_T("Disabling Motion Plus:"));
+
+ // disable it (this makes standard extensions visible again)
+ WriteData(REGISTER_EXTENSION_INIT1, 0x55);
+ return true;
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::InitializeExtension ()
+ {
+ TRACE(_T("Initialising Extension."));
+ // wibrew.org: The new way to initialize the extension is by writing 0x55 to
+ // 0x(4)A400F0, then writing 0x00 to 0x(4)A400FB. It works on all extensions, and
+ // makes the extension type bytes unencrypted. This means that you no longer have
+ // to decrypt the extension bytes using the transform listed above.
+ bInitInProgress = true;
+_ASSERT(Internal.bExtension);
+ // only initialize if it's not a MotionPlus
+ if(!bEnablingMotionPlus) {
+ WriteData (REGISTER_EXTENSION_INIT1, 0x55);
+ WriteData (REGISTER_EXTENSION_INIT2, 0x00);
+ }
+ else
+ bEnablingMotionPlus = false;
+
+ ReadAddress(REGISTER_EXTENSION_TYPE , 6);
+ }
+// ------------------------------------------------------------------------------------
+int wiimote::ParseStatus (BYTE* buff)
+ {
+ // parse the buttons
+ int changed = ParseButtons(buff);
+
+ // get the battery level
+ BYTE battery_raw = buff[6];
+ if(Internal.BatteryRaw != battery_raw)
+ changed |= BATTERY_CHANGED;
+ Internal.BatteryRaw = battery_raw;
+ // it is estimated that ~200 is the maximum battery level
+ Internal.BatteryPercent = battery_raw / 2;
+
+ // there is also a flag that shows if the battery is nearly empty
+ bool drained = buff[3] & 0x01;
+ if(drained != bBatteryDrained)
+ {
+ bBatteryDrained = drained;
+ if(drained)
+ changed |= BATTERY_DRAINED;
+ }
+
+ // leds
+ BYTE leds = buff[3] >> 4;
+ if(leds != Internal.LED.Bits)
+ changed |= LEDS_CHANGED;
+ Internal.LED.Bits = leds;
+
+ // don't handle extensions until a connection is complete
+// if(bConnectInProgress)
+// return changed;
+
+ bool extension = ((buff[3] & 0x02) != 0);
+// TRACE(_T("(extension = %s)"), (extension? _T("TRUE") : _T("false")));
+
+ if(extension != Internal.bExtension)
+ {
+ if(!Internal.bExtension)
+ {
+ TRACE(_T("Extension connected:"));
+ Internal.bExtension = true;
+ InitializeExtension();
+ }
+ else{
+ TRACE(_T("Extension disconnected."));
+ Internal.bExtension = false;
+ Internal.ExtensionType = wiimote_state::NONE;
+ bMotionPlusEnabled = false;
+ bMotionPlusExtension = false;
+ bMotionPlusDetected = false;
+ bInitInProgress = false;
+ bEnablingMotionPlus = false;
+ changed |= EXTENSION_DISCONNECTED;
+ // renable reports
+// SetReportType(ReportType);
+ }
+ }
+
+ return changed;
+ }
+// ------------------------------------------------------------------------------------
+int wiimote::ParseButtons (BYTE* buff)
+ {
+ int changed = 0;
+
+// WORD bits = *(WORD*)(buff+1);
+ WORD bits = *(WORD*)(buff+1) & Button.ALL;
+
+ if(bits != Internal.Button.Bits)
+ changed |= BUTTONS_CHANGED;
+ Internal.Button.Bits = bits;
+
+ return changed;
+ }
+// ------------------------------------------------------------------------------------
+bool wiimote::EstimateOrientationFrom (wiimote_state::acceleration &accel)
+ {
+ // Orientation estimate from acceleration data (shared between wiimote and nunchuk)
+ // return true if the orientation was updated
+
+ // assume the controller is stationary if the acceleration vector is near
+ // 1g for several updates (this may not always be correct)
+ float length_sq = square(accel.X) + square(accel.Y) + square(accel.Z);
+
+ // TODO: as I'm comparing _squared_ length, I really need different
+ // min/max epsilons...
+ #define DOT(x1,y1,z1, x2,y2,z2) ((x1*x2) + (y1*y2) + (z1*z2))
+
+ static const float epsilon = 0.2f;
+ if((length_sq >= (1.f-epsilon)) && (length_sq <= (1.f+epsilon)))
+ {
+ if(++WiimoteNearGUpdates < 2)
+ return false;
+
+ // wiimote seems to be stationary: normalize the current acceleration
+ // (ie. the assumed gravity vector)
+ float inv_len = 1.f / sqrt(length_sq);
+ float x = accel.X * inv_len;
+ float y = accel.Y * inv_len;
+ float z = accel.Z * inv_len;
+
+ // copy the values
+ accel.Orientation.X = x;
+ accel.Orientation.Y = y;
+ accel.Orientation.Z = z;
+
+ // and extract pitch & roll from them:
+ // (may not be optimal)
+ float pitch = -asin(y) * 57.2957795f;
+// float roll = asin(x) * 57.2957795f;
+ float roll = atan2(x,z) * 57.2957795f;
+ if(z < 0) {
+ pitch = (y < 0)? 180 - pitch : -180 - pitch;
+ roll = (x < 0)? -180 - roll : 180 - roll;
+ }
+
+ accel.Orientation.Pitch = pitch;
+ accel.Orientation.Roll = roll;
+
+ // show that we just updated orientation
+ accel.Orientation.UpdateAge = 0;
+#ifdef BEEP_ON_ORIENTATION_ESTIMATE
+ Beep(2000, 1);
+#endif
+ return true; // updated
+ }
+
+ // not updated this time:
+ WiimoteNearGUpdates = 0;
+ // age the last orientation update
+ accel.Orientation.UpdateAge++;
+ return false;
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::ApplyJoystickDeadZones (wiimote_state::joystick &joy)
+ {
+ // apply the deadzones to each axis (if set)
+ if((joy.DeadZone.X > 0.f) && (joy.DeadZone.X <= 1.f))
+ {
+ if(fabs(joy.X) <= joy.DeadZone.X)
+ joy.X = 0;
+ else{
+ joy.X -= joy.DeadZone.X * sign(joy.X);
+ joy.X /= 1.f - joy.DeadZone.X;
+ }
+ }
+ if((joy.DeadZone.Y > 0.f) && (joy.DeadZone.Y <= 1.f))
+ {
+ if(fabs(joy.Y) <= joy.DeadZone.Y)
+ joy.Y = 0;
+ else{
+ joy.Y -= joy.DeadZone.Y * sign(joy.Y);
+ joy.Y /= 1.f - joy.DeadZone.Y;
+ }
+ }
+ }
+// ------------------------------------------------------------------------------------
+int wiimote::ParseAccel (BYTE* buff)
+ {
+ int changed = 0;
+
+ BYTE raw_x = buff[3];
+ BYTE raw_y = buff[4];
+ BYTE raw_z = buff[5];
+
+ if((raw_x != Internal.Acceleration.RawX) ||
+ (raw_y != Internal.Acceleration.RawY) ||
+ (raw_z != Internal.Acceleration.RawZ))
+ changed |= ACCEL_CHANGED;
+
+ Internal.Acceleration.RawX = raw_x;
+ Internal.Acceleration.RawY = raw_y;
+ Internal.Acceleration.RawZ = raw_z;
+
+ // avoid / 0.0 when calibration data hasn't arrived yet
+ if(Internal.CalibrationInfo.X0)
+ {
+ Internal.Acceleration.X =
+ ((float)Internal.Acceleration.RawX - Internal.CalibrationInfo.X0) /
+ ((float)Internal.CalibrationInfo.XG - Internal.CalibrationInfo.X0);
+ Internal.Acceleration.Y =
+ ((float)Internal.Acceleration.RawY - Internal.CalibrationInfo.Y0) /
+ ((float)Internal.CalibrationInfo.YG - Internal.CalibrationInfo.Y0);
+ Internal.Acceleration.Z =
+ ((float)Internal.Acceleration.RawZ - Internal.CalibrationInfo.Z0) /
+ ((float)Internal.CalibrationInfo.ZG - Internal.CalibrationInfo.Z0);
+ }
+ else{
+ Internal.Acceleration.X =
+ Internal.Acceleration.Y =
+ Internal.Acceleration.Z = 0.f;
+ }
+
+ // see if we can estimate the orientation from the current values
+ if(EstimateOrientationFrom(Internal.Acceleration))
+ changed |= ORIENTATION_CHANGED;
+
+ return changed;
+ }
+// ------------------------------------------------------------------------------------
+int wiimote::ParseIR (BYTE* buff)
+ {
+ if(Internal.IR.Mode == wiimote_state::ir::OFF)
+ return NO_CHANGE;
+
+ // avoid garbage values when the MotionPlus is enabled, but the app is
+ // still using the extended IR report type
+ if(bMotionPlusEnabled && (Internal.IR.Mode == wiimote_state::ir::EXTENDED))
+ return NO_CHANGE;
+
+ // take a copy of the existing IR state (so we can detect changes)
+ wiimote_state::ir prev_ir = Internal.IR;
+
+ // only updates the other values if the dots are visible (so that the last
+ // valid values stay unmodified)
+ switch(Internal.IR.Mode)
+ {
+ case wiimote_state::ir::BASIC:
+ // 2 dots are encoded in 5 bytes, so read 2 at a time
+ for(unsigned step=0; step<2; step++)
+ {
+ ir::dot &dot0 = Internal.IR.Dot[step*2 ];
+ ir::dot &dot1 = Internal.IR.Dot[step*2+1];
+ const unsigned offs = 6 + (step*5); // 5 bytes for 2 dots
+
+ dot0.bVisible = !(buff[offs ] == 0xff && buff[offs+1] == 0xff);
+ dot1.bVisible = !(buff[offs+3] == 0xff && buff[offs+4] == 0xff);
+
+ if(dot0.bVisible) {
+ dot0.RawX = buff[offs ] | ((buff[offs+2] >> 4) & 0x03) << 8;;
+ dot0.RawY = buff[offs+1] | ((buff[offs+2] >> 6) & 0x03) << 8;;
+ dot0.X = 1.f - (dot0.RawX / (float)wiimote_state::ir::MAX_RAW_X);
+ dot0.Y = (dot0.RawY / (float)wiimote_state::ir::MAX_RAW_Y);
+ }
+ if(dot1.bVisible) {
+ dot1.RawX = buff[offs+3] | ((buff[offs+2] >> 0) & 0x03) << 8;
+ dot1.RawY = buff[offs+4] | ((buff[offs+2] >> 2) & 0x03) << 8;
+ dot1.X = 1.f - (dot1.RawX / (float)wiimote_state::ir::MAX_RAW_X);
+ dot1.Y = (dot1.RawY / (float)wiimote_state::ir::MAX_RAW_Y);
+ }
+ }
+ break;
+
+ case wiimote_state::ir::EXTENDED:
+ // each dot is encoded into 3 bytes
+ for(unsigned index=0; index<4; index++)
+ {
+ ir::dot &dot = Internal.IR.Dot[index];
+ const unsigned offs = 6 + (index * 3);
+
+ dot.bVisible = !(buff[offs ]==0xff && buff[offs+1]==0xff &&
+ buff[offs+2]==0xff);
+ if(dot.bVisible) {
+ dot.RawX = buff[offs ] | ((buff[offs+2] >> 4) & 0x03) << 8;
+ dot.RawY = buff[offs+1] | ((buff[offs+2] >> 6) & 0x03) << 8;
+ dot.X = 1.f - (dot.RawX / (float)wiimote_state::ir::MAX_RAW_X);
+ dot.Y = (dot.RawY / (float)wiimote_state::ir::MAX_RAW_Y);
+ dot.Size = buff[offs+2] & 0x0f;
+ }
+ }
+ break;
+
+ case wiimote_state::ir::FULL:
+ _ASSERT(0); // not supported yet;
+ break;
+ }
+
+ return memcmp(&prev_ir, &Internal.IR, sizeof(Internal.IR))? IR_CHANGED : 0;
+ }
+// ------------------------------------------------------------------------------------
+inline float wiimote::GetBalanceValue (short sensor, short min, short mid, short max)
+ {
+ if(max == mid || mid == min)
+ return 0;
+
+ float val = (sensor < mid)?
+ 68.0f * ((float)(sensor - min) / (mid - min)) :
+ 68.0f * ((float)(sensor - mid) / (max - mid)) + 68.0f;
+
+ // divide by four (so that each sensor is correct)
+ return val * 0.25f;
+ }
+// ------------------------------------------------------------------------------------
+int wiimote::ParseExtension (BYTE *buff, unsigned offset)
+ {
+ int changed = 0;
+
+ switch(Internal.ExtensionType)
+ {
+ case wiimote_state::NUNCHUK:
+ {
+ // buttons
+ bool c = (buff[offset+5] & 0x02) == 0;
+ bool z = (buff[offset+5] & 0x01) == 0;
+
+ if((c != Internal.Nunchuk.C) || (z != Internal.Nunchuk.Z))
+ changed |= NUNCHUK_BUTTONS_CHANGED;
+
+ Internal.Nunchuk.C = c;
+ Internal.Nunchuk.Z = z;
+
+ // acceleration
+ {
+ wiimote_state::acceleration &accel = Internal.Nunchuk.Acceleration;
+
+ BYTE raw_x = buff[offset+2];
+ BYTE raw_y = buff[offset+3];
+ BYTE raw_z = buff[offset+4];
+ if((raw_x != accel.RawX) || (raw_y != accel.RawY) || (raw_z != accel.RawZ))
+ changed |= NUNCHUK_ACCEL_CHANGED;
+
+ accel.RawX = raw_x;
+ accel.RawY = raw_y;
+ accel.RawZ = raw_z;
+
+ wiimote_state::nunchuk::calibration_info &calib =
+ Internal.Nunchuk.CalibrationInfo;
+ accel.X = ((float)raw_x - calib.X0) / ((float)calib.XG - calib.X0);
+ accel.Y = ((float)raw_y - calib.Y0) / ((float)calib.YG - calib.Y0);
+ accel.Z = ((float)raw_z - calib.Z0) / ((float)calib.ZG - calib.Z0);
+
+ // try to extract orientation from the accel:
+ if(EstimateOrientationFrom(accel))
+ changed |= NUNCHUK_ORIENTATION_CHANGED;
+ }
+ {
+ // joystick:
+ wiimote_state::joystick &joy = Internal.Nunchuk.Joystick;
+
+ float raw_x = buff[offset+0];
+ float raw_y = buff[offset+1];
+
+ if((raw_x != joy.RawX) || (raw_y != joy.RawY))
+ changed |= NUNCHUK_JOYSTICK_CHANGED;
+
+ joy.RawX = raw_x;
+ joy.RawY = raw_y;
+
+ // apply the calibration data
+ wiimote_state::nunchuk::calibration_info &calib =
+ Internal.Nunchuk.CalibrationInfo;
+ if(Internal.Nunchuk.CalibrationInfo.MaxX != 0x00)
+ joy.X = ((float)raw_x - calib.MidX) / ((float)calib.MaxX - calib.MinX);
+ if(calib.MaxY != 0x00)
+ joy.Y = ((float)raw_y - calib.MidY) / ((float)calib.MaxY - calib.MinY);
+
+ // i prefer the outputs to range -1 - +1 (note this also affects the
+ // deadzone calculations)
+ joy.X *= 2; joy.Y *= 2;
+
+ // apply the public deadzones to the internal state (if set)
+ joy.DeadZone = Nunchuk.Joystick.DeadZone;
+ ApplyJoystickDeadZones(joy);
+ }
+ }
+ break;
+
+ case wiimote_state::CLASSIC:
+ case wiimote_state::GH3_GHWT_GUITAR:
+ case wiimote_state::GHWT_DRUMS:
+ {
+ // buttons:
+ WORD bits = *(WORD*)(buff+offset+4);
+ bits = ~bits; // need to invert bits since 0 is down, and 1 is up
+
+ if(bits != Internal.ClassicController.Button.Bits)
+ changed |= CLASSIC_BUTTONS_CHANGED;
+
+ Internal.ClassicController.Button.Bits = bits;
+
+ // joysticks:
+ wiimote_state::joystick &joyL = Internal.ClassicController.JoystickL;
+ wiimote_state::joystick &joyR = Internal.ClassicController.JoystickR;
+
+ float l_raw_x = (float) (buff[offset+0] & 0x3f);
+ float l_raw_y = (float) (buff[offset+1] & 0x3f);
+ float r_raw_x = (float)((buff[offset+2] >> 7) |
+ ((buff[offset+1] & 0xc0) >> 5) |
+ ((buff[offset+0] & 0xc0) >> 3));
+ float r_raw_y = (float) (buff[offset+2] & 0x1f);
+
+ if((joyL.RawX != l_raw_x) || (joyL.RawY != l_raw_y))
+ changed |= CLASSIC_JOYSTICK_L_CHANGED;
+ if((joyR.RawX != r_raw_x) || (joyR.RawY != r_raw_y))
+ changed |= CLASSIC_JOYSTICK_R_CHANGED;
+
+ joyL.RawX = l_raw_x; joyL.RawY = l_raw_y;
+ joyR.RawX = r_raw_x; joyR.RawY = r_raw_y;
+
+ // apply calibration
+ wiimote_state::classic_controller::calibration_info &calib =
+ Internal.ClassicController.CalibrationInfo;
+ if(calib.MaxXL != 0x00)
+ joyL.X = (joyL.RawX - calib.MidXL) / ((float)calib.MaxXL - calib.MinXL);
+ if(calib.MaxYL != 0x00)
+ joyL.Y = (joyL.RawY - calib.MidYL) / ((float)calib.MaxYL - calib.MinYL);
+ if(calib.MaxXR != 0x00)
+ joyR.X = (joyR.RawX - calib.MidXR) / ((float)calib.MaxXR - calib.MinXR);
+ if(calib.MaxYR != 0x00)
+ joyR.Y = (joyR.RawY - calib.MidYR) / ((float)calib.MaxYR - calib.MinYR);
+
+ // i prefer the joystick outputs to range -1 - +1 (note this also affects
+ // the deadzone calculations)
+ joyL.X *= 2; joyL.Y *= 2; joyR.X *= 2; joyR.Y *= 2;
+
+ // apply the public deadzones to the internal state (if set)
+ joyL.DeadZone = ClassicController.JoystickL.DeadZone;
+ joyR.DeadZone = ClassicController.JoystickR.DeadZone;
+ ApplyJoystickDeadZones(joyL);
+ ApplyJoystickDeadZones(joyR);
+
+ // triggers
+ BYTE raw_trigger_l = ((buff[offset+2] & 0x60) >> 2) |
+ (buff[offset+3] >> 5);
+ BYTE raw_trigger_r = buff[offset+3] & 0x1f;
+
+ if((raw_trigger_l != Internal.ClassicController.RawTriggerL) ||
+ (raw_trigger_r != Internal.ClassicController.RawTriggerR))
+ changed |= CLASSIC_TRIGGERS_CHANGED;
+
+ Internal.ClassicController.RawTriggerL = raw_trigger_l;
+ Internal.ClassicController.RawTriggerR = raw_trigger_r;
+
+ if(calib.MaxTriggerL != 0x00)
+ Internal.ClassicController.TriggerL =
+ (float)Internal.ClassicController.RawTriggerL /
+ ((float)calib.MaxTriggerL - calib.MinTriggerL);
+ if(calib.MaxTriggerR != 0x00)
+ Internal.ClassicController.TriggerR =
+ (float)Internal.ClassicController.RawTriggerR /
+ ((float)calib.MaxTriggerR - calib.MinTriggerR);
+ }
+ break;
+
+ case BALANCE_BOARD:
+ {
+ wiimote_state::balance_board::sensors_raw prev_raw =
+ Internal.BalanceBoard.Raw;
+ Internal.BalanceBoard.Raw.TopR =
+ (short)((short)buff[offset+0] << 8 | buff[offset+1]);
+ Internal.BalanceBoard.Raw.BottomR =
+ (short)((short)buff[offset+2] << 8 | buff[offset+3]);
+ Internal.BalanceBoard.Raw.TopL =
+ (short)((short)buff[offset+4] << 8 | buff[offset+5]);
+ Internal.BalanceBoard.Raw.BottomL =
+ (short)((short)buff[offset+6] << 8 | buff[offset+7]);
+
+ if((Internal.BalanceBoard.Raw.TopL != prev_raw.TopL) ||
+ (Internal.BalanceBoard.Raw.TopR != prev_raw.TopR) ||
+ (Internal.BalanceBoard.Raw.BottomL != prev_raw.BottomL) ||
+ (Internal.BalanceBoard.Raw.BottomR != prev_raw.BottomR))
+ changed |= BALANCE_WEIGHT_CHANGED;
+
+ Internal.BalanceBoard.Kg.TopL =
+ GetBalanceValue(Internal.BalanceBoard.Raw.TopL,
+ Internal.BalanceBoard.CalibrationInfo.Kg0 .TopL,
+ Internal.BalanceBoard.CalibrationInfo.Kg17.TopL,
+ Internal.BalanceBoard.CalibrationInfo.Kg34.TopL);
+ Internal.BalanceBoard.Kg.TopR =
+ GetBalanceValue(Internal.BalanceBoard.Raw.TopR,
+ Internal.BalanceBoard.CalibrationInfo.Kg0 .TopR,
+ Internal.BalanceBoard.CalibrationInfo.Kg17.TopR,
+ Internal.BalanceBoard.CalibrationInfo.Kg34.TopR);
+ Internal.BalanceBoard.Kg.BottomL =
+ GetBalanceValue(Internal.BalanceBoard.Raw.BottomL,
+ Internal.BalanceBoard.CalibrationInfo.Kg0 .BottomL,
+ Internal.BalanceBoard.CalibrationInfo.Kg17.BottomL,
+ Internal.BalanceBoard.CalibrationInfo.Kg34.BottomL);
+ Internal.BalanceBoard.Kg.BottomR =
+ GetBalanceValue(Internal.BalanceBoard.Raw.BottomR,
+ Internal.BalanceBoard.CalibrationInfo.Kg0 .BottomR,
+ Internal.BalanceBoard.CalibrationInfo.Kg17.BottomR,
+ Internal.BalanceBoard.CalibrationInfo.Kg34.BottomR);
+
+ // uses these as the 'at rest' offsets? (immediately after Connect(),
+ // or if the app called CalibrateAtRest())
+ if(bCalibrateAtRest) {
+ bCalibrateAtRest = false;
+ TRACE(_T(".. Auto-removing 'at rest' BBoard offsets."));
+ Internal.BalanceBoard.AtRestKg = Internal.BalanceBoard.Kg;
+ }
+
+ // remove the 'at rest' offsets
+ Internal.BalanceBoard.Kg.TopL -= BalanceBoard.AtRestKg.TopL;
+ Internal.BalanceBoard.Kg.TopR -= BalanceBoard.AtRestKg.TopR;
+ Internal.BalanceBoard.Kg.BottomL -= BalanceBoard.AtRestKg.BottomL;
+ Internal.BalanceBoard.Kg.BottomR -= BalanceBoard.AtRestKg.BottomR;
+
+ // compute the average
+ Internal.BalanceBoard.Kg.Total = Internal.BalanceBoard.Kg.TopL +
+ Internal.BalanceBoard.Kg.TopR +
+ Internal.BalanceBoard.Kg.BottomL +
+ Internal.BalanceBoard.Kg.BottomR;
+ // and convert to Lbs
+ const float KG2LB = 2.20462262f;
+ Internal.BalanceBoard.Lb = Internal.BalanceBoard.Kg;
+ Internal.BalanceBoard.Lb.TopL *= KG2LB;
+ Internal.BalanceBoard.Lb.TopR *= KG2LB;
+ Internal.BalanceBoard.Lb.BottomL *= KG2LB;
+ Internal.BalanceBoard.Lb.BottomR *= KG2LB;
+ Internal.BalanceBoard.Lb.Total *= KG2LB;
+ }
+ break;
+
+ case MOTION_PLUS:
+ {
+ bMotionPlusDetected = true;
+ bMotionPlusEnabled = true;
+
+ short yaw = ((unsigned short)buff[offset+3] & 0xFC)<<6 |
+ (unsigned short)buff[offset+0];
+ short pitch = ((unsigned short)buff[offset+5] & 0xFC)<<6 |
+ (unsigned short)buff[offset+2];
+ short roll = ((unsigned short)buff[offset+4] & 0xFC)<<6 |
+ (unsigned short)buff[offset+1];
+
+ // we get one set of bogus values when the MotionPlus is disconnected,
+ // so ignore them
+ if((yaw != 0x3fff) || (pitch != 0x3fff) || (roll != 0x3fff))
+ {
+ wiimote_state::motion_plus::sensors_raw &raw = Internal.MotionPlus.Raw;
+
+ if((raw.Yaw != yaw) || (raw.Pitch != pitch) || (raw.Roll != roll))
+ changed |= MOTIONPLUS_SPEED_CHANGED;
+
+ raw.Yaw = yaw;
+ raw.Pitch = pitch;
+ raw.Roll = roll;
+
+ // convert to float values
+ bool yaw_slow = (buff[offset+3] & 0x2) == 0x2;
+ bool pitch_slow = (buff[offset+3] & 0x1) == 0x1;
+ bool roll_slow = (buff[offset+4] & 0x2) == 0x2;
+ float y_scale = yaw_slow? 0.05f : 0.25f;
+ float p_scale = pitch_slow? 0.05f : 0.25f;
+ float r_scale = roll_slow? 0.05f : 0.25f;
+
+ Internal.MotionPlus.Speed.Yaw = -(raw.Yaw - 0x1F7F) * y_scale;
+ Internal.MotionPlus.Speed.Pitch = -(raw.Pitch - 0x1F7F) * p_scale;
+ Internal.MotionPlus.Speed.Roll = -(raw.Roll - 0x1F7F) * r_scale;
+
+ // show if there's an extension plugged into the MotionPlus:
+ bool extension = buff[offset+4] & 1;
+ if(extension != bMotionPlusExtension)
+ {
+ if(extension) {
+ TRACE(_T(".. MotionPlus extension found."));
+ changed |= MOTIONPLUS_EXTENSION_CONNECTED;
+ }
+ else{
+ TRACE(_T(".. MotionPlus' extension disconnected."));
+ changed |= MOTIONPLUS_EXTENSION_DISCONNECTED;
+ }
+ }
+ bMotionPlusExtension = extension;
+ }
+ // while we're getting data, the plus is obviously detected/enabled
+// bMotionPlusDetected = bMotionPlusEnabled = true;
+ }
+ break;
+ }
+
+ return changed;
+ }
+// ------------------------------------------------------------------------------------
+int wiimote::ParseReadAddress (BYTE* buff)
+ {
+ // decode the address that was queried:
+ int address = buff[4]<<8 | buff[5];
+ int size = buff[3] >> 4;
+ int changed = 0;
+
+ if((buff[3] & 0x08) != 0) {
+ WARN(_T("error: read address not valid."));
+ _ASSERT(0);
+ return NO_CHANGE;
+ }
+ // address read failed (write-only)?
+ else if((buff[3] & 0x07) != 0)
+ {
+ // this also happens when attempting to detect a non-existant MotionPlus
+ if(MotionPlusDetectCount)
+ {
+ --MotionPlusDetectCount;
+ if(Internal.ExtensionType == MOTION_PLUS)
+ {
+ if(bMotionPlusDetected)
+ TRACE(_T(".. MotionPlus removed."));
+ bMotionPlusDetected = false;
+ bMotionPlusEnabled = false;
+ // the MotionPlus can sometimes get confused - initializing
+ // extenions fixes it:
+// if(address == 0xfa)
+// InitializeExtension();
+ }
+ }
+ else
+ WARN(_T("error: attempt to read from write-only register 0x%X."), buff[3]);
+
+ return NO_CHANGE;
+ }
+
+ // *NOTE*: this is a major (but convenient) hack! The returned data only
+ // contains the lower two bytes of the address that was queried.
+ // as these don't collide between any of the addresses/registers
+ // we currently read, it's OK to match just those two bytes
+
+ // skip the header
+ buff += 6;
+
+ switch(address)
+ {
+ case (REGISTER_CALIBRATION & 0xffff):
+ {
+ _ASSERT(size == 6);
+ TRACE(_T(".. got wiimote calibration."));
+ Internal.CalibrationInfo.X0 = buff[0];
+ Internal.CalibrationInfo.Y0 = buff[1];
+ Internal.CalibrationInfo.Z0 = buff[2];
+ Internal.CalibrationInfo.XG = buff[4];
+ Internal.CalibrationInfo.YG = buff[5];
+ Internal.CalibrationInfo.ZG = buff[6];
+ //changed |= CALIBRATION_CHANGED;
+ }
+ break;
+
+ // note: this covers both the normal extension and motion plus extension
+ // addresses (0x4a400fa / 0x4a600fa)
+ case (REGISTER_EXTENSION_TYPE & 0xffff):
+ {
+ _ASSERT(size == 5);
+ QWORD type = *(QWORD*)buff;
+
+// TRACE(_T("(found extension 0x%I64x)"), type);
+
+ static const QWORD NUNCHUK = 0x000020A40000ULL;
+ static const QWORD CLASSIC = 0x010120A40000ULL;
+ static const QWORD GH3_GHWT_GUITAR = 0x030120A40000ULL;
+ static const QWORD GHWT_DRUMS = 0x030120A40001ULL;
+ static const QWORD BALANCE_BOARD = 0x020420A40000ULL;
+ static const QWORD MOTION_PLUS = 0x050420A40000ULL;
+ static const QWORD MOTION_PLUS_DETECT = 0x050020a60000ULL;
+ static const QWORD MOTION_PLUS_DETECT2 = 0x050420a60000ULL;
+ static const QWORD PARTIALLY_INSERTED = 0xffffffffffffULL;
+
+ // MotionPlus: _before_ it's been activated
+ if((type == MOTION_PLUS_DETECT) || (type == MOTION_PLUS_DETECT2))
+ {
+ if(!bMotionPlusDetected) {
+ TRACE(_T("Motion Plus detected!"));
+ changed |= MOTIONPLUS_DETECTED;
+ }
+ bMotionPlusDetected = true;
+ --MotionPlusDetectCount;
+ break;
+ }
+
+ #define IF_TYPE(id) if(type == id) { \
+ /* sometimes it comes in more than once */ \
+ if(Internal.ExtensionType == wiimote_state::id)\
+ break; \
+ Internal.ExtensionType = wiimote_state::id;
+
+ // MotionPlus: once it's activated & mapped to the standard ext. port
+ IF_TYPE(MOTION_PLUS)
+ TRACE(_T(".. Motion Plus!"));
+ // and start a query for the calibration data
+ ReadAddress(REGISTER_EXTENSION_CALIBRATION, 16);
+ bMotionPlusDetected = true;
+ }
+ else IF_TYPE(NUNCHUK)
+ TRACE(_T(".. Nunchuk!"));
+ bMotionPlusEnabled = false;
+ // and start a query for the calibration data
+ ReadAddress(REGISTER_EXTENSION_CALIBRATION, 16);
+ }
+ else IF_TYPE(CLASSIC)
+ TRACE(_T(".. Classic Controller!"));
+ bMotionPlusEnabled = false;
+ // and start a query for the calibration data
+ ReadAddress(REGISTER_EXTENSION_CALIBRATION, 16);
+ }
+ else IF_TYPE(GH3_GHWT_GUITAR)
+ // sometimes it comes in more than once?
+ TRACE(_T(".. GH3/GHWT Guitar Controller!"));
+ bMotionPlusEnabled = false;
+ // and start a query for the calibration data
+ ReadAddress(REGISTER_EXTENSION_CALIBRATION, 16);
+ }
+ else IF_TYPE(GHWT_DRUMS)
+ TRACE(_T(".. GHWT Drums!"));
+ bMotionPlusEnabled = false;
+ // and start a query for the calibration data
+ ReadAddress(REGISTER_EXTENSION_CALIBRATION, 16);
+ }
+ else IF_TYPE(BALANCE_BOARD)
+ TRACE(_T(".. Balance Board!"));
+ bMotionPlusEnabled = false;
+ // and start a query for the calibration data
+ ReadAddress(REGISTER_BALANCE_CALIBRATION, 24);
+ }
+ else if(type == PARTIALLY_INSERTED) {
+ // sometimes it comes in more than once?
+ if(Internal.ExtensionType == wiimote_state::PARTIALLY_INSERTED)
+ Sleep(50);
+ TRACE(_T(".. partially inserted!"));
+ bMotionPlusEnabled = false;
+ Internal.ExtensionType = wiimote_state::PARTIALLY_INSERTED;
+ changed |= EXTENSION_PARTIALLY_INSERTED;
+ // try initializing the extension again by requesting another
+ // status report (this usually fixes it)
+ Internal.bExtension = false;
+ RequestStatusReport();
+ }
+ else{
+ TRACE(_T("unknown extension controller found (0x%I64x)"), type);
+ }
+ }
+ break;
+
+ case (REGISTER_EXTENSION_CALIBRATION & 0xffff):
+ case (REGISTER_BALANCE_CALIBRATION & 0xffff):
+ {
+// _ASSERT(((Internal.ExtensionType == BALANCE_BOARD) && (size == 31)) ||
+// ((Internal.ExtensionType != BALANCE_BOARD) && (size == 15)));
+
+ switch(Internal.ExtensionType)
+ {
+ case wiimote_state::NUNCHUK:
+ {
+ wiimote_state::nunchuk::calibration_info
+ &calib = Internal.Nunchuk.CalibrationInfo;
+
+ calib.X0 = buff[ 0];
+ calib.Y0 = buff[ 1];
+ calib.Z0 = buff[ 2];
+ calib.XG = buff[ 4];
+ calib.YG = buff[ 5];
+ calib.ZG = buff[ 6];
+ calib.MaxX = buff[ 8];
+ calib.MinX = buff[ 9];
+ calib.MidX = buff[10];
+ calib.MaxY = buff[11];
+ calib.MinY = buff[12];
+ calib.MidY = buff[13];
+
+ changed |= NUNCHUK_CONNECTED;//|NUNCHUK_CALIBRATION_CHANGED;
+ // reenable reports
+// SetReportType(ReportType);
+ }
+ break;
+
+ case wiimote_state::CLASSIC:
+ case wiimote_state::GH3_GHWT_GUITAR:
+ case wiimote_state::GHWT_DRUMS:
+ {
+ wiimote_state::classic_controller::calibration_info
+ &calib = Internal.ClassicController.CalibrationInfo;
+
+ calib.MaxXL = buff[ 0] >> 2;
+ calib.MinXL = buff[ 1] >> 2;
+ calib.MidXL = buff[ 2] >> 2;
+ calib.MaxYL = buff[ 3] >> 2;
+ calib.MinYL = buff[ 4] >> 2;
+ calib.MidYL = buff[ 5] >> 2;
+ calib.MaxXR = buff[ 6] >> 3;
+ calib.MinXR = buff[ 7] >> 3;
+ calib.MidXR = buff[ 8] >> 3;
+ calib.MaxYR = buff[ 9] >> 3;
+ calib.MinYR = buff[10] >> 3;
+ calib.MidYR = buff[11] >> 3;
+ // this doesn't seem right...
+ // calib.MinTriggerL = buff[12] >> 3;
+ // calib.MaxTriggerL = buff[14] >> 3;
+ // calib.MinTriggerR = buff[13] >> 3;
+ // calib.MaxTriggerR = buff[15] >> 3;
+ calib.MinTriggerL = 0;
+ calib.MaxTriggerL = 31;
+ calib.MinTriggerR = 0;
+ calib.MaxTriggerR = 31;
+
+ changed |= CLASSIC_CONNECTED;//|CLASSIC_CALIBRATION_CHANGED;
+ // reenable reports
+// SetReportType(ReportType);
+ }
+ break;
+
+ case BALANCE_BOARD:
+ {
+ // first part, 0 & 17kg calibration values
+ wiimote_state::balance_board::calibration_info
+ &calib = Internal.BalanceBoard.CalibrationInfo;
+
+ calib.Kg0 .TopR = (short)((short)buff[0] << 8 | buff[1]);
+ calib.Kg0 .BottomR = (short)((short)buff[2] << 8 | buff[3]);
+ calib.Kg0 .TopL = (short)((short)buff[4] << 8 | buff[5]);
+ calib.Kg0 .BottomL = (short)((short)buff[6] << 8 | buff[7]);
+
+ calib.Kg17.TopR = (short)((short)buff[8] << 8 | buff[9]);
+ calib.Kg17.BottomR = (short)((short)buff[10] << 8 | buff[11]);
+ calib.Kg17.TopL = (short)((short)buff[12] << 8 | buff[13]);
+ calib.Kg17.BottomL = (short)((short)buff[14] << 8 | buff[15]);
+
+ // 2nd part is scanned above
+ }
+ break;
+
+ case MOTION_PLUS:
+ {
+ // TODO: not known how the calibration values work
+ changed |= MOTIONPLUS_ENABLED;
+ bMotionPlusEnabled = true;
+ bInitInProgress = false;
+ // reenable reports
+// SetReportType(ReportType);
+ }
+ break;
+ }
+ case 0x34:
+ {
+ if(Internal.ExtensionType == BALANCE_BOARD)
+ {
+ wiimote_state::balance_board::calibration_info
+ &calib = Internal.BalanceBoard.CalibrationInfo;
+
+ // 2nd part of the balance board calibration,
+ // 34kg calibration values
+ calib.Kg34.TopR = (short)((short)buff[0] << 8 | buff[1]);
+ calib.Kg34.BottomR = (short)((short)buff[2] << 8 | buff[3]);
+ calib.Kg34.TopL = (short)((short)buff[4] << 8 | buff[5]);
+ calib.Kg34.BottomL = (short)((short)buff[6] << 8 | buff[7]);
+
+ changed |= BALANCE_CONNECTED;
+ // reenable reports
+ SetReportType(IN_BUTTONS_BALANCE_BOARD);
+ }
+ // else unknown what these are for
+ }
+ bInitInProgress = false;
+ }
+ break;
+
+ default:
+// _ASSERT(0); // shouldn't happen
+ break;
+ }
+
+ return changed;
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::ReadCalibration ()
+ {
+ TRACE(_T("Requestion wiimote calibration:"));
+ // this appears to change the report type to 0x31
+ ReadAddress(REGISTER_CALIBRATION, 7);
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::EnableIR (wiimote_state::ir::mode mode)
+ {
+ Internal.IR.Mode = mode;
+
+ BYTE buff [REPORT_LENGTH] = {0};
+ buff[0] = OUT_IR;
+ buff[1] = 0x04 | GetRumbleBit();
+ WriteReport(buff);
+
+ memset(buff, 0, REPORT_LENGTH);
+ buff[0] = OUT_IR2;
+ buff[1] = 0x04 | GetRumbleBit();
+ WriteReport(buff);
+
+ static const BYTE ir_sens1[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00,
+ 0xc0};
+ static const BYTE ir_sens2[] = {0x40, 0x00};
+
+ WriteData(REGISTER_IR, 0x08);
+ Sleep(25); // wait a little to make IR more reliable (for some)
+ WriteData(REGISTER_IR_SENSITIVITY_1, sizeof(ir_sens1), ir_sens1);
+ WriteData(REGISTER_IR_SENSITIVITY_2, sizeof(ir_sens2), ir_sens2);
+ WriteData(REGISTER_IR_MODE, (BYTE)mode);
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::DisableIR ()
+ {
+ Internal.IR.Mode = wiimote_state::ir::OFF;
+
+ BYTE buff [REPORT_LENGTH] = {0};
+ buff[0] = OUT_IR;
+ buff[1] = GetRumbleBit();
+ WriteReport(buff);
+
+ memset(buff, 0, REPORT_LENGTH);
+ buff[0] = OUT_IR2;
+ buff[1] = GetRumbleBit();
+ WriteReport(buff);
+ }
+// ------------------------------------------------------------------------------------
+unsigned __stdcall wiimote::HIDwriteThreadfunc (void* param)
+ {
+ _ASSERT(param);
+ TRACE(_T("(starting HID write thread)"));
+ wiimote &remote = *(wiimote*)param;
+
+ while(remote.Handle != INVALID_HANDLE_VALUE)
+ {
+ // try to write the oldest entry in the queue
+#ifdef USE_DYNAMIC_HIDQUEUE
+ if(!remote.HIDwriteQueue.empty())
+#else
+ if(!remote.HID.IsEmpty())
+#endif
+ {
+#ifdef BEEP_DEBUG_WRITES
+ Beep(1500,1);
+#endif
+ EnterCriticalSection(&remote.HIDwriteQueueLock);
+#ifdef USE_DYNAMIC_HIDQUEUE
+ BYTE *buff = remote.HIDwriteQueue.front();
+ _ASSERT(buff);
+#else
+ BYTE *buff = remote.HID.Queue[remote.HID.ReadIndex].Report;
+#endif
+ LeaveCriticalSection(&remote.HIDwriteQueueLock);
+
+ if(!_HidD_SetOutputReport(remote.Handle, buff, REPORT_LENGTH))
+ {
+ DWORD err = GetLastError();
+if(err==ERROR_BUSY)
+TRACE(_T("**** HID WRITE: BUSY ****"));
+else if(err == ERROR_NOT_READY)
+TRACE(_T("**** HID WRITE: NOT READY ****"));
+
+ if((err != ERROR_BUSY) && // "the requested resource is in use"
+ (err != ERROR_NOT_READY)) // "the device is not ready"
+ {
+ if(err == ERROR_NOT_SUPPORTED) {
+ WARN(_T("BT Stack doesn't suport HID writes!"));
+ goto remove_entry;
+ }
+ else{
+ DEEP_TRACE(_T("HID write failed (err %u)! - "), err);
+ // if this worked previously, the connection was probably lost
+ if(remote.IsConnected())
+ remote.bConnectionLost = true;
+ }
+ //_T("aborting write thread"), err);
+ //return 911;
+ }
+ }
+ else{
+remove_entry:
+ EnterCriticalSection(&remote.HIDwriteQueueLock);
+#ifdef USE_DYNAMIC_HIDQUEUE
+ remote.HIDwriteQueue.pop();
+ delete[] buff;
+#else
+ remote.HID.ReadIndex++;
+ remote.HID.ReadIndex &= (hid::MAX_QUEUE_ENTRIES-1);
+#endif
+ LeaveCriticalSection(&remote.HIDwriteQueueLock);
+ }
+ }
+ Sleep(1);
+ }
+
+ TRACE(_T("ending HID write thread"));
+ return 0;
+ }
+// ------------------------------------------------------------------------------------
+bool wiimote::WriteReport (BYTE *buff)
+ {
+#ifdef BEEP_DEBUG_WRITES
+ Beep(2000,1);
+#endif
+
+#ifdef _DEBUG
+ #define DEEP_TRACE_TYPE(type) case OUT_##type: DEEP_TRACE(_T("WriteReport: ")\
+ _T(#type)); break
+ switch(buff[0])
+ {
+ DEEP_TRACE_TYPE(NONE);
+ DEEP_TRACE_TYPE(LEDs);
+ DEEP_TRACE_TYPE(TYPE);
+ DEEP_TRACE_TYPE(IR);
+ DEEP_TRACE_TYPE(SPEAKER_ENABLE);
+ DEEP_TRACE_TYPE(STATUS);
+ DEEP_TRACE_TYPE(WRITEMEMORY);
+ DEEP_TRACE_TYPE(READMEMORY);
+ DEEP_TRACE_TYPE(SPEAKER_DATA);
+ DEEP_TRACE_TYPE(SPEAKER_MUTE);
+ DEEP_TRACE_TYPE(IR2);
+ default:
+ TRACE(_T("WriteReport: type [%02x][%02x]"), buff[1], buff[2]);
+ }
+#endif
+
+ if(bUseHIDwrite)
+ {
+ // HidD_SetOutputReport: +: works on MS Bluetooth stacks (WriteFile doesn't).
+ // -: is synchronous, so make it async
+ if(!HIDwriteThread)
+ {
+ HIDwriteThread = (HANDLE)_beginthreadex(NULL, 0, HIDwriteThreadfunc,
+ this, 0, NULL);
+ _ASSERT(HIDwriteThread);
+ if(!HIDwriteThread) {
+ WARN(_T("couldn't create HID write thread!"));
+ return false;
+ }
+ SetThreadPriority(HIDwriteThread, WORKER_THREAD_PRIORITY);
+ }
+
+ // insert the write request into the thread's queue
+#ifdef USE_DYNAMIC_HIDQUEUE
+ EnterCriticalSection(&HIDwriteQueueLock);
+ BYTE *buff_copy = new BYTE[REPORT_LENGTH];
+#else
+ // allocate the HID write queue once
+ if(!HID.Queue && !HID.Allocate())
+ return false;
+
+ EnterCriticalSection(&HIDwriteQueueLock);
+ BYTE *buff_copy = HID.Queue[HID.WriteIndex].Report;
+#endif
+ memcpy(buff_copy, buff, REPORT_LENGTH);
+
+#ifdef USE_DYNAMIC_HIDQUEUE
+ HIDwriteQueue.push(buff_copy);
+#else
+ HID.WriteIndex++;
+ HID.WriteIndex &= (HID.MAX_QUEUE_ENTRIES-1);
+
+ // check if the fixed report queue has overflown:
+ // if this ASSERT triggers, the HID write queue (that stores reports
+ // for asynchronous output by HIDwriteThreadfunc) has overflown.
+ // this can happen if the connection with the wiimote has been lost
+ // and in that case is harmless.
+ //
+ // if it happens during normal operation though you need to increase
+ // hid::MAX_QUEUE_ENTRIES to the next power-of-2 (see comments)
+ // _and_ email me the working setting so I can update the next release
+ _ASSERT(HID.WriteIndex != HID.ReadIndex);
+#endif
+ LeaveCriticalSection(&HIDwriteQueueLock);
+ return true;
+ }
+
+ // WriteFile:
+ DWORD written;
+ if(!WriteFile(Handle, buff, REPORT_LENGTH, &written, &Overlapped))
+ {
+ DWORD error = GetLastError();
+ if(error != ERROR_IO_PENDING) {
+ TRACE(_T("WriteFile failed, err: %u!"), error);
+ // if it worked previously, assume we lost the connection
+ if(IsConnected())
+ bConnectionLost = true;
+#ifndef USE_DYNAMIC_HIDQUEUE
+ HID.Deallocate();
+#endif
+ return false;
+ }
+ }
+ return true;
+ }
+// ------------------------------------------------------------------------------------
+// experimental speaker support:
+// ------------------------------------------------------------------------------------
+bool wiimote::MuteSpeaker (bool on)
+ {
+ _ASSERT(IsConnected());
+ if(!IsConnected())
+ return false;
+
+ if(Internal.Speaker.bMuted == on)
+ return true;
+
+ if(on) TRACE(_T("muting speaker." ));
+ else TRACE(_T("unmuting speaker."));
+
+ BYTE buff [REPORT_LENGTH] = {0};
+ buff[0] = OUT_SPEAKER_MUTE;
+ buff[1] = (on? 0x04 : 0x00) | GetRumbleBit();
+ if(!WriteReport(buff))
+ return false;
+ Sleep(1);
+ Internal.Speaker.bMuted = on;
+ return true;
+ }
+// ------------------------------------------------------------------------------------
+bool wiimote::EnableSpeaker (bool on)
+ {
+ _ASSERT(IsConnected());
+ if(!IsConnected())
+ return false;
+
+ if(Internal.Speaker.bEnabled == on)
+ return true;
+
+ if(on) TRACE(_T("enabling speaker.")); else TRACE(_T("disabling speaker."));
+
+ BYTE buff [REPORT_LENGTH] = {0};
+ buff[0] = OUT_SPEAKER_ENABLE;
+ buff[1] = (on? 0x04 : 0x00) | GetRumbleBit();
+ if(!WriteReport(buff))
+ return false;
+
+ if(!on) {
+ Internal.Speaker.Freq = FREQ_NONE;
+ Internal.Speaker.Volume = 0;
+ MuteSpeaker(true);
+ }
+
+ Internal.Speaker.bEnabled = on;
+ return true;
+ }
+// ------------------------------------------------------------------------------------
+#ifdef TR4 // TEMP, ignore
+ extern int hzinc;
+#endif
+// ------------------------------------------------------------------------------------
+unsigned __stdcall wiimote::SampleStreamThreadfunc (void* param)
+ {
+ TRACE(_T("(starting sample thread)"));
+ // sends a simple square wave sample stream
+ wiimote &remote = *(wiimote*)param;
+
+ static BYTE squarewave_report[REPORT_LENGTH] =
+ { OUT_SPEAKER_DATA, 20<<3, 0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,
+ 0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3, };
+ static BYTE sample_report [REPORT_LENGTH] =
+ { OUT_SPEAKER_DATA, 0 };
+
+ bool last_playing = false;
+ DWORD frame = 0;
+ DWORD frame_start = 0;
+ unsigned total_samples = 0;
+ unsigned sample_index = 0;
+ wiimote_sample *current_sample = NULL;
+
+ // TODO: duration!!
+ while(remote.IsConnected())
+ {
+ bool playing = remote.IsPlayingAudio();
+
+ if(!playing)
+ Sleep(1);
+ else{
+ const unsigned freq_hz = FreqLookup[remote.Internal.Speaker.Freq];
+#ifdef TR4
+ const float frame_ms = 1000 / ((freq_hz+hzinc) / 40.f); // 20bytes = 40 samples per write
+#else
+ const float frame_ms = 1000 / (freq_hz / 40.f); // 20bytes = 40 samples per write
+#endif
+
+ // has the sample just changed?
+ bool sample_changed = (current_sample != remote.CurrentSample);
+ current_sample = (wiimote_sample*)remote.CurrentSample;
+
+// (attempts to minimise glitches, doesn't seem to help though)
+//#define FIRSTFRAME_IS_SILENT // send all-zero for first frame
+
+#ifdef FIRSTFRAME_IS_SILENT
+ bool silent_frame = false;
+#endif
+ if(!last_playing || sample_changed) {
+ frame = 0;
+ frame_start = timeGetTime();
+ total_samples = current_sample? current_sample->length : 0;
+ sample_index = 0;
+#ifdef FIRSTFRAME_IS_SILENT
+ silent_frame = true;
+#endif
+ }
+
+ // are we streaming a sample?
+ if(current_sample)
+ {
+ if(sample_index < current_sample->length)
+ {
+ // (remember that samples are 4bit, ie. 2 per byte)
+ unsigned samples_left = (current_sample->length - sample_index);
+ unsigned report_samples = min(samples_left, (unsigned)40);
+ // round the entries up to the nearest multiple of 2
+ unsigned report_entries = (report_samples+1) >> 1;
+
+ sample_report[1] = (BYTE)((report_entries<<3) |
+ remote.GetRumbleBit());
+#ifdef FIRSTFRAME_IS_SILENT
+ if(silent_frame) {
+ // send all-zeroes
+ for(unsigned index=0; index<report_entries; index++)
+ sample_report[2+index] = 0;
+ remote.WriteReport(sample_report);
+ }
+ else
+#endif
+ {
+ for(unsigned index=0; index<report_entries; index++)
+ sample_report[2+index] =
+ current_sample->samples[(sample_index>>1)+index];
+ remote.WriteReport(sample_report);
+ sample_index += report_samples;
+ }
+ }
+ else{
+ // we reached the sample end
+ remote.CurrentSample = NULL;
+ current_sample = NULL;
+ remote.Internal.Speaker.Freq = FREQ_NONE;
+ remote.Internal.Speaker.Volume = 0;
+ }
+ }
+ // no, a squarewave
+ else{
+ squarewave_report[1] = (20<<3) | remote.GetRumbleBit();
+ remote.WriteReport(squarewave_report);
+#if 0
+ // verify that we're sending at the correct rate (we are)
+ DWORD elapsed = (timeGetTime()-frame_start);
+ unsigned total_samples = frame * 40;
+ float elapsed_secs = elapsed / 1000.f;
+ float sent_persec = total_samples / elapsed_secs;
+#endif
+ }
+
+ frame++;
+
+ // send the first two buffers immediately? (attempts to lessen startup
+ // startup glitches by assuming we're filling a small sample
+ // (or general input) buffer on the wiimote) - doesn't seem to help
+// if(frame > 2) {
+ while((timeGetTime()-frame_start) < (unsigned)(frame*frame_ms))
+ Sleep(1);
+// }
+ }
+
+ last_playing = playing;
+ }
+
+ TRACE(_T("(ending sample thread)"));
+ return 0;
+ }
+// ------------------------------------------------------------------------------------
+bool wiimote::Load16bitMonoSampleWAV (const TCHAR* filepath, wiimote_sample &out)
+ {
+ // converts unsigned 16bit mono .wav audio data to the 4bit ADPCM variant
+ // used by the Wiimote (at least the closest match so far), and returns
+ // the data in a BYTE array (caller must delete[] it when no longer needed):
+ memset(&out, 0, sizeof(out));
+
+ TRACE(_T("Loading '%s'"), filepath);
+
+ FILE *file;
+#if (_MSC_VER >= 1400) // VC 2005+
+ _tfopen_s(&file, filepath, _T("rb"));
+#else
+ file = _tfopen(filepath, _T("rb"));
+#endif
+ _ASSERT(file);
+ if(!file) {
+ WARN(_T("Couldn't open '%s"), filepath);
+ return false;
+ }
+
+ // parse the .wav file
+ struct riff_chunkheader {
+ char ckID [4];
+ DWORD ckSize;
+ char formType [4];
+ };
+ struct chunk_header {
+ char ckID [4];
+ DWORD ckSize;
+ };
+ union {
+ WAVEFORMATEX x;
+ WAVEFORMATEXTENSIBLE xe;
+ } wf = {0};
+
+ riff_chunkheader riff_chunkheader;
+ chunk_header chunk_header;
+ speaker_freq freq = FREQ_NONE;
+
+ #define READ(data) if(fread(&data, sizeof(data), 1, file) != 1) { \
+ TRACE(_T(".wav file corrupt")); \
+ fclose(file); \
+ return false; \
+ }
+ #define READ_SIZE(ptr,size) if(fread(ptr, size, 1, file) != 1) { \
+ TRACE(_T(".wav file corrupt")); \
+ fclose(file); \
+ return false; \
+ }
+ // read the riff chunk header
+ READ(riff_chunkheader);
+
+ // valid RIFF file?
+ _ASSERT(!strncmp(riff_chunkheader.ckID, "RIFF", 4));
+ if(strncmp(riff_chunkheader.ckID, "RIFF", 4))
+ goto unsupported; // nope
+ // valid WAV variant?
+ _ASSERT(!strncmp(riff_chunkheader.formType, "WAVE", 4));
+ if(strncmp(riff_chunkheader.formType, "WAVE", 4))
+ goto unsupported; // nope
+
+ // find the format & data chunks
+ while(1)
+ {
+ READ(chunk_header);
+
+ if(!strncmp(chunk_header.ckID, "fmt ", 4))
+ {
+ // not a valid .wav file?
+ if(chunk_header.ckSize < 16 ||
+ chunk_header.ckSize > sizeof(WAVEFORMATEXTENSIBLE))
+ goto unsupported;
+
+ READ_SIZE((BYTE*)&wf.x, chunk_header.ckSize);
+
+ // now we know it's true wav file
+ bool extensible = (wf.x.wFormatTag == WAVE_FORMAT_EXTENSIBLE);
+ int format = extensible? wf.xe.SubFormat.Data1 :
+ wf.x .wFormatTag;
+ // must be uncompressed PCM (the format comparisons also work on
+ // the 'extensible' header, even though they're named differently)
+ if(format != WAVE_FORMAT_PCM) {
+ TRACE(_T(".. not uncompressed PCM"));
+ goto unsupported;
+ }
+
+ // must be mono, 16bit
+ if((wf.x.nChannels != 1) || (wf.x.wBitsPerSample != 16)) {
+ TRACE(_T(".. %d bit, %d channel%s"), wf.x.wBitsPerSample,
+ wf.x.nChannels,
+ (wf.x.nChannels>1? _T("s"):_T("")));
+ goto unsupported;
+ }
+
+ // must be _near_ a supported speaker frequency range (but allow some
+ // tolerance, especially as the speaker freq values aren't final yet):
+ unsigned sample_freq = wf.x.nSamplesPerSec;
+ const unsigned epsilon = 100; // for now
+
+ for(unsigned index=1; index<ARRAY_ENTRIES(FreqLookup); index++)
+ {
+ if((sample_freq+epsilon) >= FreqLookup[index] &&
+ (sample_freq-epsilon) <= FreqLookup[index]) {
+ freq = (speaker_freq)index;
+ TRACE(_T(".. using speaker freq %u"), FreqLookup[index]);
+ break;
+ }
+ }
+ if(freq == FREQ_NONE) {
+ WARN(_T("Couldn't (loosely) match .wav samplerate %u Hz to speaker"),
+ sample_freq);
+ goto unsupported;
+ }
+ }
+ else if(!strncmp(chunk_header.ckID, "data", 4))
+ {
+ // make sure we got a valid fmt chunk first
+ if(!wf.x.nBlockAlign)
+ goto corrupt_file;
+
+ // grab the data
+ unsigned total_samples = chunk_header.ckSize / wf.x.nBlockAlign;
+ if(total_samples == 0)
+ goto corrupt_file;
+
+ short *samples = new short[total_samples];
+ size_t read = fread(samples, 2, total_samples, file);
+ fclose(file);
+ if(read != total_samples)
+ {
+ if(read == 0) {
+ delete[] samples;
+ goto corrupt_file;
+ }
+ // got a different number, but use them anyway
+ WARN(_T("found %s .wav audio data than expected (%u/%u samples)"),
+ ((read < total_samples)? _T("less") : _T("more")),
+ read, total_samples);
+
+ total_samples = read;
+ }
+
+ // and convert them
+ bool res = Convert16bitMonoSamples(samples, true, total_samples, freq,
+ out);
+ delete[] samples;
+ return res;
+ }
+ else{
+ // unknown chunk, skip its data
+ DWORD chunk_bytes = (chunk_header.ckSize + 1) & ~1L;
+ if(fseek(file, chunk_bytes, SEEK_CUR))
+ goto corrupt_file;
+ }
+ }
+
+corrupt_file:
+ WARN(_T(".wav file is corrupt"));
+ fclose(file);
+ return false;
+
+unsupported:
+ WARN(_T(".wav file format not supported (must be mono 16bit PCM)"));
+ fclose(file);
+ return false;
+ }
+// ------------------------------------------------------------------------------------
+bool wiimote::Load16BitMonoSampleRAW (const TCHAR* filepath,
+ bool _signed,
+ speaker_freq freq,
+ wiimote_sample &out)
+ {
+ // converts (.wav style) unsigned 16bit mono raw data to the 4bit ADPCM variant
+ // used by the Wiimote, and returns the data in a BYTE array (caller must
+ // delete[] it when no longer needed):
+ memset(&out, 0, sizeof(out));
+
+ // get the length of the file
+ struct _stat file_info;
+ if(_tstat(filepath, &file_info)) {
+ WARN(_T("couldn't get filesize for '%s'"), filepath);
+ return false;
+ }
+
+ DWORD len = file_info.st_size;
+ _ASSERT(len);
+ if(!len) {
+ WARN(_T("zero-size sample file '%s'"), filepath);
+ return false;
+ }
+
+ unsigned total_samples = (len+1) / 2; // round up just in case file is corrupt
+ // allocate a buffer to hold the samples to convert
+ short *samples = new short[total_samples];
+ _ASSERT(samples);
+ if(!samples) {
+ TRACE(_T("Couldn't open '%s"), filepath);
+ return false;
+ }
+
+ // load them
+ FILE *file;
+ bool res;
+#if (_MSC_VER >= 1400) // VC 2005+
+ _tfopen_s(&file, filepath, _T("rb"));
+#else
+ file = _tfopen(filepath, _T("rb"));
+#endif
+ _ASSERT(file);
+ if(!file) {
+ TRACE(_T("Couldn't open '%s"), filepath);
+ goto error;
+ }
+
+ res = (fread(samples, 1, len, file) == len);
+ fclose(file);
+ if(!res) {
+ WARN(_T("Couldn't load file '%s'"), filepath);
+ goto error;
+ }
+
+ // and convert them
+ res = Convert16bitMonoSamples(samples, _signed, total_samples, freq, out);
+ delete[] samples;
+ return res;
+
+error:
+ delete[] samples;
+ return false;
+ }
+// ------------------------------------------------------------------------------------
+bool wiimote::Convert16bitMonoSamples (const short* samples,
+ bool _signed,
+ DWORD length,
+ speaker_freq freq,
+ wiimote_sample &out)
+ {
+ // converts 16bit mono sample data to the native 4bit format used by the Wiimote,
+ // and returns the data in a BYTE array (caller must delete[] when no
+ // longer needed):
+ memset(&out, 0, sizeof(0));
+
+ _ASSERT(samples && length);
+ if(!samples || !length)
+ return false;
+
+ // allocate the output buffer
+ out.samples = new BYTE[length];
+ _ASSERT(out.samples);
+ if(!out.samples)
+ return false;
+
+ // clear it
+ memset(out.samples, 0, length);
+ out.length = length;
+ out.freq = freq;
+
+ // ADPCM code, adapted from
+ // http://www.wiindows.org/index.php/Talk:Wiimote#Input.2FOutput_Reports
+ static const int index_table[16] = { -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8 };
+ static const int diff_table [16] = { 1, 3, 5, 7, 9, 11, 13, 15,
+ -1, -3, -5, -7, -9, -11, -13, 15 };
+ static const int step_scale [16] = { 230, 230, 230, 230, 307, 409, 512, 614,
+ 230, 230, 230, 230, 307, 409, 512, 614 };
+ // Encode to ADPCM, on initialization set adpcm_prev_value to 0 and adpcm_step
+ // to 127 (these variables must be preserved across reports)
+ int adpcm_prev_value = 0;
+ int adpcm_step = 127;
+
+ for(size_t i=0; i<length; i++)
+ {
+ // convert to 16bit signed
+ int value = samples[i];// (8bit) << 8);// | samples[i]; // dither it?
+ if(!_signed)
+ value -= 32768;
+ // encode:
+ int diff = value - adpcm_prev_value;
+ BYTE encoded_val = 0;
+ if(diff < 0) {
+ encoded_val |= 8;
+ diff = -diff;
+ }
+ diff = (diff << 2) / adpcm_step;
+ if (diff > 7)
+ diff = 7;
+ encoded_val |= diff;
+ adpcm_prev_value += ((adpcm_step * diff_table[encoded_val]) / 8);
+ if(adpcm_prev_value > 0x7fff)
+ adpcm_prev_value = 0x7fff;
+ if(adpcm_prev_value < -0x8000)
+ adpcm_prev_value = -0x8000;
+ adpcm_step = (adpcm_step * step_scale[encoded_val]) >> 8;
+ if(adpcm_step < 127)
+ adpcm_step = 127;
+ if(adpcm_step > 24567)
+ adpcm_step = 24567;
+ if(i & 1)
+ out.samples[i>>1] |= encoded_val;
+ else
+ out.samples[i>>1] |= encoded_val << 4;
+ }
+
+ return true;
+ }
+// ------------------------------------------------------------------------------------
+bool wiimote::PlaySample (const wiimote_sample &sample, BYTE volume,
+ speaker_freq freq_override)
+ {
+ _ASSERT(IsConnected());
+ if(!IsConnected())
+ return false;
+
+ speaker_freq freq = freq_override? freq_override : sample.freq;
+
+ TRACE(_T("playing sample."));
+ EnableSpeaker(true);
+ MuteSpeaker (true);
+
+#if 0
+ // combine everything into one write - faster, seems to work?
+ BYTE bytes[9] = { 0x00, 0x00, 0x00, 10+freq, vol, 0x00, 0x00, 0x01, 0x01 };
+ WriteData(0x04a20001, sizeof(bytes), bytes);
+#else
+ // Write 0x01 to register 0x04a20009
+ WriteData(0x04a20009, 0x01);
+ // Write 0x08 to register 0x04a20001
+ WriteData(0x04a20001, 0x08);
+ // Write 7-byte configuration to registers 0x04a20001-0x04a20008
+ BYTE bytes[7] = { 0x00, 0x00, 0x00, 10+(BYTE)freq, volume, 0x00, 0x00 };
+ WriteData(0x04a20001, sizeof(bytes), bytes);
+ // + Write 0x01 to register 0x04a20008
+ WriteData(0x04a20008, 0x01);
+#endif
+
+ Internal.Speaker.Freq = freq;
+ Internal.Speaker.Volume = volume;
+ CurrentSample = &sample;
+
+ MuteSpeaker(false);
+
+ return StartSampleThread();
+ }
+// ------------------------------------------------------------------------------------
+bool wiimote::StartSampleThread ()
+ {
+ if(SampleThread)
+ return true;
+
+ SampleThread = (HANDLE)_beginthreadex(NULL, 0, SampleStreamThreadfunc,
+ this, 0, NULL);
+ _ASSERT(SampleThread);
+ if(!SampleThread) {
+ WARN(_T("couldn't create sample thread!"));
+ MuteSpeaker (true);
+ EnableSpeaker(false);
+ return false;
+ }
+ SetThreadPriority(SampleThread, WORKER_THREAD_PRIORITY);
+ return true;
+ }
+// ------------------------------------------------------------------------------------
+bool wiimote::PlaySquareWave (speaker_freq freq, BYTE volume)
+ {
+ _ASSERT(IsConnected());
+ if(!IsConnected())
+ return false;
+
+ // if we're already playing a sample, stop it first
+ if(IsPlayingSample())
+ CurrentSample = NULL;
+ // if we're already playing a square wave at this freq and volume, return
+ else if(IsPlayingAudio() && (Internal.Speaker.Freq == freq) &&
+ (Internal.Speaker.Volume == volume))
+ return true;
+
+ TRACE(_T("playing square wave."));
+ // stop playing samples
+ CurrentSample = 0;
+
+ EnableSpeaker(true);
+ MuteSpeaker (true);
+
+#if 0
+ // combined everything into one write - much faster, seems to work?
+ BYTE bytes[9] = { 0x00, 0x00, 0x00, freq, volume, 0x00, 0x00, 0x01, 0x1 };
+ WriteData(0x04a20001, sizeof(bytes), bytes);
+#else
+ // write 0x01 to register 0xa20009
+ WriteData(0x04a20009, 0x01);
+ // write 0x08 to register 0xa20001
+ WriteData(0x04a20001, 0x08);
+ // write default sound mode (4bit ADPCM, we assume) 7-byte configuration
+ // to registers 0xa20001-0xa20008
+ BYTE bytes[7] = { 0x00, 0x00, 0x00, 10+(BYTE)freq, volume, 0x00, 0x00 };
+ WriteData(0x04a20001, sizeof(bytes), bytes);
+ // write 0x01 to register 0xa20008
+ WriteData(0x04a20008, 0x01);
+#endif
+
+ Internal.Speaker.Freq = freq;
+ Internal.Speaker.Volume = volume;
+
+ MuteSpeaker(false);
+ return StartSampleThread();
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::RecordState (state_history &events_out,
+ unsigned max_time_ms,
+ state_change_flags change_trigger)
+ {
+ // user being naughty?
+ if(Recording.bEnabled)
+ StopRecording();
+
+ // clear the list
+ if(!events_out.empty())
+ events_out.clear();
+
+ // start recording
+ Recording.StateHistory = &events_out;
+ Recording.StartTimeMS = timeGetTime();
+ Recording.EndTimeMS = Recording.StartTimeMS + max_time_ms;
+ Recording.TriggerFlags = change_trigger;
+ // as this call happens outside the read/parse thread, set the boolean
+ // which will enable reocrding last, so that all params are in place.
+ // TODO: * stricly speaking this only works on VC2005+ or better, as it
+ // automatically places a memory barrier on volatile variables - earlier/
+ // other compilers may reorder the assignments!). *
+ Recording.bEnabled = true;
+ }
+// ------------------------------------------------------------------------------------
+void wiimote::StopRecording ()
+ {
+ if(!Recording.bEnabled)
+ return;
+
+ Recording.bEnabled = false;
+ // make sure the read/parse thread has time to notice the change (else it might
+ // still write one more state to the list)
+ Sleep(10); // too much?
+ }
+// ------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------
diff --git a/w32mote/WiiYourself/wiimote.h b/w32mote/WiiYourself/wiimote.h
new file mode 100644
index 0000000..1db2c09
--- /dev/null
+++ b/w32mote/WiiYourself/wiimote.h
@@ -0,0 +1,495 @@
+// _______________________________________________________________________________
+//
+// - WiiYourself! - native C++ Wiimote library v1.15
+// (c) gl.tter 2007-10 - http://gl.tter.org
+//
+// see License.txt for conditions of use. see History.txt for change log.
+// _______________________________________________________________________________
+//
+// wiimote.h (tab = 4 spaces)
+
+#ifdef _MSC_VER // VC
+# pragma once
+#endif
+
+#ifndef _WIIMOTE_H
+# define _WIIMOTE_H
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <tchar.h> // auto Unicode/Ansi support
+#include <queue> // for HID write method
+#include <list> // for state recording
+
+#ifndef QWORD
+ typedef unsigned __int64 QWORD;
+#endif
+
+#ifdef _MSC_VER // VC-specific: _DEBUG build only _ASSERT() sanity checks
+# include <crtdbg.h>
+#elif defined(__MINGW32__) // define NDEBUG to disable assert
+# include <assert.h>
+# define _ASSERT assert
+#else
+# define _ASSERT(x) ((void)0) // (add your compiler's implementation if you like)
+#endif
+
+#ifdef SWIGWRAPPER // Python Wrapper
+#include "Python/wiimote_state.i"
+#else
+#include "wiimote_state.h"
+#endif
+
+// configs:
+//#define USE_DYNAMIC_HIDQUEUE // deprecated
+
+// we request periodic status report updates to refresh the battery level
+// and to detect connection loss (through failed writes)
+#define REQUEST_STATUS_EVERY_MS 1000
+#define DETECT_MPLUS_EVERY_MS 1000
+#define DETECT_MPLUS_COUNT 1 // # of tries in quick succession
+
+// all threads (read/parse, audio streaming, async rumble...) use this priority
+#define WORKER_THREAD_PRIORITY THREAD_PRIORITY_HIGHEST
+
+ // internals
+#define WIIYOURSELF_VERSION_MAJOR 1
+#define WIIYOURSELF_VERSION_MINOR1 1
+#define WIIYOURSELF_VERSION_MINOR2 5
+//#define WIIYOURSELF_VERSION_BETA // not defined for non-beta releases
+#define WIIYOURSELF_VERSION_STR _T("1.15")
+
+// array sizes
+#define TOTAL_BUTTON_BITS 16 // Number of bits for (Classic)ButtonNameFromBit[]
+#define TOTAL_FREQUENCIES 10 // Number of frequencies (see speaker_freq[])
+
+ // clarity
+typedef HANDLE EVENT;
+
+
+// state data changes can be signalled to the app via a callback. Set the wiimote
+// object's 'ChangedCallback' any time to enable them, or alternatively inherit
+// from the wiimote object and override the ChangedNotifier() virtual method.
+
+// of flags indicating which state has changed since the last callback.
+typedef void (*state_changed_callback) (class wiimote &owner,
+ state_change_flags changed,
+ const wiimote_state &new_state);
+
+// internals
+typedef BOOLEAN (__stdcall *hidwrite_ptr)(HANDLE HidDeviceObject,
+ PVOID ReportBuffer,
+ ULONG ReportBufferLength);
+
+// (global due to Python wrapper)
+struct wiimote_state_event {
+ DWORD time_ms; // system timestamp in milliseconds
+ wiimote_state state;
+ };
+
+// wiimote class - connects and manages a wiimote and its optional extensions
+// (Nunchuk/Classic Controller), and exposes their state
+class wiimote : public wiimote_state
+ {
+ public:
+ wiimote ();
+ virtual ~wiimote ();
+
+ public:
+ // these can be used to identify Connect()ed wiimote objects (if both
+ // are unconnected they will pass the compare as their handles are invalid)
+ inline bool operator == (const wiimote& remote)
+ { return Handle == remote.Handle; }
+ inline bool operator != (const wiimote& remote)
+ { return Handle != remote.Handle; }
+
+ // wiimote data input mode (use with SetReportType())
+ // (only enable what you need to save battery power)
+ enum input_report
+ {
+ // combinations if buttons/acceleration/IR/Extension data
+ IN_BUTTONS = 0x30,
+ IN_BUTTONS_ACCEL = 0x31,
+ IN_BUTTONS_ACCEL_IR = 0x33, // reports IR EXTENDED data (dot sizes)
+ IN_BUTTONS_ACCEL_EXT = 0x35,
+ IN_BUTTONS_ACCEL_IR_EXT = 0x37, // reports IR BASIC data (no dot sizes)
+ IN_BUTTONS_BALANCE_BOARD = 0x32, // must use this for the balance board
+ };
+ // string versions
+ static const TCHAR* ReportTypeName [];
+
+
+ public: // convenience accessors:
+ inline bool IsConnected () const { return bStatusReceived; }
+ // if IsConnected() unexpectedly returns false, connection was probably lost
+ inline bool ConnectionLost () const { return bConnectionLost; }
+ inline bool IsBalanceBoard () const { return (Internal.bExtension &&
+ (Internal.ExtensionType==wiimote_state::BALANCE_BOARD)); }
+ inline bool NunchukConnected () const { return (Internal.bExtension &&
+ (Internal.ExtensionType==wiimote_state::NUNCHUK)); }
+ inline bool ClassicConnected () const { return (Internal.bExtension &&
+ (Internal.ExtensionType==wiimote_state::CLASSIC)); }
+ inline bool MotionPlusConnected () const { return bMotionPlusDetected; }
+ inline bool MotionPlusEnabled () const { return bMotionPlusEnabled; }
+ inline bool MotionPlusHasExtension() const { return bMotionPlusExtension; }
+ inline bool IsPlayingAudio () const { return (Internal.Speaker.Freq &&
+ Internal.Speaker.Volume); }
+ inline bool IsPlayingSample () const { return IsPlayingAudio() &&
+ (CurrentSample != NULL); }
+ inline bool IsUsingHIDwrites () const { return bUseHIDwrite; }
+ inline bool IsRecordingState () const { return Recording.bEnabled; }
+
+ static inline unsigned TotalConnected() { return _TotalConnected; }
+
+
+ public: // data
+ QWORD UniqueID; // constructed from device-specific calibration info.
+ // Note this is not guaranteed to be truly unique
+ // as several devices may contain the same calibration
+ // vluaes - but unique amongst a small number of
+ // devices.
+#ifdef ID2_FROM_DEVICEPATH
+ QWORD UniqueID2; // (low-reliabilty, left for reference)
+ // constructed from the 'device path' string (as
+ // reported by the OS/stack). This is hopefully
+ // unique as long as the devices remain installed
+ // (or at least paired).
+#endif
+ // optional callbacks - set these to your own fuctions (if required)
+ state_changed_callback ChangedCallback;
+ // you can avoid unnecessary callback overhead by specifying a mask
+ // of which state changes should trigger them (default is any)
+ state_change_flags CallbackTriggerFlags;
+ // alternatively, inherit from this class and override this virtual function:
+ virtual void ChangedNotifier (state_change_flags changed,
+ const wiimote_state &new_state) {};
+
+ // get the button name from its bit index (some bits are unused)
+ static const TCHAR* ButtonNameFromBit [TOTAL_BUTTON_BITS];
+ static const TCHAR* GetButtonNameFromBit (unsigned index)
+ {
+ _ASSERT(index < TOTAL_BUTTON_BITS);
+ if(index >= TOTAL_BUTTON_BITS)
+ return _T("[invalid index]");
+ return ButtonNameFromBit[index];
+ }
+
+ // same for the Classic Controller
+ static const TCHAR* ClassicButtonNameFromBit [TOTAL_BUTTON_BITS];
+ static const TCHAR* GetClassicButtonNameFromBit (unsigned index)
+ {
+ _ASSERT(index < TOTAL_BUTTON_BITS);
+ if(index >= TOTAL_BUTTON_BITS)
+ return _T("[invalid index]");
+ return ClassicButtonNameFromBit[index];
+ }
+
+ // get the frequency from speaker_freq enum
+ static const unsigned FreqLookup [TOTAL_FREQUENCIES];
+ static const unsigned GetFreqLookup (unsigned index)
+ {
+ _ASSERT(index < TOTAL_FREQUENCIES);
+ if(index >= TOTAL_FREQUENCIES)
+ return 0;
+ return FreqLookup[index];
+ }
+
+ public: // methods
+ // call Connect() first - returns true if wiimote was found & enabled
+ // - 'wiimote_index' specifies which *installed* (not necessarily
+ // *connected*) wiimote should be tried (1 = first, 2 = 2nd etc).
+ // if you just want the first *available* wiimote that isn't already
+ // in use, pass in FIRST_AVAILABLE (default).
+ // - 'force_hidwrites' forces HID output method (it's auto-selected
+ // when needed and less efficient, so only force for testing).
+ static const unsigned FIRST_AVAILABLE = 0xffffffff;
+ bool Connect (unsigned wiimote_index = FIRST_AVAILABLE,
+ bool force_hidwrites = false);
+ // disconnect from the controller and stop reading data from it
+ void Disconnect ();
+ // set wiimote reporting mode (call after Connnect())
+ // continous = true forces the wiimote to send constant updates, even when
+ // nothing has changed.
+ // = false only sends data when something has changed (note that
+ // acceleration data will cause frequent updates anyway as it
+ // jitters even when the wiimote is stationary)
+ void SetReportType (input_report type, bool continuous = false);
+
+ // toggle the MotionPlus extension. Call MotionPlusDetected() first to
+ // see if it's attached. Unlike normal extensions, the MotionPlus does
+ // not report itself as one until enabled. Once done, it then replaces
+ // any standard extension attached to it, so be sure to disable it
+ // if you want to read those (it's not currently known of both can
+ // be read simultaneously).
+ bool EnableMotionPlus ();
+ bool DisableMotionPlus ();
+
+ // this is used to remove unwanted 'at rest' offsets, currently only from
+ // the Balance Board. make sure there is no weight on the board before
+ // calling this. it reads the current sensor values and then removes them
+ // offsets from all subsequent KG and LB state values (the 'raw' values
+ // are never modified).
+ void CalibrateAtRest ();
+ // NOTE: the library automatically calls this when the first weight values
+ // come in after Connect()ion, but if the device wasn't at rest at
+ // the time the app can call it again later.
+
+ // to read the state via polling (reading the public state data direct from
+ // the wiimote object) you must call RefreshState() at the top of every pass.
+ // returns a combination of flags to indicate which state (if any) has
+ // changed since the last call.
+ state_change_flags RefreshState ();
+
+ // reset the wiimote (changes report type to non-continuous buttons-only,
+ // clears LEDs & rumble, mutes & disables speaker)
+ void Reset ();
+ // set/clear the wiimote LEDs
+ void SetLEDs (BYTE led_bits); // bits 0-3 are valid
+ // set/clear rumble
+ void SetRumble (bool on);
+ // alternative - rumble for a fixed amount of time (asynchronous)
+ void RumbleForAsync (unsigned milliseconds);
+
+ // *experimental* speaker support:
+ bool MuteSpeaker (bool on);
+ bool EnableSpeaker (bool on);
+ bool PlaySquareWave (speaker_freq freq, BYTE volume = 0x40);
+ // note: PlaySample currently streams from the passed-in wiimote_sample -
+ // don't delete it until playback has stopped.
+ bool PlaySample (const wiimote_sample &sample,
+ BYTE volume = 0x40,
+ speaker_freq freq_override = FREQ_NONE);
+
+ // 16bit mono sample loading/conversion to native format:
+ // .wav sample
+ static bool Load16bitMonoSampleWAV (const TCHAR* filepath,
+ wiimote_sample &out);
+ // raw 16bit mono audio data (can be signed or unsigned)
+ static bool Load16BitMonoSampleRAW (const TCHAR* filepath,
+ bool _signed,
+ speaker_freq freq,
+ wiimote_sample &out);
+ // converts a 16bit mono sample array to a wiimote_sample
+ static bool Convert16bitMonoSamples (const short* samples,
+ bool _signed,
+ DWORD length,
+ speaker_freq freq,
+ wiimote_sample &out);
+
+ // state recording - records state snapshots to a 'state_history' supplied
+ // by the caller. states are timestamped and only added
+ // to the list when the specified state changes.
+#ifndef SWIG // !Python Wrapper
+ typedef wiimote_state_event state_event;
+#endif
+ typedef std::list<state_event> state_history;
+ static const unsigned UNTIL_STOP = 0xffffffff;
+ // - pass in a 'state_history' list, and don't destroy/change it until
+ // recording is stopped. note the list will be cleared first.
+ // - you can request a specific duration (and use IsRecordingState() to detect
+ // the end), or UNTIL_STOP. StopRecording() can be called either way.
+ // - you can use 'change trigger' to specify specific state changes that will
+ // trigger an insert into the history (others are then ignored).
+ void RecordState (state_history &events_out,
+ unsigned max_time_ms = UNTIL_STOP,
+ state_change_flags change_trigger = CHANGED_ALL);
+ void StopRecording ();
+
+
+ private: // methods
+ // start reading asynchronously from the controller
+ bool BeginAsyncRead ();
+ // request status update (battery level, extension status etc)
+ void RequestStatusReport ();
+ // read address or register from Wiimote asynchronously (the result is
+ // parsed internally whenever it arrives)
+ bool ReadAddress (int address, short size);
+ // write a single BYTE to a wiimote address or register
+ inline void WriteData (int address, BYTE data) { WriteData(address, 1, &data); }
+ // write a BYTE array to a wiimote address or register
+ void WriteData (int address, BYTE size, const BYTE* buff);
+ // callback when data is ready to be processed
+ void OnReadData (DWORD bytes_read);
+ // parse individual reports by type
+ int ParseInput (BYTE* buff);
+ // detects if MotionPlus is attached (it doesn't report as a normal
+ // extesnion until it is enabled)
+ void DetectMotionPlusExtensionAsync ();
+ // initializes an extension when plugged in.
+ void InitializeExtension ();
+ // parses a status report
+ int ParseStatus (BYTE* buff);
+ // parses the buttons
+ int ParseButtons (BYTE* buff);
+ // parses accelerometer data
+ int ParseAccel (BYTE* buff);
+ bool EstimateOrientationFrom(wiimote_state::acceleration &accel);
+ void ApplyJoystickDeadZones (wiimote_state::joystick &joy);
+ // parses IR data from report
+ int ParseIR (BYTE* buff);
+ // parses data from an extension.
+ int ParseExtension (BYTE* buff, unsigned offset);
+ // parses data returned from a read report
+ int ParseReadAddress (BYTE* buff);
+ // reads calibration information stored on Wiimote
+ void ReadCalibration ();
+ float GetBalanceValue(short sensor, short min, short mid, short max);
+ // turns on the IR sensor (the mode must match the reporting mode caps)
+ void EnableIR (wiimote_state::ir::mode mode);
+ // disables the IR sensor
+ void DisableIR ();
+ // writes a report to the Wiimote (NULL = use 'WriteBuff')
+ bool WriteReport (BYTE* buff);
+ bool StartSampleThread ();
+ // returns the rumble BYTE that needs to be sent with reports.
+ inline BYTE GetRumbleBit () const { return Internal.bRumble? 0x01 : 0x00; }
+
+ // static thread funcs:
+ static unsigned __stdcall ReadParseThreadfunc (void* param);
+ static unsigned __stdcall AsyncRumbleThreadfunc (void* param);
+ static unsigned __stdcall SampleStreamThreadfunc(void* param);
+ static unsigned __stdcall HIDwriteThreadfunc (void* param);
+
+ private: // data
+ // wiimote output comands
+ enum output_report
+ {
+ OUT_NONE = 0x00,
+ OUT_LEDs = 0x11,
+ OUT_TYPE = 0x12,
+ OUT_IR = 0x13,
+ OUT_SPEAKER_ENABLE = 0x14,
+ OUT_STATUS = 0x15,
+ OUT_WRITEMEMORY = 0x16,
+ OUT_READMEMORY = 0x17,
+ OUT_SPEAKER_DATA = 0x18,
+ OUT_SPEAKER_MUTE = 0x19,
+ OUT_IR2 = 0x1a,
+ };
+ // input reports used only internally:
+ static const int IN_STATUS = 0x20;
+ static const int IN_READADDRESS = 0x21;
+ // wiimote device IDs:
+ static const int VID = 0x057e; // 'Nintendo'
+ static const int PID = 0x0306; // 'Wiimote'
+ // we could find this out the hard way using HID, but it's 22
+ static const int REPORT_LENGTH = 22;
+ // wiimote registers
+ static const int REGISTER_CALIBRATION = 0x0016;
+ static const int REGISTER_IR = 0x4b00030;
+ static const int REGISTER_IR_SENSITIVITY_1 = 0x4b00000;
+ static const int REGISTER_IR_SENSITIVITY_2 = 0x4b0001a;
+ static const int REGISTER_IR_MODE = 0x4b00033;
+ static const int REGISTER_EXTENSION_INIT1 = 0x4a400f0;
+ static const int REGISTER_EXTENSION_INIT2 = 0x4a400fb;
+ static const int REGISTER_EXTENSION_TYPE = 0x4a400fa;
+ static const int REGISTER_EXTENSION_CALIBRATION = 0x4a40020;
+ static const int REGISTER_BALANCE_CALIBRATION = 0x4a40024;
+ static const int REGISTER_MOTIONPLUS_DETECT = 0x4a600fa;
+ static const int REGISTER_MOTIONPLUS_INIT = 0x4a600f0;
+ static const int REGISTER_MOTIONPLUS_ENABLE = 0x4a600fe;
+
+ HANDLE Handle; // read/write device handle
+ OVERLAPPED Overlapped; // for async Read/WriteFile() IO
+ HANDLE ReadParseThread; // waits for overlapped reads & parses result
+ EVENT DataRead; // signals overlapped read complete
+ bool bUseHIDwrite; // alternative write method (less efficient
+ // but required for some BT stacks (eg. MS')
+ // HidD_SetOutputReport is only supported from XP onwards, so detect &
+ // load it dynamically:
+ static HMODULE HidDLL;
+ static hidwrite_ptr _HidD_SetOutputReport;
+
+ volatile bool bStatusReceived; // for output method detection
+ volatile bool bConnectInProgress; // don't handle extensions until complete
+ volatile bool bInitInProgress; // stop regular requests until complete
+ volatile bool bEnablingMotionPlus; // for special init codepath
+ volatile bool bConnectionLost; // auto-Disconnect()s if set
+volatile int MotionPlusDetectCount; // waiting for the result
+ volatile bool bMotionPlusDetected;
+ volatile bool bMotionPlusEnabled;
+ volatile bool bMotionPlusExtension;// detected one plugged into MotionPlus
+ volatile bool bCalibrateAtRest; // as soon as the first sensor values // come in after a Connect() call.
+ static unsigned _TotalCreated;
+ static unsigned _TotalConnected;
+ input_report ReportType; // type of data the wiimote delivers
+ // read buffer
+ BYTE ReadBuff [REPORT_LENGTH];
+ // for polling: state is updated on a thread internally, and made only
+ // made public via RefreshState()
+ CRITICAL_SECTION StateLock;
+ wiimote_state Internal;
+ state_change_flags InternalChanged; // state changes since last RefreshState()
+ // periodic status report requests (for battery level and connection loss
+ // detection)
+ DWORD NextStatusTime;
+ DWORD NextMPlusDetectTime;// gap between motion plus detections
+ DWORD MPlusDetectCount; // # of detection tries in quick succesion
+ // async Hidd_WriteReport() thread
+ HANDLE HIDwriteThread;
+#ifdef USE_DYNAMIC_HIDQUEUE
+ std::queue<BYTE*> HIDwriteQueue;
+#else
+ // fixed-size queue (to eliminate glitches caused by frequent dynamic memory
+ // allocations)
+ struct hid
+ {
+ hid () : Queue(NULL), ReadIndex(0), WriteIndex(0) {}
+
+ // Increase the static queue size if you get ASSERTs signalling an
+ // overflow (too many reports queued up before being sent by the write
+ // thread). These asserts are harmless though if caused as a result of
+ // loosing the wiimote connection (eg. battery runs out, or wiimote is
+ // unpaired by holding the power button).
+ // Note: MAX_QUEUE_ENTRIES _must_ be a power-of-2, as it
+ // uses index wraparound optimisations.
+ static const unsigned MAX_QUEUE_ENTRIES = 1<<7;
+
+ inline bool IsEmpty() const { return (ReadIndex == WriteIndex); }
+
+ bool Allocate () { // allocate memory (only when needed)
+ _ASSERT(!Queue); if(Queue) return true;
+ ReadIndex = WriteIndex = 0;
+ Queue = new queue_entry[MAX_QUEUE_ENTRIES];
+ _ASSERT(Queue); return (Queue != NULL);
+ }
+ void Deallocate () {
+ if(!Queue) return;
+ delete[] Queue; Queue = NULL;
+ ReadIndex = WriteIndex = 0;
+ }
+
+ struct queue_entry
+ {
+ queue_entry() { memset(Report, 0, sizeof(Report)); }
+
+ BYTE Report [REPORT_LENGTH];
+ } *Queue;
+
+ unsigned ReadIndex, WriteIndex;
+ } HID;
+#endif
+ CRITICAL_SECTION HIDwriteQueueLock; // queue must be locked before being modified
+
+ // async rumble
+ HANDLE AsyncRumbleThread; // automatically disables rumble if requested
+ volatile DWORD AsyncRumbleTimeout;
+ // orientation estimation
+ unsigned WiimoteNearGUpdates;
+ unsigned NunchukNearGUpdates;
+ // audio
+ HANDLE SampleThread;
+ const wiimote_sample* volatile CurrentSample; // otherwise playing square wave
+ // state recording
+ struct recording
+ {
+ volatile bool bEnabled;
+ state_history *StateHistory;
+ volatile DWORD StartTimeMS;
+ volatile DWORD EndTimeMS; // can be UNTIL_STOP
+ unsigned TriggerFlags; // wiimote changes trigger a state event
+ unsigned ExtTriggerFlags;// extension changes "
+ } Recording;
+ };
+
+#endif // _WIIMOTE_H \ No newline at end of file
diff --git a/w32mote/WiiYourself/wiimote_common.h b/w32mote/WiiYourself/wiimote_common.h
new file mode 100644
index 0000000..c0fd01e
--- /dev/null
+++ b/w32mote/WiiYourself/wiimote_common.h
@@ -0,0 +1,109 @@
+// _______________________________________________________________________________
+//
+// - WiiYourself! - native C++ Wiimote library v1.15 RC
+// (c) gl.tter 2007-9 - http://gl.tter.org
+//
+// see License.txt for conditions of use. see History.txt for change log.
+// _______________________________________________________________________________
+//
+// wiimote_common.h (tab = 4 spaces)
+
+// speaker support:
+enum speaker_freq
+ {
+ // (keep in sync with FreqLookup in wiimote.cpp)
+ FREQ_NONE = 0,
+ // my PC can't keep up with these using bUseHIDwrite, so I haven't
+ // been able to tune them yet
+ FREQ_4200HZ = 1,
+ FREQ_3920HZ = 2,
+ FREQ_3640HZ = 3,
+ FREQ_3360HZ = 4,
+ // these were tuned until the square-wave was glitch-free on my remote -
+ // may not be exactly right
+ FREQ_3130HZ = 5, // +190
+ FREQ_2940HZ = 6, // +180
+ FREQ_2760HZ = 7, // +150
+ FREQ_2610HZ = 8, // +140
+ FREQ_2470HZ = 9,
+ };
+
+// wiimote_sample - holds the audio sample in the native wiimote format
+struct wiimote_sample
+ {
+ wiimote_sample() : samples(NULL), length(0), freq(FREQ_NONE) {}
+ BYTE* samples;
+ DWORD length;
+ speaker_freq freq;
+ };
+
+// flags & masks that indicate which part(s) of the wiimote state have changed
+enum state_change_flags
+ {
+ // state didn't change at all
+ NO_CHANGE = 0,
+
+ // Wiimote specific:
+ CONNECTED = 1<<0, // wiimote just connected
+ CONNECTION_LOST = 1<<1,
+ BATTERY_CHANGED = 1<<2,
+ BATTERY_DRAINED = 1<<3, // close to empty
+ LEDS_CHANGED = 1<<4, // (probably redudant as wiimmote never
+ BUTTONS_CHANGED = 1<<5, // changes them unless requested)
+ ACCEL_CHANGED = 1<<6,
+ ORIENTATION_CHANGED = 1<<7,
+ IR_CHANGED = 1<<8,
+ // all wiimote flags
+ WIIMOTE_CHANGED = CONNECTION_LOST|BATTERY_CHANGED|BATTERY_DRAINED|
+ LEDS_CHANGED|BUTTONS_CHANGED|ACCEL_CHANGED|
+ ORIENTATION_CHANGED|IR_CHANGED,
+ // - Extensions -:
+ // Nunchuk:
+ NUNCHUK_CONNECTED = 1<<9,
+ NUNCHUK_BUTTONS_CHANGED = 1<<10,
+ NUNCHUK_ACCEL_CHANGED = 1<<11,
+ NUNCHUK_ORIENTATION_CHANGED = 1<<12,
+ NUNCHUK_JOYSTICK_CHANGED = 1<<13,
+ // all flags
+ NUNCHUK_CHANGED = NUNCHUK_CONNECTED|NUNCHUK_BUTTONS_CHANGED|
+ NUNCHUK_ACCEL_CHANGED|NUNCHUK_ORIENTATION_CHANGED|
+ NUNCHUK_JOYSTICK_CHANGED,
+ // Classic Controller (inc. Guitars etc):
+ CLASSIC_CONNECTED = 1<<14,
+ CLASSIC_BUTTONS_CHANGED = 1<<15,
+ CLASSIC_JOYSTICK_L_CHANGED = 1<<16,
+ CLASSIC_JOYSTICK_R_CHANGED = 1<<17,
+ CLASSIC_TRIGGERS_CHANGED = 1<<18,
+ // all flags
+ CLASSIC_CHANGED = CLASSIC_CONNECTED|CLASSIC_BUTTONS_CHANGED|
+ CLASSIC_JOYSTICK_L_CHANGED|
+ CLASSIC_JOYSTICK_R_CHANGED|
+ CLASSIC_TRIGGERS_CHANGED,
+ // Balance Board:
+ BALANCE_CONNECTED = 1<<19,
+ BALANCE_WEIGHT_CHANGED = 1<<20,
+ // all flags
+ BALANCE_CHANGED = BALANCE_CONNECTED|BALANCE_WEIGHT_CHANGED,
+
+ // Motion Plus
+ MOTIONPLUS_DETECTED = 1<<21, // attached but not enabled
+ MOTIONPLUS_ENABLED = 1<<22,
+ MOTIONPLUS_SPEED_CHANGED = 1<<23,
+ MOTIONPLUS_EXTENSION_CONNECTED = 1<<24, // an extension is found in the
+ // MotionPlus port
+ MOTIONPLUS_EXTENSION_DISCONNECTED = 1<<25, // it was disconnected
+ // all flags
+ MOTIONPLUS_CHANGED = MOTIONPLUS_DETECTED|MOTIONPLUS_ENABLED|
+ MOTIONPLUS_SPEED_CHANGED|
+ MOTIONPLUS_EXTENSION_CONNECTED|
+ MOTIONPLUS_EXTENSION_DISCONNECTED,
+ // General:
+ EXTENSION_DISCONNECTED = 1<<26,
+ EXTENSION_PARTIALLY_INSERTED = 1<<27,
+ EXTENSION_CONNECTED = NUNCHUK_CONNECTED|CLASSIC_CONNECTED|
+ BALANCE_CONNECTED|MOTIONPLUS_ENABLED,
+ EXTENSION_CHANGED = EXTENSION_DISCONNECTED|NUNCHUK_CHANGED|
+ CLASSIC_CHANGED|BALANCE_CHANGED|MOTIONPLUS_CHANGED,
+ // ALL flags:
+ CHANGED_ALL = WIIMOTE_CHANGED|EXTENSION_CHANGED,
+ };
diff --git a/w32mote/WiiYourself/wiimote_state.h b/w32mote/WiiYourself/wiimote_state.h
new file mode 100644
index 0000000..1bf167a
--- /dev/null
+++ b/w32mote/WiiYourself/wiimote_state.h
@@ -0,0 +1,379 @@
+// _______________________________________________________________________________
+//
+// - WiiYourself! - native C++ Wiimote library v1.15
+// (c) gl.tter 2007-10 - http://gl.tter.org
+//
+// see License.txt for conditions of use. see History.txt for change log.
+// _______________________________________________________________________________
+//
+// wiimote_state.h (tab = 4 spaces)
+
+// the 'wiimote_state' struct contains all the Wiimote and Extension state data
+// (buttons etc) - the wiimote class inherits from this and the app can poll
+// the data there at any time.
+#ifdef _MSC_VER // VC
+# pragma once
+#endif
+
+#ifndef _WIIMOTE_STATE_H
+# define _WIIMOTE_STATE_H
+
+#include "wiimote_common.h"
+
+
+// wiimote_state (contains the Wiimote and Extension data and settings)
+struct wiimote_state
+ {
+ friend class wiimote; // for Clear()
+
+ // calibration information (stored on the Wiimote)
+ struct calibration_info
+ {
+ BYTE X0, Y0, Z0;
+ BYTE XG, YG, ZG;
+ } CalibrationInfo;
+
+ // button state:
+ struct buttons
+ {
+ // convenience accessors
+ inline bool A () const { return (Bits & _A) != 0; }
+ inline bool B () const { return (Bits & _B) != 0; }
+ inline bool Plus () const { return (Bits & PLUS) != 0; }
+ inline bool Home () const { return (Bits & HOME) != 0; }
+ inline bool Minus () const { return (Bits & MINUS) != 0; }
+ inline bool One () const { return (Bits & ONE) != 0; }
+ inline bool Two () const { return (Bits & TWO) != 0; }
+ inline bool Up () const { return (Bits & UP) != 0; }
+ inline bool Down () const { return (Bits & DOWN) != 0; }
+ inline bool Left () const { return (Bits & LEFT) != 0; }
+ inline bool Right () const { return (Bits & RIGHT) != 0; }
+
+ // all 11 buttons stored as bits (set = pressed)
+ WORD Bits;
+
+ // button bit masks (little-endian order)
+ enum mask
+ {
+ LEFT = 0x0001,
+ RIGHT = 0x0002,
+ DOWN = 0x0004,
+ UP = 0x0008,
+ PLUS = 0x0010,
+ TWO = 0x0100,
+ ONE = 0x0200,
+ _B = 0x0400, // ie. trigger
+ _A = 0x0800,
+ MINUS = 0x1000,
+ HOME = 0x8000,
+ //
+ ALL = LEFT|RIGHT|DOWN|UP|PLUS|TWO|ONE|_A|_B|MINUS|HOME,
+ };
+ } Button;
+
+ // accelerometers state:
+ struct acceleration
+ {
+ BYTE RawX, RawY, RawZ;
+ float X, Y, Z;
+
+ // note: experimental! the orientation values can only be safely estimated
+ // if the controller isn't accelerating (otherwise there is no
+ // simple way to seperate orientation from acceleration - except
+ // perhaps using the IR reference and/or some clever assumptions).
+ // so for now the code only updates orientation if the controller
+ // appear to be stationary (by checking if the acceleration vector
+ // length is near 1G for several updates in a row).
+ // also note that there is no way to detect Yaw from the accelerometer
+ // alone when it's pointing at the screen (and I'm not curently
+ // processing IR):
+ struct orientation
+ {
+ float X, Y, Z;
+ unsigned UpdateAge; // how many acceleration updates ago the last
+ // orientation estimate was made (if this
+ // value is high, the values are out-of-date
+ // and probably shouldn't be used).
+ // Euler angle support (useful for some things).
+ // * note that decomposing to Euler angles is complex, not always reliable,
+ // and also depends on your assumptions about the order each component
+ // is applied in. you may need to handle this yourself for more
+ // complex scenarios *
+ float Pitch; // in degrees (-180 - +180)
+ float Roll; // "
+ // float Yaw;
+ } Orientation;
+ } Acceleration;
+
+ // IR camera state:
+ struct ir
+ {
+ // in theory the IR imager is 1024x768 and so should report raw coords
+ // 0-1023 x 0-767. in practice I have never seen them exceed the values
+ // below, so I'm using them instead to give the full 0-1 float range
+ // (it's possible that the edge pixels are used for processing, or masked
+ // out due to being unreliable). let me know if your wiimote reports
+ // a different range.
+ static const unsigned MAX_RAW_X = 1016;
+ static const unsigned MAX_RAW_Y = 760;
+
+ // data mode reported by the IR sensor
+ enum mode
+ {
+ OFF = 0x00,
+ BASIC = 0x01, // 10 bytes
+ EXTENDED = 0x03, // 12 bytes
+ FULL = 0x05, // 16 bytes * 2 (format unknown)
+ };
+
+ mode Mode; // read-only (depends on ReportType set)
+
+ struct dot
+ {
+ bool bVisible; // other values are not valid if == false
+ unsigned RawX;
+ unsigned RawY;
+ float X; // 0-1 (left-right)
+ float Y; // " (top -bottom)
+ int Size; // (not available in BASIC mode)
+ } Dot[4];
+ } IR;
+
+ struct leds
+ {
+ // all LEDs stored in bits 0-3 (1 = lit)
+ BYTE Bits;
+
+ // convenience accessors:
+ inline bool Lit (unsigned index)
+ { _ASSERT(index < 4);
+ return (index >= 4)? false : ((Bits & (1<<index)) != 0); }
+ } LED;
+
+ BYTE BatteryRaw; // 0 - ~200 (it seems 200 *may* be the maximum charge)
+ BYTE BatteryPercent; // (using the above assumption, where 200 raw = 100%)
+ bool bBatteryDrained; // battery is nearly flat
+ bool bRumble;
+ bool bExtension; // an extension (eg. Nunchuk) is connected.
+
+ // speaker state:
+ struct speaker
+ {
+ bool bEnabled;
+ bool bMuted;
+ speaker_freq Freq;
+ BYTE Volume;
+ } Speaker;
+
+ // the extension plugged into the Wiimote (if any)
+ enum extension_type
+ {
+ NONE = 0,
+ NUNCHUK,
+ CLASSIC,
+ GH3_GHWT_GUITAR, // GH3 or GHWT Guitar (treated as Classic)
+ GHWT_DRUMS, // not yet used
+ BALANCE_BOARD,
+ MOTION_PLUS,
+ PARTIALLY_INSERTED,
+ };
+ extension_type ExtensionType;
+
+ // joystick struct (shared between Nunchuk & Classic Controller)
+ struct joystick
+ {
+ friend class wiimote;
+
+ // raw unprocessed coordinates:
+ float RawX, RawY;
+
+ // processed coordinates in approximately -1 - +1 range (includes calibration
+ // data and deadzones) - note that due to calibration inaccuracies, the
+ // extremes may be slightly over/under (+-)1.0.
+ float X, Y;
+
+ // a 'deadzone' is a user-defined range near the joystick center which
+ // is treated as zero (joysticks often drift a little even at the center
+ // position). you can set a deadzone for each axis at any time, range is
+ // 0.0 (off) to 1.0 (entire range - not useful :). try 0.03.
+ struct deadzone
+ {
+ float X, Y;
+ } DeadZone;
+ };
+
+ // Nunchuk state (if connected)
+ struct nunchuk
+ {
+ struct calibration_info
+ {
+ BYTE X0, Y0, Z0;
+ BYTE XG, YG, ZG;
+ BYTE MinX, MidX, MaxX;
+ BYTE MinY, MidY, MaxY;
+ } CalibrationInfo;
+
+ acceleration Acceleration;
+ joystick Joystick;
+ bool C;
+ bool Z;
+ } Nunchuk;
+
+ // Classic Controller state (if connected)
+ struct classic_controller
+ {
+ // calibration information (stored on the controller)
+ struct calibration_info
+ {
+ BYTE MinXL, MidXL, MaxXL;
+ BYTE MinYL, MidYL, MaxYL;
+ BYTE MinXR, MidXR, MaxXR;
+ BYTE MinYR, MidYR, MaxYR;
+ BYTE MinTriggerL, MaxTriggerL;
+ BYTE MinTriggerR, MaxTriggerR;
+ } CalibrationInfo;
+
+ // button state
+ struct buttons
+ {
+ // convenience accessors
+ inline bool A () const { return (Bits & _A) != 0; }
+ inline bool B () const { return (Bits & _B) != 0; }
+ inline bool Plus () const { return (Bits & PLUS) != 0; }
+ inline bool Minus () const { return (Bits & MINUS) != 0; }
+ inline bool Home () const { return (Bits & HOME) != 0; }
+ inline bool Up () const { return (Bits & UP) != 0; }
+ inline bool Down () const { return (Bits & DOWN) != 0; }
+ inline bool Left () const { return (Bits & LEFT) != 0; }
+ inline bool Right () const { return (Bits & RIGHT) != 0; }
+ inline bool X () const { return (Bits & _X) != 0; }
+ inline bool Y () const { return (Bits & _Y) != 0; }
+ inline bool ZL () const { return (Bits & _ZL) != 0; }
+ inline bool ZR () const { return (Bits & _ZR) != 0; }
+ inline bool TriggerL () const { return (Bits & TRIG_L) != 0; }
+ inline bool TriggerR () const { return (Bits & TRIG_R) != 0; }
+
+ // all 15 buttons stored as bits (set = pressed)
+ WORD Bits;
+
+ // button bitmasks (little-endian order)
+ enum mask
+ {
+ TRIG_R = 0x0002,
+ PLUS = 0x0004,
+ HOME = 0x0008,
+ MINUS = 0x0010,
+ TRIG_L = 0x0020,
+ DOWN = 0x0040,
+ RIGHT = 0x0080,
+ UP = 0x0100,
+ LEFT = 0x0200,
+ _ZR = 0x0400,
+ _X = 0x0800,
+ _A = 0x1000,
+ _Y = 0x2000,
+ _B = 0x4000,
+ _ZL = 0x8000,
+ //
+ ALL = TRIG_R|PLUS|HOME|MINUS|TRIG_L|DOWN|RIGHT|UP|LEFT|
+ _ZR|_X|_A|_Y|_B|_ZL,
+ };
+ } Button;
+
+ // joysticks
+ joystick JoystickL;
+ joystick JoystickR;
+
+ // triggers
+ BYTE RawTriggerL, RawTriggerR;
+ float TriggerL, TriggerR;
+ } ClassicController;
+
+ struct balance_board
+ {
+ // values for each of the board's 4 sensors:
+ // (these values are always exposed unmodifed)
+ struct sensors_raw
+ {
+ short TopR;
+ short TopL;
+ short BottomR;
+ short BottomL;
+ };
+ struct sensors_f
+ {
+ float TopL;
+ float TopR;
+ float BottomL;
+ float BottomR;
+
+ float Total; // sum of the 4 corner weights
+ };
+
+ // calibration info
+ struct calibration_info
+ {
+ sensors_raw Kg0; // calibration at 0 Kg
+ sensors_raw Kg17; // " 17 Kg
+ sensors_raw Kg34; // " 34 Kg
+ } CalibrationInfo;
+
+ // state:
+ sensors_raw Raw; // raw values (per sensor)
+ sensors_f AtRestKg; // set by Connect() and CalibrateAtRest()
+ // (the values below have their 'at-rest' offsets automatically removed)
+ sensors_f Kg; // kilograms (per sensor)
+ sensors_f Lb; // pounds (per sensor)
+ } BalanceBoard;
+
+ struct motion_plus
+ {
+ // (these values are always exposed unmodifed)
+ struct sensors_raw
+ {
+ short Yaw;
+ short Pitch;
+ short Roll;
+ };
+ struct sensors_f
+ {
+ float Yaw;
+ float Pitch;
+ float Roll;
+ };
+
+ // state:
+ sensors_raw Raw;
+ sensors_f Speed;
+ } MotionPlus;
+
+ // ---- internal use only ----
+ protected:
+ unsigned WiimoteNearGUpdates;
+ unsigned NunchukNearGUpdates;
+
+ void Clear (bool including_deadzones)
+ {
+ joystick::deadzone nunchuk_deadzone,
+ classic_joyl_deadzone,
+ classic_joyr_deadzone;
+
+ // preserve the deadzone settings?
+ if(!including_deadzones) {
+ nunchuk_deadzone = Nunchuk.Joystick.DeadZone;
+ classic_joyl_deadzone = ClassicController.JoystickL.DeadZone;
+ classic_joyr_deadzone = ClassicController.JoystickR.DeadZone;
+ }
+
+ memset(this, 0, sizeof(wiimote_state));
+
+ // restore the deadzones?
+ if(!including_deadzones) {
+ Nunchuk.Joystick.DeadZone = nunchuk_deadzone;
+ ClassicController.JoystickL.DeadZone = classic_joyl_deadzone;
+ ClassicController.JoystickR.DeadZone = classic_joyr_deadzone;
+ }
+ }
+ };
+
+#endif // _WIIMOTE_STATE_H \ No newline at end of file
diff --git a/w32mote/wiimote-test.pd b/w32mote/wiimote-test.pd
new file mode 100644
index 0000000..940a2f8
--- /dev/null
+++ b/w32mote/wiimote-test.pd
@@ -0,0 +1,48 @@
+#N canvas 168 53 722 577 12;
+#X obj 72 435 wiimote;
+#X obj 367 187 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
+1;
+#X msg 361 217 reportIR \$1;
+#X msg 10 186 discover;
+#X obj 162 470 print state;
+#X obj 71 471 print data;
+#X msg 65 327 disconnect;
+#X msg 247 125 setLED \$1;
+#X obj 235 100 hradio 15 1 0 8 empty empty empty 0 -8 0 10 -262144
+-1 -1 0;
+#X obj 246 47 metro 100;
+#X obj 246 72 random 32;
+#X obj 249 20 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
+1;
+#X msg 517 458 setRumble \$1;
+#X obj 519 436 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
+1;
+#X msg 371 336 play foo;
+#X msg 365 286 reportAcceleration \$1;
+#X obj 370 264 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
+1;
+#X obj 508 85 osc~ 440;
+#X obj 508 110 tabwrite~ foo;
+#X msg 595 78 bang;
+#X obj 35 29 table foo 4410;
+#X floatatom 514 51 5 0 0 0 - - -;
+#X text 446 334 spielt die table "foo" (sound);
+#X connect 0 0 5 0;
+#X connect 0 1 4 0;
+#X connect 1 0 2 0;
+#X connect 2 0 0 0;
+#X connect 3 0 0 0;
+#X connect 6 0 0 0;
+#X connect 7 0 0 0;
+#X connect 8 0 7 0;
+#X connect 9 0 10 0;
+#X connect 10 0 7 0;
+#X connect 11 0 9 0;
+#X connect 12 0 0 0;
+#X connect 13 0 12 0;
+#X connect 14 0 0 0;
+#X connect 15 0 0 0;
+#X connect 16 0 15 0;
+#X connect 17 0 18 0;
+#X connect 19 0 18 0;
+#X connect 21 0 17 0;
diff --git a/w32mote/wiimote4pd.cpp b/w32mote/wiimote4pd.cpp
new file mode 100644
index 0000000..ea089c4
--- /dev/null
+++ b/w32mote/wiimote4pd.cpp
@@ -0,0 +1,525 @@
+#ifdef _WIN32
+# define MSW
+// linker->befehlsausgabe: zusätzliche flags: "/export:wiimote_setup"
+#endif
+
+
+// TODO:
+// use RefreshState(): copy internal state to actual instance
+
+#ifdef _MSC_VER
+# pragma warning(disable: 4091)
+# define WIIEXTERN __declspec(dllexport) extern
+#else
+# define WIIEXTERN extern
+#endif
+
+#include <m_pd.h>
+#include "wiimote.h"
+
+#include <map>
+
+// class and struct declarations for wiimote pd external:
+class wiimote4pd : public wiimote {
+public:
+private:
+ t_object*m_obj;
+ t_outlet *m_dataOut;
+ t_outlet *m_statusOut;
+
+ t_clock *m_clock;
+
+ bool m_changed;
+ typedef std::pair<std::string, std::vector<t_float> > datavec;
+ std::vector < datavec> m_data;
+
+ bool m_reportAccel, m_reportIR, m_reportNunchuk, m_reportClassic, m_reportBalance, m_reportMPlus, m_reportExt;
+
+ bool m_connected;
+
+ CRITICAL_SECTION DataLock;
+
+public:
+ wiimote4pd(t_object*obj, t_outlet*dOut, t_outlet*sOut) : wiimote(),
+ m_obj(obj),
+ m_dataOut(dOut), m_statusOut(sOut),
+ m_clock(NULL),
+ m_changed(NO_CHANGE),
+ m_reportAccel(false), m_reportIR(false),
+ m_reportNunchuk(false), m_reportClassic(false), m_reportBalance(false),
+ m_reportMPlus(false),
+ m_reportExt(false),
+ m_connected(false)
+ {
+ InitializeCriticalSection(&DataLock);
+ m_clock = clock_new(this, (t_method)wiimote_tickCallback);
+ }
+ virtual ~wiimote4pd(void) {
+ clock_free(m_clock);
+ DeleteCriticalSection(&DataLock);
+ }
+
+ static void wiimote_tickCallback(wiimote4pd*x) {
+ x->tick();
+ }
+
+ void error(std::string s) {
+ if(m_obj)
+ pd_error(m_obj, "%s", s.c_str());
+ else
+ ::error("[wiimote] %s", s.c_str());
+ }
+
+ void state(void) {
+ t_atom atom;
+ SETFLOAT(&atom, m_connected);
+ outlet_anything(m_statusOut, gensym("open"), 1, &atom);
+ };
+ void state(bool connected) {
+ m_connected=connected;
+ state();
+ };
+
+ void data(const std::string&id, std::vector<t_float>&d) {
+ const unsigned int len=d.size();
+ t_atom*atoms=new t_atom[len];
+ unsigned int i=0;
+ for(i=0; i<len; i++) {
+ SETFLOAT(atoms+i, d[i]);
+ }
+ outlet_anything(m_dataOut, gensym(id.c_str()), len, atoms);
+ delete[]atoms;
+ };
+
+ void data(const std::string id, t_float f) {
+ t_atom atom;
+ SETFLOAT(&atom, f);
+ outlet_anything(m_dataOut, gensym(id.c_str()), 1, &atom);
+ }
+ void data(const std::string id, t_float f0, t_float f1) {
+ t_atom atoms[2];
+ SETFLOAT(atoms+0, f0);
+ SETFLOAT(atoms+1, f1);
+ outlet_anything(m_dataOut, gensym(id.c_str()), 2, atoms);
+ }
+ void data(const std::string id, t_float f0, t_float f1, t_float f2) {
+ t_atom atoms[3];
+ SETFLOAT(atoms+0, f0);
+ SETFLOAT(atoms+1, f1);
+ SETFLOAT(atoms+2, f2);
+ outlet_anything(m_dataOut, gensym(id.c_str()), 3, atoms);
+ }
+
+
+ void Disconnect(void) {
+ wiimote::Disconnect();
+ state(false);
+ }
+
+ void Connect(unsigned wiimote_index = FIRST_AVAILABLE) {
+ m_connected=IsConnected();
+ if(m_connected) {
+ error("already connected to WiiMote");
+ state();
+ return;
+ }
+ post("discovering WiiMote");
+ m_connected=wiimote::Connect( wiimote_index );
+ if(!m_connected) {
+ error("could not find any wiimotes. Please ensure that your WiiMote is associated with your system");
+ }
+ post("total connected WiiMotes: %d", TotalConnected());
+ state();
+ }
+
+ void updateReport(void) {
+#if 0
+ m_reportAccel(false), m_reportIR(false),
+ m_reportNunchuk(false), m_reportClassic(false), m_reportBalance(false), m_reportMPlus(false), m_reportExt(false)
+
+ IN_BUTTONS = 0x30,
+ IN_BUTTONS_ACCEL = 0x31, 001
+ IN_BUTTONS_ACCEL_IR = 0x33, 011 // reports IR EXTENDED data (dot sizes)
+ IN_BUTTONS_ACCEL_EXT = 0x35, 101
+ IN_BUTTONS_ACCEL_IR_EXT = 0x37, 111 // reports IR BASIC data (no dot sizes)
+ IN_BUTTONS_BALANCE_BOARD = 0x32, 010 // must use this for the balance board
+#endif
+
+ input_report reportmode=wiimote::IN_BUTTONS;
+
+ if(m_reportNunchuk || m_reportClassic || m_reportMPlus || m_reportExt || m_reportBalance) {
+ error("Extension reporting currently not supported");
+ }
+ if(m_reportIR && !m_reportAccel) {
+ post("cannot report IR without acceleration");
+ }
+ if(m_reportAccel && m_reportIR)
+ reportmode=wiimote::IN_BUTTONS_ACCEL_IR_EXT;
+ else if(m_reportAccel)
+ reportmode=wiimote::IN_BUTTONS_ACCEL;
+
+
+ SetReportType(reportmode);
+ }
+
+ void report(const std::string id, bool state) {
+ if(!id.compare("acceleration"))m_reportAccel=state;
+ else if(!id.compare("acceleration"))m_reportAccel=state;
+ else if(!id.compare("ir"))m_reportIR=state;
+ else if(!id.compare("nunchuk"))m_reportNunchuk=state;
+ else if(!id.compare("classic"))m_reportClassic=state;
+ else if(!id.compare("balance"))m_reportBalance=state;
+ else if(!id.compare("motionplus"))m_reportMPlus=state;
+ else if(!id.compare("ext"))m_reportExt=state;
+ //else if(!id.compare("state"))m_reportState=state;
+ //else if(!id.compare("button"))m_reportButton=state;
+ else {
+ std::string err="unknown report mode '";
+ err+=id;
+ err+="'";
+ error(err);
+ }
+ updateReport();
+ }
+
+
+ inline void lock(void) {
+ EnterCriticalSection(&DataLock);
+ }
+ inline void unlock(void) {
+ LeaveCriticalSection(&DataLock);
+ }
+
+ virtual void ChangedNotifier (state_change_flags changed,
+ const wiimote_state &new_state) {
+#define ADDvMSG(VEC, id) do {datavec dv; dv.first=id; dv.second=v; VEC.push_back(dv); v.clear();} while(0)
+
+ // merge the new changed set with the cached one
+ lock();
+ bool pristine=(m_changed==false);
+ m_changed = true;
+ std::vector<t_float>v;
+ // Wiimote specific) {
+ if(changed & CONNECTION_LOST) {
+ v.push_back(0);
+ ADDvMSG(m_data, "state");
+ } if(changed & CONNECTED) {
+ v.push_back(1);
+ ADDvMSG(m_data, "state");
+ } if(changed & BATTERY_CHANGED || changed & BATTERY_DRAINED) {
+ v.push_back(new_state.BatteryPercent * 0.01f);
+ ADDvMSG(m_data, "battery");
+ } if(changed & BUTTONS_CHANGED) {
+ unsigned char but1 = (new_state.Button.Bits & 0xFF00)>>8;
+ unsigned char but2 = (new_state.Button.Bits & 0x00FF);
+ v.push_back(but2);
+ v.push_back(but1);
+ ADDvMSG(m_data, "button");
+ } if(changed & ACCEL_CHANGED) {
+ v.push_back(new_state.Acceleration.X);
+ v.push_back(new_state.Acceleration.Y);
+ v.push_back(new_state.Acceleration.Z);
+ ADDvMSG(m_data, "acceleration");
+#if 0
+ } if(changed & ORIENTATION_CHANGED) {
+ // this is a cooked version of ACCEL
+ } if(changed & LEDS_CHANGED) {
+ // they won't change on their own, would they?
+#endif
+ } if(changed & IR_CHANGED) {
+ unsigned int i;
+ for(i=0; i<4; i++) {
+ const wiimote_state::ir::dot &dot = new_state.IR.Dot[i];
+ if(dot.bVisible) {
+ v.push_back(i);
+ v.push_back(dot.RawX);
+ v.push_back(dot.RawY);
+ v.push_back(dot.Size);
+ ADDvMSG(m_data, "ir");
+ }
+ }
+ // - Extensions -
+ // Nunchuk
+#if 0
+ } if(changed & NUNCHUK_CONNECTED) {
+
+ } if(changed & NUNCHUK_BUTTONS_CHANGED) {
+ } if(changed & NUNCHUK_ACCEL_CHANGED) {
+ } if(changed & NUNCHUK_ORIENTATION_CHANGED) {
+ } if(changed & NUNCHUK_JOYSTICK_CHANGED) {
+
+ // Classic Controller (inc. Guitars etc)
+ } if(changed & CLASSIC_CONNECTED) {
+ } if(changed & CLASSIC_BUTTONS_CHANGED) {
+ } if(changed & CLASSIC_JOYSTICK_L_CHANGED) {
+ } if(changed & CLASSIC_JOYSTICK_R_CHANGED) {
+ } if(changed & CLASSIC_TRIGGERS_CHANGED) {
+
+ // Balance Board
+ } if(changed & BALANCE_CONNECTED) {
+ } if(changed & BALANCE_WEIGHT_CHANGED) {
+
+ // Motion Plus
+ } if(changed & MOTIONPLUS_DETECTED) {
+ } if(changed & MOTIONPLUS_ENABLED) {
+ } if(changed & MOTIONPLUS_SPEED_CHANGED) {
+ } if(changed & MOTIONPLUS_EXTENSION_CONNECTED) {
+ } if(changed & MOTIONPLUS_EXTENSION_DISCONNECTED) {
+#endif
+ }
+ unlock();
+ if(pristine) {
+ sys_lock();
+ clock_delay(m_clock, 0);
+ sys_unlock();
+ }
+ }
+
+ void tick(void) {
+ lock();
+ m_changed=false;
+ unsigned int i;
+ for(i=0; i<m_data.size(); i++) {
+ datavec dv=m_data[i];
+ data(dv.first, dv.second);
+ }
+ m_data.clear();
+ unlock();
+
+ }
+
+ bool array2sample(const std::string&arrayname, wiimote_sample&result, const speaker_freq freq=FREQ_3130HZ) {
+ t_garray *a = (t_garray *)pd_findbyclass(gensym(arrayname.c_str()), garray_class);
+ if(!a) {
+ std::string err="no such array";
+ err+=": '";
+ err+=arrayname;
+ err+="'";
+ error(err);
+ return false;
+ }
+ int length=0;
+ t_word *vec=NULL;
+ if(!garray_getfloatwords(a, &length, &vec)) {
+ std::string err="bad template for array";
+ err+=": '";
+ err+=arrayname;
+ err+="'";
+ error(err);
+ return false;
+ }
+ if(length<1 || NULL==vec) return false;
+ signed short *samples=new signed short[length];
+ int i;
+ for(i=0; i<length; i++) {
+ signed short s=static_cast<signed short>(32768.*vec[i].w_float);
+ samples[i]=s;
+ }
+ bool res=Convert16bitMonoSamples (samples, true, length, freq, result);
+ delete[]samples;
+ return res;
+ }
+
+ wiimote_sample m_sample;
+ void play(const std::string&arrayname) {
+ bool res=array2sample(arrayname, m_sample);
+ res=PlaySample(m_sample);
+ }
+
+
+};
+static t_class *wiimote_class;
+typedef struct _wiimote
+{
+ t_object x_obj; // standard pd object (must be first in struct)
+ wiimote4pd*x_wiimote;
+ t_outlet*x_dataOut,*x_statusOut;
+} t_wiimote;
+
+static void wiimote_ctor(t_wiimote*x){
+ if(!x->x_wiimote) {
+ try {
+ x->x_wiimote=new wiimote4pd(&x->x_obj, x->x_dataOut, x->x_statusOut);
+ } catch (int fourtytwo) {
+ error("ouch! wiimote allocation failed fatally");
+ }
+ }
+}
+
+static void wiimote_dtor(t_wiimote*x){
+ if(x->x_wiimote) {
+ delete x->x_wiimote;
+ }
+}
+
+
+static void wiimote_report(t_wiimote*x, t_symbol*s, t_float f)
+{
+ if(!x->x_wiimote)return;
+ x->x_wiimote->report(s->s_name, f>=0.5);
+#if 0
+ int flag=-1;
+ if(gensym("status")==s) flag=CWIID_RPT_STATUS;
+ else if(gensym("button")==s) flag=CWIID_RPT_BTN;
+ else if(gensym("acceleration")==s) flag=CWIID_RPT_ACC;
+ else if(gensym("ir")==s) flag=CWIID_RPT_IR;
+ else if(gensym("nunchuk")==s) flag=CWIID_RPT_NUNCHUK;
+ else if(gensym("classic")==s) flag=CWIID_RPT_CLASSIC;
+ else if(gensym("balance")==s) flag=CWIID_RPT_BALANCE;
+ else if(gensym("motionplus")==s) flag=CWIID_RPT_MOTIONPLUS;
+ else if(gensym("ext")==s) flag=CWIID_RPT_EXT;
+ else {
+ pd_error(x, "unknown report mode '%s'", s->s_name);
+ }
+
+ if(flag!=-1) {
+ if(onoff) {
+ x->reportMode |= flag;
+ } else {
+ x->reportMode &= ~flag;
+ }
+ }
+ wiimote_resetReportMode(x);
+#endif
+}
+static void wiimote_reportAcceleration(t_wiimote *x, t_floatarg f)
+{
+ if(!x->x_wiimote)return;
+ x->x_wiimote->report("acceleration", f>=0.5);
+}
+static void wiimote_reportIR(t_wiimote *x, t_floatarg f)
+{
+ if(!x->x_wiimote)return;
+ x->x_wiimote->report("ir", f>=0.5);
+}
+static void wiimote_reportNunchuk(t_wiimote *x, t_floatarg f)
+{
+ if(!x->x_wiimote)return;
+ x->x_wiimote->report("nunchuk", f>=0.5);
+}
+static void wiimote_reportMotionplus(t_wiimote *x, t_floatarg f)
+{
+ if(!x->x_wiimote)return;
+ x->x_wiimote->report("motionplus", f>=0.5);
+}
+static void wiimote_setReportMode(t_wiimote*x, t_floatarg r) {
+ if(!x->x_wiimote)return;
+ x->x_wiimote->error("setReportMode not implemented");
+}
+static void wiimote_setLED(t_wiimote *x, t_floatarg f)
+{
+ if(!x->x_wiimote)return;
+ x->x_wiimote->SetLEDs(static_cast<unsigned char>(f));
+}
+static void wiimote_setRumble(t_wiimote *x, t_floatarg f)
+{
+ if(!x->x_wiimote)return;
+ x->x_wiimote->SetRumble(f > 0.5f);
+}
+// ==============================================================
+
+// The following function attempts to discover a wiimote. It requires
+// that the user puts the wiimote into 'discoverable' mode before being
+// called. This is done by pressing the red button under the battery
+// cover, or by pressing buttons 1 and 2 simultaneously.
+// TODO: Without pressing the buttons, I get a segmentation error. So far, I don't know why.
+
+static void wiimote_discover(t_wiimote *x)
+{
+ wiimote_ctor(x);
+ x->x_wiimote->Connect();
+}
+
+static void wiimote_disconnect(t_wiimote *x)
+{
+ if(!x->x_wiimote)return;
+ x->x_wiimote->Disconnect();
+ delete x->x_wiimote;x->x_wiimote=NULL;
+}
+
+
+static void wiimote_play(t_wiimote* x, t_symbol*s) {
+ if(!x->x_wiimote)return;
+ x->x_wiimote->play(s->s_name);
+}
+
+static void wiimote_bang(t_wiimote *x)
+{
+ if(!x->x_wiimote)return;
+ x->x_wiimote->tick();
+}
+
+// ==============================================================
+// ==============================================================
+static void *wiimote_new(t_symbol*s, int argc, t_atom *argv)
+{
+ t_wiimote *x = NULL;
+ x=(t_wiimote *)pd_new(wiimote_class);
+
+ x->x_wiimote = NULL;
+ x->x_dataOut =outlet_new(&x->x_obj, 0);
+ x->x_statusOut=outlet_new(&x->x_obj, 0);
+
+ wiimote_ctor(x);
+
+ return (x);
+}
+
+static void wiimote_free(t_wiimote* x)
+{
+ delete x->x_wiimote;
+ x->x_wiimote=NULL;
+ outlet_free(x->x_dataOut ); x->x_dataOut = NULL;
+ outlet_free(x->x_statusOut); x->x_statusOut= NULL;
+
+}
+
+extern "C" {
+ WIIEXTERN void wiimote_setup(void) {
+ wiimote_class = class_new(gensym("wiimote"), (t_newmethod)wiimote_new, (t_method)wiimote_free, sizeof(t_wiimote), CLASS_DEFAULT, A_GIMME, 0);
+#if 0
+ class_addmethod(wiimote_class, (t_method) wiimote_debug, gensym("debug"), 0);
+ class_addmethod(wiimote_class, (t_method) wiimote_status, gensym("status"), 0);
+
+
+ /* connection settings */
+ // class_addmethod(wiimote_class, (t_method) wiimote_doConnect, gensym("connect"), A_DEFSYMBOL, A_DEFSYMBOL, 0);
+
+ /* query data */
+ //...
+#endif
+ class_addbang(wiimote_class, (t_method) wiimote_bang);
+
+ /* connection handling */
+ class_addmethod(wiimote_class, (t_method) wiimote_disconnect, gensym("disconnect"), A_NULL);
+ class_addmethod(wiimote_class, (t_method) wiimote_discover, gensym("discover"), A_NULL);
+
+ /* activate WiiMote stuff */
+ class_addmethod(wiimote_class, (t_method) wiimote_setLED, gensym("setLED"), A_FLOAT, 0);
+ class_addmethod(wiimote_class, (t_method) wiimote_setRumble, gensym("setRumble"), A_FLOAT, 0);
+
+ /* report modes */
+ class_addmethod(wiimote_class, (t_method) wiimote_report, gensym("report"), A_SYMBOL, A_FLOAT, 0);
+
+ /* legacy report modes */
+ class_addmethod(wiimote_class, (t_method) wiimote_setReportMode, gensym("setReportMode"), A_FLOAT, 0);
+ class_addmethod(wiimote_class, (t_method) wiimote_reportAcceleration, gensym("reportAcceleration"), A_FLOAT, 0);
+ class_addmethod(wiimote_class, (t_method) wiimote_reportIR, gensym("reportIR"), A_FLOAT, 0);
+
+ class_addmethod(wiimote_class, (t_method) wiimote_reportNunchuk, gensym("reportNunchuck"), A_FLOAT, 0);
+ class_addmethod(wiimote_class, (t_method) wiimote_reportNunchuk, gensym("reportNunchuk"), A_FLOAT, 0);
+ class_addmethod(wiimote_class, (t_method) wiimote_reportMotionplus, gensym("reportMotionplus"), A_FLOAT, 0);
+
+ /* play a sample */
+ class_addmethod(wiimote_class, (t_method) wiimote_play, gensym("play"), A_SYMBOL, 0);
+
+ post("[wiimote]: reading data from the Wii remote controller");
+ post(" (c) 2011 IOhannes m zmölnig");
+#ifdef VERSION
+ post(" version " VERSION " published under the GNU General Public License");
+#else
+ post(" published under the GNU General Public License");
+#endif
+ post("contains WiiYourself! wiimote code by gl.tter http://gl.tter.org");
+ }
+} \ No newline at end of file
diff --git a/xbee/Makefile b/xbee/Makefile
new file mode 100644
index 0000000..b7e3c40
--- /dev/null
+++ b/xbee/Makefile
@@ -0,0 +1,244 @@
+# To use this Makefile for your project, first put the name of your library in
+# LIBRARY_NAME variable. The folder for your project should have the same name
+# as your library.
+LIBRARY_NAME = xbee
+LIBRARY_VERSION = 0.1
+
+# Next, add your .c source files to the SOURCES variable. The help files will
+# be included automatically
+SOURCES =
+
+# For objects that only build on certain platforms, add those to the SOURCES
+# line for the right platforms.
+SOURCES_android =
+SOURCES_cygwin =
+SOURCES_macosx = xbee_test.c
+SOURCES_iphoneos =
+SOURCES_linux =
+SOURCES_windows =
+
+# list all pd objects (i.e. myobject.pd) files here, and their helpfiles will
+# be included automatically
+PDOBJECTS =
+
+# if you want to include any other files in the source and binary tarballs,
+# list them here. This can be anything from header files, READMEs, example
+# patches, documentation, etc.
+EXTRA_DIST = max2pd.h xbee.h xbee.c xbee_io.h xbee_io.c xbee_protocol.h
+
+
+#------------------------------------------------------------------------------#
+#
+# you shouldn't need to edit anything below here, if we did it right :)
+#
+#------------------------------------------------------------------------------#
+
+# where Pd lives
+PD_PATH = ../../../pd
+# where to install the library
+prefix = /usr/local
+libdir = $(prefix)/lib
+pkglibdir = $(libdir)/pd-externals
+objectsdir = $(pkglibdir)
+
+
+INSTALL = install
+INSTALL_FILE = $(INSTALL) -p -m 644
+INSTALL_DIR = $(INSTALL) -p -m 755 -d
+
+CFLAGS = -DPD -I$(PD_PATH)/src -Wall -W -g
+LDFLAGS =
+LIBS = -lusb -lhid
+ALLSOURCES := $(SOURCES) $(SOURCES_android) $(SOURCES_cygwin) $(SOURCES_macosx) \
+ $(SOURCES_iphoneos) $(SOURCES_linux) $(SOURCES_windows)
+
+UNAME := $(shell uname -s)
+ifeq ($(UNAME),Darwin)
+ CPU := $(shell uname -p)
+ ifeq ($(CPU),arm) # iPhone/iPod Touch
+ SOURCES += $(SOURCES_macosx)
+ EXTENSION = pd_darwin
+ OS = iphoneos
+ IPHONE_BASE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin
+ CC=$(IPHONE_BASE)/gcc
+ CPP=$(IPHONE_BASE)/cpp
+ CXX=$(IPHONE_BASE)/g++
+ ISYSROOT = -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk
+ IPHONE_CFLAGS = -miphoneos-version-min=3.0 $(ISYSROOT) -arch armv6
+ OPT_CFLAGS = -fast -funroll-loops -fomit-frame-pointer
+ CFLAGS := $(IPHONE_CFLAGS) $(OPT_CFLAGS) $(CFLAGS) \
+ -I/Applications/Pd-extended.app/Contents/Resources/include
+ LDFLAGS += -arch armv6 -bundle -undefined dynamic_lookup $(ISYSROOT)
+ LIBS += -lc
+ STRIP = strip -x
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+ else # Mac OS X
+ SOURCES += $(SOURCES_macosx)
+ EXTENSION = pd_darwin
+ OS = macosx
+ OPT_CFLAGS = -ftree-vectorize -ftree-vectorizer-verbose=2 -fast
+ FAT_FLAGS = -arch i386 -arch ppc -mmacosx-version-min=10.4
+ CFLAGS += $(FAT_FLAGS) -fPIC -I/sw/include \
+ -I/Applications/Pd-extended.app/Contents/Resources/include
+ LDFLAGS += $(FAT_FLAGS) -bundle -undefined dynamic_lookup -L/sw/lib
+ # if the 'pd' binary exists, check the linking against it to aid with stripping
+ LDFLAGS += $(shell test -e $(PD_PATH)/bin/pd && echo -bundle_loader $(PD_PATH)/bin/pd)
+ LIBS += -lc
+ STRIP = strip -x
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+ endif
+endif
+ifeq ($(UNAME),Linux)
+ SOURCES += $(SOURCES_linux)
+ EXTENSION = pd_linux
+ OS = linux
+ OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer
+ CFLAGS += -fPIC
+ LDFLAGS += -Wl,--export-dynamic -shared -fPIC
+ LIBS += -lc
+ STRIP = strip --strip-unneeded -R .note -R .comment
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m)
+endif
+ifeq (CYGWIN,$(findstring CYGWIN,$(UNAME)))
+ SOURCES += $(SOURCES_cygwin)
+ EXTENSION = dll
+ OS = cygwin
+ OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer
+ CFLAGS +=
+ LDFLAGS += -Wl,--export-dynamic -shared -L$(PD_PATH)/src
+ LIBS += -lc -lpd
+ STRIP = strip --strip-unneeded -R .note -R .comment
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+endif
+ifeq (MINGW,$(findstring MINGW,$(UNAME)))
+ SOURCES += $(SOURCES_windows)
+ EXTENSION = dll
+ OS = windows
+ OPT_CFLAGS = -O3 -funroll-loops -fomit-frame-pointer
+ WINDOWS_HACKS = -D'O_NONBLOCK=1'
+ CFLAGS += -mms-bitfields $(WINDOWS_HACKS)
+ LDFLAGS += -s -shared -Wl,--enable-auto-import
+ LIBS += -L$(PD_PATH)/src -L$(PD_PATH)/bin -L$(PD_PATH)/obj -lpd -lwsock32 -lkernel32 -luser32 -lgdi32
+ STRIP = strip --strip-unneeded -R .note -R .comment
+ DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION)
+ DISTBINDIR=$(DISTDIR)-$(OS)
+endif
+
+CFLAGS += $(OPT_CFLAGS)
+
+
+.PHONY = install libdir_install single_install install-doc install-exec install-examples clean dist etags
+
+all: $(SOURCES:.c=.$(EXTENSION))
+
+%.o: %.c
+ $(CC) $(CFLAGS) -o "$*.o" -c "$*.c"
+
+%.$(EXTENSION): %.o
+ $(CC) $(LDFLAGS) -o "$*.$(EXTENSION)" "$*.o" $(LIBS)
+ chmod a-x "$*.$(EXTENSION)"
+
+# this links everything into a single binary file
+$(LIBRARY_NAME): $(SOURCES:.c=.o) $(LIBRARY_NAME).o
+ $(CC) $(LDFLAGS) -o $(LIBRARY_NAME).$(EXTENSION) $(SOURCES:.c=.o) $(LIBRARY_NAME).o $(LIBS)
+ chmod a-x $(LIBRARY_NAME).$(EXTENSION)
+
+
+install: libdir_install
+
+# The meta and help files are explicitly installed to make sure they are
+# actually there. Those files are not optional, then need to be there.
+libdir_install: $(SOURCES:.c=.$(EXTENSION)) install-doc install-examples
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ test -z "$(strip $(SOURCES))" || (\
+ $(INSTALL_FILE) $(SOURCES:.c=.$(EXTENSION)) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) && \
+ $(STRIP) $(addprefix $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/,$(SOURCES:.c=.$(EXTENSION))))
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS) \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+
+# install library linked as single binary
+single_install: $(LIBRARY_NAME) install-doc install-exec
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(INSTALL_FILE) $(LIBRARY_NAME).$(EXTENSION) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ $(STRIP) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/$(LIBRARY_NAME).$(EXTENSION)
+
+install-doc:
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ test -z "$(strip $(SOURCES))" || \
+ $(INSTALL_FILE) $(SOURCES:.c=-help.pd) \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) \
+ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)
+# this is the only bit not really handled well...
+ $(INSTALL_FILE) README $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/README.txt
+ $(INSTALL_FILE) VERSION $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/VERSION.txt
+ $(INSTALL_FILE) CHANGES $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/CHANGES.txt
+
+install-examples:
+ test ! -d examples || (\
+ $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples && \
+ $(INSTALL_FILE) examples/*.* $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples)
+
+
+clean:
+ -rm -f -- $(SOURCES:.c=.o)
+ -rm -f -- $(SOURCES:.c=.$(EXTENSION))
+ -rm -f -- $(LIBRARY_NAME).$(EXTENSION)
+
+distclean: clean
+ -rm -f -- $(DISTBINDIR).tar.gz
+ -rm -rf -- $(DISTBINDIR)
+ -rm -f -- $(DISTDIR).tar.gz
+ -rm -rf -- $(DISTDIR)
+
+
+$(DISTBINDIR):
+ $(INSTALL_DIR) $(DISTBINDIR)
+
+libdir: all $(DISTBINDIR)
+ $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTBINDIR)
+ $(INSTALL_FILE) $(SOURCES) $(DISTBINDIR)
+ $(INSTALL_FILE) $(SOURCES:.c=-help.pd) $(DISTBINDIR)
+ test -z "$(strip $(EXTRA_DIST))" || \
+ $(INSTALL_FILE) $(EXTRA_DIST) $(DISTBINDIR)
+# tar --exclude-vcs -czpf $(DISTBINDIR).tar.gz $(DISTBINDIR)
+
+$(DISTDIR):
+ $(INSTALL_DIR) $(DISTDIR)
+
+dist: $(DISTDIR)
+ $(INSTALL_FILE) Makefile $(DISTDIR)
+ $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTDIR)
+ test -z "$(strip $(ALLSOURCES))" || \
+ $(INSTALL_FILE) $(ALLSOURCES) $(DISTDIR)
+ test -z "$(strip $(ALLSOURCES))" || \
+ $(INSTALL_FILE) $(ALLSOURCES:.c=-help.pd) $(DISTDIR)
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS) $(DISTDIR)
+ test -z "$(strip $(PDOBJECTS))" || \
+ $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) $(DISTDIR)
+ test -z "$(strip $(EXTRA_DIST))" || \
+ $(INSTALL_FILE) $(EXTRA_DIST) $(DISTDIR)
+ tar --exclude-vcs -czpf $(DISTDIR).tar.gz $(DISTDIR)
+
+
+etags:
+ etags *.h $(SOURCES) ../../pd/src/*.[ch] /usr/include/*.h /usr/include/*/*.h
+
+showpaths:
+ @echo "PD_PATH: $(PD_PATH)"
+ @echo "objectsdir: $(objectsdir)"
+ @echo "LIBRARY_NAME: $(LIBRARY_NAME)"
+ @echo "SOURCES: $(SOURCES)"
+ @echo "ALLSOURCES: $(ALLSOURCES)"
+ @echo "UNAME: $(UNAME)"
+ @echo "CPU: $(CPU)"
+
diff --git a/xbee/max2pd.h b/xbee/max2pd.h
new file mode 100644
index 0000000..6f09500
--- /dev/null
+++ b/xbee/max2pd.h
@@ -0,0 +1,19 @@
+/*
+ * this header aims to make it easy to port Max objects to Pd
+ */
+
+/* name changes */
+#define SETSYM SETSYMBOL
+
+/* Pd doesn't have longs */
+#define SETLONG SETFLOAT
+
+/* allocate memory */
+#define sysmem_newptr(size) getbytes(128)
+#define sysmem_freeptr(ptr) freebytes(ptr, 128)
+
+
+#define atom_getlong(atom) atom_getfloatarg(0, 1, atom)
+#define atom_getsym(atom) atom_getsymbolarg(0, 1, atom)
+#define object_alloc(obj_class) pd_new(obj_class)
+#define object_free(obj) pd_free((t_pd*)obj)
diff --git a/xbee/xbee.c b/xbee/xbee.c
new file mode 100644
index 0000000..c243b5f
--- /dev/null
+++ b/xbee/xbee.c
@@ -0,0 +1,340 @@
+/*
+ * xbee.c:
+ * XBee Zigbee module interface functions
+ *
+ * (c) 2006-2008 Tymm Twillman <tymm@booyaka.com>
+ *
+ */
+
+#include <stdint.h>
+#include <string.h> /* for memcpy, memset, etc. */
+
+#include "xbee_protocol.h"
+#include "xbee.h"
+
+
+#ifndef MIN
+# define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+/* In case we need to serialize access for transmission;
+ * reception is made to always come from one XBee module so
+ * shouldn't need to serialize that.
+ */
+
+#ifndef CONFIG_XBEE_REENTRANT_TX
+# define xbee_lock_frame_id(xbee) do {} while(0)
+# define xbee_unlock_frame_id(xbee) do {} while(0)
+#endif
+
+
+/* Error counters can be added later if desired */
+#define xbee_rx_crc_err(xbee) do {} while(0)
+#define xbee_rx_err(xbee) do {} while(0)
+#define xbee_rx_dropped(xbee) do {} while(0)
+#define xbee_tx_err(xbee) do {} while(0)
+#define xbee_tx_dropped(xbee) do {} while(0)
+
+
+# ifdef CONFIG_XBEE_REENTRANT_TX
+# error CONFIG_XBEE_REENTRANT_TX requires XBEE_ALLOC to be set!
+# endif
+
+#ifndef ENOMEM
+# define ENOMEM 12
+#endif
+
+
+
+/* Generate & return next 8-bit frame ID */
+static inline uint8_t xbee_next_frame_id(xbee_t *xbee)
+{
+ uint8_t frame_id;
+
+
+ xbee_lock_frame_id(xbee);
+ if (++xbee->out.frame_id == 0)
+ ++xbee->out.frame_id;
+ frame_id = xbee->out.frame_id;
+ xbee_unlock_frame_id(xbee);
+
+ return frame_id;
+}
+
+
+/* Generate CRC for an XBee packet */
+uint8_t xbee_crc(const xbee_pkt_t *pkt)
+{
+ uint8_t *pkt_data = ((uint8_t *)pkt) + sizeof(xbee_pkt_hdr_t);
+ uint16_t i;
+ uint8_t crc = 0;
+
+
+ for (i = 0; i < ntohs(((xbee_pkt_hdr_t *)pkt)->len); i++)
+ crc += *(pkt_data++);
+
+ return ~crc;
+}
+
+
+/* Accept data from an XBee module & build into valid XBEE
+ * packets
+ */
+void xbee_in(xbee_t *xbee, const void *buf, uint8_t len)
+{
+ uint8_t *data = (uint8_t *)buf;
+
+
+ while(len) {
+ switch(xbee->in.bytes_rcvd) {
+ case 0:
+ while (*data != XBEE_PKT_START) {
+ if (!--len)
+ return;
+ data++;
+ }
+
+ xbee->in.hdr_data[xbee->in.bytes_rcvd++] = *data++;
+ if (!--len)
+ return;
+
+ /* Fall thru */
+
+ case 1:
+ xbee->in.hdr_data[xbee->in.bytes_rcvd++] = *data++;
+ if (!--len)
+ return;
+
+ /* Fall thru */
+
+ case 2:
+ xbee->in.hdr_data[xbee->in.bytes_rcvd++] = *data++;
+
+ /* Got enough to get packet length */
+
+ xbee->in.bytes_left = ntohs(((xbee_pkt_hdr_t *)xbee->in.hdr_data)->len);
+
+ if (xbee->in.bytes_left > XBEE_MAX_DATA_LEN
+ || ((xbee->in.packet
+ = xbee_alloc_pkt_mem(XBEE_RECV, xbee->in.bytes_left + 4)) == NULL)
+ )
+ {
+ xbee->in.bytes_left = 0;
+ xbee_rx_err(xbee);
+ continue;
+ }
+
+ xbee->in.bytes_left++; /* Extra for crc (alloc_pkt already accounts for it) */
+
+ memcpy(&(xbee->in.packet->hdr), &(xbee->in.hdr_data),
+ sizeof(xbee->in.hdr_data));
+
+ if (!--len)
+ return;
+
+ /* Fall thru */
+
+ default:
+ while (xbee->in.bytes_left--) {
+ ((uint8_t *)xbee->in.packet)[xbee->in.bytes_rcvd++] = *data++;
+ if (!--len && xbee->in.bytes_left)
+ return;
+ }
+ }
+
+ if (xbee_crc(xbee->in.packet)
+ != ((uint8_t *)xbee->in.packet)[xbee->in.bytes_rcvd - 1])
+ {
+ xbee->in.bytes_rcvd = 0;
+ xbee_rx_crc_err(xbee);
+ continue;
+ }
+
+ if (xbee_recv_pkt(xbee, xbee->in.packet, xbee->in.bytes_rcvd)) {
+ xbee_free_pkt_mem(xbee->in.packet);
+ xbee_rx_dropped(xbee);
+ }
+
+ xbee->in.bytes_rcvd = 0;
+ }
+}
+
+
+/* Send a command to an XBee module */
+
+int xbee_send_at_cmd(xbee_t *xbee,
+ const char cmd[],
+ uint8_t param_len,
+ const uint8_t params[])
+{
+ xbee_at_cmd_pkt_t *pkt;
+ uint8_t frame_id;
+ int ret;
+
+
+ pkt = (xbee_at_cmd_pkt_t *)xbee_alloc_pkt_mem(XBEE_XMIT, param_len + 8);
+ if (pkt == NULL) {
+ xbee_tx_err();
+ return -ENOMEM;
+ }
+
+ xbee_hdr_init(pkt->hdr, param_len + 4);
+
+ pkt->type = XBEE_PKT_TYPE_ATCMD;
+
+ frame_id = xbee_next_frame_id(xbee);
+
+ pkt->frame_id = frame_id;
+
+ pkt->command[0] = cmd[0];
+ pkt->command[1] = cmd[1];
+
+ memcpy(pkt->param, params, param_len);
+ pkt->param[param_len] = xbee_crc((xbee_pkt_t *)pkt);
+
+ ret = xbee_out(xbee, (xbee_pkt_t *)pkt,
+ sizeof(xbee_at_cmd_pkt_t) + param_len + 1);
+
+ if (ret >= 0)
+ return frame_id;
+
+ xbee_free_pkt_mem((xbee_pkt_t *)pkt);
+
+ xbee_tx_err();
+
+ return ret;
+}
+
+
+/* Send a command to a remote XBee module */
+
+int xbee_send_remote_at_cmd(xbee_t *xbee,
+ const char cmd[],
+ uint8_t param_len,
+ uint8_t apply,
+ const uint8_t params[],
+ const uint8_t addr64[8],
+ const uint8_t addr16[2])
+{
+ xbee_remote_at_cmd_pkt_t *pkt;
+ uint8_t frame_id;
+ int ret;
+
+
+ pkt = (xbee_remote_at_cmd_pkt_t *)xbee_alloc_pkt_mem(XBEE_XMIT, param_len + 19);
+ if (pkt == NULL) {
+ xbee_tx_err();
+ return -ENOMEM;
+ }
+
+ xbee_hdr_init(pkt->hdr, param_len + 15);
+
+ pkt->type = XBEE_PKT_TYPE_REMOTE_ATCMD;
+
+ frame_id = xbee_next_frame_id(xbee);
+ pkt->frame_id = frame_id;
+
+ memcpy(pkt->dest64, addr64, 8);
+ memcpy(pkt->dest16, addr16, 2);
+
+ pkt->apply = apply ? 2:0;
+
+ pkt->command[0] = cmd[0];
+ pkt->command[1] = cmd[1];
+
+ memcpy(pkt->param, params, param_len);
+ pkt->param[param_len] = xbee_crc((xbee_pkt_t *)pkt);
+
+ ret = xbee_out(xbee, (xbee_pkt_t *)pkt,
+ sizeof(xbee_remote_at_cmd_pkt_t) + param_len + 1);
+
+ if (ret >= 0)
+ return frame_id;
+
+ xbee_free_pkt_mem((xbee_pkt_t *)pkt);
+
+ xbee_tx_err();
+
+ return ret;
+}
+
+
+/* Send a data packet to another module using its 64-bit unique ID */
+int xbee_send64(xbee_t *xbee, const void *data, uint8_t len, uint8_t opt, const uint8_t addr[8])
+{
+ xbee_a64_tx_pkt_t *pkt;
+ int ret;
+ uint8_t frame_id;
+
+
+ pkt = (xbee_a64_tx_pkt_t *)xbee_alloc_pkt_mem(XBEE_XMIT, len + 15);
+ if (pkt == NULL) {
+ xbee_tx_err(xbee);
+ return -ENOMEM;
+ }
+
+ xbee_hdr_init(pkt->hdr, len + 11);
+
+ pkt->type = XBEE_PKT_TYPE_TX64;
+ memcpy(pkt->dest, addr, 8);
+ pkt->opt = opt;
+ frame_id = xbee_next_frame_id(xbee);
+ pkt->frame_id = frame_id;
+ memcpy(pkt->data, data, len);
+ pkt->data[len] = xbee_crc((xbee_pkt_t *)pkt);
+
+ ret = xbee_out(xbee, (xbee_pkt_t *)pkt, len + sizeof(xbee_a64_tx_pkt_t) + 1);
+
+ if (ret >= 0)
+ return frame_id;
+
+ xbee_tx_err(xbee);
+
+ xbee_free_pkt_mem((xbee_pkt_t *)pkt);
+
+ return ret;
+}
+
+
+/* Send a data packet to another module using its 16-bit ID */
+int xbee_send16(xbee_t *xbee, const void *data, uint8_t len, uint8_t opt, const uint8_t addr[2])
+{
+ xbee_a16_tx_pkt_t *pkt;
+ uint8_t frame_id;
+ int ret;
+
+
+ pkt = (xbee_a16_tx_pkt_t *)xbee_alloc_pkt_mem(XBEE_XMIT, len + 9);
+ if (pkt == NULL) {
+ xbee_tx_err(xbee);
+ return -ENOMEM;
+ }
+
+ xbee_hdr_init(pkt->hdr, len + 5);
+
+ pkt->type = XBEE_PKT_TYPE_TX16;
+ memcpy(pkt->dest, addr, 2);
+ pkt->opt = opt;
+ frame_id = xbee_next_frame_id(xbee);
+ pkt->frame_id = frame_id;
+ memcpy(pkt->data, (uint8_t *)data, len);
+ pkt->data[len] = xbee_crc((xbee_pkt_t *)pkt);
+
+ ret = xbee_out(xbee, (xbee_pkt_t *)pkt, len + sizeof(xbee_a16_tx_pkt_t) + 1);
+
+ if (ret >= 0)
+ return frame_id;
+
+ xbee_tx_err();
+
+ xbee_free_pkt_mem((xbee_pkt_t *)pkt);
+
+ return ret;
+}
+
+
+/* Initialize this package */
+void xbee_init(xbee_t *xbee)
+{
+ memset(xbee, 0, sizeof(xbee_t));
+}
diff --git a/xbee/xbee.h b/xbee/xbee.h
new file mode 100644
index 0000000..a40487e
--- /dev/null
+++ b/xbee/xbee.h
@@ -0,0 +1,261 @@
+/*
+ * xbee.h:
+ * Maxstream XBee module Interface Header
+ *
+ * (c) 2006-2008 Tymm Twillman <tymm@booyaka.com>
+ *
+ *
+ * NOTE: This doesn't touch hardware; it's up to developers to link in functions
+ * that handle hardware communication.
+ *
+ * DEVELOPERS: Pieces you need to implement (see prototypes, below):
+ * xbee_alloc_pkt_mem (can just return static data)
+ * xbee_free_pkt_mem (can do nothing if not dynamic)
+ *
+ * xbee_out
+ * xbee_recv_pkt
+ *
+ * What you need to call from wherever you read data from UART, etc:
+ * xbee_in
+ *
+ * Incoming data from UART, etc. should be passed to xbee_in; it will
+ * be built into well-formed packets and passed to xbee_recv_pkt
+ * for further processing.
+ *
+ * Outgoing data will be passed to xbee_out to be passed off to
+ * the XBee hardware.
+ *
+ *
+ */
+
+#ifndef XBEE_H
+#define XBEE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <errno.h>
+
+/*----------------------------------------------------------------------------
+ Definitions for commands the XBee recognizes
+ ----------------------------------------------------------------------------*/
+
+/* Basic communication parameters/values */
+
+#define XBEE_CMD_CHANNEL "CH"
+#define XBEE_CMD_PAN_ID "ID"
+#define XBEE_CMD_DEST_ADDR64_HI "DH"
+#define XBEE_CMD_DEST_ADDR64_LO "DL"
+#define XBEE_CMD_SRC_ADDR16 "MY"
+#define XBEE_CMD_SER_HI "SH"
+#define XBEE_CMD_SER_LO "SL"
+#define XBEE_CMD_RAND_DLY_SLOTS "RN"
+#define XBEE_CMD_MAC_MODE "MM"
+#define XBEE_CMD_COORD_ENA "CE"
+#define XBEE_CMD_SCAN "SC"
+#define XBEE_CMD_SCAN_DURATION "SD"
+#define XBEE_CMD_ASSOC_END "A1"
+#define XBEE_CMD_ASSOC_COORD "A2"
+#define XBEE_CMD_ASSOC_STATUS "AI"
+#define XBEE_CMD_RSSI "DB"
+
+/* Transceiver Control */
+
+#define XBEE_CMD_PWR_LEVEL "PL"
+#define XBEE_CMD_CCA_THRESH "CA"
+
+/* Sleep Parameters */
+
+#define XBEE_CMD_SLEEP_MODE "SM"
+#define XBEE_CMD_SLEEP_TIMEOUT "ST"
+#define XBEE_CMD_SLEEP_PERIOD "SP"
+#define XBEE_CMD_SLEEP_PERIOD_DISASSOC "DP"
+
+/* Interface parameters */
+
+#define XBEE_CMD_DATA_RATE "BD"
+#define XBEE_CMD_PACKETIZATION_TIMEOUT "RO"
+#define XBEE_CMD_DIO7_CONFIG "D7"
+#define XBEE_CMD_DIO6_CONFIG "D6"
+#define XBEE_CMD_DIO5_CONFIG "D5"
+#define XBEE_CMD_PWM0_CONFIG "PO"
+#define XBEE_CMD_API_ENA "AP"
+#define XBEE_CMD_PULLUP_ENA "PR"
+
+/* Version Info */
+
+#define XBEE_CMD_VERS_FIRMWARE "VR"
+#define XBEE_CMD_VERS_HARDWARE "HV"
+#define XBEE_CMD_VERS_FIRM_VERBOSE "VL"
+
+/* Received Signal Strength */
+
+#define XBEE_CMD_RSSI_PWM_TIMER "RP"
+#define XBEE_CMD_RSS "DB"
+
+/* Error counters */
+
+#define XBEE_CMD_CCA_FAILS "EC"
+#define XBEE_CMD_ACK_FAILS "EA"
+
+/* AT Command Params */
+
+#define XBEE_CMD_AT_MODE_TIMEOUT "CT"
+#define XBEE_CMD_AT_GUARD_TIME "GT"
+#define XBEE_CMD_AT_CMD_CHAR "CC"
+#define XBEE_CMD_AT_EXIT "CN"
+
+/* XBEE specific routing */
+
+#define XBEE_CMD_NODE_FIND_DEST "DN"
+#define XBEE_CMD_NODE_DISCOVER "ND"
+#define XBEE_CMD_NODE_ID "NI"
+#define XBEE_CMD_ACTIVE_SCAN "AS"
+#define XBEE_CMD_FORCE_DISASSOC "DA"
+#define XBEE_CMD_ENERGY_SCAN "ED"
+#define XBEE_CMD_FORCE_POLL "FP"
+
+/* Misc */
+
+#define XBEE_CMD_WRITE_PARAMS "WR"
+#define XBEE_CMD_RESET_SOFT "FR"
+#define XBEE_CMD_APPLY_CHANGES "AC"
+#define XBEE_CMD_RESTORE_DEFAULTS "RE"
+
+
+/*----------------------------------------------------------------------------
+ Structures usefull for communicating with the XBee in API mode
+ ----------------------------------------------------------------------------*/
+
+/* Packets are wrapped with a start & length */
+typedef struct {
+ uint8_t start; /* 0x7e */
+ uint16_t len;
+} __attribute__ ((__packed__)) xbee_pkt_hdr_t;
+
+
+/* Packets can be broken up into headers, a packet type, a number of data
+ * bytes and a crc (at the end of the data)
+ */
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t data[0];
+ /* uint8_t crc; */
+} __attribute__ ((__packed__)) xbee_pkt_t;
+
+
+/* Context for tracking current state of communication with an
+ * XBee module
+ */
+typedef struct {
+ struct {
+ uint8_t bytes_left;
+ uint8_t bytes_rcvd;
+ xbee_pkt_t *packet;
+ uint8_t hdr_data[sizeof(xbee_pkt_hdr_t)];
+ } in;
+ struct {
+ uint8_t frame_id;
+ } out;
+ void *user_context; // yours to pass data around with
+} __attribute__ ((__packed__)) xbee_t;
+
+/* This is used for keeping track of your data as things get passed around
+ * through the xbee interface
+ */
+#define xbee_user_context(xbee) ((xbee).user_context)
+
+/*----------------------------------------------------------------------------
+ Internal calls
+ ----------------------------------------------------------------------------*/
+
+/* Calculate CRC on an xbee packet */
+uint8_t xbee_crc(const xbee_pkt_t *pkt);
+
+
+/*----------------------------------------------------------------------------
+ Generally all the functions you need to call
+ ----------------------------------------------------------------------------*/
+
+/* Receive data, calling xbee_recv_pkt on each packet when it's done
+ * assembling; this should be called with raw data from UART, etc.
+ * as it comes in. *** YOU NEED TO CALL THIS ***
+ */
+void xbee_in(xbee_t *xbee, const void *data, uint8_t len);
+
+/* Send a packet with a 64-bit destination address (Series 1) */
+int xbee_send64(xbee_t *xbee,
+ const void *data,
+ uint8_t len,
+ uint8_t opt,
+ const uint8_t addr[8]);
+
+/* Send a packet with a 16-bit destination address (Series 1) */
+int xbee_send16(xbee_t *xbee,
+ const void *data,
+ uint8_t len,
+ uint8_t opt,
+ const uint8_t addr[2]);
+
+/* Send a command to the xbee modem */
+int xbee_send_at_cmd(xbee_t *xbee,
+ const char cmd[],
+ uint8_t param_len,
+ const uint8_t *params);
+
+/* Send a command to a remote xbee modem (Series 2 & Newer Series 1 only) */
+int xbee_send_remote_at_cmd(xbee_t *xbee,
+ const char cmd[],
+ uint8_t param_len,
+ uint8_t apply,
+ const uint8_t params[],
+ const uint8_t addr64[8],
+ const uint8_t addr16[2]);
+
+/* Initialize the XBee interface */
+void xbee_init(xbee_t *xbee);
+
+/*----------------------------------------------------------------------------
+ MUST be provided externally to this package
+ ----------------------------------------------------------------------------*/
+
+/* Queue a packet for transmission (needs to queue packet to be sent to XBEE
+ * module; e.g. copy the packet to a UART buffer).
+ * On error, -1 should be returned and the packet should NOT be freed.
+ * On success, 0 should be returned; if XBEE_ALLOC is set, this function or
+ * someone downstream is responsible for freeing it -- the packet has been
+ * handed off. This is to minimize copying of data.
+ */
+int xbee_out(xbee_t *xbee, xbee_pkt_t *pkt, uint8_t len);
+
+/* Handle an incoming packet; the packet will be fully formed and verified
+ * for proper construction before being passed off to this function. This
+ * function should dig into the packet & process based on its contents.
+ */
+int xbee_recv_pkt(xbee_t *xbee, xbee_pkt_t *pkt, uint8_t len);
+
+
+/*----------------------------------------------------------------------------
+ Must be provided externally only if using dynamic memory (which allows more
+ than one packet to be queued at a time)
+ ----------------------------------------------------------------------------*/
+
+/* Return a buffer for an xbee packet; at least <len> bytes need to be allocated
+ *
+ * Direction since we may want to have different allocations mechanisms/etc
+ * for xmit vs recv.
+ */
+void *xbee_alloc_pkt_mem(uint8_t direction, uint8_t len);
+
+/* Free up an allocated packet */
+void xbee_free_pkt_mem(xbee_pkt_t *pkt);
+
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* #ifndef XBEE_H ... */
diff --git a/xbee/xbee_internal.h b/xbee/xbee_internal.h
new file mode 100644
index 0000000..83273d4
--- /dev/null
+++ b/xbee/xbee_internal.h
@@ -0,0 +1,248 @@
+#ifndef XBEE_INTERNAL_H
+#define XBEE_INTERNAL_H
+
+#ifdef AVR
+# define __LITTLE_ENDIAN 1234
+# define BYTE_ORDER LITTLE_ENDIAN
+#elif defined(ARM)
+# include <endian.h>
+#else
+# include <arpa/inet.h>
+#endif
+
+#include <stdint.h>
+
+#include "xbee.h"
+
+#if !defined(ntohs) && (BYTE_ORDER == LITTLE_ENDIAN)
+# define ntohs(n) ((((short)(n)) & 0xff00) >> 8 | (((short)(n)) & 0xff) << 8)
+# define htons(n) ntohs(n)
+#elif !defined(ntohs)
+# define ntohs(n) ((short)(n))
+# define htons(n) ntohs(n)
+#endif
+
+#if !defined(ntohl) && (BYTE_ORDER == LITTLE_ENDIAN)
+# define ntohl(x) ((((x)&0xff000000)>>24) \
+ |(((x)&0x00ff0000)>>8) \
+ |(((x)&0x0000ff00)<<8) \
+ |(((x)&0x000000ff)<<24))
+# define htonl(n) ntohl(n)
+#elif !defined(ntohl)
+# define ntohl(n) ((long)(n))
+# define htonl(n) ntohs(n)
+#endif
+
+/* p2p CE=0 (end devices) A1=0 (no end dev assoc) same ID/CH
+ * coordinator: CE=1, A2=n (coordinator assoc)
+ * SP= sleep perd ST= time betw sleep (should be same on
+ * coord/noncoord)
+ * assoc - coord'd only; comm between modules thru coord'r
+ * PAN's - need coordinator. A1 allows totally dynamic assoc
+ */
+
+
+/* --- General XBee Definitions --- */
+
+/* "Start of packet" byte; always sent as the first
+ * byte of each packet
+ */
+#define XBEE_PKT_START 0x7e
+
+
+/* Maximum packet size; datasheet basically says 100 payload bytes max */
+#define XBEE_MAX_DATA_LEN 128
+
+
+/* --- Bits in packets --- */
+
+/* Communication status bits */
+
+#define XBEE_STATUS_HW_RESET 0x01
+#define XBEE_STATUS_WD_RESET 0x02
+#define XBEE_STATUS_ASSOC 0x04
+#define XBEE_STATUS_DISASSOC 0x08
+#define XBEE_STATUS_SYNC_LOST 0x10
+#define XBEE_STATUS_COORD_REALIGN 0x20
+#define XBEE_STATUS_COORD_RESET 0x40
+
+/* Command status bits */
+
+#define XBEE_CMDSTATUS_OK 0
+#define XBEE_CMDSTATUS_ERR 1
+
+/* Transmit options */
+
+#define XBEE_TX_FLAG_NO_ACK 0x01
+#define XBEE_TX_FLAG_SEND_BCAST_PAN_ID 0x04
+
+/* Transmit status bits */
+
+#define XBEE_TXSTATUS_SUCCESS 0x00
+#define XBEE_TXSTATUS_NO_ACK 0x01
+#define XBEE_TXSTATUS_CCA_FAIL 0x02
+#define XBEE_TXSTATUS_PURGES 0x03
+
+/* Received options */
+
+#define XBEE_RX_FLAG_ADDR_BCAST 0x02
+#define XBEE_RX_FLAG_PAN_BCAST 0x04
+
+
+/* --- Definitions & macros for library use --- */
+
+/* For tracking memory allocations */
+#define XBEE_RECV 0x00
+#define XBEE_XMIT 0x01
+
+/* Initialize an XBee header */
+#define xbee_hdr_init(hdr, data_len) \
+ ((hdr).start = 0x7e, (hdr).len = htons(data_len))
+
+/* To get the length of the data portion of a received packet */
+
+#define xbee_recv_a64_data_len(pkt) (ntohs(pkt->hdr.len) - 11)
+#define xbee_recv_a16_data_len(pkt) (ntohs(pkt->hdr.len) - 5)
+#define xbee_cmd_resp_param_len(pkt) (ntohs(pkt->hdr.len) - 5)
+
+#ifdef XBEE_ALLOC
+# define xbee_alloc_pkt(dir, data_len) \
+ (xbee_pkt_t *)xbee_alloc_buf((dir), (data_len) + sizeof(xbee_pkt_hdr_t) + 1)
+#endif
+
+/* Types of packets from/to xbee modules; these are used
+ * in the "type" field of each packet structure
+ */
+
+typedef enum {
+ XBEE_PKT_TYPE_TX64 = 0x00,
+ XBEE_PKT_TYPE_TX16 = 0x01,
+ XBEE_PKT_TYPE_ATCMD = 0x08,
+ XBEE_PKT_TYPE_QATCMD = 0x09, /* wait til an immed param or apply cmd */
+ XBEE_PKT_TYPE_REMOTE_ATCMD = 0x17,
+ XBEE_PKT_TYPE_RX64 = 0x80,
+ XBEE_PKT_TYPE_RX16 = 0x81,
+ XBEE_PKT_TYPE_RX64_IO = 0x82,
+ XBEE_PKT_TYPE_RX16_IO = 0x83,
+ XBEE_PKT_TYPE_ATCMD_RESP = 0x88,
+ XBEE_PKT_TYPE_TX_STATUS = 0x89,
+ XBEE_PKT_TYPE_MODEM_STATUS= 0x8a,
+} xbee_pkt_type_t;
+
+
+/* --- Packet layouts --- */
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t command[2];
+ uint8_t param[0];
+} __attribute__ ((__packed__)) xbee_at_cmd_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t dest64[8];
+ uint8_t dest16[2];
+ uint8_t apply;
+ uint8_t command[2];
+ uint8_t param[0];
+} __attribute__ ((__packed__)) xbee_remote_at_cmd_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t dest[8];
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a64_tx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t dest[2];
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a16_tx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t status;
+} __attribute__ ((__packed__)) xbee_tx_status_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t status;
+} __attribute__ ((__packed__)) xbee_modem_status_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t command[2];
+ uint8_t status;
+ uint8_t param[0];
+} __attribute__ ((__packed__)) xbee_cmd_resp_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[8];
+ uint8_t rssi; /* signal strength */
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a64_rx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[2];
+ uint8_t rssi;
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a16_rx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[8];
+ uint8_t rssi; /* signal strength */
+ uint8_t opt;
+ uint8_t num_samples;
+ uint16_t ch_ind; /* bits 14-9: a5-a0 bits 8-0: d8-d0 active */
+ uint16_t data[0]; /* First sample digital if any digital chan active
+ rest are 16-bit analog rdgs */
+} __attribute__ ((__packed__)) xbee_io_a64_rx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[2];
+ uint8_t rssi;
+ uint8_t opt;
+ uint8_t num_samples;
+ uint8_t achan;
+ uint16_t ch_ind; /* bits 14-9: a5-a0 bits 8-0: d8-d0 active */
+ uint16_t data[0]; /* First sample digital if any digital chan active
+ rest are 16-bit analog rdgs */
+} __attribute__ ((__packed__)) xbee_io_a16_rx_pkt_t;
+
+#endif /* #ifndef XBEE_INTERNAL_H ... */
+
diff --git a/xbee/xbee_io.c b/xbee/xbee_io.c
new file mode 100644
index 0000000..d3fe323
--- /dev/null
+++ b/xbee/xbee_io.c
@@ -0,0 +1,268 @@
+/*
+ * xbee_io.c
+ * xbee_test
+ *
+ * Created by Tymm on 11/20/08.
+ * Copyright 2008 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/select.h>
+#include <pthread.h>
+
+#ifdef PD
+#include "m_pd.h"
+#include "max2pd.h"
+#else
+#include "ext.h"
+#endif /* PD */
+
+#include "xbee.h"
+#include "xbee_io.h"
+
+
+#ifndef MIN
+# define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+
+void xbee_io_read(xbee_t *xbee)
+{
+ int r;
+ char data[128];
+ xbee_io_context_t *ctx = xbee_user_context(*xbee);
+ int xbee_fd = ctx->fd;
+
+
+ r = read(xbee_fd, data, sizeof(data));
+ if (r <= 0) {
+ return;
+ }
+
+ xbee_in(xbee, data, r);
+}
+
+
+void xbee_io_write(xbee_t *xbee)
+{
+ int r;
+ int max_bytes_to_write;
+ xbee_io_context_t *ctx = xbee_user_context(*xbee);
+ int xbee_fd = ctx->fd;
+ int tmp_start = ctx->out_buffer_start;
+
+
+ if (tmp_start != ctx->out_buffer_end) {
+ max_bytes_to_write = (ctx->out_buffer_end + XBEE_IO_BUFSIZ - ctx->out_buffer_start) % XBEE_IO_BUFSIZ;
+
+ //for (r = 0; r < MIN(max_bytes_to_write, XBEE_IO_BUFSIZ - tmp_start); r++) {
+ // post("%02x", (unsigned char)ctx->out_buffer[tmp_start + r]);
+ //}
+
+ r = write(xbee_fd, (char *)(ctx->out_buffer + tmp_start), MIN(max_bytes_to_write, XBEE_IO_BUFSIZ - tmp_start));
+
+ if (r < 0) {
+ return;
+ }
+
+ tmp_start += r;
+ tmp_start %= XBEE_IO_BUFSIZ;
+
+ ctx->out_buffer_start = tmp_start;
+
+ if (tmp_start == 0) {
+ max_bytes_to_write -= r;
+
+ r = write(xbee_fd, (char *)(ctx->out_buffer), max_bytes_to_write);
+
+ if (r < 0) {
+ return;
+ }
+
+ tmp_start += r;
+ tmp_start %= XBEE_IO_BUFSIZ;
+ }
+
+ ctx->out_buffer_start = tmp_start;
+ }
+}
+
+
+int xbee_put_data(xbee_io_context_t *ctx, char *data, int len)
+{
+ int byte_count = 0;
+ int max_bytes;
+ int tmp_end = ctx->out_buffer_end;
+
+
+ max_bytes = MIN((ctx->out_buffer_start + XBEE_IO_BUFSIZ - tmp_end - 1) % XBEE_IO_BUFSIZ, len);
+ byte_count = MIN(max_bytes, XBEE_IO_BUFSIZ - tmp_end);
+
+ memcpy((char *)(ctx->out_buffer + tmp_end), data, byte_count);
+
+ if (byte_count != max_bytes) {
+ memcpy((char *)(ctx->out_buffer), data + byte_count, max_bytes - byte_count);
+ }
+
+ tmp_end += max_bytes;
+ tmp_end %= XBEE_IO_BUFSIZ;
+ ctx->out_buffer_end = tmp_end;
+
+ return max_bytes;
+}
+
+
+void *xbee_io_loop(void *param)
+{
+ int xbee_fd;
+ fd_set r_fds, w_fds;
+ int nfds = 0;
+ struct timeval timeout;
+ struct timeval timeout_orig = { .tv_sec = 1, .tv_usec = 0 };
+ xbee_t *xbee = param;
+ xbee_io_context_t *ctx = xbee_user_context(*xbee);
+ int notif_fd;
+ int maxfd;
+
+
+ xbee_fd = ctx->fd;
+ notif_fd = ctx->pipe_fds[0];
+
+ FD_ZERO(&r_fds);
+ FD_ZERO(&w_fds);
+
+ while(1) {
+ if (ctx->io_done) {
+ break;
+ }
+
+ nfds = 0;
+
+ FD_SET(notif_fd, &r_fds);
+ maxfd = notif_fd;
+
+ FD_SET(xbee_fd, &r_fds);
+ if (xbee_fd > maxfd)
+ maxfd = xbee_fd;
+
+ FD_CLR(xbee_fd, &w_fds);
+ if (ctx->out_buffer_start != ctx->out_buffer_end) {
+ FD_SET(xbee_fd, &w_fds);
+ }
+
+ // For Linux compat
+ timeout = timeout_orig;
+
+ nfds = select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout);
+
+ if (nfds >= 1) {
+ if (FD_ISSET(notif_fd, &r_fds)) {
+ // We've been notified that there's data to be read, or need to exit
+ char blah;
+ read(notif_fd, &blah, 1);
+ }
+
+ if (FD_ISSET(xbee_fd, &r_fds)) {
+ xbee_io_read(xbee);
+ }
+
+ if (FD_ISSET(xbee_fd, &w_fds)) {
+ xbee_io_write(xbee);
+ }
+ }
+ }
+
+ ctx->io_running = 0;
+
+ return NULL;
+}
+
+
+int xbee_kill_io_thread(xbee_t *xbee)
+{
+ xbee_io_context_t *ctx;
+ void *val;
+
+
+ ctx = xbee_user_context(*xbee);
+
+ if (ctx != NULL) {
+ if (ctx->io_running) {
+ ctx->io_done = 1;
+
+ write(ctx->pipe_fds[1], "!", 1);
+
+ if (pthread_join(ctx->io_thread, &val) < 0) {
+ return -1;
+ }
+
+ post("xbee_test: xbee io thread stopped.");
+ }
+ }
+
+ return 0;
+}
+
+
+int xbee_new_io_thread(xbee_t *xbee)
+{
+ xbee_io_context_t *ctx = xbee_user_context(*xbee);
+ int r;
+
+
+ if (!ctx->io_running) {
+ ctx->io_running = 1;
+
+ r = pthread_create(&ctx->io_thread, NULL, &xbee_io_loop, (void *)xbee);
+ if (r < 0) {
+ ctx->io_running = 0;
+ return -1;
+ }
+
+ post("xbee_test: xbee io thread started.");
+ }
+
+ return 0;
+}
+
+
+int xbee_out(xbee_t *xbee, xbee_pkt_t *pkt, uint8_t len)
+{
+ xbee_io_context_t *ctx = xbee_user_context(*xbee);
+ int r;
+
+
+ r = xbee_put_data(ctx, (void *)pkt, len);
+ xbee_free_pkt_mem(pkt);
+
+ return r;
+}
+
+
+int xbee_recv_pkt(xbee_t *xbee, xbee_pkt_t *pkt, uint8_t len)
+{
+ //int i;
+
+
+ //for (i = 0; i < len; i++) {
+ // post("GOT %02x", ((unsigned char *)pkt)[i]);
+ //}
+
+ xbee_free_pkt_mem(pkt);
+ return 0;
+}
+
+
+void *xbee_alloc_pkt_mem(uint8_t direction, uint8_t len)
+{
+ return sysmem_newptr(128);
+}
+
+
+void xbee_free_pkt_mem(xbee_pkt_t *pkt)
+{
+ sysmem_freeptr(pkt);
+}
diff --git a/xbee/xbee_io.h b/xbee/xbee_io.h
new file mode 100644
index 0000000..a7f5363
--- /dev/null
+++ b/xbee/xbee_io.h
@@ -0,0 +1,34 @@
+/*
+ * xbee_io.h
+ * xbee_test
+ *
+ * Created by Tymm on 11/24/08.
+ * Copyright 2008 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#ifndef XBEE_IO_H
+#define XBEE_IO_H
+
+#include <pthread.h>
+
+#include "xbee.h"
+
+
+#define XBEE_IO_BUFSIZ 16384
+
+typedef struct {
+ int fd;
+ int pipe_fds[2]; // For indicating new data ready
+ pthread_t io_thread;
+ int io_done;
+ int io_running;
+ int out_buffer_start;
+ int out_buffer_end;
+ char out_buffer[XBEE_IO_BUFSIZ];
+} xbee_io_context_t;
+
+int xbee_new_io_thread(xbee_t *xbee);
+int xbee_kill_io_thread(xbee_t *xbee);
+
+#endif /* #ifndef XBEE_IO_H ... */
diff --git a/xbee/xbee_protocol.h b/xbee/xbee_protocol.h
new file mode 100644
index 0000000..bb80a60
--- /dev/null
+++ b/xbee/xbee_protocol.h
@@ -0,0 +1,292 @@
+#ifndef XBEE_PROTOCOL_H
+#define XBEE_PROTOCOL_H
+
+#ifdef AVR
+# define __LITTLE_ENDIAN 1234
+# define BYTE_ORDER LITTLE_ENDIAN
+#elif defined(ARM)
+# include <endian.h>
+#else
+# include <arpa/inet.h>
+#endif
+
+#include <stdint.h>
+
+#include "xbee.h"
+
+#if !defined(ntohs) && (BYTE_ORDER == LITTLE_ENDIAN)
+# define ntohs(n) ((((short)(n)) & 0xff00) >> 8 | (((short)(n)) & 0xff) << 8)
+# define htons(n) ntohs(n)
+#elif !defined(ntohs)
+# define ntohs(n) ((short)(n))
+# define htons(n) ntohs(n)
+#endif
+
+#if !defined(ntohl) && (BYTE_ORDER == LITTLE_ENDIAN)
+# define ntohl(x) ((((x)&0xff000000)>>24) \
+ |(((x)&0x00ff0000)>>8) \
+ |(((x)&0x0000ff00)<<8) \
+ |(((x)&0x000000ff)<<24))
+# define htonl(n) ntohl(n)
+#elif !defined(ntohl)
+# define ntohl(n) ((long)(n))
+# define htonl(n) ntohs(n)
+#endif
+
+/* p2p CE=0 (end devices) A1=0 (no end dev assoc) same ID/CH
+ * coordinator: CE=1, A2=n (coordinator assoc)
+ * SP= sleep perd ST= time betw sleep (should be same on
+ * coord/noncoord)
+ * assoc - coord'd only; comm between modules thru coord'r
+ * PAN's - need coordinator. A1 allows totally dynamic assoc
+ */
+
+
+/* --- General XBee Definitions --- */
+
+/* "Start of packet" byte; always sent as the first
+ * byte of each packet
+ */
+#define XBEE_PKT_START 0x7e
+
+
+/* Maximum packet size; datasheet basically says 100 payload bytes max */
+#define XBEE_MAX_DATA_LEN 128
+
+
+/* --- Bits in packets --- */
+
+/* Communication status bits */
+
+#define XBEE_STATUS_HW_RESET 0x01
+#define XBEE_STATUS_WD_RESET 0x02
+#define XBEE_STATUS_ASSOC 0x04
+#define XBEE_STATUS_DISASSOC 0x08
+#define XBEE_STATUS_SYNC_LOST 0x10
+#define XBEE_STATUS_COORD_REALIGN 0x20
+#define XBEE_STATUS_COORD_RESET 0x40
+
+/* Command status bits */
+
+#define XBEE_CMDSTATUS_OK 0
+#define XBEE_CMDSTATUS_ERR 1
+
+/* Transmit options */
+
+#define XBEE_TX_FLAG_NO_ACK 0x01
+#define XBEE_TX_FLAG_SEND_BCAST_PAN_ID 0x04
+
+/* Transmit status bits */
+
+#define XBEE_TXSTATUS_SUCCESS 0x00
+#define XBEE_TXSTATUS_NO_ACK 0x01
+#define XBEE_TXSTATUS_CCA_FAIL 0x02
+#define XBEE_TXSTATUS_PURGES 0x03
+
+/* Received options */
+
+#define XBEE_RX_FLAG_ADDR_BCAST 0x02
+#define XBEE_RX_FLAG_PAN_BCAST 0x04
+
+
+/* --- Definitions & macros for library use --- */
+
+/* For tracking memory allocations */
+#define XBEE_RECV 0x00
+#define XBEE_XMIT 0x01
+
+/* Initialize an XBee header */
+#define xbee_hdr_init(hdr, data_len) \
+ ((hdr).start = 0x7e, (hdr).len = htons(data_len))
+
+/* To get the length of the data portion of a received packet */
+
+#define xbee_recv_a64_data_len(pkt) (ntohs(pkt->hdr.len) - 11)
+#define xbee_recv_a16_data_len(pkt) (ntohs(pkt->hdr.len) - 5)
+#define xbee_cmd_resp_param_len(pkt) (ntohs(pkt->hdr.len) - 5)
+
+#ifdef XBEE_ALLOC
+# define xbee_alloc_pkt(dir, data_len) \
+ (xbee_pkt_t *)xbee_alloc_buf((dir), (data_len) + sizeof(xbee_pkt_hdr_t) + 1)
+#endif
+
+/* Types of packets from/to xbee modules; these are used
+ * in the "type" field of each packet structure
+ */
+
+typedef enum {
+ XBEE_PKT_TYPE_TX64 = 0x00,
+ XBEE_PKT_TYPE_TX16 = 0x01,
+ XBEE_PKT_TYPE_ATCMD = 0x08,
+ XBEE_PKT_TYPE_QATCMD = 0x09, /* wait til an immed param or apply cmd */
+ XBEE_PKT_TYPE_ZB_TX_REQ = 0x10,
+ XBEE_PKT_TYPE_ZB_CMD_FRAME = 0x11, /* Not yet impl */
+ XBEE_PKT_TYPE_REMOTE_ATCMD = 0x17,
+ XBEE_PKT_TYPE_RX64 = 0x80,
+ XBEE_PKT_TYPE_RX16 = 0x81,
+ XBEE_PKT_TYPE_RX64_IO = 0x82,
+ XBEE_PKT_TYPE_RX16_IO = 0x83,
+ XBEE_PKT_TYPE_ATCMD_RESP = 0x88,
+ XBEE_PKT_TYPE_TX_STATUS = 0x89,
+ XBEE_PKT_TYPE_MODEM_STATUS = 0x8a,
+ XBEE_PKT_TYPE_ZB_TX_STATUS = 0x8b, /* Not yet impl */
+ XBEE_PKT_TYPE_ADV_MODEM_STATUS = 0x8c, /* Not yet impl */
+ XBEE_PKT_TYPE_ZB_RX = 0x90,
+ XBEE_PKT_TYPE_ZB_RX_IO = 0x92, /* Not yet impl */
+ XBEE_PKT_TYPE_RX_SENSOR = 0x94, /* Not yet impl */
+ XBEE_PKT_TYPE_NODE_IDENT = 0x95, /* Not yet impl */
+ XBEE_PKT_TYPE_REMOTE_ATCMD_RESP = 0x97,
+} xbee_pkt_type_t;
+
+
+/* --- Packet layouts --- */
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t command[2];
+ uint8_t param[0];
+} __attribute__ ((__packed__)) xbee_at_cmd_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t dest64[8];
+ uint8_t dest16[2];
+ uint8_t apply;
+ uint8_t command[2];
+ uint8_t param[0];
+} __attribute__ ((__packed__)) xbee_remote_at_cmd_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t dest[8];
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a64_tx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t dest[2];
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a16_tx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t status;
+} __attribute__ ((__packed__)) xbee_tx_status_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t status;
+} __attribute__ ((__packed__)) xbee_modem_status_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t command[2];
+ uint8_t status;
+ uint8_t param[0];
+} __attribute__ ((__packed__)) xbee_cmd_resp_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t src64[8];
+ uint8_t src16[2];
+ uint8_t command[2];
+ uint8_t status;
+ uint8_t param[0];
+} __attribute__ ((__packed__)) xbee_remote_cmd_resp_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[8];
+ uint8_t rssi; /* signal strength */
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a64_rx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[2];
+ uint8_t rssi;
+ uint8_t opt;
+ uint8_t data[0];
+} __attribute__ ((__packed__)) xbee_a16_rx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[8];
+ uint8_t rssi; /* signal strength */
+ uint8_t opt;
+ uint8_t num_samples;
+ uint16_t ch_ind; /* bits 14-9: a5-a0 bits 8-0: d8-d0 active */
+ uint16_t data[0]; /* First sample digital if any digital chan active
+ rest are 16-bit analog rdgs */
+} __attribute__ ((__packed__)) xbee_io_a64_rx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src[2];
+ uint8_t rssi;
+ uint8_t opt;
+ uint8_t num_samples;
+ uint8_t achan;
+ uint16_t ch_ind; /* bits 14-9: a5-a0 bits 8-0: d8-d0 active */
+ uint16_t data[0]; /* First sample digital if any digital chan active
+ rest are 16-bit analog rdgs */
+} __attribute__ ((__packed__)) xbee_io_a16_rx_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t frame_id;
+ uint8_t dest64[8];
+ uint8_t dest16[2];
+ uint8_t radius;
+ uint8_t opt; /* Multicast = bit 3 */
+ uint8_t data[0]; /* Up to 72 bytes/pkt */
+} __attribute__ ((__packed__)) xbee_zb_tx_req_pkt_t;
+
+
+typedef struct {
+ xbee_pkt_hdr_t hdr;
+ uint8_t type;
+ uint8_t src64[8];
+ uint8_t src16[2];
+ uint8_t opt;
+ uint8_t data[0]; /* Up to 72 bytes/pkt */
+} __attribute__ ((__packed__)) xbee_zb_rx_pkt_t;
+
+
+#endif /* #ifndef XBEE_PROTOCOL_H ... */
+
diff --git a/xbee/xbee_test-help.pd b/xbee/xbee_test-help.pd
new file mode 100644
index 0000000..f00c516
--- /dev/null
+++ b/xbee/xbee_test-help.pd
@@ -0,0 +1,2 @@
+#N canvas 191 22 450 300 10;
+#X obj 157 89 xbee_test /dev/tty.Bluetooth-Modem;
diff --git a/xbee/xbee_test.c b/xbee/xbee_test.c
new file mode 100644
index 0000000..7408b57
--- /dev/null
+++ b/xbee/xbee_test.c
@@ -0,0 +1,457 @@
+/**
+ @file
+ xbee_test - test xbee communication
+
+ @ingroup examples
+*/
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <termios.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef PD
+#include "m_pd.h"
+#include "max2pd.h"
+#else
+#include "ext.h" // standard Max include, always required
+#include "ext_obex.h" // required for new style Max object
+
+# ifdef MAC_VERSION
+# include "ext_strings.h"
+# endif
+#endif /* PD */
+
+#include "xbee.h"
+#include "xbee_io.h"
+
+////////////////////////// object struct
+typedef struct _xbee_test
+{
+ t_object a_ob; // the object itself (must be first)
+ long baud;
+ uint8_t dest_addr64[8];
+ uint8_t dest_addr16[2];
+ xbee_t *xbee;
+} t_xbee_test;
+
+
+///////////////////////// function prototypes
+//// standard set
+void *xbee_test_new(t_symbol *s, long argc, t_atom *argv);
+void xbee_test_free(t_xbee_test *x);
+void xbee_test_assist(t_xbee_test *x, void *b, long m, long a, char *s);
+//// additional methods
+void xbee_test_bang(t_xbee_test *x); // incoming bang message
+void xbee_test_close(t_xbee_test *x);
+int xbee_test_open_device(t_xbee_test *x, t_symbol *s);
+void xbee_test_dest64(t_xbee_test *x, t_symbol *s);
+void xbee_test_dest16(t_xbee_test *x, t_symbol *s);
+static void xbee_test_on(t_xbee_test *x, long dpin);
+static void xbee_test_off(t_xbee_test *x, long dpin);
+
+
+//////////////////////// global class pointer variable
+void *xbee_test_class;
+
+
+static int set_baud(int fd, int baud_rate)
+{
+ struct termios termios;
+ speed_t speed;
+
+
+ switch(baud_rate) {
+ case 50:
+ speed = B50;
+ break;
+ case 75:
+ speed = B75;
+ break;
+ case 110:
+ speed = B110;
+ break;
+ case 300:
+ speed = B300;
+ break;
+ case 600:
+ speed = B600;
+ break;
+ case 1200:
+ speed = B1200;
+ break;
+ case 2400:
+ speed = B2400;
+ break;
+ case 4800:
+ speed = B4800;
+ break;
+ case 9600:
+ speed = B9600;
+ break;
+ case 19200:
+ speed = B19200;
+ break;
+ case 38400:
+ speed = B38400;
+ break;
+ case 57600:
+ speed = B57600;
+ break;
+ case 115200:
+ speed = B115200;
+ break;
+ case 230400:
+ speed = B230400;
+ break;
+#ifdef B256000
+ case 256000:
+ speed = B256000;
+ break;
+#endif
+ default:
+ return -1;
+ }
+
+ if (tcgetattr(fd, &termios) < 0) {
+ error("xbee_test: tcgetattr failed (%d)", errno);
+ return -1;
+ }
+
+ termios.c_cflag |= CLOCAL;
+
+ if (cfsetispeed(&termios, speed) < 0) {
+ error("xbee_test: cfsetispeed failed (%d)", errno);
+ return -1;
+ }
+
+ if (cfsetospeed(&termios, speed) < 0) {
+ error("xbee_test: cfsetospeed failed (%d)", errno);
+ return -1;
+ }
+
+ if (tcsetattr(fd, TCSANOW, &termios) < 0) {
+ error("xbee_test: tcsetattr failed (%d)", errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+#ifdef PD
+
+void xbee_test_setup(void)
+{
+ xbee_test_class = class_new(gensym("xbee_test"),
+ (t_newmethod)xbee_test_new,
+ (t_method)xbee_test_free,
+ sizeof(t_xbee_test),
+ CLASS_DEFAULT,
+ A_GIMME,0);
+ class_addbang(xbee_test_class, (t_method)xbee_test_bang);
+ class_addmethod(xbee_test_class, (t_method)xbee_test_dest64, gensym("dest64"), A_DEFSYMBOL, 0);
+ class_addmethod(xbee_test_class, (t_method)xbee_test_dest16, gensym("dest16"), A_DEFSYMBOL, 0);
+ class_addmethod(xbee_test_class, (t_method)xbee_test_on, gensym("on"), A_DEFFLOAT, 0);
+ class_addmethod(xbee_test_class, (t_method)xbee_test_off, gensym("off"), A_DEFFLOAT, 0);
+}
+#else
+int main(void)
+{
+ t_class *c;
+
+
+ c = class_new("xbee_test", (method)xbee_test_new, (method)xbee_test_free, (long)sizeof(t_xbee_test), 0L, A_GIMME, 0);
+
+ class_addmethod(c, (method)xbee_test_bang, "bang", 0);
+ class_addmethod(c, (method)xbee_test_assist, "assist", A_CANT, 0);
+ class_addmethod(c, (method)xbee_test_dest64, "dest64", A_DEFSYM, 0);
+ class_addmethod(c, (method)xbee_test_dest16, "dest16", A_DEFSYM, 0);
+ class_addmethod(c, (method)xbee_test_on, "on", A_LONG, 0);
+ class_addmethod(c, (method)xbee_test_off, "off", A_LONG, 0);
+
+ class_register(CLASS_BOX, c);
+ xbee_test_class = c;
+
+ return 0;
+}
+#endif /* PD */
+
+static void xbee_test_on(t_xbee_test *x, long dpin)
+{
+ char cmd[2] = "Dx";
+ uint8_t param[] = {5};
+ xbee_io_context_t *ctx;
+ int r;
+
+
+ if (!x->xbee)
+ return;
+
+ ctx = xbee_user_context(*(x->xbee));
+ if (!ctx)
+ return;
+
+ if (dpin >= 0 && dpin <= 7) {
+ cmd[1] = dpin + '0';
+
+ r = xbee_send_remote_at_cmd(x->xbee, cmd, 1, 2, param, x->dest_addr64, x->dest_addr16);
+ if (r < 0) {
+ post("xbee_test: xbee_send_remote_at_cmd failed (%d)", errno);
+ }
+ }
+
+ // Nudge IO thread
+ write(ctx->pipe_fds[1], "!", 1);
+}
+
+
+static void xbee_test_off(t_xbee_test *x, long dpin)
+{
+ char cmd[2] = "Dx";
+ uint8_t param[] = {4};
+ xbee_io_context_t *ctx;
+ int r;
+
+
+ if (!x->xbee)
+ return;
+
+ ctx = xbee_user_context(*(x->xbee));
+ if (!ctx)
+ return;
+
+ if (dpin >= 0 && dpin <= 7) {
+ cmd[1] = dpin + '0';
+
+ r = xbee_send_remote_at_cmd(x->xbee, cmd, 1, 2, param, x->dest_addr64, x->dest_addr16);
+ if (r < 0) {
+ post ("xbee_test: xbee_send_remote_at_cmd failed (%d)", errno);
+ }
+ }
+
+ // Nudge IO thread
+ write(ctx->pipe_fds[1], "!", 1);
+}
+
+#ifndef PD
+void xbee_test_assist(t_xbee_test *x, void *b, long m, long a, char *s)
+{
+ if (m == ASSIST_INLET) { //inlet
+ sprintf(s, "I am inlet %ld", a);
+ }
+ else { // outlet
+ sprintf(s, "I am outlet %ld", a);
+ }
+}
+#endif /* NOT PD */
+
+void xbee_test_bang(t_xbee_test *x)
+{
+}
+
+
+void xbee_test_close(t_xbee_test *x)
+{
+ xbee_t *xbee = x->xbee;
+ xbee_io_context_t *ctx;
+
+
+ if (!xbee)
+ return;
+
+ xbee_kill_io_thread(x->xbee);
+
+ ctx = xbee_user_context(*xbee);
+ if (!ctx)
+ return;
+
+ if (ctx->fd >= 0) {
+ close(ctx->fd);
+ ctx->fd = -1;
+ }
+
+ if (ctx->pipe_fds[0] >= 0) {
+ close(ctx->pipe_fds[0]);
+ ctx->pipe_fds[0] = -1;
+ }
+
+ if (ctx->pipe_fds[1] >= 0) {
+ close(ctx->pipe_fds[1]);
+ ctx->pipe_fds[1] = -1;
+ }
+}
+
+
+void xbee_test_free(t_xbee_test *x)
+{
+ xbee_t *xbee = x->xbee;
+ xbee_io_context_t *ctx;
+
+
+ xbee_test_close(x);
+ if (xbee) {
+ ctx = xbee_user_context(*xbee);
+ if (ctx) {
+ sysmem_freeptr(ctx);
+ }
+
+ sysmem_freeptr(xbee);
+ }
+}
+
+
+void xbee_test_dest64(t_xbee_test *x, t_symbol *s)
+{
+ uint64_t dest;
+ int i;
+
+
+ dest = strtoll(s->s_name, NULL, 16);
+ for (i = 0; i < 8; i++) {
+ x->dest_addr64[7 - i] = dest & 0xff;
+ dest >>= 8;
+ }
+
+ post("xbee_test: set dest64 addr to %02x%02x%02x%02x%02x%02x%02x%02x",
+ x->dest_addr64[0], x->dest_addr64[1], x->dest_addr64[2], x->dest_addr64[3],
+ x->dest_addr64[4], x->dest_addr64[5], x->dest_addr64[6], x->dest_addr64[7]);
+}
+
+
+void xbee_test_dest16(t_xbee_test *x, t_symbol *s)
+{
+ uint16_t dest;
+ int i;
+
+
+ dest = strtol(s->s_name, NULL, 16);
+ for (i = 0; i < 2; i++) {
+ x->dest_addr16[1 - i] = dest & 0xff;
+ dest >>= 8;
+ }
+
+ post("xbee_test: set dest16 addr to %02x%02x", x->dest_addr16[0], x->dest_addr16[1]);
+}
+
+
+int xbee_test_open_device(t_xbee_test *x, t_symbol *s)
+{
+ xbee_io_context_t *xbee_io_context;
+ int fd;
+
+
+ if (s == gensym("")) {
+ return 0;
+ }
+
+ xbee_test_close(x);
+
+ xbee_io_context = (xbee_io_context_t *)sysmem_newptr(sizeof(xbee_io_context_t));
+ if (xbee_io_context == NULL) {
+ error("xbee_test: %s: can't allocate memory", s->s_name);
+ return -1;
+ }
+
+ memset(xbee_io_context, 0, sizeof(xbee_io_context_t));
+
+ // Pipe for nudging io thread when there's more data to write
+ if (pipe(xbee_io_context->pipe_fds) < 0) {
+ error("xbee_test: %s: can't open pipe (%d)", s->s_name, errno);
+ sysmem_freeptr(xbee_io_context);
+ return -1;
+ }
+
+ fd = open(s->s_name, O_RDWR | O_NONBLOCK, 0);
+ if (fd < 0) {
+ error("xbee_test: %s: can't open device file (error %d)", s->s_name, errno);
+ close(xbee_io_context->pipe_fds[0]);
+ close(xbee_io_context->pipe_fds[1]);
+ sysmem_freeptr(xbee_io_context);
+ return -1;
+ }
+
+ post("xbee_test: opened %s", s->s_name);
+
+ if (set_baud(fd, x->baud) < 0) {
+ post("xbee_test: %s: error setting baud to %d", s->s_name, x->baud);
+ } else {
+ post("xbee_test: baud set to %d", x->baud);
+ }
+
+ xbee_io_context->fd = fd;
+
+ xbee_user_context(*(x->xbee)) = xbee_io_context;
+
+ if (xbee_new_io_thread(x->xbee) < 0) {
+ xbee_user_context(*(x->xbee)) = NULL;
+ close(fd);
+ close(xbee_io_context->pipe_fds[0]);
+ close(xbee_io_context->pipe_fds[1]);
+ sysmem_freeptr(xbee_io_context);
+ error("xbee_test: error starting new IO thread");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void *xbee_test_new(t_symbol *s, long argc, t_atom *argv)
+{
+ t_xbee_test *x = NULL;
+ t_symbol *a;
+
+
+ if (argc < 1) {
+ error("xbee_test: No device name specified");
+ return NULL;
+ }
+
+ x = (t_xbee_test *)object_alloc(xbee_test_class);
+
+ x->baud = 9600;
+
+ x->xbee = (xbee_t *)sysmem_newptr(sizeof(*x->xbee));
+ if (x->xbee == NULL) {
+ object_free(x);
+ return NULL;
+ }
+
+ xbee_init(x->xbee);
+
+ // Set 64-bit destination to 0x0000000000ffff (broadcast)
+ memset(x->dest_addr64, 0, sizeof(x->dest_addr64));
+ x->dest_addr64[7] = 0xff;
+ x->dest_addr64[6] = 0xff;
+
+ // Set 16-bit destination to 0xfffe (ZNet broadcast)
+ x->dest_addr16[0] = 0xff;
+ x->dest_addr16[1] = 0xfe;
+
+ if (argc >= 1) {
+ if (argc >= 2) {
+ x->baud = atom_getlong(&argv[1]);
+
+ if (x->baud == 0) {
+ error("xbee_test: second argument should be a valid baud rate.");
+ sysmem_freeptr(x->xbee);
+ object_free(x);
+ return NULL;
+ }
+ }
+
+ a = atom_getsym(&argv[0]);
+ if (a != gensym("")) {
+ if (xbee_test_open_device(x, a) < 0) {
+ sysmem_freeptr(x->xbee);
+ object_free(x);
+ return NULL;
+ }
+ }
+
+ }
+
+ return (x);
+}